Add Zickert's B2 quivers
[clav.git] / mutation-finder.c
blobfc5667ff780ad0d52e0f2e2724297777052ffe40
1 /*
2 * Copyright (c) 2017, S. Gilles <sgilles@math.umd.edu>
4 * Permission to use, copy, modify, and/or distribute this software
5 * for any purpose with or without fee is hereby granted, provided
6 * that the above copyright notice and this permission notice appear
7 * in all copies.
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS *ALL
10 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING AL*L IMPLIED
11 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EV*ENT SHALL THE
12 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
13 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
14 * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
15 * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
16 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 #include <errno.h>
19 #include <limits.h>
20 #include <locale.h>
21 #include <signal.h>
22 #include <stdint.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <unistd.h>
28 #include "macros.h"
29 #include "quiver.h"
31 /* Flag for whether to print info */
32 static volatile sig_atomic_t status_requested = 0;
34 /* Signal handler */
35 static void
36 handle(int sig);
38 /* Signal handler */
39 static void handle(int sig)
41 /* XXX: I'd like to check for SIGINFO here */
42 if (sig == SIGUSR1) {
43 status_requested = 1;
44 signal(sig, handle);
48 /* Print usage */
49 static void usage(char *argv0)
51 printf("Usage: %s -s <startfile> -e <endfile>\n", argv0);
52 printf(" [ -v ] # Be verbose\n");
53 printf(" [ -c <edgecap> ]\n");
54 printf(" [ -f <frozen> [ -f <frozen> [...] ] ]\n");
55 printf(" [ -l <startlen> ] [ -m <maxlen> ]\n");
56 printf("\n");
57 printf(" startfile: a quiver file representing the start "
58 "state\n");
59 printf(" endfile: a quiver file representing the desired end "
60 "state\n");
61 printf(" edgecap: ignore mutations if the cause edges with"
62 "weights above this\n");
63 printf(" startlen: start with sequences of this length\n");
64 printf(" maxlen: halt after exhausting sequences of this "
65 "length\n");
66 printf(" frozen: a vertex with this name will not be "
67 "mutated\n");
70 static int cmp(const void *a, const void *b)
72 struct vertex **va = (struct vertex **) a;
73 struct vertex **vb = (struct vertex **) b;
75 return strcmp((*va)->name, (*vb)->name);
78 /* Load start/end quivers, figure out frozen vertices, order things, etc. */
79 static int setup(char *start_file, char *end_file, char **frozen, size_t
80 num_frozen, struct quiver *out_qstart, struct quiver *out_qend,
81 size_t *out_num_nonfrozen)
83 struct quiver a = { 0 };
84 struct quiver b = { 0 };
85 FILE *fa = 0;
86 FILE *fb = 0;
87 const char *errstr = 0;
88 int ret = 0;
89 struct vertex **averts = 0;
90 struct vertex **bverts = 0;
91 size_t *a_to_start = 0;
92 size_t *b_to_end = 0;
94 if (!out_qstart ||
95 !out_qend) {
96 fprintf(stderr, L("invalid quivers"));
97 ret = EINVAL;
98 goto done;
101 if (out_qstart->v_num ||
102 out_qend->v_num) {
103 fprintf(stderr, L("output quivers are not empty\n"));
104 ret = EINVAL;
105 goto done;
108 if (!(fa = fopen(start_file, "r"))) {
109 ret = errno;
110 perror(L("fopen"));
111 goto done;
114 if (!(fb = fopen(end_file, "r"))) {
115 ret = errno;
116 perror(L("fopen"));
117 goto done;
120 if ((ret = quiver_load_from_file(&a, fa, &errstr))) {
121 fprintf(stderr, "%s\n", errstr);
122 goto done;
125 if ((ret = quiver_load_from_file(&b, fb, &errstr))) {
126 fprintf(stderr, "%s\n", errstr);
127 goto done;
130 if (a.v_num != b.v_num) {
131 fprintf(stderr, L(
132 "Quivers do not have same number of vertices\n"));
133 ret = EINVAL;
134 goto done;
138 * Make sure there are no duplicate vertices, but don't sort in
139 * place - that would change ordering, and ordering of vertices
140 * must match user input - tuning input so that known prefixes
141 * arise early is sometimes crucial.
143 if (!(averts = malloc(a.v_num * sizeof(*averts)))) {
144 ret = errno;
145 perror(L("malloc"));
146 goto done;
149 if (!(bverts = malloc(b.v_num * sizeof(*bverts)))) {
150 ret = errno;
151 perror(L("malloc"));
152 goto done;
155 for (size_t j = 0; j < a.v_num; ++j) {
156 averts[j] = &(a.v[j]);
157 bverts[j] = &(b.v[j]);
160 qsort(averts, a.v_num, sizeof(averts[0]), cmp);
161 qsort(bverts, b.v_num, sizeof(bverts[0]), cmp);
163 for (size_t j = 0; j < a.v_num; ++j) {
164 if (j &&
165 !strcmp(averts[j]->name, averts[j - 1]->name)) {
166 fprintf(stderr, L("Duplicate vertex \"%s\"\n"),
167 averts[j]->name);
168 ret = EINVAL;
169 goto done;
172 int d = strcmp(averts[j]->name, bverts[j]->name);
174 if (d > 0) {
175 fprintf(stderr, L("Vertex \"%s\" not in both files\n"),
176 bverts[j]->name);
177 ret = EINVAL;
178 goto done;
181 if (d < 0) {
182 fprintf(stderr, L("Vertex \"%s\" not in both files\n"),
183 averts[j]->name);
184 ret = EINVAL;
185 goto done;
188 if (averts[j]->fatness != bverts[j]->fatness) {
189 fprintf(stderr, L(
190 "Vertex \"%s\" has inconsistent fatness\n"),
191 averts[j]->name);
192 ret = EINVAL;
193 goto done;
197 /* a_to_s[i] = j <=> "vertex i in a is vertex j in out_qstart" */
198 if (!(a_to_start = malloc(a.v_num * sizeof(*a_to_start)))) {
199 ret = errno;
200 perror(L("malloc"));
201 goto done;
204 if (!(b_to_end = malloc(b.v_num * sizeof(*b_to_end)))) {
205 ret = errno;
206 perror(L("malloc"));
207 goto done;
211 * Order so that frozen vertices are all at the end - required
212 * for incrementation algorithm
214 for (uint_fast8_t want_frozen = 0; want_frozen <= 1; want_frozen++) {
215 for (size_t j = 0; j < a.v_num; ++j) {
216 uint_fast8_t is_frozen = 0;
218 for (size_t k = 0; k < num_frozen; ++k) {
219 is_frozen = is_frozen ||
220 !strcmp(a.v[j].name, frozen[k]);
222 if (is_frozen) {
223 k = num_frozen;
227 if (is_frozen != want_frozen) {
228 continue;
231 if ((ret = quiver_add_vertex(out_qstart,
232 &(a_to_start[j]),
233 a.v[j].name,
234 a.v[j].fatness, 0, 0,
235 &errstr))) {
236 fprintf(stderr, "%s\n", errstr);
237 goto done;
240 if (!want_frozen &&
241 out_num_nonfrozen) {
242 *out_num_nonfrozen = a_to_start[j] + 1;
245 size_t tj = 0;
247 if ((ret = quiver_add_vertex(out_qend, &tj, a.v[j].name,
248 a.v[j].fatness, 0, 0,
249 &errstr))) {
250 fprintf(stderr, "%s\n", errstr);
251 goto done;
254 for (size_t k = 0; k < b.v_num; ++k) {
255 if (!(strcmp(b.v[k].name,
256 out_qend->v[tj].name))) {
257 b_to_end[k] = tj;
263 /* Add the edges in correct order */
264 for (size_t j = 0; j < a.v_num; ++j) {
265 size_t tj = a_to_start[j];
267 for (size_t k = 0; k < a.v_num; ++k) {
268 size_t tk = a_to_start[k];
269 size_t idx_o = tj * out_qstart->v_len + tk;
270 size_t idx_a = j * a.v_len + k;
272 out_qstart->e[idx_o] = a.e[idx_a];
276 for (size_t j = 0; j < b.v_num; ++j) {
277 size_t tj = b_to_end[j];
279 for (size_t k = 0; k < b.v_num; ++k) {
280 size_t tk = b_to_end[k];
281 size_t idx_o = tj * out_qend->v_len + tk;
282 size_t idx_b = j * b.v_len + k;
284 out_qend->e[idx_o] = b.e[idx_b];
288 done:
289 quiver_clean(&a);
290 quiver_clean(&b);
291 fclose(fa);
292 fclose(fb);
293 free(averts);
294 free(bverts);
295 free(a_to_start);
296 free(b_to_end);
298 return ret;
301 /* Main loop */
302 int main(int argc, char **argv)
304 int ret = 0;
305 struct quiver q = { 0 };
306 struct quiver qend = { 0 };
307 char *frozen[argc];
308 size_t fidx = 0;
309 char *start_file = 0;
310 char *end_file = 0;
311 long start_len_l = 0;
312 long max_len_l = 0;
313 size_t start_len = 0;
314 size_t max_len = (size_t) -1;
315 size_t warn_level = (size_t) -1;
316 struct rational *target = 0;
317 char *p = 0;
318 const char *errstr = 0;
319 uint_fast8_t verbose = 0;
321 setlocale(LC_ALL, "");
322 int opt = 0;
324 for (int j = 0; j < argc; ++j) {
325 frozen[j] = 0;
328 while ((opt = getopt(argc, argv, "vhs:e:c:m:l:f:")) != -1) {
329 switch (opt) {
330 case 'v':
331 verbose++;
332 break;
333 case 'h':
334 usage(argv[0]);
335 ret = 0;
336 goto done;
337 case 's':
338 start_file = optarg;
339 break;
340 case 'e':
341 end_file = optarg;
342 break;
343 case 'c':
344 warn_level = strtoll(optarg, &p, 0);
346 if (p &&
347 *p) {
348 fprintf(stderr, "Invalid edge cap: %s\n",
349 optarg);
350 ret = EINVAL;
351 goto done;
354 break;
355 case 'l':
356 start_len_l = strtoll(optarg, &p, 0);
358 if ((start_len_l <= 0) ||
359 (p &&
360 *p)) {
361 fprintf(stderr, "Invalid start length: %s\n",
362 optarg);
363 ret = EINVAL;
364 goto done;
367 start_len = start_len_l;
368 break;
369 case 'm':
370 max_len_l = strtoll(optarg, &p, 0);
372 if ((max_len_l <= 0) ||
373 (p &&
374 *p)) {
375 fprintf(stderr, "Invalid max length: %s\n",
376 optarg);
377 ret = EINVAL;
378 goto done;
381 max_len = max_len_l;
382 break;
383 case 'f':
384 frozen[fidx] = optarg;
385 fidx++;
386 break;
387 default:
388 usage(argv[0]);
389 ret = EINVAL;
390 goto done;
394 if (!start_file ||
395 !end_file) {
396 usage(argv[0]);
397 ret = EINVAL;
398 goto done;
401 size_t num_nonfrozen = 0;
403 if (setup(start_file, end_file, frozen, fidx, &q, &qend,
404 &num_nonfrozen)) {
405 goto done;
408 if (warn_level == (size_t) -1) {
409 warn_level = 0;
411 for (size_t k = 0; k < q.v_num; ++k) {
412 if (warn_level < q.v[k].fatness) {
413 warn_level = q.v[k].fatness;
417 warn_level = warn_level + 1;
420 if (warn_level >= UINT_MAX) {
421 warn_level = UINT_MAX - 1;
424 if ((ret = quiver_set_warning_threshold(&q, warn_level, &errstr))) {
425 fprintf(stderr, "%s\n", errstr);
426 goto done;
429 /* Save off qend's vertices. XXX: Why not just USE qend? */
430 if (!(target = malloc(sizeof *target * qend.v_num * qend.v_num))) {
431 ret = errno;
432 perror(L("malloc"));
433 goto done;
436 for (size_t i = 0; i < qend.v_num * qend.v_num; ++i) {
437 target[i] = (struct rational) { .p = 0, .q = 1 };
440 for (size_t i = 0; i < qend.v_num; ++i) {
441 for (size_t j = 0; j < qend.v_num; ++j) {
442 target[i * qend.v_num + j] = qend.e[i * qend.v_len + j];
446 /* Now, the long haul*/
447 size_t len = 0;
449 if (start_len) {
450 len = start_len - 1;
453 signal(SIGUSR1, handle);
454 size_t s = 0;
455 size_t c_idx;
456 int warn = 0;
457 uint_fast8_t correct = 0;
459 while (len < max_len) {
460 len++;
461 size_t sequence[len];
463 for (size_t k = 0; k < len; ++k) {
464 /* This will get pruned, it's okay */
465 sequence[k] = 0;
468 c_idx = 0;
469 have_full_sequence:
471 if (status_requested) {
472 status_requested = 0;
473 printf("Current sequence %s", q.v[sequence[0]].name);
475 for (size_t k = 1; k < len; ++k) {
476 printf(", %s", q.v[sequence[k]].name);
479 printf("\n");
480 fflush(stdout);
484 * Here, we have computed a full sequence that we want
485 * to check. c_idx is in [0, len - 1], and the quiver
486 * has been mutated following the sequence up to (but
487 * not including) c_idx.
489 * This code is duplicated from increment_sequence
490 * because it is reasonable to start the whole process
491 * with a sequence that may, in fact, produce warnings.
493 warn = 0;
495 while (c_idx < len) {
496 /* Prune duplicates (involutions) */
497 if (c_idx &&
498 sequence[c_idx] == sequence[c_idx - 1]) {
499 goto increment_sequence;
502 /* Proceed one more mutation */
503 quiver_mutate(&q, sequence[c_idx], &warn, 0);
505 if (warn) {
506 /* That mutation caused a warning: skip it */
507 quiver_mutate(&q, sequence[c_idx], &warn, 0);
509 /* And try the sequence, starting at c_idx */
510 goto increment_sequence;
514 * Skip ... X, Y ... when X > Y and the edge
515 * from X to Y is trivial
517 if (c_idx &&
518 sequence[c_idx] < sequence[c_idx - 1]) {
519 size_t x = sequence[c_idx - 1];
520 size_t y = sequence[c_idx];
521 struct rational *xy = &(q.e[x * q.v_len + y]);
522 struct rational *yx = &(q.e[y * q.v_len + x]);
524 if (!xy->p &&
525 !yx->p) {
526 /* Undo mutation */
527 quiver_mutate(&q, sequence[c_idx], 0,
530 /* And try the sequence, starting at c_idx */
531 goto increment_sequence;
535 c_idx++;
539 * Here, there's a full sequence, and the quiver has
540 * been mutated according to it (all of it). We want
541 * to check if the result matches the target.
543 correct = 1;
545 for (size_t i = 0; i < q.v_num; ++i) {
546 for (size_t j = 0; j < q.v_num; ++j) {
547 struct rational *e = &(q.e[i * q.v_len + j]);
548 struct rational *f = &(target[i * q.v_num + j]);
550 if (e->p * f->q != f->p * e->q) {
551 correct = 0;
552 i = q.v_num + 1;
553 j = i;
558 if (correct) {
559 printf("SEQUENCE: %s", q.v[sequence[0]].name);
561 for (size_t k = 1; k < len; ++k) {
562 printf(", %s", q.v[sequence[k]].name);
565 printf("\n");
566 fflush(stdout);
569 c_idx = len - 1;
570 quiver_mutate(&q, sequence[c_idx], 0, 0);
571 increment_sequence:
574 * Here, we have some kind of sequence. The quiver agrees
575 * with it up to (but not including) c_idx, and we want
576 * to increment it at c_idx.
578 s++;
579 sequence[c_idx]++;
581 if (verbose &&
582 c_idx == 2) {
583 double completed = sequence[0] * num_nonfrozen *
584 num_nonfrozen + sequence[1] *
585 num_nonfrozen +
586 sequence[2] - 1;
587 double total = num_nonfrozen * num_nonfrozen *
588 num_nonfrozen;
590 printf("Length %zu: completed %.3g%%\n", len, (100.0 *
591 completed)
592 / total);
593 fflush(stdout);
596 /* Skip over repeats: each mutation is an involution */
597 if (c_idx > 0 &&
598 sequence[c_idx] == sequence[c_idx - 1]) {
599 sequence[c_idx]++;
602 /* Wrap back to 0 */
603 if (sequence[c_idx] >= num_nonfrozen) {
604 if (!c_idx) {
605 goto exhausted_len;
608 c_idx--;
609 quiver_mutate(&q, sequence[c_idx], 0, 0);
610 goto increment_sequence;
613 if (c_idx != len - 1) {
615 * Here, we now have a start of a sequence.
616 * The quiver agrees with our start up to (but
617 * not including) c_idx, and we want to possibly
618 * complete this to a full sequence. First,
619 * however, we need to make sure that we don't
620 * introduce any warnings at c_idx.
622 * This single-mutation testing block could be
623 * ignored, but I have a vague suspicion it will
624 * be a hot path.
626 warn = 0;
627 quiver_mutate(&q, sequence[c_idx], &warn, 0);
629 if (warn) {
630 /* That mutation caused a warning: skip it */
631 quiver_mutate(&q, sequence[c_idx], &warn, 0);
633 /* And try the sequence, starting at c_idx */
634 goto increment_sequence;
638 * Quivers almost commute: in particular if
639 * the edge v_i -> v_j is zero, then mutation
640 * at v_i and v_j commute. So if we're at X, Y
641 * in the list, and X > Y, then we've actually
642 * already computed that branch of the tree,
643 * back when it was Y, X. Therefore, increment X.
645 if (c_idx &&
646 sequence[c_idx] < sequence[c_idx - 1]) {
647 size_t x = sequence[c_idx - 1];
648 size_t y = sequence[c_idx];
649 struct rational *xy = &(q.e[x * q.v_len + y]);
650 struct rational *yx = &(q.e[y * q.v_len + x]);
652 if (!xy->p &&
653 !yx->p) {
654 /* Undo mutation */
655 quiver_mutate(&q, sequence[c_idx], 0,
658 /* And try the sequence, starting at c_idx */
659 goto increment_sequence;
664 * Here the quiver agrees with our start up
665 * to and including c_idx, and we need to fill
666 * it out into a full sequence so that it can
667 * be tested. We don't need to perform all the
668 * mutations of that full sequence, but we do
669 * need to generate the minimal one.
671 for (size_t i = c_idx + 1; i < len; ++i) {
672 sequence[i] = 1 - !!((i - c_idx) % 2);
675 c_idx++;
678 goto have_full_sequence;
679 exhausted_len:
682 * Here, we have c_idx = 0, and the quiver has not been
683 * mutated by anything in sequence
685 printf("Exhausted length %zu\n", len);
688 fflush(stdout);
689 done:
690 free(target);
692 return ret;