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.
29 #define zmax(x, y) (((x) > (y)) ? (x) : (y))
31 static int cmp(const void *a
, const void *b
)
33 const size_t *as
= (const size_t *) a
;
34 const size_t *bs
= (const size_t *) b
;
38 } else if (*as
> *bs
) {
45 int check_iso(struct quiver
*a
, struct quiver
*b
, const char **fix
, size_t
57 size_t next_highest
= 0;
58 size_t next_highest_idx
= 0;
62 a_i is { f₁, f₂, …, fₘ, u₁, u₂, …, uₙ₋ₘ }, where f's are
63 fixed indices and u₁ < u₂ < … < uₙ₋ₘ .
65 b_i will be the same, but as the sequence progresses the
66 u's will be permuted, and we require always that a and
67 b, when restricted to indices less than i_idx, are
70 if (!(a_i
= calloc(n
, sizeof *a_i
))) {
75 if (!(b_i
= calloc(n
, sizeof *b_i
))) {
80 for (size_t j
= 0; j
< fix_len
; ++j
) {
81 const char *name
= fix
[j
];
83 for (size_t k
= 0; k
< n
; ++k
) {
84 if (!(strcmp(a
->v
[k
].name
, name
))) {
85 printf("Setting a_i[%zu] = %zu\n", i_idx
, k
);
89 if (!(strcmp(b
->v
[k
].name
, name
))) {
90 printf("Setting b_i[%zu] = %zu\n", i_idx
, k
);
96 /* Fill out the permutation to make u's increasing */
97 for (i_idx
= fix_len
; i_idx
< n
; ++i_idx
) {
98 /* This is incredibly inefficient, but it's readable */
102 /* Find smallest a_next value that's not already used */
103 for (a_next
= 0; a_next
< n
; ++a_next
) {
106 for (size_t k
= 0; k
< i_idx
; ++k
) {
107 if (a_i
[k
] == a_next
) {
117 /* The same; for b */
118 for (b_next
= 0; b_next
< n
; ++b_next
) {
121 for (size_t k
= 0; k
< i_idx
; ++k
) {
122 if (b_i
[k
] == b_next
) {
138 * Require that, restricted to the fixed parts, we have a
141 for (size_t j
= 0; j
< fix_len
; ++j
) {
145 if (a
->v
[ja
].fatness
!= b
->v
[jb
].fatness
) {
147 "Fixed vertex \u00ab%s\u00bb has inconsistent fatness %d, %d\n",
148 a
->v
[ja
].name
, a
->v
[ja
].fatness
,
153 for (size_t k
= 0; k
< fix_len
; ++k
) {
156 struct rational
*ea
= &a
->e
[ja
* a
->v_len
+ ka
];
157 struct rational
*eb
= &b
->e
[jb
* b
->v_len
+ kb
];
159 if (ea
->p
!= eb
->p
||
163 for (size_t l
= 0; l
< n
; ++l
) {
164 printf("%zu ", a_i
[l
]);
165 if (l
+ 1 == fix_len
) {
172 for (size_t l
= 0; l
< n
; ++l
) {
173 printf("%zu ", b_i
[l
]);
174 if (l
+ 1 == fix_len
) {
181 "S: \u00ab%s\u00bb \u2192 \u00ab%s\u00bb, %d/%d\n",
182 a
->v
[ja
].name
, a
->v
[ka
].name
, ea
->p
,
185 "T: \u00ab%s\u00bb \u2192 \u00ab%s\u00bb, %d/%d\n",
186 b
->v
[jb
].name
, b
->v
[kb
].name
, eb
->p
,
188 fprintf(stderr
, "These are fixed vertices; so ");
189 fprintf(stderr
, "no isomorphism is possible\n");
197 check_partial_isomorphism
:
200 * At this point, a_i and b_i are valid permutations
201 * (completely, up to the last position). Furthermore, the
202 * map Gₐ[a_i[k] <-> Gᵦ[b_i[k]], restricted to k < i_idx,
203 * gives a graph isomorphism. We seek to check if incrementing
204 * i_idx by one works.
209 if (a
->v
[ia
].fatness
!= b
->v
[ib
].fatness
) {
210 goto increment_i_idxth_place
;
213 for (size_t j
= 0; j
< i_idx
; ++j
) {
216 struct rational
*ea
= &a
->e
[ia
* a
->v_len
+ ja
];
217 struct rational
*eb
= &b
->e
[ib
* b
->v_len
+ jb
];
218 struct rational
*fa
= &a
->e
[ja
* a
->v_len
+ ia
];
219 struct rational
*fb
= &b
->e
[jb
* b
->v_len
+ ib
];
221 if (ea
->p
!= eb
->p
||
225 goto increment_i_idxth_place
;
230 * Expanding the isomorphism works. Good, let's try
231 * to expand a bit more
236 goto check_partial_isomorphism
;
239 /* The whole thing is an isomorphism. Good, we're done. */
240 printf("Isomorphism found\n");
242 printf("------------|------------\n");
244 for (size_t j
= 0; j
< n
; ++j
) {
248 printf("%11s | %s\n", a
->v
[ja
].name
, b
->v
[jb
].name
);
253 increment_i_idxth_place
:
255 * Try to get the next in the orderings of b_i.
256 * It's a permutation, so we're in the easy case
257 * of the incrementation algorithm.
259 next_highest
= (size_t) -1;
260 next_highest_idx
= i_idx
;
262 for (size_t j
= i_idx
+ 1; j
< n
; ++j
) {
263 if (b_i
[j
] < next_highest
&&
264 b_i
[j
] > b_i
[i_idx
]) {
265 next_highest
= b_i
[j
];
266 next_highest_idx
= j
;
270 if (next_highest
== (size_t) -1) {
271 if (i_idx
<= fix_len
) {
272 /* We've checked all we can */
273 printf("No isomorphism possible\n");
278 goto increment_i_idxth_place
;
282 b_i
[i_idx
] = b_i
[next_highest_idx
];
283 b_i
[next_highest_idx
] = t
;
284 qsort(b_i
+ i_idx
+ 1, n
- i_idx
- 1, sizeof *b_i
, cmp
);
294 static void usage(char *argv0
)
296 printf("Usage: %s -s <file> -t <file> [ -f <v> [ -f <v> [ ... ] ] ]\n",
298 printf(" <file>: a quiver file output by clav\n");
299 printf(" <v>: a name of a vertex in both files\n");
301 printf("Find (and possibly output) some graph isomorphism between s\n");
302 printf("and t, fixing every vertex named by ‘-f’.\n");
304 printf("Returns 0 if such an isomorphism was found, < 0 if not.\n");
308 int main(int argc
, char **argv
)
312 const char *sarg
= 0;
313 const char *targ
= 0;
314 struct quiver qs
= { 0 };
315 struct quiver qt
= { 0 };
318 const char **fixnames
= 0;
319 const char *errstr
= 0;
322 if (!(fixnames
= calloc(1 + (argc
/ 2), sizeof *fixnames
))) {
327 while ((opt
= getopt(argc
, argv
, "hs:t:f:")) != -1) {
336 fixnames
[fidx
] = optarg
;
350 fprintf(stderr
, "\u2018-s\u2019 is required\n");
356 fprintf(stderr
, "\u2018-t\u2019 is required\n");
361 if (!(fs
= fopen(sarg
, "r"))) {
366 if ((ret
= quiver_load_from_file(&qs
, fs
, &errstr
))) {
367 fprintf(stderr
, "cannot load \u00ab%s\u00bb: %s\n", sarg
,
375 if (!(ft
= fopen(targ
, "r"))) {
380 if ((ret
= quiver_load_from_file(&qt
, ft
, &errstr
))) {
381 fprintf(stderr
, "cannot load \u00ab%s\u00bb: %s\n", sarg
,
389 if (qs
.v_num
!= qt
.v_num
) {
391 "\u00ab%s\u00bb and \u00ab%s\u00bb have different |V|\n",
397 /* Make sure all the fixed vertices are present exactly once */
398 for (size_t j
= 0; j
< fidx
; ++j
) {
399 const char *f
= fixnames
[j
];
403 for (size_t k
= 0; k
< qs
.v_num
; ++k
) {
404 seen_in_s
+= !strcmp(qs
.v
[k
].name
, f
);
405 seen_in_t
+= !strcmp(qt
.v
[k
].name
, f
);
408 if (seen_in_s
!= 1 ||
410 fprintf(stderr
, "\u00ab%s\u00bb appears\n", f
);
411 fprintf(stderr
, " %d time%s in \u00ab%s\u00bb\n",
412 seen_in_s
, (seen_in_s
== 1) ? " " : "s", sarg
);
413 fprintf(stderr
, " %d time%s in \u00ab%s\u00bb\n",
414 seen_in_t
, (seen_in_t
== 1) ? " " : "s", targ
);
419 /* Make sure all vertices in all files are distinct */
420 for (size_t j
= 0; j
< qs
.v_num
; ++j
) {
421 for (size_t k
= 0; k
< j
; ++k
) {
422 if (!(strcmp(qs
.v
[j
].name
, qs
.v
[k
].name
))) {
424 "\u00ab%s\u00bb is not distinct in \u00ab%s\u00bb\n",
428 if (!(strcmp(qt
.v
[j
].name
, qt
.v
[k
].name
))) {
430 "\u00ab%s\u00bb is not distinct in \u00ab%s\u00bb\n",
437 * No immediate reason why there shouldn't be an isomorphism.
440 ret
= check_iso(&qs
, &qt
, fixnames
, fidx
);