123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712 |
- /*************************************************************************
- * Copyright (c) 2011 AT&T Intellectual Property
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution, and is available at
- * https://www.eclipse.org/legal/epl-v10.html
- *
- * Contributors: Details at https://graphviz.org
- *************************************************************************/
- #include "config.h"
- #include <math.h>
- #include <neatogen/neato.h>
- #include <neatogen/stress.h>
- #include <stdlib.h>
- #include <time.h>
- #include <util/alloc.h>
- #ifndef _WIN32
- #include <unistd.h>
- #endif
- static double Epsilon2;
- static Agnode_t *choose_node(graph_t *, int);
- static void make_spring(graph_t *, Agnode_t *, Agnode_t *, double);
- static void move_node(graph_t *, int, Agnode_t *);
- static double fpow32(double x)
- {
- x = sqrt(x);
- return x * x * x;
- }
- static double distvec(double *p0, double *p1, double *vec)
- {
- int k;
- double dist = 0.0;
- for (k = 0; k < Ndim; k++) {
- vec[k] = p0[k] - p1[k];
- dist += vec[k] * vec[k];
- }
- dist = sqrt(dist);
- return dist;
- }
- double **new_array(int m, int n, double ival)
- {
- int i, j;
- double **rv = gv_calloc(m, sizeof(double*));
- double *mem = gv_calloc(m * n, sizeof(double));
- for (i = 0; i < m; i++) {
- rv[i] = mem;
- mem += n;
- for (j = 0; j < n; j++)
- rv[i][j] = ival;
- }
- return rv;
- }
- void free_array(double **rv)
- {
- if (rv) {
- free(rv[0]);
- free(rv);
- }
- }
- static double ***new_3array(int m, int n, int p, double ival)
- {
- int i, j, k;
- double ***rv = gv_calloc(m + 1, sizeof(double**));
- for (i = 0; i < m; i++) {
- rv[i] = gv_calloc(n + 1, sizeof(double*));
- for (j = 0; j < n; j++) {
- rv[i][j] = gv_calloc(p, sizeof(double));
- for (k = 0; k < p; k++)
- rv[i][j][k] = ival;
- }
- rv[i][j] = NULL; /* NULL terminate so we can clean up */
- }
- rv[i] = NULL;
- return rv;
- }
- static void free_3array(double ***rv)
- {
- int i, j;
- if (rv) {
- for (i = 0; rv[i]; i++) {
- for (j = 0; rv[i][j]; j++)
- free(rv[i][j]);
- free(rv[i]);
- }
- free(rv);
- }
- }
- /* lenattr:
- * Return 1 if attribute not defined
- * Return 2 if attribute string bad
- */
- static int lenattr(edge_t* e, Agsym_t* index, double* val)
- {
- char* s;
- if (index == NULL)
- return 1;
- s = agxget(e, index);
- if (*s == '\0') return 1;
- if (sscanf(s, "%lf", val) < 1 || *val < 0 || (*val == 0 && !Nop)) {
- agwarningf("bad edge len \"%s\"", s);
- return 2;
- }
- else
- return 0;
- }
- /* degreeKind;
- * Returns degree of n ignoring loops and multiedges.
- * Returns 0, 1 or many (2)
- * For case of 1, returns other endpoint of edge.
- */
- static int degreeKind(graph_t * g, node_t * n, node_t ** op)
- {
- edge_t *ep;
- int deg = 0;
- node_t *other = NULL;
- for (ep = agfstedge(g, n); ep; ep = agnxtedge(g, ep, n)) {
- if (aghead(ep) == agtail(ep))
- continue; /* ignore loops */
- if (deg == 1) {
- if ((agtail(ep) == n && aghead(ep) == other) || /* ignore multiedge */
- (agtail(ep) == other && aghead(ep) == n))
- continue;
- return 2;
- } else { /* deg == 0 */
- if (agtail(ep) == n)
- other = aghead(ep);
- else
- other = agtail(ep);
- *op = other;
- deg++;
- }
- }
- return deg;
- }
- /* prune:
- * np is at end of a chain. If its degree is 0, remove it.
- * If its degree is 1, remove it and recurse.
- * If its degree > 1, stop.
- * The node next is the next node to be visited during iteration.
- * If it is equal to a node being deleted, set it to next one.
- * Delete from root graph, since G may be a connected component subgraph.
- * Return next.
- */
- static node_t *prune(graph_t * G, node_t * np, node_t * next)
- {
- node_t *other;
- int deg;
- while (np) {
- deg = degreeKind(G, np, &other);
- if (deg == 0) {
- if (next == np)
- next = agnxtnode(G, np);
- agdelete(G->root, np);
- np = 0;
- } else if (deg == 1) {
- if (next == np)
- next = agnxtnode(G, np);
- agdelete(G->root, np);
- np = other;
- } else
- np = 0;
- }
- return next;
- }
- static double setEdgeLen(graph_t * G, node_t * np, Agsym_t* lenx, double dfltlen)
- {
- edge_t *ep;
- double total_len = 0.0;
- double len;
- int err;
- for (ep = agfstout(G, np); ep; ep = agnxtout(G, ep)) {
- if ((err = lenattr(ep, lenx, &len))) {
- if (err == 2) agerr(AGPREV, " in %s - setting to %.02f\n", agnameof(G), dfltlen);
- len = dfltlen;
- }
- ED_dist(ep) = len;
- total_len += len;
- }
- return total_len;
- }
- /* scan_graph_mode:
- * Prepare the graph and data structures depending on the layout mode.
- * If Reduce is true, eliminate singletons and trees. Since G may be a
- * subgraph, we remove the nodes from the root graph.
- * Return the number of nodes in the reduced graph.
- */
- int scan_graph_mode(graph_t * G, int mode)
- {
- int i, nV, nE, deg;
- char *str;
- node_t *np, *xp, *other;
- double total_len = 0.0;
- double dfltlen = 1.0;
- Agsym_t* lenx;
- if (Verbose)
- fprintf(stderr, "Scanning graph %s, %d nodes\n", agnameof(G),
- agnnodes(G));
- /* Eliminate singletons and trees */
- if (Reduce) {
- for (np = agfstnode(G); np; np = xp) {
- xp = agnxtnode(G, np);
- deg = degreeKind(G, np, &other);
- if (deg == 0) { /* singleton node */
- agdelete(G->root, np);
- } else if (deg == 1) {
- agdelete(G->root, np);
- xp = prune(G, other, xp);
- }
- }
- }
- nV = agnnodes(G);
- nE = agnedges(G);
- lenx = agattr(G, AGEDGE, "len", 0);
- if (mode == MODE_KK) {
- Epsilon = .0001 * nV;
- getdouble(G, "epsilon", &Epsilon);
- if ((str = agget(G->root, "Damping")))
- Damping = atof(str);
- else
- Damping = .99;
- GD_neato_nlist(G) = gv_calloc(nV + 1, sizeof(node_t*));
- for (i = 0, np = agfstnode(G); np; np = agnxtnode(G, np)) {
- GD_neato_nlist(G)[i] = np;
- ND_id(np) = i++;
- ND_heapindex(np) = -1;
- total_len += setEdgeLen(G, np, lenx, dfltlen);
- }
- } else if (mode == MODE_SGD) {
- Epsilon = .01;
- getdouble(G, "epsilon", &Epsilon);
- GD_neato_nlist(G) = gv_calloc(nV + 1, sizeof(node_t*)); // not sure why but sometimes needs the + 1
- for (i = 0, np = agfstnode(G); np; np = agnxtnode(G, np)) {
- GD_neato_nlist(G)[i] = np;
- ND_id(np) = i++;
- total_len += setEdgeLen(G, np, lenx, dfltlen);
- }
- } else {
- Epsilon = DFLT_TOLERANCE;
- getdouble(G, "epsilon", &Epsilon);
- for (i = 0, np = agfstnode(G); np; np = agnxtnode(G, np)) {
- ND_id(np) = i++;
- total_len += setEdgeLen(G, np, lenx, dfltlen);
- }
- }
- str = agget(G, "defaultdist");
- if (str && str[0])
- Initial_dist = fmax(Epsilon, atof(str));
- else
- Initial_dist = total_len / (nE > 0 ? nE : 1) * sqrt(nV) + 1;
- if (!Nop && mode == MODE_KK) {
- GD_dist(G) = new_array(nV, nV, Initial_dist);
- GD_spring(G) = new_array(nV, nV, 1.0);
- GD_sum_t(G) = new_array(nV, Ndim, 1.0);
- GD_t(G) = new_3array(nV, nV, Ndim, 0.0);
- }
- return nV;
- }
- int scan_graph(graph_t * g)
- {
- return scan_graph_mode(g, MODE_KK);
- }
- void free_scan_graph(graph_t * g)
- {
- free(GD_neato_nlist(g));
- if (!Nop) {
- free_array(GD_dist(g));
- free_array(GD_spring(g));
- free_array(GD_sum_t(g));
- free_3array(GD_t(g));
- GD_t(g) = NULL;
- }
- }
- void jitter_d(node_t * np, int nG, int n)
- {
- int k;
- for (k = n; k < Ndim; k++)
- ND_pos(np)[k] = nG * drand48();
- }
- void jitter3d(node_t * np, int nG)
- {
- jitter_d(np, nG, 2);
- }
- void randompos(node_t * np, int nG)
- {
- ND_pos(np)[0] = nG * drand48();
- ND_pos(np)[1] = nG * drand48();
- if (Ndim > 2)
- jitter3d(np, nG);
- }
- void initial_positions(graph_t * G, int nG)
- {
- int init, i;
- node_t *np;
- static int once = 0;
- if (Verbose)
- fprintf(stderr, "Setting initial positions\n");
- init = checkStart(G, nG, INIT_RANDOM);
- if (init == INIT_REGULAR)
- return;
- if (init == INIT_SELF && once == 0) {
- agwarningf("start=0 not supported with mode=self - ignored\n");
- once = 1;
- }
- for (i = 0; (np = GD_neato_nlist(G)[i]); i++) {
- if (hasPos(np))
- continue;
- randompos(np, 1);
- }
- }
- void diffeq_model(graph_t * G, int nG)
- {
- int i, j, k;
- double dist, **D, **K, del[MAXDIM], f;
- node_t *vi, *vj;
- edge_t *e;
- if (Verbose) {
- fprintf(stderr, "Setting up spring model: ");
- start_timer();
- }
- /* init springs */
- K = GD_spring(G);
- D = GD_dist(G);
- for (i = 0; i < nG; i++) {
- for (j = 0; j < i; j++) {
- f = Spring_coeff / (D[i][j] * D[i][j]);
- if ((e = agfindedge(G, GD_neato_nlist(G)[i], GD_neato_nlist(G)[j])))
- f = f * ED_factor(e);
- K[i][j] = K[j][i] = f;
- }
- }
- /* init differential equation solver */
- for (i = 0; i < nG; i++)
- for (k = 0; k < Ndim; k++)
- GD_sum_t(G)[i][k] = 0.0;
- for (i = 0; (vi = GD_neato_nlist(G)[i]); i++) {
- for (j = 0; j < nG; j++) {
- if (i == j)
- continue;
- vj = GD_neato_nlist(G)[j];
- dist = distvec(ND_pos(vi), ND_pos(vj), del);
- for (k = 0; k < Ndim; k++) {
- GD_t(G)[i][j][k] =
- GD_spring(G)[i][j] * (del[k] -
- GD_dist(G)[i][j] * del[k] /
- dist);
- GD_sum_t(G)[i][k] += GD_t(G)[i][j][k];
- }
- }
- }
- if (Verbose) {
- fprintf(stderr, "%.2f sec\n", elapsed_sec());
- }
- }
- /* total_e:
- * Return 2*energy of system.
- */
- static double total_e(graph_t * G, int nG)
- {
- int i, j, d;
- double e = 0.0; /* 2*energy */
- double t0; /* distance squared */
- double t1;
- node_t *ip, *jp;
- for (i = 0; i < nG - 1; i++) {
- ip = GD_neato_nlist(G)[i];
- for (j = i + 1; j < nG; j++) {
- jp = GD_neato_nlist(G)[j];
- for (t0 = 0.0, d = 0; d < Ndim; d++) {
- t1 = ND_pos(ip)[d] - ND_pos(jp)[d];
- t0 += t1 * t1;
- }
- e = e + GD_spring(G)[i][j] *
- (t0 + GD_dist(G)[i][j] * GD_dist(G)[i][j]
- - 2.0 * GD_dist(G)[i][j] * sqrt(t0));
- }
- }
- return e;
- }
- void solve_model(graph_t * G, int nG)
- {
- node_t *np;
- Epsilon2 = Epsilon * Epsilon;
- while ((np = choose_node(G, nG))) {
- move_node(G, nG, np);
- }
- if (Verbose) {
- fprintf(stderr, "\nfinal e = %f", total_e(G, nG));
- fprintf(stderr, " %d%s iterations %.2f sec\n",
- GD_move(G), GD_move(G) == MaxIter ? "!" : "",
- elapsed_sec());
- }
- if (GD_move(G) == MaxIter)
- agwarningf("Max. iterations (%d) reached on graph %s\n",
- MaxIter, agnameof(G));
- }
- static void update_arrays(graph_t * G, int nG, int i)
- {
- int j, k;
- double del[MAXDIM], dist, old;
- node_t *vi, *vj;
- vi = GD_neato_nlist(G)[i];
- for (k = 0; k < Ndim; k++)
- GD_sum_t(G)[i][k] = 0.0;
- for (j = 0; j < nG; j++) {
- if (i == j)
- continue;
- vj = GD_neato_nlist(G)[j];
- dist = distvec(ND_pos(vi), ND_pos(vj), del);
- for (k = 0; k < Ndim; k++) {
- old = GD_t(G)[i][j][k];
- GD_t(G)[i][j][k] =
- GD_spring(G)[i][j] * (del[k] -
- GD_dist(G)[i][j] * del[k] / dist);
- GD_sum_t(G)[i][k] += GD_t(G)[i][j][k];
- old = GD_t(G)[j][i][k];
- GD_t(G)[j][i][k] = -GD_t(G)[i][j][k];
- GD_sum_t(G)[j][k] += GD_t(G)[j][i][k] - old;
- }
- }
- }
- #define Msub(i,j) M[(i)*Ndim+(j)]
- static void D2E(graph_t * G, int nG, int n, double *M)
- {
- int i, l, k;
- node_t *vi, *vn;
- double scale, sq, t[MAXDIM];
- double **K = GD_spring(G);
- double **D = GD_dist(G);
- vn = GD_neato_nlist(G)[n];
- for (l = 0; l < Ndim; l++)
- for (k = 0; k < Ndim; k++)
- Msub(l, k) = 0.0;
- for (i = 0; i < nG; i++) {
- if (n == i)
- continue;
- vi = GD_neato_nlist(G)[i];
- sq = 0.0;
- for (k = 0; k < Ndim; k++) {
- t[k] = ND_pos(vn)[k] - ND_pos(vi)[k];
- sq += (t[k] * t[k]);
- }
- scale = 1 / fpow32(sq);
- for (k = 0; k < Ndim; k++) {
- for (l = 0; l < k; l++)
- Msub(l, k) += K[n][i] * D[n][i] * t[k] * t[l] * scale;
- Msub(k, k) +=
- K[n][i] * (1.0 - D[n][i] * (sq - t[k] * t[k]) * scale);
- }
- }
- for (k = 1; k < Ndim; k++)
- for (l = 0; l < k; l++)
- Msub(k, l) = Msub(l, k);
- }
- node_t *choose_node(graph_t * G, int nG)
- {
- int i, k;
- double m, max;
- node_t *choice, *np;
- static int cnt = 0;
- cnt++;
- if (GD_move(G) >= MaxIter)
- return NULL;
- max = 0.0;
- choice = NULL;
- for (i = 0; i < nG; i++) {
- np = GD_neato_nlist(G)[i];
- if (ND_pinned(np) > P_SET)
- continue;
- for (m = 0.0, k = 0; k < Ndim; k++)
- m += GD_sum_t(G)[i][k] * GD_sum_t(G)[i][k];
- /* could set the color=energy of the node here */
- if (m > max) {
- choice = np;
- max = m;
- }
- }
- if (max < Epsilon2)
- choice = NULL;
- else {
- if (Verbose && cnt % 100 == 0) {
- fprintf(stderr, "%.3f ", sqrt(max));
- if (cnt % 1000 == 0)
- fprintf(stderr, "\n");
- }
- }
- return choice;
- }
- void move_node(graph_t * G, int nG, node_t * n)
- {
- int i, m;
- double b[MAXDIM] = {0};
- double c[MAXDIM] = {0};
- m = ND_id(n);
- double *a = gv_calloc((size_t)Ndim * Ndim, sizeof(double));
- D2E(G, nG, m, a);
- for (i = 0; i < Ndim; i++)
- c[i] = -GD_sum_t(G)[m][i];
- solve(a, b, c, Ndim);
- for (i = 0; i < Ndim; i++) {
- b[i] = (Damping + 2 * (1 - Damping) * drand48()) * b[i];
- ND_pos(n)[i] += b[i];
- }
- GD_move(G)++;
- update_arrays(G, nG, m);
- if (test_toggle()) {
- double sum = 0;
- for (i = 0; i < Ndim; i++) {
- sum += fabs(b[i]);
- } /* Why not squared? */
- sum = sqrt(sum);
- fprintf(stderr, "%s %.3f\n", agnameof(n), sum);
- }
- free(a);
- }
- static node_t **Heap;
- static int Heapsize;
- static node_t *Src;
- static void heapup(node_t * v)
- {
- int i, par;
- node_t *u;
- for (i = ND_heapindex(v); i > 0; i = par) {
- par = (i - 1) / 2;
- u = Heap[par];
- if (ND_dist(u) <= ND_dist(v))
- break;
- Heap[par] = v;
- ND_heapindex(v) = par;
- Heap[i] = u;
- ND_heapindex(u) = i;
- }
- }
- static void heapdown(node_t * v)
- {
- int i, left, right, c;
- node_t *u;
- i = ND_heapindex(v);
- while ((left = 2 * i + 1) < Heapsize) {
- right = left + 1;
- if ((right < Heapsize)
- && (ND_dist(Heap[right]) < ND_dist(Heap[left])))
- c = right;
- else
- c = left;
- u = Heap[c];
- if (ND_dist(v) <= ND_dist(u))
- break;
- Heap[c] = v;
- ND_heapindex(v) = c;
- Heap[i] = u;
- ND_heapindex(u) = i;
- i = c;
- }
- }
- void neato_enqueue(node_t * v)
- {
- int i;
- assert(ND_heapindex(v) < 0);
- i = Heapsize++;
- ND_heapindex(v) = i;
- Heap[i] = v;
- if (i > 0)
- heapup(v);
- }
- node_t *neato_dequeue(void)
- {
- int i;
- node_t *rv, *v;
- if (Heapsize == 0)
- return NULL;
- rv = Heap[0];
- i = --Heapsize;
- v = Heap[i];
- Heap[0] = v;
- ND_heapindex(v) = 0;
- if (i > 1)
- heapdown(v);
- ND_heapindex(rv) = -1;
- return rv;
- }
- void shortest_path(graph_t * G, int nG)
- {
- node_t *v;
- Heap = gv_calloc(nG + 1, sizeof(node_t*));
- if (Verbose) {
- fprintf(stderr, "Calculating shortest paths: ");
- start_timer();
- }
- for (v = agfstnode(G); v; v = agnxtnode(G, v))
- s1(G, v);
- if (Verbose) {
- fprintf(stderr, "%.2f sec\n", elapsed_sec());
- }
- free(Heap);
- }
- void s1(graph_t * G, node_t * node)
- {
- node_t *v, *u;
- edge_t *e;
- int t;
- double f;
- for (t = 0; (v = GD_neato_nlist(G)[t]); t++)
- ND_dist(v) = Initial_dist;
- Src = node;
- ND_dist(Src) = 0;
- ND_hops(Src) = 0;
- neato_enqueue(Src);
- while ((v = neato_dequeue())) {
- if (v != Src)
- make_spring(G, Src, v, ND_dist(v));
- for (e = agfstedge(G, v); e; e = agnxtedge(G, e, v)) {
- if ((u = agtail(e)) == v)
- u = aghead(e);
- f = ND_dist(v) + ED_dist(e);
- if (ND_dist(u) > f) {
- ND_dist(u) = f;
- if (ND_heapindex(u) >= 0)
- heapup(u);
- else {
- ND_hops(u) = ND_hops(v) + 1;
- neato_enqueue(u);
- }
- }
- }
- }
- }
- void make_spring(graph_t * G, node_t * u, node_t * v, double f)
- {
- int i, j;
- i = ND_id(u);
- j = ND_id(v);
- GD_dist(G)[i][j] = GD_dist(G)[j][i] = f;
- }
|