char *p = agget(g, "model");
char c;
- if (!p || (!(c = *p)))
+ if (!p || (!(c = *p))) /* if p is NULL or "" */
return MODEL_SHORTPATH;
if ((c == 'c') && streq(p, "circuit"))
return MODEL_CIRCUIT;
else if (streq(p, "shortpath"))
return MODEL_SHORTPATH;
}
+ if ((c == 'm') && streq(p, "mds")) {
+#ifndef WITH_CGRAPH
+ if (agindex(g->root->proto->e, "len") >= 0)
+#else /* WITH_CGRAPH */
+ if (agattr(g, AGEDGE, "len", 0))
+#endif /* WITH_CGRAPH */
+ return MODEL_MDS;
+ else {
+ agerr(AGWARN,
+ "edges in graph %s have no len attribute. Hence, the mds model\n", agnameof(g));
+ agerr(AGPREV, "is inappropriate. Reverting to the shortest path model.\n");
+ return MODEL_SHORTPATH;
+ }
+ }
agerr(AGWARN,
"Unknown value %s for attribute \"model\" in graph %s - ignored\n",
p, agnameof(g));
/* majorization:
* Solve stress using majorization.
* Old neato attributes to incorporate:
- * weight?
- * model will be MODE_MAJOR, MODE_HIER or MODE_IPSEP
+ * weight
+ * mode will be MODE_MAJOR, MODE_HIER or MODE_IPSEP
*/
static void
majorization(graph_t *mg, graph_t * g, int nv, int mode, int model, int dim, int steps)
freeGraphData(gp);
}
+/* mds_model:
+ * Assume the matrix already contains shortest path values.
+ * Use the actual lengths provided the input for edges.
+ */
+static void mds_model(graph_t * g, int nG)
+{
+ long i, j;
+ node_t *v;
+ edge_t *e;
+
+ for (v = agfstnode(g); v; v = agnxtnode(g, v)) {
+ for (e = agfstout(g, v); e; e = agnxtout(g, e)) {
+ i = AGID(agtail(e));
+ j = AGID(aghead(e));
+ if (i == j)
+ continue;
+ GD_dist(g)[i][j] = GD_dist(g)[j][i] = GD_dist(e);
+ }
+ }
+}
+
/* kkNeato:
* Solve using gradient descent a la Kamada-Kawai.
*/
agerr(AGPREV, "the graph into connected components.\n");
shortest_path(g, nG);
}
+ } else if (model == MODEL_MDS) {
+ shortest_path(g, nG);
+ mds_model(g, nG);
} else
shortest_path(g, nG);
initial_positions(g, nG);
return Dij;
}
+
+/* mdsModel:
+ * Update matrix with actual edge lengths
+ */
+float*
+mdsModel(vtx_data * graph, int nG)
+{
+ int i, j, e;
+ float *Dij;
+ int shift = 0;
+ double delta;
+
+ if (graph->ewgts == NULL) return 0;
+
+ /* first, compute shortest paths to fill in non-edges */
+ Dij = compute_weighted_apsp_packed(graph, nG);
+
+ /* then, replace edge entries will user-supplied len */
+ for (i = 0; i < nG; i++) {
+ shift += i;
+ for (e = 1; e < graph[i].nedges; e++) {
+ j = graph[i].edges[e];
+ if (j < i) continue;
+ delta += abs(Dij[i*nG + j - shift] - graph[i].ewgts[e]);
+ Dij[i*nG + j - shift] = graph[i].ewgts[e];
+ }
+ }
+ if (Verbose) {
+ fprintf (stderr, "mdsModel: delta = %f\n", delta);
+ }
+ return Dij;
+}
+
/* compute_apsp_packed:
* Assumes integral weights > 0.
*/
agerr(AGPREV,
"is undefined. Reverting to the shortest path model.\n");
}
+ } else if (model == MODEL_MDS) {
+ if (Verbose)
+ fprintf(stderr, "Calculating MDS model");
+ Dij = mdsModel(graph, n);
}
if (!Dij) {
if (Verbose)