/** * @file * @brief Matrix Market-DOT converter */ /************************************************************************* * 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 #define STANDALONE #include #include #include #include #include #include #include #include "mmio.h" #include #include #include #include "matrix_market.h" #include typedef struct { Agrec_t h; int id; } Agnodeinfo_t; #define ND_id(n) (((Agnodeinfo_t*)(n->base.data))->id) static char *cmd; static double Hue2RGB(double v1, double v2, double H) { if (H < 0.0) H += 1.0; if (H > 1.0) H -= 1.0; if (6.0 * H < 1.0) return (v1 + (v2 - v1) * 6.0 * H); if (2.0 * H < 1.0) return v2; if (3.0 * H < 2.0) return v1 + (v2 - v1) * (2.0 / 3.0 - H) * 6.0; return v1; } static char *hue2rgb(double hue, agxbuf *xb) { double v1, v2, lightness = .5, saturation = 1; int red, blue, green; v2 = lightness + saturation - saturation * lightness; v1 = 2.0 * lightness - v2; red = (int) (255.0 * Hue2RGB(v1, v2, hue + 1.0 / 3.0) + 0.5); green = (int) (255.0 * Hue2RGB(v1, v2, hue) + 0.5); blue = (int) (255.0 * Hue2RGB(v1, v2, hue - 1.0 / 3.0) + 0.5); agxbprint(xb, "#%02x%02x%02x", red, green, blue); return agxbuse(xb); } static Agraph_t *makeDotGraph(SparseMatrix A, char *name, int dim, double * x, int with_color, int with_label, int with_val) { Agraph_t *g; Agnode_t *n; Agnode_t *h; Agedge_t *e; int i, j; Agsym_t *sym = NULL, *sym2 = NULL, *sym3 = NULL; int *ia = A->ia; int *ja = A->ja; double *val = A->a; Agnode_t **arr = gv_calloc(A->m, sizeof(Agnode_t*)); double *color = NULL; name = strip_dir(name); if (with_color) { if (A->type == MATRIX_TYPE_REAL && !val) { fprintf (stderr, "Warning: input matrix has no values, -c flag ignored\n"); with_color = 0; } else if (A->type != MATRIX_TYPE_REAL && !x) { fprintf (stderr, "Warning: input has no coordinates, -c flag ignored\n"); with_color = 0; } } if (A->is_undirected) { g = agopen("G", Agundirected, NULL); } else { g = agopen("G", Agdirected, NULL); } if (with_val) { sym = agattr(g, AGEDGE, "len", "1"); } agxbuf xb = {0}; if (with_label) { agxbprint (&xb, "%s. %d nodes, %d edges.", name, A->m, A->nz); agattr(g, AGRAPH, "label", agxbuse (&xb)); } for (i = 0; i < A->m; i++) { agxbprint(&xb, "%d", i); n = agnode(g, agxbuse(&xb), 1); agbindrec(n, "nodeinfo", sizeof(Agnodeinfo_t), true); ND_id(n) = i; arr[i] = n; } if (with_color) { double maxdist = 0.; double mindist = 0.; bool first = true; sym2 = agattr(g, AGEDGE, "color", ""); sym3 = agattr(g, AGEDGE, "wt", ""); agattr(g, AGRAPH, "bgcolor", "black"); color = gv_calloc(A->nz, sizeof(double)); for (n = agfstnode(g); n; n = agnxtnode(g, n)) { i = ND_id(n); if (A->type != MATRIX_TYPE_REAL) { for (j = ia[i]; j < ia[i + 1]; j++) { color[j] = distance(x, dim, i, ja[j]); if (i != ja[j]) { if (first) { mindist = color[j]; first = false; } else { mindist = fmin(mindist, color[j]); } } maxdist = fmax(color[j], maxdist); } } else { for (j = ia[i]; j < ia[i + 1]; j++) { if (val) color[j] = fabs(val[j]); else color[j] = 1; if (i != ja[j]) { if (first) { mindist = color[j]; first = false; } else { mindist = fmin(mindist, color[j]); } } maxdist = fmax(color[j], maxdist); } } } for (n = agfstnode(g); n; n = agnxtnode(g, n)) { i = ND_id(n); for (j = ia[i]; j < ia[i + 1]; j++) { color[j] = (color[j] - mindist) / fmax(maxdist - mindist, 0.000001); } } } for (n = agfstnode(g); n; n = agnxtnode(g, n)) { i = ND_id(n); for (j = ia[i]; j < ia[i + 1]; j++) { h = arr[ja[j]]; e = agedge(g, n, h, NULL, 1); if (sym && val) { agxbprint(&xb, "%f", val[j]); agxset(e, sym, agxbuse(&xb)); } if (with_color) { agxset(e, sym2, hue2rgb(.65 * color[j], &xb)); agxbprint(&xb, "%f", color[j]); agxset(e, sym3, agxbuse(&xb)); } } } agxbfree (&xb); free(color); free(arr); return g; } static char* useString = "Usage: %s [-uvcl] [-o file] matrix_market_filename\n\ -u - make graph undirected\n\ -U i - treat non-square matrix as a bipartite graph\n\ i = 0 never\n\ i = 1 if pattern unsymmetric (default)\n\ i = 2 if matrix unsymmetric\n\ i = 3 always\n\ -v - assign len to edges\n\ -c - assign color and wt to edges\n\ -l - add label\n\ -o - output file \n"; static void usage(int eval) { fprintf(stderr, useString, cmd); graphviz_exit(eval); } static FILE *openF(char *fname, char *mode) { FILE *f = fopen(fname, mode); if (!f) { fprintf(stderr, "Could not open %s for %s\n", fname, *mode == 'r' ? "reading" : "writing"); graphviz_exit(1); } return f; } typedef struct { FILE *inf; FILE *outf; char *infile; int undirected; int with_label; int with_color; int with_val; int bipartite; } parms_t; static void init(int argc, char **argv, parms_t * p) { int c; cmd = argv[0]; opterr = 0; while ((c = getopt(argc, argv, ":o:uvclU:?")) != -1) { switch (c) { case 'o': p->outf = openF(optarg, "w"); break; case 'l': p->with_label = 1; break; case 'u': p->undirected = 1; break; case 'v': p->with_val = 1; break; case 'c': p->with_color = 1; break; case 'U':{ int u; if (sscanf(optarg,"%d",&u) <= 0 || u < 0 || u > BIPARTITE_ALWAYS) { usage(1); } else { p->bipartite = u; } break; } case ':': fprintf(stderr, "%s: option -%c missing argument - ignored\n", cmd, optopt); break; case '?': if (optopt == '\0' || optopt == '?') usage(0); else { fprintf(stderr, "%s: option -%c unrecognized\n", cmd, optopt); usage(1); } break; default: UNREACHABLE(); } } argv += optind; argc -= optind; if (argc > 0) { p->infile = argv[0]; p->inf = openF(argv[0], "r"); } } int main(int argc, char *argv[]) { Agraph_t *g = 0; SparseMatrix A = NULL; int dim=0; parms_t pv; /* ======================= set parameters ==================== */ pv.inf = stdin; pv.outf = stdout; pv.infile = "stdin"; pv.undirected = 0; pv.with_label = 0; pv.with_color = 0; pv.with_val = 0; pv.bipartite = BIPARTITE_PATTERN_UNSYM; init(argc, argv, &pv); /* ======================= read graph ==================== */ A = SparseMatrix_import_matrix_market(pv.inf); if (!A) { fprintf (stderr, "Unable to read input file \"%s\"\n", pv.infile); usage(1); } A = SparseMatrix_to_square_matrix(A, pv.bipartite); if (!A) { fprintf(stderr, "cannot import from file %s\n", pv.infile); graphviz_exit(1); } if (pv.undirected) { SparseMatrix B; B = SparseMatrix_make_undirected(A); SparseMatrix_delete(A); A = B; } g = makeDotGraph(A, pv.infile, dim, NULL, pv.with_color, pv.with_label, pv.with_val); agwrite(g, pv.outf); graphviz_exit(0); }