mm2gv.c 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338
  1. /**
  2. * @file
  3. * @brief <a href=https://math.nist.gov/MatrixMarket/>Matrix Market</a>-DOT converter
  4. */
  5. /*************************************************************************
  6. * Copyright (c) 2011 AT&T Intellectual Property
  7. * All rights reserved. This program and the accompanying materials
  8. * are made available under the terms of the Eclipse Public License v1.0
  9. * which accompanies this distribution, and is available at
  10. * https://www.eclipse.org/legal/epl-v10.html
  11. *
  12. * Contributors: Details at https://graphviz.org
  13. *************************************************************************/
  14. #include "config.h"
  15. #include <util/alloc.h>
  16. #define STANDALONE
  17. #include <cgraph/cgraph.h>
  18. #include <stdbool.h>
  19. #include <stdio.h>
  20. #include <stdlib.h>
  21. #include <string.h>
  22. #include <math.h>
  23. #include <assert.h>
  24. #include "mmio.h"
  25. #include <sparse/SparseMatrix.h>
  26. #include <util/agxbuf.h>
  27. #include <util/unreachable.h>
  28. #include "matrix_market.h"
  29. #include <getopt.h>
  30. typedef struct {
  31. Agrec_t h;
  32. int id;
  33. } Agnodeinfo_t;
  34. #define ND_id(n) (((Agnodeinfo_t*)(n->base.data))->id)
  35. static char *cmd;
  36. static double Hue2RGB(double v1, double v2, double H)
  37. {
  38. if (H < 0.0)
  39. H += 1.0;
  40. if (H > 1.0)
  41. H -= 1.0;
  42. if (6.0 * H < 1.0)
  43. return (v1 + (v2 - v1) * 6.0 * H);
  44. if (2.0 * H < 1.0)
  45. return v2;
  46. if (3.0 * H < 2.0)
  47. return v1 + (v2 - v1) * (2.0 / 3.0 - H) * 6.0;
  48. return v1;
  49. }
  50. static char *hue2rgb(double hue, agxbuf *xb) {
  51. double v1, v2, lightness = .5, saturation = 1;
  52. int red, blue, green;
  53. v2 = lightness + saturation - saturation * lightness;
  54. v1 = 2.0 * lightness - v2;
  55. red = (int) (255.0 * Hue2RGB(v1, v2, hue + 1.0 / 3.0) + 0.5);
  56. green = (int) (255.0 * Hue2RGB(v1, v2, hue) + 0.5);
  57. blue = (int) (255.0 * Hue2RGB(v1, v2, hue - 1.0 / 3.0) + 0.5);
  58. agxbprint(xb, "#%02x%02x%02x", red, green, blue);
  59. return agxbuse(xb);
  60. }
  61. static Agraph_t *makeDotGraph(SparseMatrix A, char *name, int dim,
  62. double * x, int with_color, int with_label, int with_val)
  63. {
  64. Agraph_t *g;
  65. Agnode_t *n;
  66. Agnode_t *h;
  67. Agedge_t *e;
  68. int i, j;
  69. Agsym_t *sym = NULL, *sym2 = NULL, *sym3 = NULL;
  70. int *ia = A->ia;
  71. int *ja = A->ja;
  72. double *val = A->a;
  73. Agnode_t **arr = gv_calloc(A->m, sizeof(Agnode_t*));
  74. double *color = NULL;
  75. name = strip_dir(name);
  76. if (with_color) {
  77. if (A->type == MATRIX_TYPE_REAL && !val) {
  78. fprintf (stderr, "Warning: input matrix has no values, -c flag ignored\n");
  79. with_color = 0;
  80. }
  81. else if (A->type != MATRIX_TYPE_REAL && !x) {
  82. fprintf (stderr, "Warning: input has no coordinates, -c flag ignored\n");
  83. with_color = 0;
  84. }
  85. }
  86. if (A->is_undirected) {
  87. g = agopen("G", Agundirected, NULL);
  88. } else {
  89. g = agopen("G", Agdirected, NULL);
  90. }
  91. if (with_val) {
  92. sym = agattr(g, AGEDGE, "len", "1");
  93. }
  94. agxbuf xb = {0};
  95. if (with_label) {
  96. agxbprint (&xb, "%s. %d nodes, %d edges.", name, A->m, A->nz);
  97. agattr(g, AGRAPH, "label", agxbuse (&xb));
  98. }
  99. for (i = 0; i < A->m; i++) {
  100. agxbprint(&xb, "%d", i);
  101. n = agnode(g, agxbuse(&xb), 1);
  102. agbindrec(n, "nodeinfo", sizeof(Agnodeinfo_t), true);
  103. ND_id(n) = i;
  104. arr[i] = n;
  105. }
  106. if (with_color) {
  107. double maxdist = 0.;
  108. double mindist = 0.;
  109. bool first = true;
  110. sym2 = agattr(g, AGEDGE, "color", "");
  111. sym3 = agattr(g, AGEDGE, "wt", "");
  112. agattr(g, AGRAPH, "bgcolor", "black");
  113. color = gv_calloc(A->nz, sizeof(double));
  114. for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
  115. i = ND_id(n);
  116. if (A->type != MATRIX_TYPE_REAL) {
  117. for (j = ia[i]; j < ia[i + 1]; j++) {
  118. color[j] = distance(x, dim, i, ja[j]);
  119. if (i != ja[j]) {
  120. if (first) {
  121. mindist = color[j];
  122. first = false;
  123. } else {
  124. mindist = fmin(mindist, color[j]);
  125. }
  126. }
  127. maxdist = fmax(color[j], maxdist);
  128. }
  129. } else {
  130. for (j = ia[i]; j < ia[i + 1]; j++) {
  131. if (val) color[j] = fabs(val[j]);
  132. else color[j] = 1;
  133. if (i != ja[j]) {
  134. if (first) {
  135. mindist = color[j];
  136. first = false;
  137. } else {
  138. mindist = fmin(mindist, color[j]);
  139. }
  140. }
  141. maxdist = fmax(color[j], maxdist);
  142. }
  143. }
  144. }
  145. for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
  146. i = ND_id(n);
  147. for (j = ia[i]; j < ia[i + 1]; j++) {
  148. color[j] = (color[j] - mindist) / fmax(maxdist - mindist, 0.000001);
  149. }
  150. }
  151. }
  152. for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
  153. i = ND_id(n);
  154. for (j = ia[i]; j < ia[i + 1]; j++) {
  155. h = arr[ja[j]];
  156. e = agedge(g, n, h, NULL, 1);
  157. if (sym && val) {
  158. agxbprint(&xb, "%f", val[j]);
  159. agxset(e, sym, agxbuse(&xb));
  160. }
  161. if (with_color) {
  162. agxset(e, sym2, hue2rgb(.65 * color[j], &xb));
  163. agxbprint(&xb, "%f", color[j]);
  164. agxset(e, sym3, agxbuse(&xb));
  165. }
  166. }
  167. }
  168. agxbfree (&xb);
  169. free(color);
  170. free(arr);
  171. return g;
  172. }
  173. static char* useString = "Usage: %s [-uvcl] [-o file] matrix_market_filename\n\
  174. -u - make graph undirected\n\
  175. -U i - treat non-square matrix as a bipartite graph\n\
  176. i = 0 never\n\
  177. i = 1 if pattern unsymmetric (default)\n\
  178. i = 2 if matrix unsymmetric\n\
  179. i = 3 always\n\
  180. -v - assign len to edges\n\
  181. -c - assign color and wt to edges\n\
  182. -l - add label\n\
  183. -o <file> - output file \n";
  184. static void usage(int eval)
  185. {
  186. fprintf(stderr, useString, cmd);
  187. graphviz_exit(eval);
  188. }
  189. static FILE *openF(char *fname, char *mode)
  190. {
  191. FILE *f = fopen(fname, mode);
  192. if (!f) {
  193. fprintf(stderr, "Could not open %s for %s\n", fname,
  194. *mode == 'r' ? "reading" : "writing");
  195. graphviz_exit(1);
  196. }
  197. return f;
  198. }
  199. typedef struct {
  200. FILE *inf;
  201. FILE *outf;
  202. char *infile;
  203. int undirected;
  204. int with_label;
  205. int with_color;
  206. int with_val;
  207. int bipartite;
  208. } parms_t;
  209. static void init(int argc, char **argv, parms_t * p)
  210. {
  211. int c;
  212. cmd = argv[0];
  213. opterr = 0;
  214. while ((c = getopt(argc, argv, ":o:uvclU:?")) != -1) {
  215. switch (c) {
  216. case 'o':
  217. p->outf = openF(optarg, "w");
  218. break;
  219. case 'l':
  220. p->with_label = 1;
  221. break;
  222. case 'u':
  223. p->undirected = 1;
  224. break;
  225. case 'v':
  226. p->with_val = 1;
  227. break;
  228. case 'c':
  229. p->with_color = 1;
  230. break;
  231. case 'U':{
  232. int u;
  233. if (sscanf(optarg,"%d",&u) <= 0 || u < 0 || u > BIPARTITE_ALWAYS) {
  234. usage(1);
  235. } else {
  236. p->bipartite = u;
  237. }
  238. break;
  239. }
  240. case ':':
  241. fprintf(stderr, "%s: option -%c missing argument - ignored\n", cmd, optopt);
  242. break;
  243. case '?':
  244. if (optopt == '\0' || optopt == '?')
  245. usage(0);
  246. else {
  247. fprintf(stderr,
  248. "%s: option -%c unrecognized\n", cmd,
  249. optopt);
  250. usage(1);
  251. }
  252. break;
  253. default:
  254. UNREACHABLE();
  255. }
  256. }
  257. argv += optind;
  258. argc -= optind;
  259. if (argc > 0) {
  260. p->infile = argv[0];
  261. p->inf = openF(argv[0], "r");
  262. }
  263. }
  264. int main(int argc, char *argv[])
  265. {
  266. Agraph_t *g = 0;
  267. SparseMatrix A = NULL;
  268. int dim=0;
  269. parms_t pv;
  270. /* ======================= set parameters ==================== */
  271. pv.inf = stdin;
  272. pv.outf = stdout;
  273. pv.infile = "stdin";
  274. pv.undirected = 0;
  275. pv.with_label = 0;
  276. pv.with_color = 0;
  277. pv.with_val = 0;
  278. pv.bipartite = BIPARTITE_PATTERN_UNSYM;
  279. init(argc, argv, &pv);
  280. /* ======================= read graph ==================== */
  281. A = SparseMatrix_import_matrix_market(pv.inf);
  282. if (!A) {
  283. fprintf (stderr, "Unable to read input file \"%s\"\n", pv.infile);
  284. usage(1);
  285. }
  286. A = SparseMatrix_to_square_matrix(A, pv.bipartite);
  287. if (!A) {
  288. fprintf(stderr, "cannot import from file %s\n", pv.infile);
  289. graphviz_exit(1);
  290. }
  291. if (pv.undirected) {
  292. SparseMatrix B;
  293. B = SparseMatrix_make_undirected(A);
  294. SparseMatrix_delete(A);
  295. A = B;
  296. }
  297. g = makeDotGraph(A, pv.infile, dim, NULL, pv.with_color, pv.with_label, pv.with_val);
  298. agwrite(g, pv.outf);
  299. graphviz_exit(0);
  300. }