2 * Copyright (c) 2018, 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
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.
31 /* Flag for whether to print info */
32 static volatile sig_atomic_t status_requested
= 0;
39 static void handle(int sig
)
41 /* XXX: I'd like to check for SIGINFO here */
49 static void usage(char *argv0
)
51 printf("Usage: %s -s <startfile> -e <endfile>\n", argv0
);
52 printf(" [ -v ] # Be verbose\n");
53 printf(" [ -R ] # Require all non-frozen in sequence\n");
54 printf(" [ -V ] # Test via valency heuristic, not exactness\n");
55 printf(" [ -c <edgecap> ]\n");
56 printf(" [ -f <frozen> [ -f <frozen> [...] ] ]\n");
57 printf(" [ -l <startlen> ] [ -m <maxlen> ]\n");
59 printf(" startfile: a quiver file representing the start "
61 printf(" endfile: a quiver file representing the desired end "
63 printf(" edgecap: ignore mutations if the cause edges with"
64 "weights above this\n");
65 printf(" startlen: start with sequences of this length\n");
66 printf(" maxlen: halt after exhausting sequences of this "
68 printf(" frozen: a vertex with this name will not be "
72 static int cmp(const void *a
, const void *b
)
74 struct vertex
**va
= (struct vertex
**) a
;
75 struct vertex
**vb
= (struct vertex
**) b
;
77 return strcmp((*va
)->name
, (*vb
)->name
);
80 /* Dumb valency-based checksum for graphs. */
81 static uintmax_t valency_checksum(struct quiver
const * const q
)
87 const size_t N
= sizeof (uintmax_t) * sizeof (char) / 4;
88 struct rational r
= { 0 };
91 * This looks really, really stupid, but there is a bit of
92 * thought put into it. Most graphs on which this will run
93 * have, say, <30 vertices, so each sum will probably fit
94 * in quarter of a uintmax_t easily. That makes the or-ing
95 * slightly relevant. Also, the whole point of this heuristic
96 * is that we don't know how the graph could be shuffled,
97 * so dependence on indices is frowned upon, which leaves
98 * most serialization-based checksums out.
100 for (size_t j
= 0; j
< q
->v_num
; ++j
) {
101 for (size_t k
= 0; k
< q
->v_num
; ++k
) {
102 quiver_get_sigma(q
, j
, k
, &r
);
104 c_b
= c_b
+ r
.p
* r
.p
;
105 c_c
= c_c
+ r
.p
* r
.q
;
106 c_d
= c_d
+ r
.q
* r
.q
;
110 return (c_a
<< (3 * N
)) | (c_b
<< (2 * N
)) | (c_c
<< N
) | c_d
;
113 /* Load start/end quivers, figure out frozen vertices, order things, etc. */
114 static int setup(char *start_file
, char *end_file
, char **frozen
, size_t
115 num_frozen
, struct quiver
*out_qstart
, struct quiver
*out_qend
,
116 size_t *out_num_nonfrozen
)
118 struct quiver a
= { 0 };
119 struct quiver b
= { 0 };
122 const char *errstr
= 0;
124 struct vertex
**averts
= 0;
125 struct vertex
**bverts
= 0;
126 size_t *a_to_start
= 0;
127 size_t *b_to_end
= 0;
131 fprintf(stderr
, L("invalid quivers"));
136 if (out_qstart
->v_num
||
138 fprintf(stderr
, L("output quivers are not empty\n"));
143 if (!(fa
= fopen(start_file
, "r"))) {
146 fprintf(stderr
, "cannot open file %s\n", start_file
);
150 if (!(fb
= fopen(end_file
, "r"))) {
153 fprintf(stderr
, "cannot open file %s\n", end_file
);
157 if ((ret
= quiver_load_from_file(&a
, fa
, &errstr
))) {
158 fprintf(stderr
, "%s\n", errstr
);
162 if ((ret
= quiver_load_from_file(&b
, fb
, &errstr
))) {
163 fprintf(stderr
, "%s\n", errstr
);
167 if (a
.v_num
!= b
.v_num
) {
169 "Quivers do not have same number of vertices\n"));
175 * Make sure there are no duplicate vertices, but don't sort in
176 * place - that would change ordering, and ordering of vertices
177 * must match user input - tuning input so that known prefixes
178 * arise early is sometimes crucial.
180 if (!(averts
= calloc(a
.v_num
, sizeof *averts
))) {
186 if (!(bverts
= calloc(b
.v_num
, sizeof *bverts
))) {
192 for (size_t j
= 0; j
< a
.v_num
; ++j
) {
193 averts
[j
] = &(a
.v
[j
]);
194 bverts
[j
] = &(b
.v
[j
]);
197 qsort(averts
, a
.v_num
, sizeof(averts
[0]), cmp
);
198 qsort(bverts
, b
.v_num
, sizeof(bverts
[0]), cmp
);
200 for (size_t j
= 0; j
< a
.v_num
; ++j
) {
202 !strcmp(averts
[j
]->name
, averts
[j
- 1]->name
)) {
203 fprintf(stderr
, L("Duplicate vertex \"%s\"\n"),
209 int d
= strcmp(averts
[j
]->name
, bverts
[j
]->name
);
212 fprintf(stderr
, L("Vertex \"%s\" not in both files\n"),
219 fprintf(stderr
, L("Vertex \"%s\" not in both files\n"),
225 if (averts
[j
]->fatness
!= bverts
[j
]->fatness
) {
227 "Vertex \"%s\" has inconsistent fatness\n"),
234 /* a_to_s[i] = j <=> "vertex i in a is vertex j in out_qstart" */
235 if (!(a_to_start
= calloc(a
.v_num
, sizeof(*a_to_start
)))) {
241 if (!(b_to_end
= calloc(b
.v_num
, sizeof(*b_to_end
)))) {
248 * Order so that frozen vertices are all at the end - required
249 * for incrementation algorithm
251 for (uint_fast8_t want_frozen
= 0; want_frozen
<= 1; want_frozen
++) {
252 for (size_t j
= 0; j
< a
.v_num
; ++j
) {
253 uint_fast8_t is_frozen
= 0;
255 for (size_t k
= 0; k
< num_frozen
; ++k
) {
256 is_frozen
= is_frozen
||
257 !strcmp(a
.v
[j
].name
, frozen
[k
]);
264 if (is_frozen
!= want_frozen
) {
268 if ((ret
= quiver_add_vertex(out_qstart
,
271 a
.v
[j
].fatness
, 0, 0, 0,
273 fprintf(stderr
, "%s\n", errstr
);
279 *out_num_nonfrozen
= a_to_start
[j
] + 1;
284 if ((ret
= quiver_add_vertex(out_qend
, &tj
, a
.v
[j
].name
,
285 a
.v
[j
].fatness
, 0, 0, 0,
287 fprintf(stderr
, "%s\n", errstr
);
291 for (size_t k
= 0; k
< b
.v_num
; ++k
) {
292 if (!(strcmp(b
.v
[k
].name
,
293 out_qend
->v
[tj
].name
))) {
300 /* Add the edges in correct order */
301 for (size_t j
= 0; j
< a
.v_num
; ++j
) {
302 size_t tj
= a_to_start
[j
];
304 for (size_t k
= 0; k
< a
.v_num
; ++k
) {
305 size_t tk
= a_to_start
[k
];
306 size_t idx_o
= tj
* out_qstart
->v_len
+ tk
;
307 size_t idx_a
= j
* a
.v_len
+ k
;
309 out_qstart
->e
[idx_o
] = a
.e
[idx_a
];
313 for (size_t j
= 0; j
< b
.v_num
; ++j
) {
314 size_t tj
= b_to_end
[j
];
316 for (size_t k
= 0; k
< b
.v_num
; ++k
) {
317 size_t tk
= b_to_end
[k
];
318 size_t idx_o
= tj
* out_qend
->v_len
+ tk
;
319 size_t idx_b
= j
* b
.v_len
+ k
;
321 out_qend
->e
[idx_o
] = b
.e
[idx_b
];
346 int main(int argc
, char **argv
)
349 struct quiver q
= { 0 };
350 struct quiver qend
= { 0 };
353 char *start_file
= 0;
355 long start_len_l
= 0;
357 size_t start_len
= 0;
358 size_t max_len
= (size_t) -1;
359 size_t warn_level
= (size_t) -1;
360 struct rational
*target
= 0;
362 const char *errstr
= 0;
363 uint_fast8_t verbose
= 0;
364 uint_fast8_t require_all_nonfrozen
= 0;
365 uint_fast8_t check_valency_only
= 0;
366 int *seen_in_seq
= 0;
367 uintmax_t desired_valency_checksum
= 0;
368 uintmax_t this_valency_checksum
= 0;
370 setlocale(LC_ALL
, "");
373 for (int j
= 0; j
< argc
; ++j
) {
377 while ((opt
= getopt(argc
, argv
, "vhRVs:e:c:m:l:f:")) != -1) {
387 require_all_nonfrozen
= 1;
390 check_valency_only
= 1;
399 warn_level
= strtoll(optarg
, &p
, 0);
403 fprintf(stderr
, "Invalid edge cap: %s\n",
411 start_len_l
= strtoll(optarg
, &p
, 0);
413 if ((start_len_l
<= 0) ||
416 fprintf(stderr
, "Invalid start length: %s\n",
422 start_len
= start_len_l
;
425 max_len_l
= strtoll(optarg
, &p
, 0);
427 if ((max_len_l
<= 0) ||
430 fprintf(stderr
, "Invalid max length: %s\n",
439 frozen
[fidx
] = optarg
;
456 size_t num_nonfrozen
= 0;
458 if (setup(start_file
, end_file
, frozen
, fidx
, &q
, &qend
,
463 if (warn_level
== (size_t) -1) {
466 for (size_t k
= 0; k
< q
.v_num
; ++k
) {
467 if (warn_level
< q
.v
[k
].fatness
) {
468 warn_level
= q
.v
[k
].fatness
;
472 warn_level
= warn_level
+ 1;
475 if (warn_level
>= UINT_MAX
) {
476 warn_level
= UINT_MAX
- 1;
479 if ((ret
= quiver_set_warning_threshold(&q
, warn_level
, &errstr
))) {
480 fprintf(stderr
, "%s\n", errstr
);
484 /* Save off qend's vertices. XXX: Why not just USE qend? */
485 if ((qend
.v_num
* qend
.v_num
) / qend
.v_num
!= qend
.v_num
) {
486 /* Should never happen - isn't qend.v_num capped? */
487 ret
= errno
= EOVERFLOW
;
492 if (!(target
= calloc(qend
.v_num
* qend
.v_num
, sizeof *target
))) {
498 if (!(seen_in_seq
= calloc(qend
.v_num
, sizeof *seen_in_seq
))) {
504 for (size_t i
= 0; i
< qend
.v_num
* qend
.v_num
; ++i
) {
505 target
[i
] = (struct rational
) { .p
= 0, .q
= 1 };
508 for (size_t i
= 0; i
< qend
.v_num
; ++i
) {
509 for (size_t j
= 0; j
< qend
.v_num
; ++j
) {
510 target
[i
* qend
.v_num
+ j
] = qend
.e
[i
* qend
.v_len
+ j
];
514 if (check_valency_only
) {
515 desired_valency_checksum
= valency_checksum(&qend
);
518 /* Now, the long haul*/
525 signal(SIGUSR1
, handle
);
529 uint_fast8_t correct
= 0;
531 while (len
< max_len
) {
533 size_t sequence
[len
];
535 for (size_t k
= 0; k
< num_nonfrozen
; ++k
) {
539 for (size_t k
= 0; k
< len
; ++k
) {
540 /* This will get pruned, it's okay */
542 seen_in_seq
[sequence
[k
]]++;
548 if (status_requested
) {
549 status_requested
= 0;
550 printf("Current sequence %s", q
.v
[sequence
[0]].name
);
552 for (size_t k
= 1; k
< len
; ++k
) {
553 printf(", %s", q
.v
[sequence
[k
]].name
);
561 * Here, we have computed a full sequence that we want
562 * to check. c_idx is in [0, len - 1], and the quiver
563 * has been mutated following the sequence up to (but
564 * not including) c_idx.
566 * This code is duplicated from increment_sequence
567 * because it is reasonable to start the whole process
568 * with a sequence that may, in fact, produce warnings.
573 * If we are looking for a flip, we're probably
574 * going to require that all non-frozen vertices
575 * participate in the mutation.
577 if (require_all_nonfrozen
&&
579 for (size_t j
= 0; j
< num_nonfrozen
; ++j
) {
580 if (!seen_in_seq
[j
]) {
581 goto increment_sequence
;
586 while (c_idx
< len
) {
587 /* Prune duplicates (mutations are involutions) */
589 sequence
[c_idx
] == sequence
[c_idx
- 1]) {
590 goto increment_sequence
;
593 /* Proceed one more mutation */
594 quiver_mutate(&q
, sequence
[c_idx
], &warn
, 0);
597 /* That mutation caused a warning: skip it */
598 quiver_mutate(&q
, sequence
[c_idx
], 0, 0);
600 /* And try the sequence, starting at c_idx */
601 goto increment_sequence
;
605 * Skip ... X, Y ... when X > Y and the edge
606 * from X to Y is trivial
609 sequence
[c_idx
] < sequence
[c_idx
- 1]) {
610 size_t x
= sequence
[c_idx
- 1];
611 size_t y
= sequence
[c_idx
];
612 struct rational
*xy
= &(q
.e
[x
* q
.v_len
+ y
]);
613 struct rational
*yx
= &(q
.e
[y
* q
.v_len
+ x
]);
618 quiver_mutate(&q
, sequence
[c_idx
], 0,
621 /* And try the sequence, starting at c_idx */
622 goto increment_sequence
;
630 * Here, there's a full sequence, and the quiver has
631 * been mutated according to it (all of it). We want
632 * to check if the result matches the target.
634 if (check_valency_only
) {
635 /* Take a short-cut */
636 this_valency_checksum
= valency_checksum(&q
);
638 if (this_valency_checksum
!= desired_valency_checksum
) {
645 for (size_t i
= 0; i
< q
.v_num
; ++i
) {
646 for (size_t j
= 0; j
< q
.v_num
; ++j
) {
647 struct rational
*e
= &(q
.e
[i
* q
.v_len
+ j
]);
648 struct rational
*f
= &(target
[i
* q
.v_num
+ j
]);
650 if (e
->p
* f
->q
!= f
->p
* e
->q
) {
659 check_valency_only
) {
660 printf("SEQUENCE: %s", q
.v
[sequence
[0]].name
);
662 for (size_t k
= 1; k
< len
; ++k
) {
663 printf(", %s", q
.v
[sequence
[k
]].name
);
672 quiver_mutate(&q
, sequence
[c_idx
], 0, 0);
676 * Here, we have some kind of sequence. The quiver agrees
677 * with it up to (but not including) c_idx, and we want
678 * to increment it at c_idx.
681 seen_in_seq
[sequence
[c_idx
]]--;
683 seen_in_seq
[sequence
[c_idx
]]++;
687 double completed
= sequence
[0] * num_nonfrozen
*
688 num_nonfrozen
+ sequence
[1] *
691 double total
= num_nonfrozen
* num_nonfrozen
*
694 printf("Length %zu: completed %.3g%%\n", len
, (100.0 *
700 /* Skip over repeats: each mutation is an involution */
702 sequence
[c_idx
] == sequence
[c_idx
- 1]) {
703 seen_in_seq
[sequence
[c_idx
]]--;
705 seen_in_seq
[sequence
[c_idx
]]++;
709 if (sequence
[c_idx
] >= num_nonfrozen
) {
715 quiver_mutate(&q
, sequence
[c_idx
], 0, 0);
716 goto increment_sequence
;
719 if (c_idx
!= len
- 1) {
721 * Here, we now have a start of a sequence.
722 * The quiver agrees with our start up to (but
723 * not including) c_idx, and we want to possibly
724 * complete this to a full sequence. First,
725 * however, we need to make sure that we don't
726 * introduce any warnings at c_idx.
728 * This single-mutation testing block could be
729 * ignored, but I have a vague suspicion it will
733 quiver_mutate(&q
, sequence
[c_idx
], &warn
, 0);
736 /* That mutation caused a warning: skip it */
737 quiver_mutate(&q
, sequence
[c_idx
], 0, 0);
739 /* And try the sequence, starting at c_idx */
740 goto increment_sequence
;
744 * Quivers almost commute: in particular if
745 * the edge v_i -> v_j is zero, then mutation
746 * at v_i and v_j commute. So if we're at X, Y
747 * in the list, and X > Y, then we've actually
748 * already computed that branch of the tree,
749 * back when it was Y, X. Therefore, increment X.
752 sequence
[c_idx
] < sequence
[c_idx
- 1]) {
753 size_t x
= sequence
[c_idx
- 1];
754 size_t y
= sequence
[c_idx
];
755 struct rational
*xy
= &(q
.e
[x
* q
.v_len
+ y
]);
756 struct rational
*yx
= &(q
.e
[y
* q
.v_len
+ x
]);
761 quiver_mutate(&q
, sequence
[c_idx
], 0,
764 /* And try the sequence, starting at c_idx */
765 goto increment_sequence
;
770 * Here the quiver agrees with our start up
771 * to and including c_idx, and we need to fill
772 * it out into a full sequence so that it can
773 * be tested. We don't need to perform all the
774 * mutations of that full sequence, but we do
775 * need to generate the minimal one.
777 for (size_t i
= c_idx
+ 1; i
< len
; ++i
) {
778 seen_in_seq
[sequence
[i
]]--;
779 sequence
[i
] = 1 - !!((i
- c_idx
) % 2);
780 seen_in_seq
[sequence
[i
]]++;
786 goto have_full_sequence
;
790 * Here, we have c_idx = 0, and the quiver has not been
791 * mutated by anything in sequence
793 printf("Exhausted length %zu\n", len
);