Dist m4ri 0.16.1
Computing distance of a classical or quantum CSS code
Loading...
Searching...
No Matches
util_io.c
Go to the documentation of this file.
1#include <unistd.h>
2#include <ctype.h>
3#include <errno.h>
4#include <string.h>
5#include <math.h>
6#include "util_io.h"
7
9 .debug=3,
10 .method=3,
11 .classical=-1,
12 .steps=100000,
13 .css=1,
14 .smax=0,
15 .wmax=0,
16 .dmin=1, /* the trivial lower bound (as documented in --help); smaller values are treated as 1 */
17 .dmax=0,
18 .wmin=1,
19 .noscan=0,
20 .fdem=NULL,
21 .pmin=0.0,
22 .merge=1,
23 .nvar_orig=0,
24 .col_orig=NULL,
25 .col_class=NULL,
26 .nvar_merged=0,
27 .fdrop=NULL,
28 .drop_mode=1,
29 .drop_set=0,
30 .drop_none=0,
31 .drop_list=NULL,
32 .outD=NULL,
33 .ndrop=0,
34 .ndrop_rows=0,
35 .ndrop_cols=0,
36 .drop_A=0,
37 .sort_rows=-1, /* (auto: 1 for matrices, 0 for a DEM) */
38 .rows_sorted=0,
39 .cc_prune=1,
40 .start_list=NULL,
41 .start_num=0,
42 .start_scan=0,
43 .cbeg=-1,
44 .cend=-1,
45 .seed=0,
46 .dist=0,
47 .dist_max=0,
48 .dist_min=0,
49 .max_row_wgt_H =0,
50 .max_col_wgt_H =0,
51 .n0=0,
52 .nvar=0,
53 .nchk=0,
54 .maxC=0,
55 .dW=0,
56 .finC=NULL,
57 .outC=NULL,
58 .irred=1,
59 .ortho=1,
60 .codewords=NULL,
61 .num_cws=0,
62 .min_w=INT_MAX,
63 .cw_max_w=0,
64 .cw_cnt_w=NULL,
65 .cw_hits_w=NULL,
66 .finH=NULL,
67 .finG=NULL,
68 .finL=NULL,
69 .fin="",
70 .spaH=NULL,
71 .spaG=NULL,
72 .spaL=NULL,
73 .maskL=NULL,
74 .threads=0,
75 .dexp=0,
76 .dstop=0,
77 .timeout=60.0,
78 .nothrottle=0,
79 .chunk_size=0,
80 .ksub=0,
81 .kwin=0,
82 .win_mode=0,
83 .win_hyb=0, /* (auto: 256 for a DEM, 512 for matrices) */
84 .pairs=-1, /* (auto) */
85 .round_D=0,
86 .round_L=0,
87 .round_score=-1.0, /* (not estimated) */
88 .min_hits=5,
89 .cov_cws=100,
90 .refresh=0
91};
92
93params_t * const p = &prm;
94
95void print_short_help(const char *prog) {
96 fprintf(stderr, SHORT_HELP, prog, DIST_M4RI_VERSION, prog);
97}
98
105static void parse_start_list(const char * const str, params_t * const p){
106 if (p->start_list) {
107 free(p->start_list);
108 p->start_list = NULL;
109 }
110 p->start_num = 0;
111 int cnt = 1;
112 for (const char *c = str; *c; c++)
113 if (*c == ',')
114 cnt++;
115 int *list = malloc(cnt * sizeof(int));
116 if (!list)
117 ERROR("memory allocation");
118 const char *s = str;
119 for (int k = 0; k < cnt; k++) {
120 char *end = NULL;
121 errno = 0;
122 long val = strtol(s, &end, 10);
123 if ((end == s) || errno || (val > INT_MAX) || (val < INT_MIN) || ((*end != ',') && (*end != '\0')))
124 ERROR("invalid start='%s': expected a comma-separated list of column indices, e.g., start=0,48", str);
125 list[k] = (int) val;
126 s = end + 1;
127 }
128 if ((cnt == 1) && (list[0] < 0)) { /* legacy default `start=-1`: all columns */
129 free(list);
130 return;
131 }
132 for (int k = 0; k < cnt; k++)
133 if (list[k] < 0)
134 ERROR("invalid start='%s': column indices must be non-negative", str);
135 p->start_list = list;
136 p->start_num = cnt;
137}
138
139static int cmp_int(const void *a, const void *b){
140 const int x = *(const int *) a, y = *(const int *) b;
141 return (x > y) - (x < y);
142}
143
146static int parse_debug_value(const char * const str, const char * const arg){
147 const int hex = (str[0] == '0') && ((str[1] == 'x') || (str[1] == 'X'));
148 const char * const digits = hex ? str + 2 : str;
149 char *end = NULL;
150 errno = 0;
151 const long val = strtol(digits, &end, hex ? 16 : 10);
152 if ((end == digits) || (*end != '\0') || errno || (val < 0) || (val > INT_MAX))
153 ERROR("invalid '%s': expected a non-negative decimal or hexadecimal (0x...) integer", arg);
154 return (int) val;
155}
156
159static char * file_arg_value(const int argc, char **argv, int * const i, const size_t len){
160 if (strlen(argv[*i]) > len)
161 return argv[*i] + len;
162 if (*i + 1 >= argc)
163 ERROR("argv[%d]='%s': missing file name", *i, argv[*i]);
164 return argv[++(*i)];
165}
166
168static int csr_nnz(const csr_t * const M){
169 return (M->nz == -1) ? M->p[M->rows] : M->nz;
170}
171
173static int csr_max_col_wght(const csr_t * const M){
174 if ((M->nz != -1) || (M->cols <= 0)) return 0;
175 int *cnt = calloc(M->cols, sizeof(int));
176 if (!cnt)
177 ERROR("memory allocation");
178 int wmax = 0;
179 for (int j = 0; j < M->p[M->rows]; j++)
180 if (++cnt[M->i[j]] > wmax)
181 wmax = cnt[M->i[j]];
182 free(cnt);
183 return wmax;
184}
185
186void csr_dump(FILE *stream, const csr_t * const M, const char name[], const int debug){
187 if (!(debug & DBG_MATRICES) || !M || !stream) return;
188 fprintf(stream, "# matrix %s: %d x %d, %d nonzeros", name, M->rows, M->cols, csr_nnz(M));
189 if ((M->cols >= 150) && !(debug & DBG_LARGE)) {
190 fprintf(stream, " (not shown for n >= 150 without debug bit 2048)\n");
191 return;
192 }
193 fprintf(stream, " ('1': nonzero, '.': zero)\n");
194 char *row = malloc(M->cols + 1);
195 if (!row)
196 ERROR("memory allocation");
197 mzd_t *D = mzd_from_csr(NULL, M);
198 for (int i = 0; i < M->rows; i++) {
199 for (int j = 0; j < M->cols; j++)
200 row[j] = mzd_read_bit(D, i, j) ? '1' : '.';
201 row[M->cols] = '\0';
202 fprintf(stream, "# %s\n", row);
203 }
204 mzd_free(D);
205 free(row);
206}
207
209static void print_matrix_summary(FILE *stream, const char name[], const csr_t * const M, const char note[]){
210 fprintf(stream, "# %s: %d x %d%s, %d nonzeros, max row weight %d, max column weight %d\n", name, M->rows, M->cols,
211 note, csr_nnz(M), (M->nz == -1) ? csr_max_row_wght(M) : 0, csr_max_col_wght(M));
212}
213
214static const char * drop_label(const params_t * const p);
215
217static void print_round_summary(FILE *stream, const params_t * const p){
218 if (p->round_D > 0)
219 fprintf(stream, "# rounds of the DEM: D=%d detectors and L=%d columns per round (estimated: score %.3f)\n",
220 p->round_D, p->round_L, p->round_score);
221 else
222 fprintf(stream, "# rounds of the DEM: no round structure found (best score %.3f < 0.5)\n",
223 (p->round_score > 0.0) ? p->round_score : 0.0);
224}
225
227static void print_window_summary(FILE *stream, const params_t * const p){
228 fprintf(stream, "# RW windows: %d columns on every other step, uniform permutations on the others (",
229 rw_hybrid_window(p, p->nvar));
230 if (p->win_hyb > 0)
231 fprintf(stream, "win_hyb=%d)\n", p->win_hyb);
232 else
233 fprintf(stream, "win_hyb=0: 256 for a DEM, 512 for matrices)\n");
234}
235
237static void print_input_summary(FILE *stream, const params_t * const p){
238 const char * const type = p->classical ? "classical code" : (p->fdem ? "detector error model" : "quantum CSS code");
239 fprintf(stream, "# input: %s, n=%d (", type, p->nvar);
240 if (p->fdem) {
241 fprintf(stream, "fdem='%s'", p->fdem);
242 if (p->pmin > 0.0)
243 fprintf(stream, ", pmin=%g", p->pmin);
244 } else {
245 fprintf(stream, "finH='%s'", p->finH);
246 if (p->finG)
247 fprintf(stream, ", finG='%s'", p->finG);
248 if (p->finL)
249 fprintf(stream, ", finL='%s'", p->finL);
250 }
251 fprintf(stream, ")\n");
252 print_matrix_summary(stream, "H", p->spaH, p->fdem ? " (detectors)" : "");
253 if (p->nvar_merged > 0)
254 fprintf(stream, "# merged %d equal columns of the DEM: %d of %d columns kept (merge=0 to keep all)\n",
255 p->nvar_orig - p->nvar_merged, p->nvar_merged, p->nvar_orig);
256 if (p->ndrop > 0)
257 fprintf(stream, "# dropped %d of %d detectors (drop=%s), exact: the %d columns touching them have twins (%s), "
258 "%d columns kept\n", p->ndrop, p->ndrop_rows, drop_label(p), p->ndrop_cols,
259 p->drop_A ? "L + A H with A != 0" : "the same observables", p->nvar);
260 else if (p->drop_none)
261 fprintf(stream, "# drop=auto: no detector can be dropped exactly (the full DEM is used)\n");
262 if (p->rows_sorted)
263 fprintf(stream, "# rows of H sorted by increasing weight for CC (sort_rows=1; sort_rows=0 keeps their order)\n");
264 if (p->round_score >= 0.0)
265 print_round_summary(stream, p);
266 if ((p->method & 1) && (p->kwin == 0) && (p->nvar >= 500))
267 print_window_summary(stream, p);
268 if (p->spaG)
269 print_matrix_summary(stream, "G", p->spaG, "");
270 if (p->spaL)
271 print_matrix_summary(stream, "L", p->spaL, p->fdem ? " (observables)" : (p->finL ? "" : " (from H and G)"));
272 if (!p->irred)
273 fprintf(stream, "# irred=0 (expert option): reducible codewords are also recorded, as before version 0.14.8\n");
274}
275
277static int csr_rank(const csr_t * const M){
278 mzd_t *D = mzd_from_csr(NULL, M);
279 const int rank = mzd_echelonize(D, 0);
280 mzd_free(D);
281 return rank;
282}
283
285static void print_code_params(FILE *stream, const params_t * const p){
286 struct timespec t0, t1;
287 clock_gettime(CLOCK_MONOTONIC, &t0);
288 const int n = p->nvar;
289 const int rank_H = csr_rank(p->spaH);
290 int k;
291 char buf[128];
292 if (p->classical || !p->spaL) {
293 k = n - rank_H;
294 snprintf(buf, sizeof(buf), "k=n-rank(H)=%d", k);
295 } else if (p->spaG) { /* CSS code with H=Hx and G=Hz */
296 const int rank_G = csr_rank(p->spaG);
297 k = n - rank_H - rank_G;
298 snprintf(buf, sizeof(buf), "rank(G)=%d, k=n-rank(H)-rank(G)=%d", rank_G, k);
299 } else { /* the logical operators L independent of the rows of H */
300 mzd_t *mH = mzd_from_csr(NULL, p->spaH);
301 mzd_t *mL = mzd_from_csr(NULL, p->spaL);
302 mzd_t *mHL = mzd_stack(NULL, mH, mL);
303 const int rank_L = mzd_echelonize(mL, 0);
304 k = mzd_echelonize(mHL, 0) - rank_H;
305 mzd_free(mHL);
306 mzd_free(mL);
307 mzd_free(mH);
308 snprintf(buf, sizeof(buf), "rank(L)=%d, k=rank([H;L])-rank(H)=%d", rank_L, k);
309 }
310 clock_gettime(CLOCK_MONOTONIC, &t1);
311 fprintf(stream, "# code parameters: n=%d, rank(H)=%d, dim ker(H)=%d, %s (%.3g s)\n", n, rank_H, n - rank_H, buf,
312 (double)(t1.tv_sec - t0.tv_sec) + 1e-9 * (double)(t1.tv_nsec - t0.tv_nsec));
313 if (k <= 0)
314 fprintf(stream, "# Warning: k=%d, there are no non-trivial codewords\n", k);
315}
316
321static int csr_sort_rows_by_weight(csr_t * const M){
322 if (M->nz != -1)
323 csr_compress(M);
324 const int m = M->rows, nnz = M->p[m];
325 int wmax = 0, sorted = 1;
326 for (int r = 0; r < m; r++) {
327 const int w = M->p[r + 1] - M->p[r];
328 if (w > wmax)
329 wmax = w;
330 if ((r > 0) && (w < M->p[r] - M->p[r - 1]))
331 sorted = 0;
332 }
333 if (sorted)
334 return 0;
335 int * const pos = calloc((size_t)wmax + 2, sizeof(int)); /* the first new row of each weight */
336 int * const order = malloc((size_t)m * sizeof(int)); /* new row -> old row */
337 int * const p2 = malloc(((size_t)m + 1) * sizeof(int));
338 int * const i2 = malloc(((size_t)nnz + 1) * sizeof(int));
339 if (!pos || !order || !p2 || !i2)
340 ERROR("memory allocation");
341 for (int r = 0; r < m; r++)
342 pos[M->p[r + 1] - M->p[r] + 1]++;
343 for (int w = 0; w <= wmax; w++)
344 pos[w + 1] += pos[w];
345 for (int r = 0; r < m; r++)
346 order[pos[M->p[r + 1] - M->p[r]]++] = r;
347 int j = 0;
348 for (int k = 0; k < m; k++) {
349 const int r = order[k];
350 p2[k] = j;
351 for (int t = M->p[r]; t < M->p[r + 1]; t++)
352 i2[j++] = M->i[t];
353 }
354 p2[m] = j;
355 memcpy(M->p, p2, ((size_t)m + 1) * sizeof(int));
356 memcpy(M->i, i2, (size_t)nnz * sizeof(int));
357 free(i2);
358 free(p2);
359 free(order);
360 free(pos);
361 return 1;
362}
363
364static void dem_merge_columns(params_t * const p);
365static void dem_drop_detectors(params_t * const p);
366static void dem_write_drop_list(const params_t * const p);
367static void dem_round_estimate(params_t * const p);
368
369/* Help topics (--help TOPIC, --morehelp TOPIC): the entries of the full help text MORE_HELP for some parameters. */
370#define HELP_MAX_NAMES 8
371#define HELP_NAME_LEN 32
372#define HELP_MAX_MEMBERS 16
373
374typedef struct {
375 int sec; /* the line of its section header, or -1 */
376 int beg, end; /* its lines beg..end-1 */
377 int is_sec; /* 1 for a whole section (its header is the line beg) */
379 char key[HELP_MAX_NAMES][HELP_NAME_LEN]; /* the normalized names, see help_key() */
380 char name[HELP_MAX_NAMES][HELP_NAME_LEN]; /* the names as written */
381 int nmembers; /* a group (section "Option groups"): the number of its members, or 0 */
382 char member[HELP_MAX_MEMBERS][HELP_NAME_LEN]; /* the normalized names of its members */
384
385/* The normalized form key of the name s (its first n characters, up to '='): lowercase, without '-', '_', and white
386 * space (at most HELP_NAME_LEN - 1 characters). */
387static void help_key(char * const key, const char * const s, const size_t n) {
388 size_t k = 0;
389 for (size_t i = 0; (i < n) && s[i] && (s[i] != '=') && (k + 1 < HELP_NAME_LEN); i++)
390 if ((s[i] != '-') && (s[i] != '_') && !isspace((unsigned char)s[i]))
391 key[k++] = (char)tolower((unsigned char)s[i]);
392 key[k] = '\0';
393}
394
395/* Adds the name s (its first n characters) to the entry e, unless it is already there. */
396static void help_add_name(help_entry_t * const e, const char * const s, size_t n) {
397 char key[HELP_NAME_LEN];
398 help_key(key, s, n);
399 if (!key[0] || (e->nnames >= HELP_MAX_NAMES))
400 return;
401 for (int i = 0; i < e->nnames; i++)
402 if (strcmp(e->key[i], key) == 0)
403 return;
404 if (n >= HELP_NAME_LEN)
405 n = HELP_NAME_LEN - 1;
406 memcpy(e->name[e->nnames], s, n);
407 e->name[e->nnames][n] = '\0';
408 strcpy(e->key[e->nnames++], key);
409}
410
411/* The length of the name at s, -{0,2}[A-Za-z][A-Za-z0-9_-]*, or 0 if there is none. */
412static size_t help_name_len(const char * const s) {
413 size_t i = 0;
414 while ((i < 2) && (s[i] == '-'))
415 i++;
416 if (!isalpha((unsigned char)s[i]))
417 return 0;
418 while (isalnum((unsigned char)s[i]) || (s[i] == '_') || (s[i] == '-'))
419 i++;
420 return i;
421}
422
423/* Reads the names in the first column of a help line s (after its indentation of 2 spaces) into the entry e: names
424 * separated by ", " or "/", each optionally followed by "=VALUE" or " [VALUE]", up to 2 spaces or the end of the line
425 * (e.g., "method=[int]" or "-h, --help"). Returns 0 if s does not start with such a list (e.g., a line of text). */
426static int help_read_names(help_entry_t * const e, const char *s) {
427 e->nnames = 0;
428 for (;;) {
429 const size_t n = help_name_len(s);
430 if (!n)
431 return 0;
432 help_add_name(e, s, n);
433 s += n;
434 if (*s == '=') /* =VALUE */
435 while (*s && (*s != ' ') && (*s != ','))
436 s++;
437 if ((s[0] == ' ') && (s[1] == '[')) { /* [VALUE] */
438 const char * const t = strchr(s, ']');
439 if (!t)
440 return 0;
441 s = t + 1;
442 }
443 if ((*s == '\0') || ((s[0] == ' ') && (s[1] == ' '))) /* the end of the first column */
444 return 1;
445 if ((s[0] == ',') && (s[1] == ' '))
446 s += 2;
447 else if (*s == '/')
448 s++;
449 else
450 return 0;
451 }
452}
453
454/* Whether s starts with the lowercase string prefix, ignoring the case of s. */
455static int help_has_prefix(const char *s, const char *prefix) {
456 for (; *prefix; s++, prefix++)
457 if (tolower((unsigned char)*s) != *prefix)
458 return 0;
459 return 1;
460}
461
462/* Adds the aliases in the help line s to the entry e: the names after "alias:" or "aliases:" (in any case), separated
463 * by commas, e.g., "(alias: dest)", "Alias: batch=[int].", or "Aliases: --no-throttle, -no-throttle, nothrottle." */
464static void help_read_aliases(help_entry_t * const e, const char * const s) {
465 for (const char *p = s; *p; p++) {
466 const char *q;
467 if (help_has_prefix(p, "aliases:"))
468 q = p + 8;
469 else if (help_has_prefix(p, "alias:"))
470 q = p + 6;
471 else
472 continue;
473 for (;;) {
474 while (*q == ' ')
475 q++;
476 const size_t n = help_name_len(q);
477 if (!n)
478 break;
479 help_add_name(e, q, n);
480 q += n;
481 if (*q == '=') /* =VALUE, e.g., =[int] */
482 while (*q && (*q != ',') && (*q != ' ') && (*q != ')'))
483 q++;
484 if (*q != ',')
485 break;
486 q++;
487 }
488 }
489}
490
491/* Adds the members of a group listed at s to the entry e: names separated by commas and white space (see the section
492 * "Option groups" of MORE_HELP). */
493static void help_read_members(help_entry_t * const e, const char *s) {
494 for (;;) {
495 while ((*s == ' ') || (*s == ','))
496 s++;
497 const size_t n = help_name_len(s);
498 if (!n)
499 return;
500 if (e->nmembers < HELP_MAX_MEMBERS)
501 help_key(e->member[e->nmembers++], s, n);
502 s += n;
503 }
504}
505
506/* Whether the entry e is a member of the group g (one of its names is listed by g). */
507static int help_is_member(const help_entry_t * const g, const help_entry_t * const e) {
508 for (int i = 0; i < g->nmembers; i++)
509 for (int j = 0; j < e->nnames; j++)
510 if (strcmp(g->member[i], e->key[j]) == 0)
511 return 1;
512 return 0;
513}
514
515/* Whether the topic key matches the name key of a group, also as singular or plural with a trailing 's' (e.g.,
516 * window and windows). */
517static int help_group_key_match(const char * const gkey, const char * const key) {
518 const size_t lg = strlen(gkey), lk = strlen(key);
519 if (strcmp(gkey, key) == 0)
520 return 1;
521 if ((lg == lk + 1) && (gkey[lk] == 's') && (strncmp(gkey, key, lk) == 0))
522 return 1;
523 return (lk == lg + 1) && (key[lg] == 's') && (strncmp(gkey, key, lg) == 0);
524}
525
526/* Splits the help text into lines (the line ends are replaced by '\0'; *lines: the line starts, *nlines) and returns
527 * the number of its entries (*ent, to be freed with *lines). A section header is a line without indentation which ends
528 * with ':'. An entry starts with a line indented by 2 spaces with the names (see help_read_names()), and continues
529 * with the lines indented by more; a section header with "(NAME=[", e.g., "Debug output bitmap (debug=[int], ...):",
530 * starts an entry NAME with the whole section. The names of an entry include its aliases (see help_read_aliases()).
531 * The entries of the section "Option groups" are groups: the description lists their members (see
532 * help_read_members()). */
533static int help_entries(char * const text, char *** const lines, int * const nlines, help_entry_t ** const ent) {
534 int nl = 0, cap = 256;
535 char **ln = malloc((size_t)cap * sizeof(char *));
536 if (!ln)
537 ERROR("memory allocation");
538 for (char *s = text; *s; ) {
539 if (nl == cap) {
540 cap *= 2;
541 ln = realloc(ln, (size_t)cap * sizeof(char *));
542 if (!ln)
543 ERROR("memory allocation");
544 }
545 ln[nl++] = s;
546 char * const t = strchr(s, '\n');
547 if (!t)
548 break;
549 *t = '\0';
550 s = t + 1;
551 }
552 help_entry_t * const en = calloc((size_t)nl + 1, sizeof(help_entry_t));
553 if (!en)
554 ERROR("memory allocation");
555 int ne = 0, sec = -1, groups = 0;
556 help_entry_t *cur = NULL; /* the entry continued by the following lines, if any */
557 for (int i = 0; i < nl; i++) {
558 const char * const s = ln[i];
559 const size_t len = strlen(s);
560 if (len && !isspace((unsigned char)s[0]) && (s[len - 1] == ':')) { /* a section header */
561 sec = i;
562 cur = NULL;
563 groups = (strncmp(s, "Option groups", 13) == 0);
564 const char * const p = strchr(s, '(');
565 const size_t n = p ? help_name_len(p + 1) : 0;
566 if (n && (p[1 + n] == '=') && (p[2 + n] == '[')) { /* a whole section, e.g., debug */
567 cur = &en[ne++];
568 *cur = (help_entry_t){.sec = i, .beg = i, .end = i + 1, .is_sec = 1};
569 help_add_name(cur, p + 1, n);
570 }
571 }
572 else if (cur && cur->is_sec) { /* within a whole section */
573 if (len)
574 cur->end = i + 1;
575 help_read_aliases(cur, s);
576 }
577 else if ((s[0] == ' ') && (s[1] == ' ') && s[2] && (s[2] != ' ') && help_read_names(&en[ne], s + 2)) {
578 cur = &en[ne++]; /* a new entry */
579 cur->sec = sec;
580 cur->beg = i;
581 cur->end = i + 1;
582 if (groups) { /* a group: its members after the first column */
583 const char * const d = strstr(s + 2, " ");
584 if (d)
585 help_read_members(cur, d);
586 }
587 else
588 help_read_aliases(cur, s);
589 }
590 else if (cur && (s[0] == ' ') && (s[1] == ' ') && (s[2] == ' ')) { /* a continuation line */
591 cur->end = i + 1;
592 if (cur->nmembers > 0)
593 help_read_members(cur, s);
594 else
595 help_read_aliases(cur, s);
596 }
597 else
598 cur = NULL;
599 }
600 *lines = ln;
601 *nlines = nl;
602 *ent = en;
603 return ne;
604}
605
606/* The edit (Levenshtein) distance of the strings a and b (each of at most HELP_NAME_LEN - 1 characters). */
607static int help_edit_distance(const char * const a, const char * const b) {
608 const size_t la = strlen(a), lb = strlen(b);
609 int d[HELP_NAME_LEN]; /* one row of the table: d[j] for b[0..j-1] */
610 for (size_t j = 0; j <= lb; j++)
611 d[j] = (int)j;
612 for (size_t i = 1; i <= la; i++) {
613 int diag = d[0];
614 d[0] = (int)i;
615 for (size_t j = 1; j <= lb; j++) {
616 const int up = d[j];
617 int best = diag + (a[i - 1] != b[j - 1]);
618 if (up + 1 < best)
619 best = up + 1;
620 if (d[j - 1] + 1 < best)
621 best = d[j - 1] + 1;
622 d[j] = best;
623 diag = up;
624 }
625 }
626 return d[lb];
627}
628
629/* Writes to stderr that the topic s (n characters, normalized: key) has no help entry, with up to 5 of the closest
630 * names (by the edit distance of the normalized names: at most (len + 2) / 3, and at least 1, for a key of length
631 * len), the closest first. */
632static void help_no_entry(const char * const prog, const char * const s, const int n, const char * const key,
633 const help_entry_t * const ent, const int ne) {
634 const int maxd = ((int)strlen(key) + 2) / 3 > 1 ? ((int)strlen(key) + 2) / 3 : 1;
635 const char *best[5];
636 int bestd[5], nb = 0;
637 for (int i = 0; i < ne; i++)
638 for (int j = 0; j < ent[i].nnames; j++) {
639 const int d = help_edit_distance(key, ent[i].key[j]);
640 int pos = nb;
641 if (d > maxd)
642 continue;
643 while ((pos > 0) && (bestd[pos - 1] > d))
644 pos--;
645 if (pos >= 5)
646 continue;
647 if (nb < 5)
648 nb++;
649 for (int k = nb - 1; k > pos; k--) {
650 best[k] = best[k - 1];
651 bestd[k] = bestd[k - 1];
652 }
653 best[pos] = ent[i].name[j];
654 bestd[pos] = d;
655 }
656 fprintf(stderr, "%s: no help entry for '%.*s'", prog, n, s);
657 for (int k = 0; k < nb; k++)
658 fprintf(stderr, "%s%s", k ? ", " : "; did you mean ", best[k]);
659 fprintf(stderr, "%s (--morehelp lists all parameters)\n", nb ? "?" : "");
660}
661
662/* Prints the entries of the full help (MORE_HELP) for the ntopics topics (--help TOPIC, --morehelp TOPIC): each is
663 * one or more names separated by commas, compared without case, '-', '_', and '=VALUE' with the names of the entries
664 * and their aliases (see help_entries()), and with the names of the groups (section "Option groups", also as singular
665 * or plural). A group gives all its members, and an entry also the other members of its groups and these groups (one
666 * level: "see also"). The entries are printed in the order of the full help, under their section headers. Returns
667 * 0, 1 if some topic has no entry (then the closest names are suggested on stderr), or -1 if there is no topic (e.g.,
668 * --help=), and nothing is printed. */
669static int print_help_topics(const char * const prog, const int ntopics, const char * const topics[]) {
670 const int len = snprintf(NULL, 0, MORE_HELP, prog, DIST_M4RI_VERSION, prog);
671 char * const text = malloc((size_t)len + 1);
672 if (!text)
673 ERROR("memory allocation");
674 snprintf(text, (size_t)len + 1, MORE_HELP, prog, DIST_M4RI_VERSION, prog);
675 char **lines;
676 int nlines;
677 help_entry_t *ent;
678 const int ne = help_entries(text, &lines, &nlines, &ent);
679 char * const chosen = calloc((size_t)ne + 1, 1);
680 char * const asked = calloc((size_t)ne + 1, 1);
681 if (!chosen || !asked)
682 ERROR("memory allocation");
683 int status = 0, nkeys = 0;
684 for (int t = 0; t < ntopics; t++)
685 for (const char *s = topics[t]; *s; ) {
686 const char * const comma = strchr(s, ',');
687 int n = comma ? (int)(comma - s) : (int)strlen(s);
688 const char * const next = comma ? comma + 1 : s + n;
689 while ((n > 0) && isspace((unsigned char)*s)) {
690 s++;
691 n--;
692 }
693 while ((n > 0) && isspace((unsigned char)s[n - 1]))
694 n--;
695 char key[HELP_NAME_LEN];
696 help_key(key, s, (size_t)n);
697 if (key[0]) {
698 int found = 0;
699 nkeys++;
700 for (int i = 0; i < ne; i++)
701 for (int j = 0; j < ent[i].nnames; j++)
702 if ((ent[i].nmembers > 0) ? help_group_key_match(ent[i].key[j], key) : (strcmp(ent[i].key[j], key) == 0))
703 asked[i] = found = 1;
704 if (!found) {
705 help_no_entry(prog, s, n, key, ent, ne);
706 status = 1;
707 }
708 }
709 s = next;
710 }
711 for (int g = 0; g < ne; g++) { /* the groups asked for or with a member asked for: the group and all its members */
712 if (ent[g].nmembers <= 0)
713 continue;
714 int use = asked[g];
715 for (int i = 0; (i < ne) && !use; i++)
716 use = asked[i] && (ent[i].nmembers == 0) && help_is_member(&ent[g], &ent[i]);
717 if (!use)
718 continue;
719 chosen[g] = 1;
720 for (int i = 0; i < ne; i++)
721 if ((ent[i].nmembers == 0) && help_is_member(&ent[g], &ent[i]))
722 chosen[i] = 1;
723 }
724 for (int i = 0; i < ne; i++)
725 chosen[i] |= asked[i];
726 int sec = -2, any = 0;
727 for (int i = 0; i < ne; i++) {
728 if (!chosen[i])
729 continue;
730 if (ent[i].sec != sec) { /* the section header, once (a whole section: its first line) */
731 if (any)
732 printf("\n");
733 if ((ent[i].sec >= 0) && !ent[i].is_sec)
734 printf("%s\n", lines[ent[i].sec]);
735 sec = ent[i].sec;
736 }
737 for (int k = ent[i].beg; k < ent[i].end; k++)
738 printf("%s\n", lines[k]);
739 any = 1;
740 }
741 free(asked);
742 free(chosen);
743 free(ent);
744 free(lines);
745 free(text);
746 return nkeys ? status : -1;
747}
748
749/* The kind of the help option arg: 2 for --morehelp (also -morehelp, --more-help, -more-help), 1 for --help (also
750 * -help, -h, -?), or 0; *topic is set to the text after '=' (e.g., --help=maxC), or to NULL. */
751static int help_option(const char * const arg, const char ** const topic) {
752 static const char * const opts[] = {"--morehelp", "-morehelp", "--more-help", "-more-help",
753 "--help", "-help", "-h", "-?", NULL};
754 *topic = NULL;
755 for (int k = 0; opts[k]; k++) {
756 const size_t len = strlen(opts[k]);
757 if ((strncmp(arg, opts[k], len) == 0) && ((arg[len] == '\0') || (arg[len] == '='))) {
758 if (arg[len] == '=')
759 *topic = arg + len + 1;
760 return k < 4 ? 2 : 1;
761 }
762 }
763 return 0;
764}
765
766void var_init(int argc, char **argv, params_t * const p){
767 int dbg=0;
768 int swit=0;
769 double prob=0.0;
770 long long int dbg_ll=0;
771
772 for (int i = 1; i < argc; i++) /* scan arguments for version */
773 if ((strcmp(argv[i], "--version") == 0) || (strcmp(argv[i], "-version") == 0)) {
774 printf("dist_m4ri version %s\n", DIST_M4RI_VERSION);
775 exit(0);
776 }
777
778 /* scan arguments for the help options: --morehelp (full help) or --help (commonly used parameters); with a TOPIC (the
779 * next argument if it does not start with '-' and has no '=', or --help=TOPIC), both print only the entries of the
780 * full help for TOPIC (see print_help_topics()) */
781 int help_kind = 0, ntopics = 0;
782 const char ** const topics = calloc((size_t)argc + 1, sizeof(char *));
783 if (!topics)
784 ERROR("memory allocation");
785 for (int i = 1; i < argc; i++) {
786 const char *topic;
787 const int kind = help_option(argv[i], &topic);
788 if (!kind)
789 continue;
790 if (kind > help_kind)
791 help_kind = kind;
792 if (!topic && (i + 1 < argc) && (argv[i + 1][0] != '-') && !strchr(argv[i + 1], '='))
793 topic = argv[++i];
794 if (topic)
795 topics[ntopics++] = topic;
796 }
797 if (help_kind) {
798 const int status = ntopics ? print_help_topics(argv[0], ntopics, topics) : -1;
799 if (status < 0) {
800 if (help_kind == 2)
801 printf(MORE_HELP, argv[0], DIST_M4RI_VERSION, argv[0]);
802 else
803 printf(USAGE, argv[0], DIST_M4RI_VERSION, argv[0]);
804 }
805 free(topics);
806 exit(status > 0 ? 1 : 0);
807 }
808 free(topics);
809
810 if (argc <= 1) {
811 fprintf(stderr, "%s: no command-line arguments given\n\n", argv[0]);
812 print_short_help(argv[0]);
813 exit(-1);
814 }
815
816 int refresh_set=0;
817 int steps_set=0;
818
819 /* `debug` is parsed first, so that DBG_ARGS applies to all arguments: the first `debug=` argument replaces the
820 * default, further ones are OR-combined (e.g., `debug=0` alone is silent, `debug=0 debug=4` the same as `debug=4`) */
821 int debug_set=0;
822 for (int i = 1; i < argc; i++)
823 if (0 == strncmp(argv[i], "debug=", 6)) {
824 const int val = parse_debug_value(argv[i] + 6, argv[i]);
825 p->debug = debug_set ? (p->debug | val) : val;
826 debug_set = 1;
827 }
828 if (p->debug & DBG_ARGS)
829 fprintf(stderr, "# debug=%d (0x%x)%s\n", p->debug, (unsigned int) p->debug, debug_set ? "" : " (default)");
830
831 for(int i=1; i<argc; i++){
832 if (0 == strncmp(argv[i], "debug=", 6)){
833 }
834 else if (sscanf(argv[i],"css=%d",&dbg)==1){
835 p->css=dbg;
836 if (p->debug & DBG_ARGS)
837 fprintf(stderr, "# read %s, css=%d\n",argv[i],p->css);
838 }
839 else if (0==strncmp(argv[i],"finH=",5)){
840 p->finH = file_arg_value(argc, argv, &i, 5);
841 if (p->debug & DBG_ARGS)
842 fprintf(stderr, "# read %s, finH=%s; setting fin=\"\"\n",argv[i],p->finH);
843 if (strlen(p->fin) != 0)
844 fprintf(stderr, "# Warning: finH='%s' given: ignoring fin='%s'\n", p->finH, p->fin);
845 p->fin="";
846 }
847 else if (0==strncmp(argv[i],"finL=",5)){
848 p->finL = file_arg_value(argc, argv, &i, 5);
849 if (p->debug & DBG_ARGS)
850 fprintf(stderr, "# read %s, finL=%s; setting fin=\"\"\n",argv[i],p->finL);
851 if (strlen(p->fin) != 0)
852 fprintf(stderr, "# Warning: finL='%s' given: ignoring fin='%s'\n", p->finL, p->fin);
853 p->fin="";
854 }
855 else if (0==strncmp(argv[i],"finG=",5)){
856 p->finG = file_arg_value(argc, argv, &i, 5);
857 if (p->debug & DBG_ARGS)
858 fprintf(stderr, "# read %s, finG=%s; setting fin=\"\"\n",argv[i],p->finG);
859 if (strlen(p->fin) != 0)
860 fprintf(stderr, "# Warning: finG='%s' given: ignoring fin='%s'\n", p->finG, p->fin);
861 p->fin="";
862 }
863 else if (0==strncmp(argv[i],"fin=",4)){
864 if(p->finH)
865 ERROR("arg[%d]='%s' in conflict with finH=%s\n",i,argv[i],p->finH);
866 if(p->finG)
867 ERROR("arg[%d]='%s' in conflict with finG=%s\n",i,argv[i],p->finG);
868 if(p->finL)
869 ERROR("arg[%d]='%s' in conflict with finL=%s\n",i,argv[i],p->finL);
870 p->fin = file_arg_value(argc, argv, &i, 4);
871 }
872 else if (sscanf(argv[i],"method=%d",&dbg)==1){
873 p->method=dbg;
874 if (p->debug & DBG_ARGS)
875 fprintf(stderr, "# read %s, method=%d\n",argv[i],p->method);
876 if( (p->method<=0) || (p->method>3)) {
877 fprintf(stderr, "%s: unsupported method=%d specified\n\n", argv[0], p->method);
878 print_short_help(argv[0]);
879 exit(-1);
880 }
881 }
882 else if (sscanf(argv[i],"smax=%d",&dbg)==1){
883 p->smax=dbg;
884 if (p->debug & DBG_ARGS)
885 fprintf(stderr, "# read %s, smax=%d\n",argv[i],p->smax);
886 }
887 else if (sscanf(argv[i],"wmax=%d",&dbg)==1){
888 p->wmax=dbg;
889 if (p->debug & DBG_ARGS)
890 fprintf(stderr, "# read %s, wmax=%d\n",argv[i],p->wmax);
891 }
892 else if (sscanf(argv[i],"dmin=%d",&dbg)==1){
893 p->dmin=dbg;
894 if (p->debug & DBG_ARGS)
895 fprintf(stderr, "# read %s, dmin=%d\n",argv[i],p->dmin);
896 }
897 else if (sscanf(argv[i],"dmax=%d",&dbg)==1){
898 p->dmax=dbg;
899 if (p->debug & DBG_ARGS)
900 fprintf(stderr, "# read %s, dmax=%d\n",argv[i],p->dmax);
901 }
902 else if (0==strncmp(argv[i],"start=",6)){
903 parse_start_list(argv[i]+6, p);
904 if (p->debug & DBG_ARGS)
905 fprintf(stderr, "# read %s, start_num=%d\n",argv[i],p->start_num);
906 }
907 else if (sscanf(argv[i],"start_scan=%d",&dbg)==1){
908 if (dbg != 0 && dbg != 1)
909 ERROR("invalid '%s': start_scan should be 0 or 1\n", argv[i]);
910 p->start_scan=dbg;
911 if (p->debug & DBG_ARGS)
912 fprintf(stderr, "# read %s, start_scan=%d\n",argv[i],p->start_scan);
913 }
914 else if (sscanf(argv[i],"cbeg=%d",&dbg)==1){
915 p->cbeg=dbg;
916 if (p->debug & DBG_ARGS)
917 fprintf(stderr, "# read %s, cbeg=%d\n",argv[i],p->cbeg);
918 }
919 else if (sscanf(argv[i],"cend=%d",&dbg)==1){
920 p->cend=dbg;
921 if (p->debug & DBG_ARGS)
922 fprintf(stderr, "# read %s, cend=%d\n",argv[i],p->cend);
923 }
924 else if (sscanf(argv[i],"wmin=%d",&dbg)==1){
925 p->wmin=dbg;
926 if (p->debug & DBG_ARGS)
927 fprintf(stderr, "# read %s, wmin=%d\n",argv[i],p->wmin);
928 }
929 else if (sscanf(argv[i],"steps=%d",&dbg)==1){
930 p->steps=dbg;
931 steps_set=1;
932 if (p->debug & DBG_ARGS)
933 fprintf(stderr, "# read %s, steps=%d\n",argv[i],p->steps);
934 }
935 else if (sscanf(argv[i],"seed=%d",&dbg)==1){
936 p->seed=dbg;
937 if (p->debug & DBG_ARGS)
938 fprintf(stderr, "# read %s, seed=%d\n",argv[i],p->seed);
939 }
940 else if (sscanf(argv[i],"noscan=%d",&dbg)==1){
941 p->noscan=dbg;
942 if (p->debug & DBG_ARGS)
943 fprintf(stderr, "# read %s, noscan=%d\n",argv[i],p->noscan);
944 }
945 else if (0==strncmp(argv[i],"fdem=",5)){
946 p->fdem = file_arg_value(argc, argv, &i, 5);
947 if (p->debug & DBG_ARGS)
948 fprintf(stderr, "# read %s, fdem=%s\n",argv[i],p->fdem);
949 }
950 else if (sscanf(argv[i],"pmin=%lg",&prob)==1){
951 p->pmin=prob;
952 if (p->debug & DBG_ARGS)
953 fprintf(stderr, "# read %s, pmin=%g\n",argv[i],p->pmin);
954 }
955 else if (sscanf(argv[i],"merge=%d",&dbg)==1){
956 if ((dbg != 0) && (dbg != 1))
957 ERROR("invalid '%s': merge should be 0 or 1\n", argv[i]);
958 p->merge=dbg;
959 if (p->debug & DBG_ARGS)
960 fprintf(stderr, "# read %s, merge=%d\n",argv[i],p->merge);
961 }
962 else if (sscanf(argv[i],"sort_rows=%d",&dbg)==1){
963 if ((dbg != 0) && (dbg != 1))
964 ERROR("invalid '%s': sort_rows should be 0 or 1\n", argv[i]);
965 p->sort_rows=dbg;
966 if (p->debug & DBG_ARGS)
967 fprintf(stderr, "# read %s, sort_rows=%d\n",argv[i],p->sort_rows);
968 }
969 else if (sscanf(argv[i],"irred=%d",&dbg)==1){
970 if ((dbg != 0) && (dbg != 1))
971 ERROR("invalid '%s': irred should be 0 or 1\n", argv[i]);
972 p->irred=dbg;
973 if (p->debug & DBG_ARGS)
974 fprintf(stderr, "# read %s, irred=%d\n",argv[i],p->irred);
975 }
976 else if (sscanf(argv[i],"ortho=%d",&dbg)==1){
977 if ((dbg != 0) && (dbg != 1))
978 ERROR("invalid '%s': ortho should be 0 or 1\n", argv[i]);
979 p->ortho=dbg;
980 if (p->debug & DBG_ARGS)
981 fprintf(stderr, "# read %s, ortho=%d\n",argv[i],p->ortho);
982 }
983 else if (sscanf(argv[i],"cc_prune=%d",&dbg)==1){
984 if ((dbg < 0) || (dbg > 2))
985 ERROR("invalid '%s': cc_prune should be 0, 1, or 2\n", argv[i]);
986 p->cc_prune=dbg;
987 if (p->debug & DBG_ARGS)
988 fprintf(stderr, "# read %s, cc_prune=%d\n",argv[i],p->cc_prune);
989 }
990 else if (0==strncmp(argv[i],"drop=",5)){
991 char * const val = file_arg_value(argc, argv, &i, 5);
992 p->drop_set = 1;
993 if (strcmp(val, "0") == 0)
994 p->drop_mode = 0;
995 else if ((strcmp(val, "1") == 0) || (strcmp(val, "auto") == 0))
996 p->drop_mode = 1;
997 else {
998 p->drop_mode = 2;
999 p->fdrop = val;
1000 }
1001 if (p->debug & DBG_ARGS)
1002 fprintf(stderr, "# read %s, drop=%s\n",argv[i],val);
1003 }
1004 else if (0==strncmp(argv[i],"outD=",5)){
1005 p->outD = file_arg_value(argc, argv, &i, 5);
1006 if (p->debug & DBG_ARGS)
1007 fprintf(stderr, "# read %s, outD=%s\n",argv[i],p->outD);
1008 }
1009 else if (0==strncmp(argv[i],"finC=",5)){
1010 p->finC = file_arg_value(argc, argv, &i, 5);
1011 if (p->debug & DBG_ARGS)
1012 fprintf(stderr, "# read %s, finC=%s\n",argv[i],p->finC);
1013 }
1014 else if (0==strncmp(argv[i],"outC=",5)){
1015 p->outC = file_arg_value(argc, argv, &i, 5);
1016 if (p->debug & DBG_ARGS)
1017 fprintf(stderr, "# read %s, outC=%s\n",argv[i],p->outC);
1018 }
1019 else if (sscanf(argv[i],"maxC=%lld",&dbg_ll)==1){
1020 p->maxC=dbg_ll;
1021 if (p->debug & DBG_ARGS)
1022 fprintf(stderr, "# read %s, maxC=%lld\n",argv[i],p->maxC);
1023 }
1024 else if (sscanf(argv[i],"dW=%d",&dbg)==1){
1025 p->dW=dbg;
1026 if (p->debug & DBG_ARGS)
1027 fprintf(stderr, "# read %s, dW=%d\n",argv[i],p->dW);
1028 }
1029 else if (sscanf(argv[i],"classical=%d",&dbg)==1){
1030 p->classical=dbg;
1031 if (p->debug & DBG_ARGS)
1032 fprintf(stderr, "# read %s, classical=%d\n",argv[i],p->classical);
1033 }
1034 else if (sscanf(argv[i],"threads=%d",&dbg)==1){
1035 p->threads=dbg;
1036 if (p->debug & DBG_ARGS)
1037 fprintf(stderr, "# read %s, threads=%d\n",argv[i],p->threads);
1038 }
1039 else if (sscanf(argv[i],"dexp=%d",&dbg)==1){
1040 p->dexp=dbg;
1041 if (p->debug & DBG_ARGS)
1042 fprintf(stderr, "# read %s, dexp=%d\n",argv[i],p->dexp);
1043 }
1044 else if (sscanf(argv[i],"dest=%d",&dbg)==1){
1045 p->dexp=dbg;
1046 if (p->debug & DBG_ARGS)
1047 fprintf(stderr, "# read %s, dest=%d (alias for dexp)\n",argv[i],p->dexp);
1048 }
1049 else if (sscanf(argv[i],"dstop=%d",&dbg)==1){
1050 if (dbg < 0)
1051 ERROR("invalid '%s': dstop must be non-negative", argv[i]);
1052 p->dstop=dbg;
1053 if (p->debug & DBG_ARGS)
1054 fprintf(stderr, "# read %s, dstop=%d\n",argv[i],p->dstop);
1055 }
1056 else if (sscanf(argv[i],"timeout=%lf",&prob)==1){
1057 p->timeout=prob;
1058 if (p->debug & DBG_ARGS)
1059 fprintf(stderr, "# read %s, timeout=%g\n",argv[i],p->timeout);
1060 }
1061 else if (sscanf(argv[i],"nothrottle=%d",&dbg)==1){
1062 p->nothrottle=dbg;
1063 if (p->debug & DBG_ARGS)
1064 fprintf(stderr, "# read %s, nothrottle=%d\n",argv[i],p->nothrottle);
1065 }
1066 else if (strcmp(argv[i], "--no-throttle") == 0 || strcmp(argv[i], "-no-throttle") == 0
1067 || strcmp(argv[i], "nothrottle") == 0) {
1068 p->nothrottle=1;
1069 if (p->debug & DBG_ARGS)
1070 fprintf(stderr, "# read %s, nothrottle=1\n",argv[i]);
1071 }
1072 else if (sscanf(argv[i],"chunk_size=%d",&dbg)==1 || sscanf(argv[i],"batch=%d",&dbg)==1){
1073 p->chunk_size=dbg;
1074 if (p->debug & DBG_ARGS)
1075 fprintf(stderr, "# read %s, chunk_size=%d\n",argv[i],p->chunk_size);
1076 }
1077 else if (sscanf(argv[i],"ksub=%d",&dbg)==1){
1078 p->ksub=dbg;
1079 if (p->debug & DBG_ARGS)
1080 fprintf(stderr, "# read %s, ksub=%d\n",argv[i],p->ksub);
1081 }
1082 else if (sscanf(argv[i],"kwin=%d",&dbg)==1 || sscanf(argv[i],"win=%d",&dbg)==1){
1083 p->kwin=dbg;
1084 if (p->debug & DBG_ARGS)
1085 fprintf(stderr, "# read %s, kwin=%d\n",argv[i],p->kwin);
1086 }
1087 else if (sscanf(argv[i],"win_mode=%d",&dbg)==1){
1088 p->win_mode=dbg;
1089 if (p->debug & DBG_ARGS)
1090 fprintf(stderr, "# read %s, win_mode=%d\n",argv[i],p->win_mode);
1091 }
1092 else if (sscanf(argv[i],"win_hyb=%d",&dbg)==1){
1093 if (dbg < 0)
1094 ERROR("invalid '%s': win_hyb should be 0 (256 columns for a DEM, 512 for matrices) or positive\n", argv[i]);
1095 p->win_hyb=dbg;
1096 if (p->debug & DBG_ARGS)
1097 fprintf(stderr, "# read %s, win_hyb=%d\n",argv[i],p->win_hyb);
1098 }
1099 else if (sscanf(argv[i],"pairs=%d",&dbg)==1){
1100 if ((dbg < -1) || (dbg > RW_PAIRS_MAX_BITS))
1101 ERROR("invalid '%s': pairs should be -1 (auto), 0 (off), or 1 to %d (random pivot rows of the buckets)\n",
1102 argv[i], RW_PAIRS_MAX_BITS);
1103 p->pairs=dbg;
1104 if (p->debug & DBG_ARGS)
1105 fprintf(stderr, "# read %s, pairs=%d\n",argv[i],p->pairs);
1106 }
1107 else if (sscanf(argv[i],"min_hits=%d",&dbg)==1 || sscanf(argv[i],"max_hits=%d",&dbg)==1){
1108 p->min_hits=dbg;
1109 if (p->debug & DBG_ARGS)
1110 fprintf(stderr, "# read %s, min_hits=%d\n",argv[i],p->min_hits);
1111 }
1112 else if (sscanf(argv[i],"cov_cws=%d",&dbg)==1){
1113 p->cov_cws=dbg;
1114 if (p->debug & DBG_ARGS)
1115 fprintf(stderr, "# read %s, cov_cws=%d\n",argv[i],p->cov_cws);
1116 }
1117 else if (sscanf(argv[i],"refresh=%d",&dbg)==1){
1118 p->refresh=dbg;
1119 refresh_set=1;
1120 if (p->debug & DBG_ARGS)
1121 fprintf(stderr, "# read %s, refresh=%d\n",argv[i],p->refresh);
1122 }
1123 else{ /* unrecognized option */
1124 fprintf(stderr, "%s: unrecognized parameter \"%s\" at position %d\n\n", argv[0], argv[i], i);
1125 print_short_help(argv[0]);
1126 exit(-1);
1127 }
1128 } /* end parameter scan cycle */
1129
1130 if (!refresh_set && p->ksub > 0) {
1131 p->refresh = 5000;
1132 }
1133
1134 if (p->noscan && p->method != 2) {
1135 ERROR("noscan=1 only works with method=2");
1136 }
1137
1138 if (!p->fdem && strlen(p->fin) == 0 && !p->finH && !p->finG && !p->finL) {
1139 p->fin = "../examples/try";
1140 }
1141
1142 if (p->fdem) {
1143 if (p->finH || p->finG || p->finL || strlen(p->fin) > 0) {
1144 ERROR("Cannot specify matrix files (fin, finH, finG, finL) along with fdem");
1145 }
1146 }
1147 if (p->pmin != 0.0 && !p->fdem) {
1148 ERROR("pmin can only be used when fdem is specified");
1149 }
1150 if (p->drop_set && (p->drop_mode != 0) && !p->fdem) {
1151 ERROR("drop can only be used when fdem is specified");
1152 }
1153 if (p->outD && !p->fdem) {
1154 ERROR("outD can only be used when fdem is specified");
1155 }
1156 if (p->drop_set && (p->drop_mode != 0) && (p->classical == 1)) {
1157 ERROR("drop cannot be combined with classical=1 (the observables are needed)");
1158 }
1159 if ((p->drop_mode == 2) && (access(p->fdrop, R_OK) != 0)) {
1160 ERROR("drop=%s: cannot read this file (%s); use drop=0, drop=1 or drop=auto, or a file", p->fdrop,
1161 strerror(errno));
1162 }
1163
1164 if (p->dmin < 0) {
1165 ERROR("parameter dmin=%d cannot be negative\n", p->dmin);
1166 }
1167 if (p->dmax < 0) {
1168 ERROR("parameter dmax=%d cannot be negative\n", p->dmax);
1169 }
1170 if (p->dmin > 0 && p->dmax > 0 && p->dmin > p->dmax) {
1171 ERROR("parameter dmin=%d cannot be larger than dmax=%d\n", p->dmin, p->dmax);
1172 }
1173 if (p->wmax > 0 && p->dmin > p->wmax) {
1174 ERROR("parameter dmin=%d cannot be larger than wmax=%d\n", p->dmin, p->wmax);
1175 }
1176
1177 if (p->wmax > 0 && p->wmin > p->wmax) {
1178 ERROR("parameter wmin=%d cannot be larger than wmax=%d\n", p->wmin, p->wmax);
1179 }
1180 if (p->start_num > 0) { /* `start` list uses unlimited clusters: cannot be mixed with split runs */
1181 if (p->cbeg >= 0 || p->cend >= 0) {
1182 ERROR("Cannot specify start along with cbeg or cend\n");
1183 }
1184 }
1185 if (p->start_scan) { /* the scan rule refers to the order of the columns: not after merging the columns of a DEM */
1186 if (p->start_num == 0)
1187 ERROR("start_scan=1 needs a start list (start=...)\n");
1188 if (p->fdem)
1189 ERROR("start_scan=1 is not available with fdem (the equal columns of a DEM are merged)\n");
1190 }
1191
1192 if (p->method == 1) {
1193 if (p->cbeg >= 0 || p->cend >= 0 || p->start_num > 0) {
1194 ERROR("Parameters start, cbeg, and cend only work with CC method (method=2 or method=3)\n");
1195 }
1196 }
1197
1198 if (p->cbeg >= 0 && p->cend >= 0 && p->cbeg > p->cend) {
1199 ERROR("cbeg=%d cannot be larger than cend=%d\n", p->cbeg, p->cend);
1200 }
1201 if (p->noscan && p->smax > 0) {
1202 fprintf(stderr,
1203 "# WARNING: smax=%d disabled (set to 0) because noscan=1 skips small cluster weights\n",
1204 p->smax);
1205 p->smax = 0;
1206 } else if (p->dmin > 1 && p->smax > 0) {
1207 fprintf(stderr,
1208 "# WARNING: smax=%d disabled (set to 0) because dmin=%d skips small cluster weights\n",
1209 p->smax, p->dmin);
1210 p->smax = 0;
1211 }
1212
1213 if (p->method == 2) {
1214 if (steps_set) {
1215 fprintf(stderr, "# WARNING: steps=%d is ignored for CC method\n", p->steps);
1216 }
1217 }
1218
1219 if (p->method == 1) { /* RW */
1220 if (p->steps <= 0)
1221 ERROR("parameter steps=%d should be positive for RW method=%d", p->steps, p->method);
1222 }
1223
1224
1225
1226 if((strlen(p->fin)!=0) && (!p->finH)){
1227 int len = strlen(p->fin);
1228 char *s = (char *) malloc((len+6)*sizeof(char));
1229 if(!s)
1230 ERROR("memory allocation");
1231 sprintf(s,"%s%s",p->fin,swit>0?"X.mtx":"Z.mtx");
1232 p->finG=s;
1233 s = (char *) malloc((len+6)*sizeof(char));
1234 if(!s)
1235 ERROR("memory allocation");
1236 sprintf(s,"%s%s",p->fin,swit>0?"Z.mtx":"X.mtx");
1237 p->finH=s;
1238 if (p->debug & DBG_ARGS)
1239 fprintf(stderr, "# read 'fin=%s'; " //"since switch=%d "
1240 "assigning \n# finH=%s\n# finG=%s\n",
1241 p->fin,// swit,
1242 p->finH,p->finG);
1243 }
1244
1245 if (p->fdem) {
1246 read_dem_file(p->fdem, &(p->spaH), &(p->spaL), p->pmin, p->debug);
1247 if (p->merge)
1248 dem_merge_columns(p);
1249 if ((p->drop_mode != 0) && (p->classical != 1))
1250 dem_drop_detectors(p);
1251 if (p->outD)
1252 dem_write_drop_list(p);
1253 /* the rounds of the DEM for the input summary (in the detector order of the DEM, i.e., before sort_rows) */
1254 if (p->debug & DBG_SUMMARY)
1255 dem_round_estimate(p);
1256 if (p->classical == -1) p->classical = 0;
1257 p->nvar = p->spaH->cols;
1258 p->n0 = p->nvar;
1259 p->nchk = p->spaL->rows;
1260 csr_dump(stderr, p->spaH, "H", p->debug);
1261 csr_dump(stderr, p->spaL, "L", p->debug);
1262 } else {
1263 if (p->finH){
1264 p->spaH=csr_mm_read(p->finH,p->spaH,0);
1265 if (p->debug & DBG_ARGS)
1266 fprintf(stderr, "# read H <- file '%s'\n",p->finH);
1267 csr_dump(stderr, p->spaH, "H", p->debug);
1268 }
1269 else
1270 ERROR("need to specify H=Hx input file name; use fin=[str] or finH=[str]\n");
1271
1272 if((p->finG) && (p->finL))
1273 ERROR("either G=Hz or L=Lx matrix should be specified but not both! finG='%s' finL='%s'\n",
1274 p->finG, p->finL);
1275
1276 if(p->finG){
1277 if (p->classical == -1) p->classical = 0;
1278 p->spaG=csr_mm_read(p->finG,p->spaG,0);
1279 if (p->debug & DBG_ARGS)
1280 fprintf(stderr, "# read G <- file '%s'\n",p->finG);
1281 if(p->ortho && csr_csr_mul_non_zero(p->spaH, p->spaG))
1282 ERROR("rows of H and G matrices are not orthogonal");
1283 csr_dump(stderr, p->spaG, "G", p->debug);
1284 }
1285 else if (p->finL){
1286 if (p->classical == -1) p->classical = 0;
1287 p->spaL=csr_mm_read(p->finL,p->spaL,0);
1288 if (p->debug & DBG_ARGS)
1289 fprintf(stderr, "# read L <- file '%s'\n",p->finL);
1290 csr_dump(stderr, p->spaL, "L", p->debug);
1291 p->nchk = p->spaL->rows;
1292 }
1293 else{
1294 if (p->classical == -1) p->classical = 1;
1295 p->spaG=NULL;
1296 }
1297 }
1298
1299 if(p->method & 2){ /* CC */
1300 if ((p->wmax<=0) && ((p->method & 1 )==0)) {
1301 /* the CC rounds end with the timeout, or at the latest once the bounds coincide (w = dmax - 1), or once
1302 * dmin >= dstop (w = dstop - 1) */
1303 if (p->timeout <= 0.0 && p->dmax <= 0 && p->dstop <= 0) {
1304 ERROR("either parameter wmax>0, dmax>0, dstop>0, or timeout>0 should be specified for CC method=%d",
1305 p->method);
1306 }
1307 p->wmax = MAX_W - 1;
1308 }
1309 if(p->wmax>=MAX_W)
1310 ERROR("increase MAX_W=%d defined in 'util_io.h'",MAX_W);
1311 for(int i=0; i<MAX_W; i++)
1312 p->swei[i]=p->spaH->rows +1;
1313 }
1314
1315 if (p->seed<=0){
1316 p->seed = time(NULL) - 1000 * p->seed + 10*getpid();
1317 if (p->debug & DBG_ARGS)
1318 fprintf(stderr, "# initializing rng from time(NULL), seed=%d\n",p->seed);
1319 }
1320 else if (p->debug & DBG_ARGS)
1321 fprintf(stderr, "# initializing rng from seed=%d\n",p->seed);
1322
1323 srand(p->seed);
1324
1325 rci_t n = (p->spaH)-> cols;
1326 if ((!p->classical) && ((p->spaG) && (n != (p->spaG) -> cols)))
1327 ERROR("Column count mismatch in H and G matrices: %d != %d",
1328 (p->spaH)-> cols, (p->spaG)->cols);
1329 p->nvar = n;
1330 p->n0 = n;
1331 if (p->css!=1)
1332 ERROR("Non-CSS codes are currently not supported, css=%d",p->css);
1333
1334 /* start, cbeg, and cend refer to the original columns (before merging the equal columns of a DEM, and dropping) */
1335 const int n_orig = (p->nvar_orig > 0) ? p->nvar_orig : p->nvar;
1336 if (p->cbeg >= n_orig) {
1337 ERROR("cbeg=%d cannot be larger than nvar-1=%d\n", p->cbeg, n_orig-1);
1338 }
1339 if (p->cend >= n_orig) {
1340 ERROR("cend=%d cannot be larger than nvar-1=%d\n", p->cend, n_orig-1);
1341 }
1342 if (p->start_num > 0) { /* range check, map to the merged columns, sort, and remove duplicates */
1343 int num = 0;
1344 for (int k = 0; k < p->start_num; k++) {
1345 if (p->start_list[k] >= n_orig)
1346 ERROR("start column %d cannot be larger than nvar-1=%d\n", p->start_list[k], n_orig-1);
1347 const int c = p->col_class ? p->col_class[p->start_list[k]] : p->start_list[k];
1348 if (c >= 0) /* (-1: a dropped column without a twin, see drop) */
1349 p->start_list[num++] = c;
1350 }
1351 if (num == 0)
1352 ERROR("no start column left: the columns listed touch only dropped detectors (drop=%s)\n", drop_label(p));
1353 p->start_num = num;
1354 qsort(p->start_list, p->start_num, sizeof(int), cmp_int);
1355 num = 1;
1356 for (int k = 1; k < p->start_num; k++)
1357 if (p->start_list[k] != p->start_list[num-1])
1358 p->start_list[num++] = p->start_list[k];
1359 p->start_num = num;
1360 }
1361
1362 if((p->spaG) && (p->spaL==NULL)){
1365 p->spaL = Lx_for_CSS_code(p->spaH,p->spaG);
1366 p->nchk = p->spaL->rows;
1367 csr_dump(stderr, p->spaL, "L (from H and G)", p->debug);
1368 }
1369
1370 if (p->classical) {
1371 if (p->finL != NULL || p->finG != NULL) {
1372 ERROR("Conflict: classical=1 specified, but finL or finG was also provided.");
1373 }
1374 if (p->spaL != NULL) {
1375 if (p->debug & DBG_SUMMARY) {
1376 fprintf(stderr, "# Warning: classical=1 specified, discarding L matrix (logical operators)\n");
1377 }
1378 p->spaL = csr_free(p->spaL);
1379 }
1380 } else {
1381 if (p->spaL == NULL) {
1382 ERROR("L matrix (logical operators) is required for quantum code (classical=0).\n"
1383 "Provide finL, fdem, or finG to construct it. Alternatively, set classical=1 "
1384 "to find the distance of the stabilizer code as a classical code.");
1385 }
1386 /* column masks of L: the check L c != 0 of a candidate codeword c takes O(|c|) operations (for up to 64 rows
1387 * of L) instead of O(nnz(L)) */
1388 p->maskL = colmask_from_csr(p->spaL);
1389 }
1390
1391 /* sort_rows: the rows of H by increasing weight (default: for matrices, not for a DEM, whose detectors are in time
1392 * order), once L is known; the distance, the codewords, and RW do not depend on the order of the rows of H */
1393 if (p->sort_rows < 0)
1394 p->sort_rows = p->fdem ? 0 : 1;
1395 if (p->sort_rows) {
1396 p->rows_sorted = csr_sort_rows_by_weight(p->spaH);
1397 if (p->rows_sorted)
1398 csr_dump(stderr, p->spaH, "H (rows sorted by weight)", p->debug);
1399 }
1400
1401 if ((p->method <= 0) || (p->method > 3)){
1402 fprintf(stderr, "%s: invalid method=%d specified\n\n", argv[0], p->method);
1403 print_short_help(argv[0]);
1404 exit(-1);
1405 }
1406
1407 if (p->debug & DBG_SUMMARY)
1408 print_input_summary(stderr, p);
1409 if (p->debug & DBG_PARAMS)
1410 print_code_params(stderr, p);
1411
1412 if ((p->debug & DBG_SUMMARY) && !p->fdem && (p->method & 2) && p->smax == 0) {
1413 fprintf(stderr, "# Warning: smax=0, confinement profile is not computed\n");
1414 }
1415
1416 /* Warnings for the expert options (ortho and the CC options) and for the experimental ksub (printed regardless of
1417 * `debug`) */
1418 if (!p->ortho) {
1419 if (p->spaG)
1420 fprintf(stderr, "# WARNING: ortho=0 (expert option): the rows of H and G are not checked for orthogonality\n"
1421 "# (H G^T = 0); the result is valid only if they are orthogonal\n");
1422 else
1423 fprintf(stderr, "# WARNING: ortho=0 is ignored without finG (the check H G^T = 0 needs G)\n");
1424 }
1425 if (p->noscan) {
1426 const int d0 = (p->dmin > 1) ? p->dmin : 1;
1427 if (d0 < p->wmax)
1428 fprintf(stderr,
1429 "# WARNING: noscan=1 (expert option): CC checks only weight w=wmax=%d; weights %d..%d are not\n"
1430 "# scanned, so a codeword found only gives an upper bound (dmax), and dmin=%d is not raised\n",
1431 p->wmax, d0, p->wmax - 1, d0);
1432 else
1433 fprintf(stderr,
1434 "# WARNING: noscan=1 (expert option): CC checks only weight w=wmax=%d; the result relies on\n"
1435 "# the supplied dmin=%d being a certified lower bound\n", p->wmax, p->dmin);
1436 }
1437 if (p->start_num > 0) {
1438 fprintf(stderr, "# WARNING: start=");
1439 for (int k = 0; (k < p->start_num) && (k < 8); k++)
1440 fprintf(stderr, "%s%d", k ? "," : "", col_orig_index(p, p->start_list[k]));
1441 if (p->start_scan)
1442 fprintf(stderr,
1443 "%s start_scan=1 (expert options): CC clusters are grown only from %d listed column(s), each with\n"
1444 "# the larger columns only; dmin is valid only if a code symmetry maps every minimum-weight codeword\n"
1445 "# to one whose smallest column is listed\n",
1446 (p->start_num > 8) ? ",..." : "", p->start_num);
1447 else
1448 fprintf(stderr,
1449 "%s (expert option): CC clusters are grown only from %d listed column(s)\n"
1450 "# (not limited to larger column indices); dmin is valid only if a code symmetry maps every\n"
1451 "# minimum-weight codeword to one containing a listed column\n",
1452 (p->start_num > 8) ? ",..." : "", p->start_num);
1453 }
1454 if (p->cbeg >= 0 || p->cend >= 0) {
1455 const int beg = (p->cbeg >= 0) ? p->cbeg : 0;
1456 const int end = (p->cend >= 0) ? p->cend : n_orig - 1;
1457 fprintf(stderr,
1458 "# WARNING: cbeg/cend (expert option for split runs): CC starts only from columns [%d,%d];\n"
1459 "# dmin only covers codewords whose smallest column is in this range; take the minimum of\n"
1460 "# dmin (and of dmax) over runs covering all columns 0..%d\n", beg, end, n_orig - 1);
1461 if (p->col_class) { /* the merged columns with their first (original) column in the range (in order) */
1462 int b = 0, e = -1;
1463 for (int j = 0; j < p->nvar; j++) {
1464 if (p->col_orig[j] < beg) b = j + 1;
1465 if (p->col_orig[j] <= end) e = j;
1466 }
1467 p->cbeg = b;
1468 p->cend = e;
1469 }
1470 }
1471 if ((p->win_hyb > 0) && (p->method & 1)) {
1472 if (p->kwin > 0)
1473 fprintf(stderr, "# WARNING: win_hyb=%d is ignored with kwin=%d (a window of kwin columns on every RW step)\n",
1474 p->win_hyb, p->kwin);
1475 else if (p->nvar < 500)
1476 fprintf(stderr, "# WARNING: win_hyb=%d is ignored for n=%d < 500 (uniform permutations in all RW steps)\n",
1477 p->win_hyb, p->nvar);
1478 }
1479 if ((p->pairs > 0) && (p->ksub > 0) && (p->method & 1))
1480 fprintf(stderr, "# WARNING: pairs=%d is ignored with ksub=%d (no RW pairs with the experimental ksub)\n", p->pairs,
1481 p->ksub);
1482 /* The experimental RW option ksub (with or without min_hits, also if it falls back to ksub=0 for m < nu) */
1483 if (p->ksub > 0 && (p->method & 1)) {
1484 fprintf(stderr,
1485 "# WARNING: ksub=%d is experimental and should not be used: a RW step reduces the span of ksub rows\n"
1486 "# of a fixed basis of ker(H), so that a few codewords are found much more often than others; with\n"
1487 "# min_hits, RW may end early, and exp(-<n>) underestimates the probability to miss a lighter codeword\n",
1488 p->ksub);
1489 }
1490}
1491
1492void var_kill(params_t * const p){
1493 if (p->start_list) {
1494 free(p->start_list);
1495 p->start_list = NULL;
1496 p->start_num = 0;
1497 }
1498 free(p->col_orig);
1499 free(p->col_class);
1500 p->col_orig = p->col_class = NULL;
1501 free(p->drop_list);
1502 p->drop_list = NULL;
1503 if(p->spaL)
1504 csr_free(p->spaL);
1505 p->maskL = colmask_free(p->maskL);
1506 if(p->spaH)
1507 csr_free(p->spaH);
1508 if(p->spaG)
1509 csr_free(p->spaG);
1510 if (strlen(p->fin) != 0) {
1511 if (p->finH) {
1512 free(p->finH);
1513 p->finH = NULL;
1514 }
1515 if (p->finG) {
1516 free(p->finG);
1517 p->finG = NULL;
1518 }
1519 }
1520
1521 cw_vec_t *cw, *tmp;
1522 HASH_ITER(hh, p->codewords, cw, tmp) {
1523 HASH_DEL(p->codewords, cw);
1524 free(cw);
1525 }
1526 p->num_cws = 0;
1527 p->min_w = INT_MAX;
1528 p->cw_max_w = 0;
1529 free(p->cw_cnt_w);
1530 free(p->cw_hits_w);
1531 p->cw_cnt_w = NULL;
1532 p->cw_hits_w = NULL;
1533}
1534
1535typedef struct {
1536 char **lines;
1537 int size;
1540
1541static dem_program_t *read_dem_to_program(const char *fnam) {
1542 FILE *f = fopen(fnam, "r");
1543 if (f == NULL) {
1544 printf("FILE I/O ERROR: %s\n", strerror(errno));
1545 ERROR("can't open the (DEM) file %s for reading\n", fnam);
1546 }
1547
1548 dem_program_t *prog = malloc(sizeof(dem_program_t));
1549 prog->size = 0;
1550 prog->capacity = 100;
1551 prog->lines = malloc(prog->capacity * sizeof(char *));
1552
1553 char *buf = NULL;
1554 size_t bufsiz = 0;
1555 ssize_t linelen;
1556
1557 while ((linelen = getline(&buf, &bufsiz, f)) >= 0) {
1558 if (linelen > 0 && buf[linelen - 1] == '\n') {
1559 buf[linelen - 1] = '\0';
1560 }
1561 if (prog->size >= prog->capacity) {
1562 prog->capacity *= 2;
1563 prog->lines = realloc(prog->lines, prog->capacity * sizeof(char *));
1564 }
1565 prog->lines[prog->size++] = strdup(buf);
1566 }
1567 if (buf) free(buf);
1568 fclose(f);
1569 return prog;
1570}
1571
1572static void free_dem_program(dem_program_t *prog) {
1573 for (int i = 0; i < prog->size; i++) {
1574 free(prog->lines[i]);
1575 }
1576 free(prog->lines);
1577 free(prog);
1578}
1579
1580/* The `cnt` entries (row, column) of one DEM error line, all in the same column: sort them by row (insertion sort, few
1581 * entries), remove the pairs of equal rows (the sum mod 2), and return the number of entries left. The components of
1582 * a decomposed error (separated by `^`) are summed: a detector or an observable listed twice in the line cancels. */
1583static int dem_line_mod2(int_pair * const e, const int cnt) {
1584 for (int i = 1; i < cnt; i++) {
1585 const int_pair x = e[i];
1586 int j = i - 1;
1587 while (j >= 0 && e[j].a > x.a) {
1588 e[j + 1] = e[j];
1589 j--;
1590 }
1591 e[j + 1] = x;
1592 }
1593 int out = 0;
1594 for (int i = 0; i < cnt;) {
1595 if (i + 1 < cnt && e[i].a == e[i + 1].a) {
1596 i += 2; /* a repeated entry cancels */
1597 } else {
1598 e[out++] = e[i++];
1599 }
1600 }
1601 return out;
1602}
1603
1604static void parse_instructions(dem_program_t *prog, int *p_line_idx,
1605 int *p_iD, int_pair **p_inH, int *p_maxH, int *p_r,
1606 int *p_iL, int_pair **p_inL, int *p_maxL, int *p_k,
1607 int *p_n, double pmin, int *p_detector_shift, int debug) {
1608 while (*p_line_idx < prog->size) {
1609 char *line = prog->lines[*p_line_idx];
1610 (*p_line_idx)++;
1611
1612 char *c = line;
1613 while (isspace(*c)) c++;
1614
1615 if (*c == '\0' || *c == '#') continue;
1616
1617 if (*c == '}') {
1618 return;
1619 }
1620
1621 int num = 0;
1622 int val = 0;
1623 double prob = 0.0;
1624
1625 // Parse repeat
1626 if (sscanf(c, "repeat %d { %n", &val, &num) == 1) {
1627 int start_idx = *p_line_idx;
1628 int temp_idx = start_idx;
1629 for (int r = 0; r < val; r++) {
1630 temp_idx = start_idx;
1631 parse_instructions(prog, &temp_idx,
1632 p_iD, p_inH, p_maxH, p_r,
1633 p_iL, p_inL, p_maxL, p_k,
1634 p_n, pmin, p_detector_shift, debug);
1635 }
1636 if (val > 0) {
1637 *p_line_idx = temp_idx;
1638 } else {
1639 int depth = 1;
1640 while (*p_line_idx < prog->size && depth > 0) {
1641 char *s = prog->lines[*p_line_idx];
1642 (*p_line_idx)++;
1643 while (isspace(*s)) s++;
1644 if (strncmp(s, "repeat", 6) == 0 && strchr(s, '{')) depth++;
1645 if (*s == '}') depth--;
1646 }
1647 }
1648 continue;
1649 }
1650
1651 // Parse shift_detectors
1652 int shift_val = 0;
1653 if (sscanf(c, "shift_detectors ( %*[^)] ) %d %n", &shift_val, &num) == 1) {
1654 *p_detector_shift += shift_val;
1655 continue;
1656 } else if (sscanf(c, "shift_detectors %d %n", &shift_val, &num) == 1) {
1657 *p_detector_shift += shift_val;
1658 continue;
1659 }
1660
1661 // Parse error
1662 if (sscanf(c, "error( %lg ) %n", &prob, &num) == 1) {
1663 if ((prob <= 0) || (prob >= 1))
1664 ERROR("probability should be in (0,1) exclusive p=%g\n"
1665 "line %d: '%s'\n", prob, *p_line_idx, line);
1666 c += num;
1667
1668 if (prob < pmin) {
1669 continue;
1670 }
1671 const int iD0 = *p_iD, iL0 = *p_iL; /* the first entries of this error line */
1672
1673 while (1) {
1674 while (isspace(c[0])) c++;
1675 if (c[0] == '\0' || c[0] == '#' || c[0] == '\n') break;
1676
1677 num = 0;
1678 if (sscanf(c, "D%d%n", &val, &num) == 1) {
1679 c += num;
1680 assert(val >= 0);
1681 int shifted_val = val + *p_detector_shift;
1682 if (shifted_val >= *p_r)
1683 *p_r = shifted_val + 1;
1684 if (*p_iD >= *p_maxH) {
1685 *p_maxH = 2 * (*p_maxH);
1686 *p_inH = realloc(*p_inH, (*p_maxH) * sizeof(**p_inH));
1687 }
1688 (*p_inH)[*p_iD].a = shifted_val;
1689 (*p_inH)[*p_iD].b = *p_n;
1690 (*p_iD)++;
1691 } else if (sscanf(c, "L%d%n", &val, &num) == 1) {
1692 c += num;
1693 assert(val >= 0);
1694 if (val >= *p_k)
1695 *p_k = val + 1;
1696 if (*p_iL >= *p_maxL) {
1697 *p_maxL = 2 * (*p_maxL);
1698 *p_inL = realloc(*p_inL, (*p_maxL) * sizeof(**p_inL));
1699 }
1700 (*p_inL)[*p_iL].a = val;
1701 (*p_inL)[*p_iL].b = *p_n;
1702 (*p_iL)++;
1703 } else if (c[0] == '^') {
1704 c++;
1705 } else {
1706 ERROR("unrecognized entry %s in error line %d: '%s'\n", c, *p_line_idx, line);
1707 }
1708 }
1709 /* the components separated by `^` are summed mod 2 */
1710 *p_iD = iD0 + dem_line_mod2(*p_inH + iD0, *p_iD - iD0);
1711 *p_iL = iL0 + dem_line_mod2(*p_inL + iL0, *p_iL - iL0);
1712 (*p_n)++;
1713 continue;
1714 }
1715
1716 if (strncmp(c, "detector", 8) == 0) {
1717 continue;
1718 }
1719
1720 if (strncmp(c, "logical_observable", 18) == 0) {
1721 continue;
1722 }
1723
1724 ERROR("unrecognized DEM entry in line %d: '%s'\n", *p_line_idx, line);
1725 }
1726}
1727
1728void read_dem_file(char *fnam, csr_t **p_spaH, csr_t **p_spaL, double pmin, int debug){
1729 dem_program_t *prog = read_dem_to_program(fnam);
1730
1731 int maxH=100, maxL=100;
1732 int_pair * inH = malloc(maxH*sizeof(int_pair));
1733 int_pair * inL = malloc(maxL*sizeof(int_pair));
1734 if ((!inH)||(!inL))
1735 ERROR("memory allocation failed\n");
1736
1737 int r=-1, k=-1, n=0;
1738 int iD=0, iL=0;
1739 int detector_shift = 0;
1740 int line_idx = 0;
1741
1742 parse_instructions(prog, &line_idx,
1743 &iD, &inH, &maxH, &r,
1744 &iL, &inL, &maxL, &k,
1745 &n, pmin, &detector_shift, debug);
1746
1747 if (line_idx < prog->size) {
1748 ERROR("Unmatched '}' in DEM file %s at line %d\n", fnam, line_idx);
1749 }
1750
1751 if (debug & DBG_ARGS)
1752 fprintf(stderr, "# read DEM %s: rows_H=%d rows_L=%d cols=%d; nz_H=%d nz_L=%d\n",fnam,r,k,n,iD,iL);
1753 if((r<=0)||(k<=0)||(n<=0))
1754 ERROR("invalid DEM file %s: rows_H=%d rows_L=%d cols=%d; nz_H=%d nz_L=%d\n",
1755 fnam,r,k,n,iD,iL);
1756
1757 *p_spaH = csr_from_pairs(*p_spaH, iD, inH, r, n);
1758 *p_spaL = csr_from_pairs(*p_spaL, iL, inL, k, n);
1759
1760 free(inH);
1761 free(inL);
1762 free_dem_program(prog);
1763}
1764
1765/* A 64-bit hash of an integer (the splitmix64 output function) */
1766static inline uint64_t dem_hash64(uint64_t z) {
1767 z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9ULL;
1768 z = (z ^ (z >> 27)) * 0x94d049bb133111ebULL;
1769 return z ^ (z >> 31);
1770}
1771
1772typedef struct {
1773 uint64_t h;
1774 int j;
1776
1777static int dem_col_hash_cmp(const void *a, const void *b) {
1778 const dem_col_hash_t *x = a, *y = b;
1779 if (x->h != y->h) return (x->h < y->h) ? -1 : 1;
1780 return (x->j > y->j) - (x->j < y->j);
1781}
1782
1783/* Are the columns a and b equal in H and L (rows a and b of their transposes HT and LT, sorted)? */
1784static int dem_cols_equal(const csr_t * const HT, const csr_t * const LT, const int a, const int b) {
1785 const int wh = HT->p[a + 1] - HT->p[a], wl = LT->p[a + 1] - LT->p[a];
1786 if ((wh != HT->p[b + 1] - HT->p[b]) || (wl != LT->p[b + 1] - LT->p[b])) return 0;
1787 return (memcmp(HT->i + HT->p[a], HT->i + HT->p[b], (size_t)wh * sizeof(int)) == 0) &&
1788 (memcmp(LT->i + LT->p[a], LT->i + LT->p[b], (size_t)wl * sizeof(int)) == 0);
1789}
1790
1791/* The rows of the CSR matrix M restricted to the columns j with keep[j] >= 0, renumbered to keep[j] (increasing in j,
1792 * so that the rows stay sorted) */
1793static csr_t * csr_keep_columns(const csr_t * const M, const int * const keep, const int ncols) {
1794 int nz = 0;
1795 for (int t = 0; t < M->p[M->rows]; t++) nz += (keep[M->i[t]] >= 0);
1796 csr_t * const R = csr_init(NULL, M->rows, ncols, nz > 0 ? nz : 1);
1797 int t2 = 0;
1798 for (int r = 0; r < M->rows; r++) {
1799 R->p[r] = t2;
1800 for (int t = M->p[r]; t < M->p[r + 1]; t++)
1801 if (keep[M->i[t]] >= 0) R->i[t2++] = keep[M->i[t]];
1802 }
1803 R->p[M->rows] = t2;
1804 R->nz = -1; /* compressed form */
1805 return R;
1806}
1807
1808/* Merge the equal columns of the DEM matrices H and L (error mechanisms with the same detectors and observables, e.g.,
1809 * from stim's DEM of a circuit with a REPEAT block): a codeword with two equal columns contains a lighter one (without
1810 * both), so that the distance does not change, only the number of codewords. The first column of each class
1811 * represents it, and the classes keep the order of their first columns, so that a sorted support stays sorted when
1812 * mapped back to the original columns (p->col_orig; p->col_class: original column -> class). Nothing changes if all
1813 * columns are distinct. */
1814static void dem_merge_columns(params_t * const p) {
1815 const int n = p->spaH->cols;
1816 if (n <= 1) return;
1817 csr_t * const HT = csr_transpose(NULL, p->spaH);
1818 csr_t * const LT = csr_transpose(NULL, p->spaL);
1819 dem_col_hash_t * const ch = malloc((size_t)n * sizeof(dem_col_hash_t));
1820 int * const rep = malloc((size_t)n * sizeof(int)); /* the first column equal to column j */
1821 if (!ch || !rep) ERROR("memory allocation");
1822 for (int j = 0; j < n; j++) { /* (XOR of row hashes: equal columns have equal hashes) */
1823 uint64_t h = 0;
1824 for (int t = HT->p[j]; t < HT->p[j + 1]; t++) h ^= dem_hash64(2 * (uint64_t)HT->i[t] + 1);
1825 for (int t = LT->p[j]; t < LT->p[j + 1]; t++) h ^= dem_hash64(2 * (uint64_t)LT->i[t] + 2);
1826 ch[j].h = h;
1827 ch[j].j = j;
1828 }
1829 qsort(ch, n, sizeof(dem_col_hash_t), dem_col_hash_cmp);
1830 int nclass = 0;
1831 const int strip_zero_hl = (p->classical != 1 && p->smax == 0);
1832 for (int a = 0; a < n;) { /* a run of equal hashes, in increasing column order */
1833 int b = a + 1;
1834 while (b < n && ch[b].h == ch[a].h) b++;
1835 for (int s = a; s < b; s++) {
1836 const int j = ch[s].j;
1837 if (strip_zero_hl && HT->p[j + 1] == HT->p[j] && LT->p[j + 1] == LT->p[j]) {
1838 rep[j] = -1; /* (0,0) column: no detectors and no observables */
1839 continue;
1840 }
1841 rep[j] = j;
1842 for (int t = a; t < s; t++) { /* an earlier column of the run (a first one) equal to j */
1843 const int i = ch[t].j;
1844 if (rep[i] == i && dem_cols_equal(HT, LT, i, j)) {
1845 rep[j] = i;
1846 break;
1847 }
1848 }
1849 if (rep[j] == j) nclass++;
1850 }
1851 a = b;
1852 }
1853 free(ch);
1854 csr_free(HT);
1855 csr_free(LT);
1856 if (nclass == n || nclass == 0) { /* all columns distinct (or all (0,0)) */
1857 free(rep);
1858 return;
1859 }
1860 int * const col_class = malloc((size_t)n * sizeof(int));
1861 int * const col_orig = malloc((size_t)nclass * sizeof(int));
1862 int * const keep = malloc((size_t)n * sizeof(int));
1863 if (!col_class || !col_orig || !keep) ERROR("memory allocation");
1864 for (int j = 0, c = 0; j < n; j++) {
1865 if (rep[j] == j) {
1866 col_orig[c] = j;
1867 col_class[j] = keep[j] = c++;
1868 } else if (rep[j] >= 0) {
1869 col_class[j] = col_class[rep[j]]; /* (rep[j] < j) */
1870 keep[j] = -1;
1871 } else {
1872 col_class[j] = keep[j] = -1;
1873 }
1874 }
1875 csr_t * const H2 = csr_keep_columns(p->spaH, keep, nclass);
1876 csr_t * const L2 = csr_keep_columns(p->spaL, keep, nclass);
1877 csr_free(p->spaH);
1878 csr_free(p->spaL);
1879 p->spaH = H2;
1880 p->spaL = L2;
1881 p->nvar_orig = n;
1882 p->nvar_merged = nclass;
1883 p->col_orig = col_orig;
1884 p->col_class = col_class;
1885 free(keep);
1886 free(rep);
1887}
1888
1889/* Are rows a and b of the CSR matrix M (sorted) equal? */
1890static int csr_rows_equal(const csr_t * const M, const int a, const int b) {
1891 const int w = M->p[a + 1] - M->p[a];
1892 return (w == M->p[b + 1] - M->p[b]) && (memcmp(M->i + M->p[a], M->i + M->p[b], (size_t)w * sizeof(int)) == 0);
1893}
1894
1895/* Are the kept detectors (inM[r] == 0) of column j the detectors of column t (rows of HT, sorted; t has no dropped
1896 * detector)? */
1897static int dem_kept_equal(const csr_t * const HT, const char * const inM, const int j, const int t) {
1898 int s = HT->p[t];
1899 for (int u = HT->p[j]; u < HT->p[j + 1]; u++) {
1900 if (inM[HT->i[u]]) continue;
1901 if (s >= HT->p[t + 1] || HT->i[s] != HT->i[u]) return 0;
1902 s++;
1903 }
1904 return s == HT->p[t + 1];
1905}
1906
1907/* The rows of the CSR matrix M with keep[r] >= 0, renumbered to keep[r] (increasing in r) */
1908static csr_t * csr_keep_rows(const csr_t * const M, const int * const keep, const int nrows) {
1909 int nz = 0;
1910 for (int r = 0; r < M->rows; r++)
1911 if (keep[r] >= 0) nz += M->p[r + 1] - M->p[r];
1912 csr_t * const R = csr_init(NULL, nrows, M->cols, nz > 0 ? nz : 1);
1913 int t2 = 0, r2 = 0;
1914 for (int r = 0; r < M->rows; r++) {
1915 if (keep[r] < 0) continue;
1916 R->p[r2++] = t2;
1917 for (int t = M->p[r]; t < M->p[r + 1]; t++) R->i[t2++] = M->i[t];
1918 }
1919 R->p[nrows] = t2;
1920 R->nz = -1; /* compressed form */
1921 return R;
1922}
1923
1924/* Read the detectors to drop from the file fnam (see dem_drop_detectors()): 0-based indices separated by white space or
1925 * commas, lines starting with '#' or '%' are comments. Sets inM[r] = 1 for the detectors r < m (a larger index is
1926 * ignored: no error mechanism has this detector) and returns their number. */
1927static int dem_read_drop_list(const char fnam[], const int m, char * const inM) {
1928 FILE * const f = fopen(fnam, "r");
1929 if (!f)
1930 ERROR("can't open the drop file '%s' for reading: %s", fnam, strerror(errno));
1931 char *line = NULL;
1932 size_t cap = 0;
1933 int num = 0, lineno = 0;
1934 while (getline(&line, &cap, f) != -1) {
1935 lineno++;
1936 char *s = line;
1937 while (isspace((unsigned char)*s)) s++;
1938 if (*s == '#' || *s == '%') continue;
1939 for (char *tok = strtok(s, " \t\r\n,"); tok; tok = strtok(NULL, " \t\r\n,")) {
1940 char *end;
1941 errno = 0;
1942 const long v = strtol(tok, &end, 10);
1943 if ((*end != '\0') || errno || (v < 0) || (v > INT_MAX))
1944 ERROR("drop file '%s', line %d: invalid entry '%s' (expected 0-based detector indices)", fnam, lineno, tok);
1945 if ((v < m) && !inM[v]) {
1946 inM[v] = 1;
1947 num++;
1948 }
1949 }
1950 }
1951 free(line);
1952 fclose(f);
1953 return num;
1954}
1955
1956/* The drop= setting, for messages: "auto", "0", or the file name */
1957static const char * drop_label(const params_t * const p) {
1958 return (p->drop_mode == 1) ? "auto" : ((p->drop_mode == 2) ? p->fdrop : "0");
1959}
1960
1961enum { DROP_UNK = 0, DROP_YES = 1, DROP_NO = 2 };
1962
1963static double drop_wall_time(void) { /* seconds (monotonic clock) */
1964 struct timespec ts;
1965 clock_gettime(CLOCK_MONOTONIC, &ts);
1966 return (double)ts.tv_sec + 1e-9 * (double)ts.tv_nsec;
1967}
1968
1969/* The columns of the DEM to (re)examine in the propagation of dem_drop_auto(): a queue without duplicates */
1970typedef struct {
1971 int *q; /* the columns, a circular buffer of size n */
1972 char *in; /* in[j]: column j is in the queue */
1973 int n, head, len;
1974} drop_queue_t;
1975
1976static void drop_queue_push(drop_queue_t * const Q, const int j) {
1977 if (Q->in[j]) return;
1978 Q->in[j] = 1;
1979 Q->q[(Q->head + Q->len++) % Q->n] = j;
1980}
1981
1982static int drop_queue_pop(drop_queue_t * const Q) {
1983 const int j = Q->q[Q->head];
1984 Q->head = (Q->head + 1) % Q->n;
1985 Q->len--;
1986 Q->in[j] = 0;
1987 return j;
1988}
1989
1990static int drop_find(int * const parent, int x) { /* union-find with path halving */
1991 while (parent[x] != x) {
1992 parent[x] = parent[parent[x]];
1993 x = parent[x];
1994 }
1995 return x;
1996}
1997
1998typedef struct { /* a group of detectors (dropped all or none), see dem_drop_auto() */
2000} drop_group_t;
2001
2002static int drop_group_cmp(const void *a, const void *b) { /* the largest groups first, then by their root */
2003 const drop_group_t *x = a, *y = b;
2004 if (x->size != y->size) return (x->size < y->size) ? 1 : -1;
2005 return (x->root > y->root) - (x->root < y->root);
2006}
2007
2008/* drop=auto: find a set M of detectors of the DEM which can be dropped exactly with A = 0 (every column j touching M
2009 * has a twin with the same other detectors and the same observables, or none if L(j) = 0 and j has no other detector,
2010 * see dem_drop_detectors()), from the DEM alone. Sets inM[r] = 1 for the detectors of M and returns |M|.
2011 * - The droppable sets are closed under union, so that there is a largest one, which gives the smallest problem.
2012 * - Sub(j): the columns t with the same observables whose detectors are a proper subset of those of j. If j touches
2013 * M, its detectors outside M are those of one t in Sub(j) (or none, if L(j) = 0). So a column with Sub(j) empty
2014 * (O-minimal) is dropped all or nothing (its detectors form a group), and not at all if L(j) != 0.
2015 * - Seeds: the groups not kept, the largest first. Once a detector of j is dropped, the candidates (the elements of
2016 * Sub(j), and none if L(j) = 0, consistent with the detectors of j already dropped or kept) force the others: a
2017 * detector in no candidate is dropped, one in all of them is kept (bit masks of the positions in j). Without any
2018 * candidate, no droppable set contains the seed group, which is kept. Otherwise, the detectors dropped are kept in M
2019 * if every column touching them has the mask of its other detectors in Sub(j).
2020 * A column with more than 64 detectors keeps its detectors (still exact). The result is verified again in
2021 * dem_drop_detectors(), which also allows A != 0 (drop=FILE). */
2022static int dem_drop_auto(const params_t * const p, char * const inM) {
2023 const csr_t * const H = p->spaH; /* row r: the columns with detector r */
2024 const int m = H->rows, n = H->cols;
2025 const double t0 = (p->debug & DBG_TIMING) ? drop_wall_time() : 0.0;
2026 csr_t * const HT = csr_transpose(NULL, p->spaH); /* row j: the detectors of column j (sorted) */
2027 csr_t * const LT = csr_transpose(NULL, p->spaL); /* row j: the observables of column j (sorted) */
2028 int * const fp = calloc((size_t)m + 2, sizeof(int)); /* the columns by their first (smallest) detector */
2029 int * const fi = malloc(((size_t)n + 1) * sizeof(int));
2030 int * const stamp = calloc((size_t)m + 1, sizeof(int)); /* stamp[r] = j + 1: detector r is in column j */
2031 char * const posv = malloc((size_t)m + 1); /* the position of detector r in column j */
2032 int * const sp = malloc(((size_t)n + 1) * sizeof(int)); /* Sub(j): entries sp[j] .. sp[j + 1] - 1 */
2033 char * const big = calloc((size_t)n + 1, 1); /* column j has more than 64 detectors */
2034 if (!fp || !fi || !stamp || !posv || !sp || !big) ERROR("memory allocation");
2035 for (int j = 0; j < n; j++)
2036 if (HT->p[j + 1] > HT->p[j]) fp[HT->i[HT->p[j]] + 1]++;
2037 for (int r = 0; r < m; r++) fp[r + 1] += fp[r];
2038 for (int j = 0, *cnt = fp; j < n; j++) /* (fp[r] counts up to the end of the list of r, restored below) */
2039 if (HT->p[j + 1] > HT->p[j]) fi[cnt[HT->i[HT->p[j]]]++] = j;
2040 for (int r = m; r > 0; r--) fp[r] = fp[r - 1];
2041 fp[0] = 0;
2042 size_t cap = (size_t)n + 16, ns = 0;
2043 int *st = malloc(cap * sizeof(int)); /* Sub(j): the column t */
2044 uint64_t *sm = malloc(cap * sizeof(uint64_t)); /* Sub(j): the mask of the positions of the detectors of t in j */
2045 if (!st || !sm) ERROR("memory allocation");
2046 for (int j = 0; j < n; j++) {
2047 sp[j] = (int)ns;
2048 const int b = HT->p[j], w = HT->p[j + 1] - b;
2049 if (w > 64) big[j] = 1;
2050 if ((w < 2) || (w > 64)) continue;
2051 for (int u = 0; u < w; u++) {
2052 stamp[HT->i[b + u]] = j + 1;
2053 posv[HT->i[b + u]] = (char)u;
2054 }
2055 for (int u = 0; u < w; u++) { /* the columns t whose first detector is in j */
2056 const int r = HT->i[b + u];
2057 for (int s = fp[r]; s < fp[r + 1]; s++) {
2058 const int t = fi[s], bt = HT->p[t], wt = HT->p[t + 1] - bt;
2059 if ((wt >= w) || (stamp[HT->i[bt + wt - 1]] != j + 1) || !csr_rows_equal(LT, j, t)) continue;
2060 uint64_t mask = 0;
2061 int v = 0;
2062 for (; (v < wt) && (stamp[HT->i[bt + v]] == j + 1); v++) mask |= 1ULL << posv[HT->i[bt + v]];
2063 if (v < wt) continue;
2064 if (ns == cap) {
2065 cap *= 2;
2066 st = realloc(st, cap * sizeof(int));
2067 sm = realloc(sm, cap * sizeof(uint64_t));
2068 if (!st || !sm) ERROR("memory allocation");
2069 }
2070 st[ns] = t;
2071 sm[ns++] = mask;
2072 }
2073 }
2074 }
2075 sp[n] = (int)ns;
2076 free(posv);
2077 free(stamp);
2078 free(fi);
2079 free(fp);
2080 /* the groups of the O-minimal columns (all or nothing); kept: those with observables, and big columns */
2081 int * const parent = malloc(((size_t)m + 1) * sizeof(int));
2082 char * const state = calloc((size_t)m + 1, 1);
2083 char * const keepg = calloc((size_t)m + 1, 1); /* keepg[root]: the group is kept */
2084 if (!parent || !state || !keepg) ERROR("memory allocation");
2085 for (int r = 0; r < m; r++) parent[r] = r;
2086 for (int j = 0; j < n; j++) {
2087 const int b = HT->p[j], w = HT->p[j + 1] - b;
2088 if ((w == 0) || big[j] || (sp[j + 1] > sp[j])) continue;
2089 const int r0 = drop_find(parent, HT->i[b]);
2090 for (int u = 1; u < w; u++) {
2091 const int r = drop_find(parent, HT->i[b + u]);
2092 if (r != r0) parent[r] = r0;
2093 }
2094 }
2095 for (int j = 0; j < n; j++) {
2096 const int b = HT->p[j], w = HT->p[j + 1] - b;
2097 if (w == 0) continue;
2098 if (big[j]) {
2099 for (int u = 0; u < w; u++) keepg[drop_find(parent, HT->i[b + u])] = 1;
2100 } else if ((sp[j + 1] == sp[j]) && (LT->p[j + 1] > LT->p[j])) {
2101 keepg[drop_find(parent, HT->i[b])] = 1;
2102 }
2103 }
2104 int * const gsize = calloc((size_t)m + 1, sizeof(int));
2105 if (!gsize) ERROR("memory allocation");
2106 for (int r = 0; r < m; r++) /* (then parent[r] is the root of r for all r) */
2107 parent[r] = drop_find(parent, r);
2108 for (int r = 0; r < m; r++)
2109 if (H->p[r + 1] == H->p[r]) keepg[r] = 1; /* (a detector in no column is kept: an empty row of H) */
2110 for (int r = 0; r < m; r++) {
2111 const int g = parent[r];
2112 gsize[g]++;
2113 if (keepg[g]) state[r] = DROP_NO;
2114 }
2115 int ng = 0;
2116 for (int r = 0; r < m; r++) ng += (parent[r] == r) && !keepg[r];
2117 drop_group_t * const grp = malloc(((size_t)ng + 1) * sizeof(drop_group_t));
2118 int * const gp = calloc((size_t)m + 2, sizeof(int)); /* the detectors by group */
2119 int * const gi = malloc(((size_t)m + 1) * sizeof(int));
2120 if (!grp || !gp || !gi) ERROR("memory allocation");
2121 for (int r = 0, g = 0; r < m; r++)
2122 if ((parent[r] == r) && !keepg[r]) grp[g++] = (drop_group_t){.root = r, .size = gsize[r]};
2123 qsort(grp, ng, sizeof(drop_group_t), drop_group_cmp);
2124 for (int r = 0; r < m; r++) gp[parent[r] + 1]++;
2125 for (int r = 0; r < m; r++) gp[r + 1] += gp[r];
2126 for (int r = 0, *cnt = gsize; r < m; r++) cnt[r] = gp[r];
2127 for (int r = 0; r < m; r++) gi[gsize[parent[r]]++] = r;
2128 /* the seeds: propagation, with an undo log */
2129 drop_queue_t Q = {.q = malloc(((size_t)n + 1) * sizeof(int)), .in = calloc((size_t)n + 1, 1), .n = n + 1};
2130 int * const ulog = malloc(((size_t)m + 1) * sizeof(int));
2131 if (!Q.q || !Q.in || !ulog) ERROR("memory allocation");
2132 int n_ok = 0, n_fail = 0, n_unver = 0;
2133 long long int work = 0;
2134 const long long int work_max = 64LL * (HT->p[n] + n) + 1000000LL; /* (seeds are skipped beyond this budget) */
2135 for (int g = 0; g < ng; g++) {
2136 const int g0 = gp[grp[g].root], g1 = gp[grp[g].root + 1];
2137 int unk = (g0 < g1);
2138 for (int s = g0; (s < g1) && unk; s++) unk = (state[gi[s]] == DROP_UNK);
2139 if (!unk) continue; /* (already dropped or kept) */
2140 if (work > work_max) break;
2141 int nlog = 0, fail = 0;
2142 for (int s = g0; s < g1; s++) {
2143 const int r = gi[s];
2144 state[r] = DROP_YES;
2145 ulog[nlog++] = r;
2146 for (int t = H->p[r]; t < H->p[r + 1]; t++) drop_queue_push(&Q, H->i[t]);
2147 }
2148 while (Q.len > 0) {
2149 const int j = drop_queue_pop(&Q);
2150 if (fail) continue; /* (empties the queue) */
2151 work++;
2152 const int b = HT->p[j], w = HT->p[j + 1] - b;
2153 if (big[j]) continue; /* (its detectors are kept) */
2154 uint64_t md = 0, mk = 0, mu = 0;
2155 for (int u = 0; u < w; u++) {
2156 const char s = state[HT->i[b + u]];
2157 if (s == DROP_YES) md |= 1ULL << u;
2158 else if (s == DROP_NO) mk |= 1ULL << u;
2159 else mu |= 1ULL << u;
2160 }
2161 if (!md) continue;
2162 uint64_t any = 0, all = ~0ULL;
2163 int nc = 0;
2164 for (int s = sp[j]; s < sp[j + 1]; s++) {
2165 if ((sm[s] & md) || ((sm[s] & mk) != mk)) continue;
2166 any |= sm[s];
2167 all &= sm[s];
2168 nc++;
2169 }
2170 if ((LT->p[j + 1] == LT->p[j]) && !mk) { /* none: all detectors of j dropped */
2171 all = 0;
2172 nc++;
2173 }
2174 if (nc == 0) {
2175 fail = 1;
2176 continue;
2177 }
2178 const uint64_t fd = mu & ~any, fk = mu & all;
2179 for (int u = 0; u < w; u++) {
2180 if (!((fd | fk) & (1ULL << u))) continue;
2181 const int r = HT->i[b + u];
2182 state[r] = (fd & (1ULL << u)) ? DROP_YES : DROP_NO;
2183 ulog[nlog++] = r;
2184 for (int t = H->p[r]; t < H->p[r + 1]; t++) drop_queue_push(&Q, H->i[t]);
2185 }
2186 }
2187 if (!fail) { /* the check: the other detectors of every column touching a detector dropped here */
2188 for (int l = 0; (l < nlog) && !fail; l++) {
2189 const int r = ulog[l];
2190 if (state[r] != DROP_YES) continue;
2191 for (int t = H->p[r]; (t < H->p[r + 1]) && !fail; t++) {
2192 const int j = H->i[t], b = HT->p[j], w = HT->p[j + 1] - b;
2193 uint64_t mo = 0; /* the positions of the detectors of j not dropped */
2194 for (int u = 0; u < w; u++)
2195 if (state[HT->i[b + u]] != DROP_YES) mo |= 1ULL << u;
2196 int ok = (mo == 0) && (LT->p[j + 1] == LT->p[j]);
2197 for (int s = sp[j]; (s < sp[j + 1]) && !ok; s++) ok = (sm[s] == mo);
2198 if (!ok) fail = 2;
2199 }
2200 }
2201 }
2202 if (fail) { /* undo; a contradiction (fail = 1): no droppable set contains the group, which is kept */
2203 for (int l = 0; l < nlog; l++) state[ulog[l]] = DROP_UNK;
2204 if (fail == 1) {
2205 for (int s = g0; s < g1; s++) state[gi[s]] = DROP_NO;
2206 n_fail++;
2207 } else {
2208 n_unver++;
2209 }
2210 } else {
2211 n_ok++;
2212 }
2213 }
2214 int num = 0;
2215 for (int r = 0; r < m; r++)
2216 if (state[r] == DROP_YES) {
2217 inM[r] = 1;
2218 num++;
2219 }
2220 if (p->debug & DBG_TIMING)
2221 fprintf(stderr, "# drop=auto: %d of %d detectors found (%d groups to try: %d dropped, %d kept, %d not verified%s; "
2222 "%zu sub-columns), %.3f s\n", num, m, ng, n_ok, n_fail, n_unver,
2223 (work > work_max) ? ", work limit reached" : "", ns, drop_wall_time() - t0);
2224 free(ulog);
2225 free(Q.in);
2226 free(Q.q);
2227 free(gi);
2228 free(gp);
2229 free(grp);
2230 free(gsize);
2231 free(keepg);
2232 free(state);
2233 free(parent);
2234 free(sm);
2235 free(st);
2236 free(big);
2237 free(sp);
2238 csr_free(LT);
2239 csr_free(HT);
2240 return num;
2241}
2242
2243/* Write the detectors dropped from the DEM (0-based, increasing, see dem_drop_detectors()) to the file p->outD, as a
2244 * drop file (also when none is dropped: then only the comment line). */
2245static void dem_write_drop_list(const params_t * const p) {
2246 FILE * const f = fopen(p->outD, "w");
2247 if (!f)
2248 ERROR("can't open the file outD='%s' for writing: %s", p->outD, strerror(errno));
2249 const int m = (p->ndrop > 0) ? p->ndrop_rows : p->spaH->rows;
2250 fprintf(f, "# %d of %d detectors dropped (drop=%s%s), 0-based\n", p->ndrop, m,
2251 (p->classical == 1) ? "0, classical=1" : drop_label(p), p->drop_none ? ": none can be dropped exactly" : "");
2252 for (int i = 0; i < p->ndrop; i++)
2253 fprintf(f, "%d%c", p->drop_list[i], ((i % 16 == 15) || (i == p->ndrop - 1)) ? '\n' : ' ');
2254 fclose(f);
2255 if (p->debug & DBG_ARGS)
2256 fprintf(stderr, "# wrote the %d detectors dropped to '%s'\n", p->ndrop, p->outD);
2257}
2258
2259/* Drop the detectors (a set M) from a DEM if this is verified to be exact: those found by dem_drop_auto() (drop=auto,
2260 * the default), or those listed in the file p->fdrop (e.g., the minority-basis detectors of a CSS circuit). Every
2261 * column j touching M needs a twin: a column t touching no detector
2262 * of M with the same other (kept) detectors, or no column if j has no kept detector, such that L(t) = L(j) + A H_M(j)
2263 * for one binary matrix A (A = 0 for the usual CSS circuits; found by elimination otherwise). Replacing the columns
2264 * of a codeword c by their twins (two equal twins cancel) gives a codeword of weight at most |c| without the detectors
2265 * M: the kept detectors do not change, and L changes by A H_M c = 0. So the distance of the DEM is that of the
2266 * submatrix of the kept detectors and the columns touching no detector of M (with L), and its codewords are codewords
2267 * of the DEM. Otherwise, a note is printed and the DEM is not changed. The column maps p->col_orig and p->col_class
2268 * (see dem_merge_columns()) refer to the original columns; a column with no kept detector maps to -1 (it is dropped
2269 * from a finC codeword). The detectors dropped are kept in p->drop_list (for outD). */
2270static void dem_drop_detectors(params_t * const p) {
2271 const int m = p->spaH->rows, n = p->spaH->cols, k = p->spaL->rows;
2272 char * const inM = calloc((size_t)m + 1, 1);
2273 if (!inM) ERROR("memory allocation");
2274 const char * const label = drop_label(p);
2275 const int num = (p->drop_mode == 1) ? dem_drop_auto(p, inM) : dem_read_drop_list(p->fdrop, m, inM);
2276 if (num == 0) {
2277 if (p->drop_mode == 1)
2278 p->drop_none = 1;
2279 else
2280 fprintf(stderr, "# Note: drop=%s: none of the listed detectors is in the DEM, nothing dropped\n", label);
2281 free(inM);
2282 return;
2283 }
2284 csr_t * const HT = csr_transpose(NULL, p->spaH);
2285 csr_t * const LT = csr_transpose(NULL, p->spaL);
2286 char * const touch = calloc((size_t)n + 1, 1); /* column j touches a detector of M */
2287 uint64_t * const hk = malloc(((size_t)n + 1) * sizeof(uint64_t)); /* hash of the kept detectors of a column */
2288 dem_col_hash_t * const hs = malloc(((size_t)n + 1) * sizeof(dem_col_hash_t)); /* the columns not touching M */
2289 int * const twin = malloc(((size_t)n + 1) * sizeof(int)); /* the twin of a column (itself if it does not touch M) */
2290 if (!touch || !hk || !hs || !twin) ERROR("memory allocation");
2291 int ns = 0, nj = 0;
2292 for (int j = 0; j < n; j++) {
2293 uint64_t h = 0;
2294 for (int t = HT->p[j]; t < HT->p[j + 1]; t++) {
2295 if (inM[HT->i[t]]) touch[j] = 1;
2296 else h ^= dem_hash64((uint64_t)HT->i[t] + 1);
2297 }
2298 hk[j] = h;
2299 if (touch[j]) {
2300 nj++;
2301 } else {
2302 hs[ns].h = h;
2303 hs[ns].j = j;
2304 ns++;
2305 }
2306 }
2307 qsort(hs, ns, sizeof(dem_col_hash_t), dem_col_hash_cmp);
2308 int fail = -1, need_A = 0;
2309 const char *why = NULL;
2310 for (int j = 0; (j < n) && (fail < 0); j++) {
2311 if (!touch[j]) {
2312 twin[j] = j;
2313 continue;
2314 }
2315 int nk = 0; /* the number of kept detectors of column j */
2316 for (int t = HT->p[j]; t < HT->p[j + 1]; t++) nk += !inM[HT->i[t]];
2317 if (nk == 0) { /* no column needed: L(j) = A H_M(j) */
2318 twin[j] = -1;
2319 if (LT->p[j + 1] > LT->p[j]) need_A = 1;
2320 continue;
2321 }
2322 int lo = 0, hi = ns; /* the first column not touching M with the hash of the kept detectors of j */
2323 while (lo < hi) {
2324 const int mid = lo + (hi - lo) / 2;
2325 if (hs[mid].h < hk[j]) lo = mid + 1;
2326 else hi = mid;
2327 }
2328 int tw = -1;
2329 for (int s = lo; (s < ns) && (hs[s].h == hk[j]); s++) {
2330 const int t = hs[s].j;
2331 if (!dem_kept_equal(HT, inM, j, t)) continue;
2332 if (tw < 0) {
2333 tw = t;
2334 } else if (!csr_rows_equal(LT, tw, t)) { /* (then the two columns are a codeword of weight 2) */
2335 fail = j;
2336 why = "it has two twins with different observables";
2337 break;
2338 }
2339 }
2340 if (fail >= 0) break;
2341 if (tw < 0) {
2342 fail = j;
2343 why = "no column with the same other detectors touches none of the dropped ones";
2344 break;
2345 }
2346 twin[j] = tw;
2347 if (!csr_rows_equal(LT, j, tw)) need_A = 1;
2348 }
2349 if ((fail < 0) && need_A) {
2350 /* a matrix A with A H_M(j) = L(j) + L(twin(j)) for all columns j touching M exists iff no row of the row echelon
2351 * form of the rows [H_M(j) | L(j) + L(twin(j))] has its leading entry in the L part */
2352 int * const mind = malloc(((size_t)m + 1) * sizeof(int)); /* the index of a detector of M among them */
2353 if (!mind) ERROR("memory allocation");
2354 for (int r = 0, c = 0; r < m; r++) mind[r] = inM[r] ? c++ : -1;
2355 mzd_t * const E = mzd_init(nj, num + k);
2356 for (int j = 0, row = 0; j < n; j++) {
2357 if (!touch[j]) continue;
2358 for (int t = HT->p[j]; t < HT->p[j + 1]; t++)
2359 if (inM[HT->i[t]]) mzd_write_bit(E, row, mind[HT->i[t]], 1);
2360 for (int t = LT->p[j]; t < LT->p[j + 1]; t++) mzd_flip_bit(E, row, num + LT->i[t]);
2361 if (twin[j] >= 0)
2362 for (int t = LT->p[twin[j]]; t < LT->p[twin[j] + 1]; t++) mzd_flip_bit(E, row, num + LT->i[t]);
2363 row++;
2364 }
2365 const int rank = mzd_echelonize(E, 0);
2366 for (int r = 0; (r < rank) && (fail < 0); r++) {
2367 int c = 0;
2368 while ((c < num) && !mzd_read_bit(E, r, c)) c++;
2369 if (c >= num) {
2370 fail = n; /* (not a single column) */
2371 why = "the observables of the twins are not L + A H for any matrix A";
2372 }
2373 }
2374 mzd_free(E);
2375 free(mind);
2376 }
2377 if (fail >= 0) {
2378 if (fail < n)
2379 fprintf(stderr, "# Note: drop=%s not used: column %d (1-based) touches a dropped detector, but %s; using the "
2380 "full DEM\n", label, 1 + col_orig_index(p, fail), why);
2381 else
2382 fprintf(stderr, "# Note: drop=%s not used: %s; using the full DEM\n", label, why);
2383 } else { /* the submatrix of the kept detectors and of the columns not touching M */
2384 int * const keepcol = malloc(((size_t)n + 1) * sizeof(int));
2385 int * const keeprow = malloc(((size_t)m + 1) * sizeof(int));
2386 if (!keepcol || !keeprow) ERROR("memory allocation");
2387 int n2 = 0, m2 = 0;
2388 for (int j = 0; j < n; j++) keepcol[j] = touch[j] ? -1 : n2++;
2389 for (int r = 0; r < m; r++) keeprow[r] = inM[r] ? -1 : m2++;
2390 const int n_orig = (p->nvar_orig > 0) ? p->nvar_orig : n;
2391 int * const col_orig = malloc(((size_t)n2 + 1) * sizeof(int));
2392 int * const col_class = malloc(((size_t)n_orig + 1) * sizeof(int));
2393 if (!col_orig || !col_class) ERROR("memory allocation");
2394 for (int j = 0; j < n; j++)
2395 if (keepcol[j] >= 0) col_orig[keepcol[j]] = col_orig_index(p, j); /* (increasing) */
2396 for (int o = 0; o < n_orig; o++) {
2397 const int c = p->col_class ? p->col_class[o] : o;
2398 col_class[o] = (c >= 0 && twin[c] >= 0) ? keepcol[twin[c]] : -1;
2399 }
2400 csr_t * const H1 = csr_keep_columns(p->spaH, keepcol, n2);
2401 csr_t * const H2 = csr_keep_rows(H1, keeprow, m2);
2402 csr_t * const L2 = csr_keep_columns(p->spaL, keepcol, n2);
2403 csr_free(H1);
2404 csr_free(p->spaH);
2405 csr_free(p->spaL);
2406 p->spaH = H2;
2407 p->spaL = L2;
2408 free(p->col_orig);
2409 free(p->col_class);
2410 p->col_orig = col_orig;
2411 p->col_class = col_class;
2412 p->nvar_orig = n_orig;
2413 p->ndrop = num;
2414 p->ndrop_rows = m;
2415 p->ndrop_cols = nj;
2416 p->drop_A = need_A;
2417 p->drop_list = malloc(((size_t)num + 1) * sizeof(int));
2418 if (!p->drop_list) ERROR("memory allocation");
2419 for (int r = 0, c = 0; r < m; r++)
2420 if (inM[r]) p->drop_list[c++] = r;
2421 free(keepcol);
2422 free(keeprow);
2423 }
2424 free(twin);
2425 free(hs);
2426 free(hk);
2427 free(touch);
2428 csr_free(LT);
2429 csr_free(HT);
2430 free(inM);
2431}
2432
2433/* The rounds of a DEM, for the input summary (debug&1). The detectors of the DEM of a circuit are in time order, and
2434 * a measurement error flips the same check in two consecutive rounds: two detectors D apart, D the number of detectors
2435 * per round. Candidates: the 8 most frequent differences 2 <= D <= m/3 of the two detectors of the columns with two
2436 * detectors. Score: the fraction of the columns (with their detectors below m - D) whose detectors shifted by D are
2437 * those of another column (a time translation by one round). The candidate with the best score (the more frequent
2438 * one for equal scores) is accepted if its score is at least 1/2: then round_D = D and round_L = n D / m columns per
2439 * round (rounded); otherwise (e.g., a circuit whose rounds differ, as with SWAP routing) both stay 0. round_score is
2440 * the best score. For the reduced DEM (after merge and drop, before sort_rows). */
2441static void dem_round_estimate(params_t * const p) {
2442 const int m = p->spaH->rows, n = p->spaH->cols;
2443 p->round_D = p->round_L = 0;
2444 p->round_score = 0.0;
2445 if ((m < 6) || (n < 2)) return;
2446 csr_t * const HT = csr_transpose(NULL, p->spaH); /* row j: the detectors of column j (sorted) */
2447 int * const cnt = calloc((size_t)m, sizeof(int)); /* cnt[D]: the columns with two detectors D apart */
2448 if (!cnt) ERROR("memory allocation");
2449 for (int j = 0; j < n; j++)
2450 if (HT->p[j + 1] - HT->p[j] == 2)
2451 cnt[HT->i[HT->p[j] + 1] - HT->i[HT->p[j]]]++;
2452 enum { NCAND = 8 };
2453 int cand[NCAND], ncand = 0; /* by decreasing count, the smaller D first for equal counts */
2454 for (int D = 2; D <= m / 3; D++) {
2455 if ((cnt[D] == 0) || ((ncand == NCAND) && (cnt[D] <= cnt[cand[NCAND - 1]]))) continue;
2456 int k = (ncand < NCAND) ? ncand++ : NCAND - 1;
2457 for (; (k > 0) && (cnt[cand[k - 1]] < cnt[D]); k--)
2458 cand[k] = cand[k - 1];
2459 cand[k] = D;
2460 }
2461 free(cnt);
2462 /* the hash of a set of detectors: the XOR of the keys of its detectors; the columns with detectors in a hash table
2463 * (open addressing with linear probing) */
2464 size_t size = 4;
2465 while (size < 2 * (size_t)n) size <<= 1;
2466 uint64_t * const key = malloc((size_t)m * sizeof(uint64_t));
2467 uint64_t * const hcol = malloc((size_t)n * sizeof(uint64_t));
2468 int * const table = malloc(size * sizeof(int));
2469 if (!key || !hcol || !table) ERROR("memory allocation");
2470 for (int d = 0; d < m; d++) key[d] = dem_hash64((uint64_t)d + 1);
2471 for (size_t s = 0; s < size; s++) table[s] = -1;
2472 for (int j = 0; j < n; j++) {
2473 uint64_t h = 0;
2474 for (int t = HT->p[j]; t < HT->p[j + 1]; t++) h ^= key[HT->i[t]];
2475 hcol[j] = h;
2476 if (HT->p[j + 1] > HT->p[j]) {
2477 size_t s = (size_t)h & (size - 1);
2478 while (table[s] >= 0) s = (s + 1) & (size - 1);
2479 table[s] = j;
2480 }
2481 }
2482 int best_D = 0;
2483 for (int c = 0; c < ncand; c++) {
2484 const int D = cand[c];
2485 long tot = 0, hit = 0;
2486 for (int j = 0; j < n; j++) {
2487 const int * const dj = HT->i + HT->p[j];
2488 const int w = HT->p[j + 1] - HT->p[j];
2489 if ((w == 0) || (dj[w - 1] + D >= m)) continue;
2490 tot++;
2491 uint64_t h = 0;
2492 for (int t = 0; t < w; t++) h ^= key[dj[t] + D];
2493 for (size_t s = (size_t)h & (size - 1); table[s] >= 0; s = (s + 1) & (size - 1)) {
2494 const int i = table[s];
2495 const int * const di = HT->i + HT->p[i];
2496 if ((hcol[i] != h) || (HT->p[i + 1] - HT->p[i] != w)) continue;
2497 int t = 0;
2498 while ((t < w) && (di[t] == dj[t] + D)) t++;
2499 if (t == w) { /* column i: column j shifted by D */
2500 hit++;
2501 break;
2502 }
2503 }
2504 }
2505 const double score = (tot > 0) ? (double)hit / (double)tot : 0.0;
2506 if (score > p->round_score) {
2507 p->round_score = score;
2508 best_D = D;
2509 }
2510 }
2511 free(table);
2512 free(hcol);
2513 free(key);
2514 csr_free(HT);
2515 if (p->round_score >= 0.5) {
2516 const long L = lround((double)n * best_D / m);
2517 p->round_D = best_D;
2518 p->round_L = (L > 1) ? (int)L : 1;
2519 }
2520}
2521
2522FILE * nzlist_w_new(const char fnam[], const char comment[]){
2523 FILE *f=fopen(fnam,"w");
2524 if(!f){
2525 fprintf(stderr, "FILE I/O ERROR: %s\n", strerror(errno));
2526 ERROR("can't open file %s for writing",fnam);
2527 }
2528 fprintf(f,"%%%% NZLIST\n");
2529 if(comment)
2530 fprintf(f,"%% %s\n",comment);
2531 return f;
2532}
2533
2534int nzlist_w_append(FILE *f, const cw_vec_t * const vec){
2535 assert(vec && vec->weight >0 );
2536 assert(f!=NULL);
2537 const int w=vec->weight;
2538 if(fprintf(f,"%d ",w)<=0)
2539 ERROR("can't write to `NZLIST` file");
2540 for(int i=0; i < w; i++) /* (the original columns of a merged DEM: still increasing) */
2541 if(fprintf(f," %d%s", 1 + col_orig_index(&prm, vec->arr[i]), i+1 < w ? "" :"\n")<=0)
2542 ERROR("can't write to `NZLIST` file");
2543 return 0;
2544}
2545
2546FILE * nzlist_r_open(const char fnam[], long long int *lineno){
2547 FILE *f=fopen(fnam,"r");
2548 if(!f)
2549 return(NULL);
2550 *lineno=1;
2551 int c=fgetc(f);
2552 while(c=='%'){
2553 do{
2554 c=fgetc(f);
2555 if(feof(f))
2556 return NULL;
2557 }
2558 while(c!='\n');
2559 (*lineno)++;
2560 c=fgetc(f);
2561 }
2562 ungetc(c,f);
2563 return f;
2564}
2565
2566cw_vec_t * nzlist_r_one(FILE *f, cw_vec_t * vec, const char fnam[], long long int *lineno){
2567 assert(f!=NULL);
2568 if ( ferror (f)|| feof(f) )
2569 return NULL;
2570 int w;
2571
2572 int c=fgetc(f);
2573 while(c=='%'){
2574 do{
2575 c=fgetc(f);
2576 if(feof(f))
2577 return NULL;
2578 }
2579 while(c!='\n');
2580 (*lineno)++;
2581 c=fgetc(f);
2582 }
2583 ungetc(c,f);
2584
2585 if(fscanf(f," %d",&w) != 1){
2586 if (feof(f)) return NULL;
2587 fprintf(stderr, "%s:%lld: invalid NZLIST entry\n", fnam, *lineno);
2588 ERROR("expected an integer");
2589 }
2590 if ((vec!=NULL) && (vec->weight<w)){
2591 free(vec);
2592 vec=NULL;
2593 }
2594 if(vec==NULL){
2595 vec = calloc(sizeof(cw_vec_t)+w*sizeof(int), sizeof(char));
2596 if(!vec)
2597 ERROR("memory allocation");
2598 }
2599 vec->weight = w;
2600 vec->cnt = 1;
2601 for(int i=0; i<w; i++){
2602 if(fscanf(f," %d ",vec->arr + i) != 1){
2603 fprintf(stderr, "%s:%lld: invalid entry of weight w=%d\n",fnam, *lineno, w);
2604 ERROR("expected an integer i=%d of %d",i,w);
2605 }
2606 vec->arr[i]--;
2607 }
2608
2609 for(int i=1; i<w; i++){
2610 if((vec->arr[i-1] < 0) || (vec->arr[i-1] >= vec->arr[i])){
2611 fprintf(stderr, "%s:%lld: invalid entry of weight w=%d\n",fnam, *lineno, w);
2612 ERROR("expected strictly increasing positive entries");
2613 }
2614 }
2615 (*lineno)++;
2616 return vec;
2617}
2618
2619/* Codewords are collected (and exported with outC) with outC or maxC */
2620static inline int cw_collecting(const params_t * const p) {
2621 return (p->outC != NULL) || (p->maxC > 0);
2622}
2623
2624/* Width of the collection window: codewords of weight up to min_w + dW are collected (with outC or maxC) */
2625static inline int cw_window_dw(const params_t * const p) {
2626 return (cw_collecting(p) && p->dW > 0) ? p->dW : 0;
2627}
2628
2629/* Size of the representative set of lowest-weight codewords for the `min_hits` statistic (0: only the
2630 * minimum-weight codewords) */
2631static inline long long int cw_rep_size(const params_t * const p) {
2632 return (p->min_hits > 0 && p->cov_cws > 0) ? p->cov_cws : 0;
2633}
2634
2635long long int codeword_window_count(const params_t * const p) {
2636 if (p->min_w == INT_MAX || !p->cw_cnt_w) return 0;
2637 const long long int top = (long long int)p->min_w + cw_window_dw(p);
2638 const int w_top = (top < p->cw_max_w) ? (int)top : p->cw_max_w;
2639 long long int num = 0;
2640 for (int w = p->min_w; w <= w_top; w++) num += p->cw_cnt_w[w];
2641 return num;
2642}
2643
2645 return (p->maxC > 0) && (codeword_window_count(p) >= p->maxC);
2646}
2647
2648int codeword_feed_limit(const params_t * const p) {
2649 if (p->min_hits <= 0) return 0;
2650 if (p->min_w == INT_MAX) return INT_MAX;
2651 const long long int rep = cw_rep_size(p);
2652 if (rep <= 0) return p->min_w + 1;
2653 if (p->num_cws < rep) return INT_MAX;
2654 return p->cw_max_w + 1;
2655}
2656
2657/* Remove the heaviest weight classes from the hash as long as the remaining codewords keep the collection window
2658 * and at least `cw_rep_size()` codewords (whole weight classes are kept) */
2659static void cw_trim(params_t * const p) {
2660 if (p->min_w == INT_MAX || p->cw_max_w <= p->min_w) return;
2661 const long long int rep = cw_rep_size(p);
2662 if (rep > 0 && p->num_cws <= rep) return;
2663 const long long int top = (long long int)p->min_w + cw_window_dw(p);
2664 if (top >= p->cw_max_w) return;
2665 int keep = (int)top;
2666 if (rep > 0) { /* the smallest weight such that the codewords up to this weight form the representative set */
2667 long long int cum = 0;
2668 int w_rep = p->cw_max_w;
2669 for (int w = p->min_w; w < p->cw_max_w; w++) {
2670 cum += p->cw_cnt_w[w];
2671 if (cum >= rep) {
2672 w_rep = w;
2673 break;
2674 }
2675 }
2676 if (w_rep > keep) keep = w_rep;
2677 }
2678 if (keep >= p->cw_max_w) return;
2679 cw_vec_t *cw, *tmp;
2680 HASH_ITER(hh, p->codewords, cw, tmp) {
2681 if (cw->weight > keep) {
2682 p->cw_cnt_w[cw->weight]--;
2683 p->cw_hits_w[cw->weight] -= cw->cnt;
2684 HASH_DEL(p->codewords, cw);
2685 free(cw);
2686 p->num_cws--;
2687 }
2688 }
2689 p->cw_max_w = keep;
2690 while (p->cw_max_w > p->min_w && p->cw_cnt_w[p->cw_max_w] == 0) p->cw_max_w--;
2691}
2692
2693cw_vec_t * codeword_add_maybe(params_t * const p, const int arr[], int weight, int src) {
2694 if (weight <= 0 || weight > p->nvar) return p->codewords;
2695 if (!p->cw_cnt_w) { /* weight histograms, allocated on first use */
2696 p->cw_cnt_w = calloc((size_t)p->nvar + 2, sizeof(long long int));
2697 p->cw_hits_w = calloc((size_t)p->nvar + 2, sizeof(long long int));
2698 if (!p->cw_cnt_w || !p->cw_hits_w) ERROR("memory allocation");
2699 }
2700
2701 const size_t keylen = weight * sizeof(int);
2702 cw_vec_t *pvec = NULL;
2703 HASH_FIND(hh, p->codewords, arr, keylen, pvec);
2704 if (pvec) { /* a known codeword: one more hit */
2705 pvec->cnt++;
2706 pvec->cnt_u += (src == CW_SRC_RW_UNIFORM);
2707 pvec->cnt_w += (src == CW_SRC_RW_WINDOW);
2708 p->cw_hits_w[weight]++;
2709 return p->codewords;
2710 }
2711
2712 int insert;
2713 if (weight < p->min_w) {
2714 insert = 1; /* a new minimum weight is always recorded (also with maxC) */
2715 } else if ((long long int)weight <= (long long int)p->min_w + cw_window_dw(p)) { /* the collection window */
2716 if (cw_collecting(p))
2717 insert = !codeword_maxc_reached(p);
2718 else /* weight == min_w: at most cov_cws codewords tracked for `min_hits` (all if cov_cws <= 0) */
2719 insert = (p->cov_cws <= 0) || (p->cw_cnt_w[weight] < p->cov_cws);
2720 } else { /* heavier: only to keep a representative set of cov_cws lowest-weight codewords for `min_hits` */
2721 const long long int rep = cw_rep_size(p);
2722 insert = (rep > 0) && (p->num_cws < rep || weight < p->cw_max_w);
2723 }
2724 if (!insert) return p->codewords;
2725
2726 cw_vec_t *entry = malloc(sizeof(cw_vec_t) + keylen);
2727 if (!entry) ERROR("memory allocation");
2728 entry->weight = weight;
2729 entry->cnt = 1;
2730 entry->cnt_u = (src == CW_SRC_RW_UNIFORM);
2731 entry->cnt_w = (src == CW_SRC_RW_WINDOW);
2732 memcpy(entry->arr, arr, keylen);
2733 HASH_ADD(hh, p->codewords, arr, keylen, entry);
2734 p->num_cws++;
2735 p->cw_cnt_w[weight]++;
2736 p->cw_hits_w[weight]++;
2737 if (weight > p->cw_max_w) p->cw_max_w = weight;
2738 if (weight < p->min_w) p->min_w = weight;
2739
2740 /* gradually replace heavier codewords by lighter ones */
2741 cw_trim(p);
2742 return p->codewords;
2743}
2744
2745void compute_min_w_hit_stats(const params_t * const p, int *min_cnt, int *max_cnt,
2746 double *avg_cnt, double *stdev_cnt) {
2747 *min_cnt = 0;
2748 *max_cnt = 0;
2749 *avg_cnt = 0.0;
2750 *stdev_cnt = 0.0;
2751 if (!p || !p->codewords || p->min_w == INT_MAX) return;
2752
2753 long long n_cws = 0;
2754 long long total_hits = 0;
2755 int c_min = INT_MAX;
2756 int c_max = 0;
2757 cw_vec_t *cw, *tmp;
2758 HASH_ITER(hh, p->codewords, cw, tmp) {
2759 if (cw->weight == p->min_w) {
2760 n_cws++;
2761 total_hits += cw->cnt;
2762 if (cw->cnt < c_min) c_min = cw->cnt;
2763 if (cw->cnt > c_max) c_max = cw->cnt;
2764 }
2765 }
2766 if (n_cws <= 0) return;
2767
2768 double avg = (double)total_hits / (double)n_cws;
2769 double sum_sq = 0.0;
2770 HASH_ITER(hh, p->codewords, cw, tmp) {
2771 if (cw->weight == p->min_w) {
2772 double diff = (double)cw->cnt - avg;
2773 sum_sq += diff * diff;
2774 }
2775 }
2776 *min_cnt = c_min;
2777 *max_cnt = c_max;
2778 *avg_cnt = avg;
2779 *stdev_cnt = sqrt(sum_sq / (double)n_cws);
2780}
2781
2782/* Hit statistics of the codewords of one weight in hash */
2783typedef struct {
2784 long long int n; /* number of codewords */
2785 long long int hits; /* total hits */
2786 double sumsq; /* sum of squared hit counts */
2787 int c_min, c_max; /* smallest and largest hit count */
2789
2790/* Hit statistics of the codewords in hash of each weight w_lo..w_hi (one pass over the hash; free() the result) */
2791static cw_class_stats_t *cw_class_stats(const params_t * const p, const int w_lo, const int w_hi) {
2792 const int nw = w_hi - w_lo + 1;
2793 cw_class_stats_t *cs = calloc(nw > 0 ? nw : 1, sizeof(cw_class_stats_t));
2794 if (!cs) ERROR("memory allocation");
2795 for (int i = 0; i < nw; i++) cs[i].c_min = INT_MAX;
2796 cw_vec_t *cw, *tmp;
2797 HASH_ITER(hh, p->codewords, cw, tmp) {
2798 if (cw->weight < w_lo || cw->weight > w_hi) continue;
2799 cw_class_stats_t * const c = &cs[cw->weight - w_lo];
2800 c->n++;
2801 c->hits += cw->cnt;
2802 c->sumsq += (double)cw->cnt * (double)cw->cnt;
2803 if (cw->cnt < c->c_min) c->c_min = cw->cnt;
2804 if (cw->cnt > c->c_max) c->c_max = cw->cnt;
2805 }
2806 return cs;
2807}
2808
2809void codeword_hit_stats(const params_t * const p, cw_hit_stats_t * const st) {
2810 memset(st, 0, sizeof(*st));
2811 if (p->min_w == INT_MAX || !p->cw_cnt_w || p->cw_cnt_w[p->min_w] <= 0) return;
2812 const long long int rep = cw_rep_size(p);
2813 st->w_lo = st->w_hi = p->min_w;
2814 st->min_cws = p->cw_cnt_w[p->min_w];
2815 st->min_hits = p->cw_hits_w[p->min_w];
2816 for (int w = p->min_w; w <= p->cw_max_w; w++) { /* the lowest weight classes with at least `rep` codewords */
2817 if (p->cw_cnt_w[w] <= 0) continue;
2818 st->set_cws += p->cw_cnt_w[w];
2819 st->set_hits += p->cw_hits_w[w];
2820 st->w_hi = w;
2821 if (st->set_cws >= rep) break;
2822 }
2823 const double avg_min = (double)st->min_hits / (double)st->min_cws;
2824 const double avg_set = (double)st->set_hits / (double)st->set_cws;
2825 st->avg = (avg_set < avg_min) ? avg_set : avg_min;
2826}
2827
2828void print_codeword_stats(FILE *stream, const params_t * const p) {
2829 if (!stream || !p) return;
2830 if (p->num_cws <= 0 || !p->codewords || p->min_w == INT_MAX) {
2831 fprintf(stream, "# codewords accumulated: total=0\n");
2832 return;
2833 }
2834
2835 const int w_lo = p->min_w;
2836 const int w_hi = (p->cw_max_w > w_lo) ? p->cw_max_w : w_lo;
2837 cw_class_stats_t *cs = cw_class_stats(p, w_lo, w_hi);
2838 /* without DBG_LARGE, at most 4 heavier weight classes are shown individually, the others in one line */
2839 int shown = 0, rest_classes = 0, rest_lo = 0, rest_hi = 0, rest_min = INT_MAX, rest_max = 0;
2840 long long rest_cws = 0, rest_hits = 0;
2841 for (int w = w_lo; w <= w_hi; w++) {
2842 const cw_class_stats_t * const c = &cs[w - w_lo];
2843 if (c->n <= 0) continue;
2844 const double avg = (double)c->hits / (double)c->n;
2845 const double var = c->sumsq / (double)c->n - avg * avg;
2846 const double stdev = (var > 0.0) ? sqrt(var) : 0.0;
2847 if (w == w_lo) {
2848 fprintf(stream,
2849 "# codewords accumulated: total=%lld, min_w=%d: cws=%lld, total_hits=%lld, "
2850 "hits min=%d, max=%d, avg=%.2f, stdev=%.2f",
2851 p->num_cws, w, c->n, c->hits, c->c_min, c->c_max, avg, stdev);
2852 if (p->min_hits > 0) {
2853 cw_hit_stats_t st;
2854 codeword_hit_stats(p, &st);
2855 fprintf(stream, ", <n>=%.2f over %lld cws of w=%d..%d (min_hits=%d, cov_cws=%d)",
2856 st.avg, st.set_cws, st.w_lo, st.w_hi, p->min_hits, p->cov_cws);
2857 }
2858 fprintf(stream, "\n");
2859 } else if ((p->debug & DBG_LARGE) || (shown < 4)) {
2860 shown++;
2861 fprintf(stream,
2862 "# codewords w=%d: cws=%lld, total_hits=%lld, "
2863 "hits min=%d, max=%d, avg=%.2f, stdev=%.2f\n",
2864 w, c->n, c->hits, c->c_min, c->c_max, avg, stdev);
2865 } else {
2866 if (rest_classes++ == 0) rest_lo = w;
2867 rest_hi = w;
2868 rest_cws += c->n;
2869 rest_hits += c->hits;
2870 if (c->c_min < rest_min) rest_min = c->c_min;
2871 if (c->c_max > rest_max) rest_max = c->c_max;
2872 }
2873 }
2874 if (rest_classes > 0)
2875 fprintf(stream, "# codewords w=%d..%d: cws=%lld in %d weight classes, total_hits=%lld, hits min=%d, max=%d "
2876 "(each class with debug bit 2048)\n", rest_lo, rest_hi, rest_cws, rest_classes, rest_hits, rest_min,
2877 rest_max);
2878 free(cs);
2879}
2880
2881void print_rw_step_stats(FILE *stream, const params_t * const p) {
2882 if (!stream || !p || !p->codewords || p->min_w == INT_MAX) return;
2883 enum { NW = 3 }; /* the weights min_w, min_w + 1, min_w + 2 */
2884 long long n[NW] = {0}, cws_u[NW] = {0}, hits_u[NW] = {0}, cws_w[NW] = {0}, hits_w[NW] = {0}, cws_b[NW] = {0};
2885 cw_vec_t *cw, *tmp;
2886 HASH_ITER(hh, p->codewords, cw, tmp) {
2887 const int k = cw->weight - p->min_w;
2888 if (k < 0 || k >= NW) continue;
2889 n[k]++;
2890 if (cw->cnt_u > 0) {
2891 cws_u[k]++;
2892 hits_u[k] += cw->cnt_u;
2893 }
2894 if (cw->cnt_w > 0) {
2895 cws_w[k]++;
2896 hits_w[k] += cw->cnt_w;
2897 }
2898 cws_b[k] += (cw->cnt_u > 0) && (cw->cnt_w > 0);
2899 }
2900 for (int k = 0; k < NW; k++)
2901 if (n[k] > 0)
2902 fprintf(stream, "# RW step types, w=%d: uniform steps: cws=%lld, hits=%lld; window steps: cws=%lld, hits=%lld; "
2903 "both: cws=%lld (of %lld cws)\n", p->min_w + k, cws_u[k], hits_u[k], cws_w[k], hits_w[k], cws_b[k], n[k]);
2904}
2905
2907 if (p->min_hits <= 0) return 0;
2908 cw_hit_stats_t st;
2909 codeword_hit_stats(p, &st);
2910 return (st.set_cws > 0) && (st.avg >= (double)p->min_hits);
2911}
2912
2913/* Upper tail probability P(X >= x) of the chi-square distribution with `df` degrees of freedom (exact for df <= 2,
2914 * Wilson-Hilferty approximation otherwise) */
2915static double chi2_upper_tail(const double x, const long long int df) {
2916 if (!(x > 0.0)) return 1.0;
2917 if (df == 1) return erfc(sqrt(0.5 * x));
2918 if (df == 2) return exp(-0.5 * x);
2919 const double s = 2.0 / (9.0 * (double)df);
2920 const double z = (cbrt(x / (double)df) - (1.0 - s)) / sqrt(s);
2921 return 0.5 * erfc(z / sqrt(2.0));
2922}
2923
2924/* Mean mu of the Poisson distribution whose zero-truncated version has the mean m > 1, i.e., m = mu / (1 - e^-mu):
2925 * Newton iteration for the convex function g(mu) = mu - m (1 - e^-mu), starting to the right of the root */
2926static double ztp_mu(const double m) {
2927 double mu = m;
2928 for (int it = 0; it < 200; it++) {
2929 const double e = exp(-mu);
2930 const double dg = 1.0 - m * e;
2931 if (!(dg > 0.0)) break;
2932 const double step = (mu - m * (1.0 - e)) / dg;
2933 mu -= step;
2934 if (fabs(step) <= 1e-12 * (1.0 + mu)) break;
2935 }
2936 return mu;
2937}
2938
2939int codeword_hit_check(FILE *stream, const params_t * const p) {
2940 if (!p || p->min_w == INT_MAX || !p->cw_cnt_w) return 0;
2941 cw_hit_stats_t st;
2942 codeword_hit_stats(p, &st);
2943 if (st.set_cws <= 0) return 0;
2944 cw_class_stats_t *cs = cw_class_stats(p, st.w_lo, st.w_hi);
2945 int num_bad = 0;
2946 for (int w = st.w_lo; w <= st.w_hi; w++) {
2947 const cw_class_stats_t * const c = &cs[w - st.w_lo];
2948 if (c->n < 2) continue;
2949 const double n = (double)c->n;
2950 const double m = (double)c->hits / n;
2951 if (m <= 1.0 + 1e-9) continue; /* every codeword found once: no information */
2952 double s2 = (c->sumsq - n * m * m) / (n - 1.0); /* sample variance of the hit counts */
2953 if (s2 < 0.0) s2 = 0.0;
2954 /* with equal hit rates, a hit count is Poisson(mu) conditioned on >= 1 (zero-truncated), with mean m and
2955 * variance v0; codewords tracked only after their first hit have a smaller variance (the test is conservative) */
2956 const double mu = ztp_mu(m);
2957 const double v0 = m * (1.0 + mu - m);
2958 if (!(mu > 0.0) || !(v0 > 0.0)) continue;
2959 const double pval = chi2_upper_tail((n - 1.0) * s2 / v0, c->n - 1); /* dispersion test */
2960 const double cv2 = (s2 - v0) / (mu * mu); /* squared relative spread of the hit rates */
2961 if (!(pval < 1e-3) || !(cv2 >= 1.0)) continue; /* significant and strong: spread of hit rates >= their mean */
2962 /* a lighter codeword with a gamma-distributed hit rate of the same relative spread is missed with the
2963 * probability E[exp(-lambda)] = (1 + cv2 mu)^(-1/cv2), instead of exp(-mu) with equal hit rates: warn if this
2964 * is at least 3 times larger */
2965 const double p_unif = exp(-mu);
2966 const double p_spread = pow(1.0 + cv2 * mu, -1.0 / cv2);
2967 if (p_spread >= 3.0 * p_unif) {
2968 num_bad++;
2969 if (stream) {
2970 fprintf(stream,
2971 "# Warning: non-uniform RW hit counts of the %lld codewords of weight %d: hits min=%d, max=%d, "
2972 "avg=%.2f, stdev=%.2f (expected %.2f; p=%.1e)\n"
2973 "# some codewords are found much more often than others of the same weight (relative spread of "
2974 "hit rates %.2f):\n"
2975 "# a lighter codeword may be missed with probability ~%.2g rather than exp(-%.2f)=%.2g; "
2976 "consider a larger min_hits\n",
2977 c->n, w, c->c_min, c->c_max, m, sqrt(s2), sqrt(v0), pval, sqrt(cv2), p_spread, mu, p_unif);
2978 }
2979 }
2980 }
2981 free(cs);
2982 return num_bad;
2983}
2984
2985/* Logarithm of the binomial coefficient C(a, b), 0 <= b <= a */
2986static double log_binom(const double a, const double b) {
2987 return lgamma(a + 1.0) - lgamma(b + 1.0) - lgamma(a - b + 1.0);
2988}
2989
2990double rw_infoset_find_prob(const int n, const int rank, const int w, const int pairs_l) {
2991 const int k = n - rank; /* number of non-pivot columns */
2992 if (n <= 0 || rank < 0 || k <= 0 || w < 1 || w > n || w - 1 > rank) return 0.0;
2993 /* exactly one of the w positions among the k non-pivot columns */
2994 double p = exp(log((double)w) + log_binom(n - w, k - 1) - log_binom(n, k));
2995 /* the RW pairs: exactly two of them, and the other w - 2 (pivot) positions avoid the l pivot rows of the buckets */
2996 if ((pairs_l > 0) && (w >= 2) && (k >= 2) && (w - 2 + pairs_l <= rank))
2997 p += exp(log_binom(w, 2) + log_binom(n - w, k - 2) - log_binom(n, k) + log_binom(rank - w + 2, pairs_l) -
2998 log_binom(rank, pairs_l));
2999 return (p < 1.0) ? p : 1.0;
3000}
3001
3002/* Format a duration in seconds (s, min, h, or days) */
3003static void format_duration(char * const buf, const size_t size, const double t) {
3004 if (t < 120.0) snprintf(buf, size, "%.3g s", t);
3005 else if (t < 7200.0) snprintf(buf, size, "%.3g min", t / 60.0);
3006 else if (t < 172800.0) snprintf(buf, size, "%.3g h", t / 3600.0);
3007 else snprintf(buf, size, "%.3g days", t / 86400.0);
3008}
3009
3010void print_rw_infoset_estimate(FILE *stream, const int n, const int rank, const int w_lo, const int w_hi,
3011 const long steps, const long steps_total, const double t_step, const int pairs_l) {
3012 if (!stream || w_lo < 1 || w_hi < w_lo) return;
3013 int w_hard = w_hi; /* the weight hardest to find (smallest probability) */
3014 double p1 = rw_infoset_find_prob(n, rank, w_hi, pairs_l);
3015 for (int w = w_lo; w < w_hi; w++) {
3016 const double pw = rw_infoset_find_prob(n, rank, w, pairs_l);
3017 if (pw < p1) {
3018 p1 = pw;
3019 w_hard = w;
3020 }
3021 }
3022 if (!(p1 > 0.0)) {
3023 fprintf(stream, "# information-set estimate: a codeword of weight %d cannot be found by RW (n=%d, rank(H)=%d)\n",
3024 w_hard, n, rank);
3025 return;
3026 }
3027 const double lq = (p1 < 1.0) ? log1p(-p1) : -INFINITY; /* log of the miss probability per step */
3028 const double p_miss = (steps > 0) ? exp((double)steps * lq) : 1.0;
3029 /* RW steps for a 1% miss probability: uniform steps, scaled to all steps with the fraction of uniform steps */
3030 const double unif_steps_1pc = (p1 < 1.0) ? ceil(log(0.01) / lq) : 1.0;
3031 const double steps_1pc = (steps > 0 && steps_total > steps) ? ceil(unif_steps_1pc * steps_total / steps)
3032 : unif_steps_1pc;
3033 char steps_str[32], time_str[64] = "", pairs_str[48] = "";
3034 snprintf(steps_str, sizeof(steps_str), (steps_1pc < 1e9) ? "%.0f" : "%.3g", steps_1pc);
3035 if (t_step > 0.0) {
3036 char dur[32];
3037 format_duration(dur, sizeof(dur), steps_1pc * t_step);
3038 snprintf(time_str, sizeof(time_str), ", ~%s", dur);
3039 }
3040 if (pairs_l > 0)
3041 snprintf(pairs_str, sizeof(pairs_str), " with pairs (l=%d)", pairs_l);
3042 fprintf(stream,
3043 "# information-set estimate (uniform random information sets%s, n=%d, rank(H)=%d): a codeword of weight "
3044 "%d\n"
3045 "# is found with probability %.3g per RW step, missed in %ld uniform steps with probability %.2g "
3046 "(1%% after %s RW steps in total%s)\n",
3047 pairs_str, n, rank, w_hard, p1, steps, p_miss, steps_str, time_str);
3048}
3049
3050/* finC with irred=1: reduce the nontrivial codeword arr[0..w-1] (sorted) to an irreducible nontrivial codeword which it
3051 * contains. While a nonempty proper subset has zero syndrome (csr_cols_dependency()), keep the lighter one of the
3052 * subset and its complement which has a nonzero syndrome of L (at least one has, since the trivial codewords form a
3053 * linear space; for a classical code, both are nontrivial). `sub` and `rest`: room for w entries each. Returns the
3054 * new weight (arr stays sorted). */
3055static int codeword_reduce(const params_t * const p, const csr_t * const HT, int arr[], int w, cols_ws_t * const ws,
3056 int sub[], int rest[]) {
3057 int k;
3058 while ((k = csr_cols_dependency(HT, w, arr, ws, sub)) > 0) {
3059 int nr = 0;
3060 for (int i = 0, j = 0; i < w; i++) {
3061 if (j < k && sub[j] == arr[i]) j++;
3062 else rest[nr++] = arr[i];
3063 }
3064 const int nt_sub = !p->maskL || colmask_syndrome_non_zero(p->maskL, k, sub);
3065 const int nt_rest = !p->maskL || colmask_syndrome_non_zero(p->maskL, nr, rest);
3066 const int keep_sub = nt_sub && (!nt_rest || k <= nr);
3067 memcpy(arr, keep_sub ? sub : rest, (size_t)(keep_sub ? k : nr) * sizeof(int));
3068 w = keep_sub ? k : nr;
3069 }
3070 return w;
3071}
3072
3073long long int nzlist_read(const char fnam[], params_t *p){
3074 long long int count = 0, lineno;
3075 long long int skipped_invalid = 0, reduced = 0;
3076 csr_t *HT = NULL; /* irred=1: the transpose of H, the workspace, and room for the parts, see codeword_reduce() */
3077 cols_ws_t *ws = NULL;
3078 int *sub = NULL, *rest = NULL;
3079 assert(fnam);
3080 FILE * f=nzlist_r_open(fnam, &lineno);
3081 if(!f){
3082 if ((p->outC ==NULL) || (strcmp(fnam,p->outC)!=0)){
3083 fprintf(stderr, "codeword input file I/O ERROR: %s, outC=%s\n", strerror(errno),p->outC);
3084 ERROR("can't open file %s for reading",fnam);
3085 }
3086 else
3087 return 0;
3088 }
3089 cw_vec_t *entry=NULL;
3090 const int n_orig = (p->nvar_orig > 0) ? p->nvar_orig : p->nvar; /* finC refers to the original columns */
3091 while((entry=nzlist_r_one(f,NULL, fnam, &lineno))){
3092 if (codeword_maxc_reached(p)) {
3093 free(entry);
3094 break;
3095 }
3096 int valid = (entry->weight > 0) && (entry->arr[entry->weight - 1] < n_orig); /* column indices in range */
3097 if (valid && p->col_class) { /* the merged (dropped) columns of a DEM: two equal columns (twins) cancel */
3098 int w = 0;
3099 for (int i = 0; i < entry->weight; i++) { /* (a column with no kept detector is dropped, see drop) */
3100 const int c = p->col_class[entry->arr[i]];
3101 if (c >= 0) entry->arr[w++] = c;
3102 }
3103 qsort(entry->arr, w, sizeof(int), cmp_int);
3104 const int w0 = w;
3105 w = 0;
3106 for (int i = 0; i < w0;) {
3107 if (i + 1 < w0 && entry->arr[i] == entry->arr[i + 1]) {
3108 i += 2;
3109 } else {
3110 entry->arr[w++] = entry->arr[i++];
3111 }
3112 }
3113 entry->weight = w;
3114 valid = (w > 0);
3115 }
3116 if (valid && p->spaH) {
3117 if (sparse_syndrome_non_zero(p->spaH, entry->weight, entry->arr)) {
3118 valid = 0;
3119 }
3120 }
3121 if (valid && p->spaL) {
3122 if (!sparse_syndrome_non_zero(p->spaL, entry->weight, entry->arr)) {
3123 valid = 0;
3124 }
3125 }
3126 if (valid && p->irred && p->spaH && entry->weight > 1) { /* an irreducible part of a reducible codeword */
3127 if (!HT) {
3128 HT = csr_transpose(NULL, p->spaH);
3129 ws = cols_ws_init(p->spaH->rows);
3130 sub = malloc((size_t)(p->spaH->cols + 1) * sizeof(int));
3131 rest = malloc((size_t)(p->spaH->cols + 1) * sizeof(int));
3132 if (!sub || !rest) ERROR("memory allocation");
3133 }
3134 const int w0 = entry->weight;
3135 entry->weight = codeword_reduce(p, HT, entry->arr, w0, ws, sub, rest);
3136 if (entry->weight < w0) reduced++;
3137 }
3138 if (!valid) {
3139 skipped_invalid++;
3140 free(entry);
3141 continue;
3142 }
3143 if((p->wmax==0) ||((p->wmax) && (entry->weight <= p->wmax))){
3144 const size_t keylen = entry->weight * sizeof(int);
3145 cw_vec_t *known = NULL, *added = NULL;
3146 HASH_FIND(hh, p->codewords, entry->arr, keylen, known);
3147 p->codewords = codeword_add_maybe(p, entry->arr, entry->weight, CW_SRC_OTHER);
3148 if (!known) {
3149 HASH_FIND(hh, p->codewords, entry->arr, keylen, added);
3150 if (added) count++;
3151 }
3152 }
3153 free(entry);
3154 }
3155 fclose(f);
3156 csr_free(HT);
3157 cols_ws_free(ws);
3158 free(sub);
3159 free(rest);
3160 if (skipped_invalid > 0) {
3161 fprintf(stderr,
3162 "# Warning: skipped %lld invalid codewords (column out of range, not orthogonal to H, or orthogonal "
3163 "to L)\n",
3164 skipped_invalid);
3165 }
3166 if (p->debug & DBG_SUMMARY) {
3167 fprintf(stderr, "# read %lld codewords from %s, total %lld", count, fnam, p->num_cws);
3168 if (p->min_w != INT_MAX)
3169 fprintf(stderr, ", min weight %d", p->min_w);
3170 if (reduced > 0)
3171 fprintf(stderr, "; %lld reducible codewords replaced by an irreducible part (irred=1)", reduced);
3172 fprintf(stderr, "\n");
3173 }
3174 if ((p->debug & DBG_CODEWORDS) && (p->min_w != INT_MAX)) { /* a lightest codeword (the hash holds only finC ones) */
3175 cw_vec_t *cw, *tmp;
3176 HASH_ITER(hh, p->codewords, cw, tmp) {
3177 if (cw->weight == p->min_w) {
3178 print_codeword_support(stderr, "# finC: lightest codeword", cw->arr, cw->weight, p->debug);
3179 break;
3180 }
3181 }
3182 }
3183 return count;
3184}
3185
3186void print_codeword_support(FILE *stream, const char *prefix, const int arr[], const int weight, const int debug){
3187 if (!stream) return;
3188 const int max = ((debug & DBG_LARGE) || (weight <= DBG_CW_MAX)) ? weight : DBG_CW_MAX;
3189 fprintf(stream, "%s of weight %d (1-based columns):", prefix, weight);
3190 for (int i = 0; i < max; i++)
3191 fprintf(stream, " %d", col_orig_index(&prm, arr[i]) + 1); /* (original columns of a merged DEM) */
3192 if (max < weight)
3193 fprintf(stream, " ... (%d more)", weight - max);
3194 fprintf(stream, "\n");
3195}
3196
3197long long int nzlist_write(const char fnam[], const char comment[], params_t *p){
3198 long long int count=0;
3199 assert(fnam);
3200 FILE * f = nzlist_w_new(fnam, comment);
3201 /* only the collection window (weight up to min_w + dW): the hash may also hold heavier codewords, which are kept
3202 * for the `min_hits` statistic only */
3203 const long long int w_top = (p->min_w == INT_MAX) ? 0 : (long long int)p->min_w + cw_window_dw(p);
3204 cw_vec_t *pvec;
3205
3206 for(pvec = p->codewords; pvec != NULL; pvec = (cw_vec_t *)(pvec->hh.next)){
3207 if (pvec->weight > w_top) continue;
3208 count ++;
3209 nzlist_w_append(f,pvec);
3210 }
3211 fclose(f);
3212 return count;
3213}
int arr[0]
Definition util_hash.h:42
int cnt_u
Definition util_hash.h:40
int cnt
Definition util_hash.h:39
UT_hash_handle hh
Definition util_hash.h:37
int cnt_w
Definition util_hash.h:41
int weight
Definition util_hash.h:38
void * next
Definition uthash.h:1129
int nz
Definition util_m4ri.h:123
int * p
Definition util_m4ri.h:125
int * i
Definition util_m4ri.h:126
int rows
Definition util_m4ri.h:121
int cols
Definition util_m4ri.h:122
long long int n
Definition util_io.c:2784
long long int hits
Definition util_io.c:2785
Hit statistics for the min_hits stopping criterion, see codeword_hit_stats().
Definition util_io.h:396
long long int min_cws
Definition util_io.h:397
long long int min_hits
Definition util_io.h:398
long long int set_cws
Definition util_io.h:399
long long int set_hits
Definition util_io.h:400
uint64_t h
Definition util_io.c:1773
char ** lines
Definition util_io.c:1536
char * in
Definition util_io.c:1972
Definition util_io.c:374
char name[HELP_MAX_NAMES][HELP_NAME_LEN]
Definition util_io.c:380
int nnames
Definition util_io.c:378
int beg
Definition util_io.c:376
int nmembers
Definition util_io.c:381
int sec
Definition util_io.c:375
char key[HELP_MAX_NAMES][HELP_NAME_LEN]
Definition util_io.c:379
int is_sec
Definition util_io.c:377
int end
Definition util_io.c:376
char member[HELP_MAX_MEMBERS][HELP_NAME_LEN]
Definition util_io.c:382
#define HASH_DEL(head, delptr)
Definition uthash.h:507
#define HASH_ITER(hh, head, el, tmp)
Definition uthash.h:1060
#define HASH_ADD(hh, head, fieldname, keylen_in, add)
Definition uthash.h:427
#define HASH_FIND(hh, head, keyptr, keylen, out)
Definition uthash.h:165
void read_dem_file(char *fnam, csr_t **p_spaH, csr_t **p_spaL, double pmin, int debug)
Read a Detector Error Model (DEM) file and construct H and L matrices.
Definition util_io.c:1728
long long int codeword_window_count(const params_t *const p)
Number of codewords in the collection window (weight up to min_w + dW with outC or maxC,...
Definition util_io.c:2635
#define HELP_MAX_NAMES
Definition util_io.c:370
#define HELP_NAME_LEN
Definition util_io.c:371
void compute_min_w_hit_stats(const params_t *const p, int *min_cnt, int *max_cnt, double *avg_cnt, double *stdev_cnt)
Compute hit count statistics (min, max, avg, stdev) for minimum-weight codewords.
Definition util_io.c:2745
void codeword_hit_stats(const params_t *const p, cw_hit_stats_t *const st)
Compute the hit statistics of the min_hits stopping criterion.
Definition util_io.c:2809
int nzlist_w_append(FILE *f, const cw_vec_t *const vec)
Definition util_io.c:2534
int check_min_hits_convergence(const params_t *const p)
Check whether the QDistRnd-style hit count stopping condition is met.
Definition util_io.c:2906
FILE * nzlist_w_new(const char fnam[], const char comment[])
Definition util_io.c:2522
void print_short_help(const char *prog)
Print short help message listing all allowed parameters to stderr.
Definition util_io.c:95
@ DROP_UNK
Definition util_io.c:1961
@ DROP_YES
Definition util_io.c:1961
@ DROP_NO
Definition util_io.c:1961
void var_kill(params_t *const p)
Clean up and free memory allocated in the params_t structure.
Definition util_io.c:1492
FILE * nzlist_r_open(const char fnam[], long long int *lineno)
Definition util_io.c:2546
params_t *const p
Definition util_io.c:93
double rw_infoset_find_prob(const int n, const int rank, const int w, const int pairs_l)
Information-set estimate of the probability that a RW step finds a given codeword of weight w.
Definition util_io.c:2990
long long int nzlist_read(const char fnam[], params_t *p)
Read codewords from a .nz list file and add them to the codeword hash.
Definition util_io.c:3073
cw_vec_t * nzlist_r_one(FILE *f, cw_vec_t *vec, const char fnam[], long long int *lineno)
Definition util_io.c:2566
int codeword_maxc_reached(const params_t *const p)
Returns 1 if maxC > 0 and maxC codewords of the collection window are collected.
Definition util_io.c:2644
int codeword_feed_limit(const params_t *const p)
Weight limit (exclusive) of RW codewords needed for the min_hits statistic.
Definition util_io.c:2648
void print_rw_infoset_estimate(FILE *stream, const int n, const int rank, const int w_lo, const int w_hi, const long steps, const long steps_total, const double t_step, const int pairs_l)
Print the information-set estimate of the probability that RW missed a codeword lighter than the uppe...
Definition util_io.c:3010
void print_codeword_support(FILE *stream, const char *prefix, const int arr[], const int weight, const int debug)
Print the support of a codeword (1-based column indices) in one line, for DBG_CODEWORDS.
Definition util_io.c:3186
int codeword_hit_check(FILE *stream, const params_t *const p)
Check the uniformity of the hit counts within each weight class of the representative set.
Definition util_io.c:2939
void print_rw_step_stats(FILE *stream, const params_t *const p)
DBG_SUMMARY: the codewords of the lowest three weights in hash found by the RW steps with a uniform r...
Definition util_io.c:2881
long long int nzlist_write(const char fnam[], const char comment[], params_t *p)
Write the collected codewords from the hash table to a .nz file.
Definition util_io.c:3197
cw_vec_t * codeword_add_maybe(params_t *const p, const int arr[], int weight, int src)
Add a candidate codeword to the hash table, or count one more hit of a known codeword.
Definition util_io.c:2693
params_t prm
Definition util_io.c:8
void print_codeword_stats(FILE *stream, const params_t *const p)
Print accumulated codeword and hit statistics to the given stream.
Definition util_io.c:2828
void csr_dump(FILE *stream, const csr_t *const M, const char name[], const int debug)
Print a binary CSR matrix row by row ('1': nonzero, '.': zero), for DBG_MATRICES.
Definition util_io.c:186
#define HELP_MAX_MEMBERS
Definition util_io.c:372
void var_init(int argc, char **argv, params_t *const p)
Initialize parameters and load matrices from command line arguments.
Definition util_io.c:766
#define SHORT_HELP
Definition util_io.h:553
#define DIST_M4RI_VERSION
Definition util_io.h:544
#define DBG_ARGS
Definition util_io.h:37
#define DBG_TIMING
Definition util_io.h:34
#define MORE_HELP
Definition util_io.h:629
struct CW_VEC_T cw_vec_t
Definition util_io.h:49
#define USAGE
Definition util_io.h:566
#define DBG_SUMMARY
Definition util_io.h:31
#define DBG_CW_MAX
Definition util_io.h:43
#define DBG_CODEWORDS
Definition util_io.h:36
#define DBG_LARGE
Definition util_io.h:40
@ CW_SRC_RW_WINDOW
Definition util_io.h:253
@ CW_SRC_OTHER
Definition util_io.h:253
@ CW_SRC_RW_UNIFORM
Definition util_io.h:253
#define DBG_MATRICES
Definition util_io.h:38
#define RW_PAIRS_MAX_BITS
The largest number of random pivot rows of the buckets of the RW pairs (pairs, a 64-bit key).
Definition util_io.h:278
#define DBG_PARAMS
Definition util_io.h:35
#define MAX_W
Definition util_io.h:47
cols_ws_t * cols_ws_init(const int m)
Allocate the workspace of csr_cols_rank() and csr_cols_dependency() for a matrix H with m rows.
Definition util_m4ri.c:1011
colmask_t * colmask_free(colmask_t *cm)
Free the column masks allocated by colmask_from_csr().
Definition util_m4ri.c:1003
cols_ws_t * cols_ws_free(cols_ws_t *ws)
Free the workspace allocated by cols_ws_init().
Definition util_m4ri.c:1022
int csr_csr_mul_non_zero(const csr_t *const A, const csr_t *const B)
Check if the product of two sparse matrices A * B^T is non-zero.
Definition util_m4ri.c:342
csr_t * csr_from_pairs(csr_t *mat, const int nz, int_pair *const prs, const int nrows, const int ncols)
Construct a CSR sparse matrix from an array of coordinate pairs.
Definition util_m4ri.c:597
csr_t * csr_transpose(csr_t *dst, const csr_t *const p)
Transpose a compressed CSR sparse matrix.
Definition util_m4ri.c:163
csr_t * csr_mm_read(char *fin, csr_t *mat, int transpose)
Read a sparse matrix from a Matrix Market (.mtx) file into CSR format.
Definition util_m4ri.c:655
csr_t * Lx_for_CSS_code(const csr_t *const Hx, const csr_t *const Hz)
Compute logical generator matrix Lx for a CSS code.
Definition util_m4ri.c:934
csr_t * csr_init(csr_t *mat, int rows, int cols, int nzmax)
Initialize or reallocate a CSR sparse matrix to target size.
Definition util_m4ri.c:539
mzd_t * mzd_from_csr(mzd_t *dst, const csr_t *p)
Convert a CSR sparse matrix to an MZD dense matrix.
Definition util_m4ri.c:186
csr_t * csr_free(csr_t *p)
Free memory allocated for a CSR sparse matrix.
Definition util_m4ri.c:522
void csr_compress(csr_t *mat)
Compress CSR matrix (sort indices and remove duplicates/zeros).
Definition util_m4ri.c:574
int csr_max_row_wght(const csr_t *const p)
return max row weight of CSR matrix p TODO: add code for List of Pairs
Definition util_m4ri.c:145
colmask_t * colmask_from_csr(const csr_t *const A)
Construct the column bit masks of a sparse binary matrix (see colmask_t).
Definition util_m4ri.c:985
int csr_cols_dependency(const csr_t *const HT, const int cnt, const int cols[], cols_ws_t *const ws, int sub[])
A nonempty proper subset of a set of columns of H with zero sum, if there is one.
Definition util_m4ri.c:1123
#define ERROR(fmt,...)
Definition util_m4ri.h:13