91static pthread_mutex_t m4ri_mem_mutex = PTHREAD_MUTEX_INITIALIZER;
93static inline mzd_t *safe_mzd_from_csr(mzd_t *dst,
const csr_t *
p) {
94 pthread_mutex_lock(&m4ri_mem_mutex);
96 pthread_mutex_unlock(&m4ri_mem_mutex);
100static inline mzd_t *safe_mzd_init(rci_t r, rci_t c) {
101 pthread_mutex_lock(&m4ri_mem_mutex);
102 mzd_t *res = mzd_init(r, c);
103 pthread_mutex_unlock(&m4ri_mem_mutex);
107static inline void safe_mzd_free(mzd_t *M) {
109 pthread_mutex_lock(&m4ri_mem_mutex);
111 pthread_mutex_unlock(&m4ri_mem_mutex);
114static inline mzp_t *safe_mzp_init(rci_t length) {
115 pthread_mutex_lock(&m4ri_mem_mutex);
116 mzp_t *res = mzp_init(length);
117 pthread_mutex_unlock(&m4ri_mem_mutex);
121static inline void safe_mzp_free(mzp_t *P) {
123 pthread_mutex_lock(&m4ri_mem_mutex);
125 pthread_mutex_unlock(&m4ri_mem_mutex);
128static inline double get_time_sec(
void) {
130 clock_gettime(CLOCK_MONOTONIC, &ts);
131 return (
double)ts.tv_sec + (double)ts.tv_nsec * 1e-9;
135static inline uint64_t mix64(uint64_t z) {
136 z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9ULL;
137 z = (z ^ (z >> 27)) * 0x94d049bb133111ebULL;
138 return z ^ (z >> 31);
141static inline uint64_t splitmix64(uint64_t *state) {
142 return mix64(*state += 0x9e3779b97f4a7c15ULL);
145static inline int rand_uniform_thread(
int max, uint64_t *state) {
146 if (max <= 1)
return 0;
147 return (
int)(splitmix64(state) % (uint64_t)max);
150static inline mzp_t * mzp_rand_thread(mzp_t *q, rci_t length, uint64_t *state) {
151 if (q == NULL)
return NULL;
152 for (
int i = 0; i <= (int)length - 2; i++) {
153 q->values[i] = i + rand_uniform_thread(length - i, state);
155 for (
int i = length - 1; i < (int)q->length; i++) {
161#define CC_SPLIT_MAX_LEVELS 3
197 _Alignas(64) atomic_int dmin;
199 atomic_int cc_found_weight;
200 atomic_bool cc_exact;
201 atomic_bool stop_flag;
202 atomic_bool rw_stop_flag;
203 atomic_int stop_reason;
204 atomic_int cw_feed_limit;
207 _Alignas(64)
long total_rw_steps;
208 atomic_long rw_steps_started;
209 atomic_long rw_steps_completed;
210 atomic_llong rw_busy_ns;
211 atomic_long rw_timed_steps;
213 atomic_long rw_unif_steps;
214 atomic_int rw_pairs_l;
215 atomic_llong rw_pairs_checked;
216 atomic_llong rw_hits1;
217 atomic_llong rw_hits2;
218 const uint64_t *rw_lhash;
221 _Alignas(64) atomic_llong cc_next;
224 bool even_weight_only;
228 atomic_int cc_active_workers;
229 atomic_int cc_target_workers;
230 atomic_int cc_round_active;
231 atomic_llong cc_busy_ns;
232 atomic_llong cc_nodes;
235 pthread_mutex_t cw_mutex;
238 atomic_llong cw_red_cc;
239 atomic_llong cw_red_rw;
244 pthread_rwlock_t basis_rwlock;
245 atomic_long next_refresh_step;
248 double cc_time_per_weight[
MAX_W];
291static inline void ctx_stop(
distfork_ctx_t *
const ctx,
const int reason) {
293 atomic_compare_exchange_strong(&ctx->stop_reason, &none, reason);
294 atomic_store(&ctx->stop_flag,
true);
300static inline int ctx_target(
const distfork_ctx_t *
const ctx,
const int cur_dmax) {
301 const int dstop = ctx->
p->
dstop;
302 return (dstop > 0 && (cur_dmax == 0 || dstop < cur_dmax)) ? dstop : cur_dmax;
322#define CC_IDX_BITS 32
323#define CC_LEV_SHIFT 48
324static inline long long cc_pack(
const int w,
const int lev,
const int idx) {
327static inline int cc_unpack_w(
const long long v) {
return (
int)((v >>
CC_IDX_BITS) & 0xffff); }
328static inline int cc_unpack_lev(
const long long v) {
return (
int)((v >>
CC_LEV_SHIFT) & 0xf); }
329static inline int cc_unpack_idx(
const long long v) {
return (
int)(v & 0xffffffffLL); }
332static inline int cc_round_beg(
const params_t *
const p) {
333 return (
p->start_num > 0) ? 0 : ((
p->cbeg >= 0) ?
p->cbeg : 0);
338static inline int cc_round_end(
const params_t *
const p,
const int w) {
339 if (
p->start_num > 0)
return p->start_num - 1;
340 const int nvar =
p->spaH->cols;
341 return (
p->cend >= 0) ? minint(
p->cend, nvar - w) : (nvar - w);
345static inline int cc_unit_last(
const distfork_ctx_t *
const ctx,
const int w,
const int lev) {
346 return (cc_round_end(ctx->
p, w) + 1) * ctx->cc_R[lev] - 1;
349#define CC_SPLIT_UNITS_PER_THREAD 4.0
350#define CC_SPLIT_MIN_WORK 1e-3
352#define CC_SPLIT_CLAIMS_PER_WORK 1e4
353#define CC_SPLIT_CLAIMS_UNKNOWN 65536
364static int cc_round_levels(
const distfork_ctx_t *
const ctx,
const int w,
const int beg,
const int end,
367 const int n_start = end - beg + 1;
369 if (nthr <= 1 || n_start <= 0 || lmax <= 0 || ctx->cc_B <= 1)
return 0;
370 const int busy = (ctx->
p->
start_num > 0) ? minint(nthr, n_start) : nthr;
373 double units = (ctx->
p->
start_num > 0) ? (
double)n_start : (double)n_start / w;
376 const long long R = ctx->cc_R[lev + 1];
377 if (R <= 0 || (
double)n_start * (
double)R > max_units || ((
long long)end + 1) * R > (
long long)INT_MAX - nthr - 1)
380 units *= ctx->cc_branch;
387static inline void ctx_update_feed_limit(
distfork_ctx_t *
const ctx) {
389 if (atomic_load_explicit(&ctx->cw_feed_limit, memory_order_relaxed) != feed) {
390 atomic_store_explicit(&ctx->cw_feed_limit, feed, memory_order_relaxed);
399 if (!ctx->
p->
irred || !ws || cnt < 2)
return true;
410 if (!cw_irreducible(ctx, warg->
irr_ws, err->
wei, err->
vec)) {
411 atomic_fetch_add_explicit(&ctx->cw_red_cc, 1, memory_order_relaxed);
415 pthread_mutex_lock(&ctx->cw_mutex);
417 ctx_update_feed_limit(ctx);
418 int cur_d = atomic_load(&ctx->dmax);
419 if (
p->min_w < cur_d || cur_d == 0) {
420 atomic_store(&ctx->dmax,
p->min_w);
423 snprintf(prefix,
sizeof(prefix),
"# [thread %d] CC codeword", warg->
tid);
427 int cur_cc_found = atomic_load(&ctx->cc_found_weight);
428 if (cur_cc_found == 0 || err->
wei < cur_cc_found) {
429 atomic_store(&ctx->cc_found_weight, err->
wei);
431 if (!
p->outC &&
p->maxC == 0) {
439 pthread_mutex_unlock(&ctx->cw_mutex);
443static inline int bit_get(
const uint64_t *
const b,
const int i) {
return (
int)((b[i >> 6] >> (i & 63)) & 1); }
444static inline void bit_flip(uint64_t *
const b,
const int i) { b[i >> 6] ^= (uint64_t)1 << (i & 63); }
447static inline int bits_first(
const uint64_t *
const b,
const int from,
const int limit) {
448 if (from >= limit)
return limit;
450 const int k_last = (limit - 1) >> 6;
451 uint64_t x = b[k] & (~(uint64_t)0 << (from & 63));
453 if (++k > k_last)
return limit;
456 const int r = (k << 6) + __builtin_ctzll(x);
457 return (r < limit) ? r : limit;
506static bool cc_walk_found(
cc_walk_t *
const t,
const int c1,
const int c2) {
509 for (
int i = 0; i < t->
w; i++) cw->
vec[n++] = t->
cols[i];
510 if (c1 >= 0) cw->
vec[n++] = c1;
512 for (
int i = 1; i < n; i++) {
513 const int x = cw->
vec[i];
515 while (j >= 0 && cw->
vec[j] > x) {
516 cw->
vec[j + 1] = cw->
vec[j];
523 if (mL && !colmask_syndrome_non_zero(mL, n, cw->
vec))
return false;
524 return cc_record(t->
ctx, t->
warg, cw);
528static bool cc_sum2_equal(
const uint64_t *
const S,
const int sw,
const csr_t *
const mHT,
const int c1,
const int c2) {
529 int a = mHT->
p[c1], b = mHT->
p[c2], k = 0;
530 const int a_end = mHT->
p[c1 + 1], b_end = mHT->
p[c2 + 1];
531 while (a < a_end || b < b_end) {
533 if (b >= b_end) r = mHT->
i[a++];
534 else if (a >= a_end) r = mHT->
i[b++];
535 else if (mHT->
i[a] < mHT->
i[b]) r = mHT->
i[a++];
536 else if (mHT->
i[b] < mHT->
i[a]) r = mHT->
i[b++];
542 if (!bit_get(S, r))
return false;
556static __attribute__((noinline))
int cc_walk_last2(
cc_walk_t *
const t,
const int sw,
const uint64_t h,
557 const int i1_beg,
const int i1_end) {
560 const uint64_t *
const hc = hs->
hc;
561 const uint64_t *
const bits = hs->
bits;
562 const uint64_t bmask = hs->
bmask;
563 const csr_t *
const mH = t->
mH;
564 const int col_min = t->
col_min;
565 for (
int i1 = i1_beg; i1 < i1_end; i1++) {
566 const int c1 = mH->
i[i1];
567 if (c1 <= col_min)
continue;
568 const uint64_t key = h ^ hc[c1];
569 const uint64_t fb = key & bmask;
570 if (!((bits[fb >> 6] >> (fb & 63)) & 1))
continue;
571 int j = (int)(key >> hs->
tshift);
573 if (hs->
tab[j].
cnt == 0)
continue;
575 if (bit_get(t->
E, c1))
continue;
577 for (
int k = hs->
tab[j].
beg; k < k_end; k++) {
578 const int c2 = hs->
cols[k];
579 if (c2 <= col_min || c2 == c1 || bit_get(t->
E, c2))
continue;
580 if (!cc_sum2_equal(t->
S, sw, t->
mHT, c1, c2))
continue;
582 if (atomic_load_explicit(&ctx->stop_flag, memory_order_relaxed))
return 0;
583 const int cur_dmax = atomic_load_explicit(&ctx->dmax, memory_order_relaxed);
584 if (cur_dmax > 0 && ctx->
p->
dW >= 0 && minint(t->
w_limit, cur_dmax + ctx->
p->
dW) < t->
w + 2)
return 0;
585 if (cc_walk_found(t, c1, c2))
return 1;
596static int cc_walk(
cc_walk_t *
const t,
const int sw,
const int r0,
const uint64_t h) {
598 if (atomic_load_explicit(&ctx->stop_flag, memory_order_relaxed))
return 0;
601 int current_limit = t->
w_limit;
602 const int cur_dmax = atomic_load_explicit(&ctx->dmax, memory_order_relaxed);
603 if (cur_dmax > 0 &&
p->dW >= 0) current_limit = minint(t->
w_limit, cur_dmax +
p->dW);
604 if (w >= current_limit)
return 0;
606 const csr_t *
const mH = t->
mH;
609 int i1_beg = mH->
p[r0], i1_end = mH->
p[r0 + 1];
611 i1_beg += t->
fix[w - 1];
612 if (i1_beg >= i1_end)
return 0;
615 const int col_min = t->
col_min;
616 uint64_t *
const S = t->
S;
617 uint64_t *
const E = t->
E;
619 if (w + 1 == current_limit) {
620 for (
int i1 = i1_beg; i1 < i1_end; i1++) {
621 const int c = mH->
i[i1];
622 if (c <= col_min)
continue;
623 const int pb = mHT->
p[c];
624 if (mHT->
p[c + 1] - pb != sw)
continue;
626 while (k < sw && bit_get(S, mHT->
i[pb + k])) k++;
627 if (k < sw || bit_get(E, c))
continue;
628 if (cc_walk_found(t, -1, c))
return 1;
632 if (w + 2 == current_limit && w + 1 > t->
nfix)
return cc_walk_last2(t, sw, h, i1_beg, i1_end);
635 const int max_reach = (current_limit - (w + 1)) * t->
max_col_wt;
636 const uint64_t *
const hc = t->
hs->
hc;
637 for (
int i1 = i1_beg; i1 < i1_end; i1++) {
638 const int c1 = mH->
i[i1];
639 if (c1 <= col_min)
continue;
640 const int pb = mHT->
p[c1], pe = mHT->
p[c1 + 1];
641 if (sw - (pe - pb) > max_reach || bit_get(E, c1))
continue;
643 for (
int k = pb; k < pe; k++) nset += bit_get(S, mHT->
i[k]);
644 const int sw1 = sw + (pe - pb) - 2 * nset;
645 if (sw1 <= 0 || sw1 > max_reach)
continue;
648 for (
int k = pb; k < pe; k++) {
649 const int r = mHT->
i[k];
650 if (!bit_get(S, r) && r < rmin) rmin = r;
653 const int r1 = (rmin < r0) ? rmin : bits_first(S, r0 + 1, rmin);
655 t->
cols[t->
w++] = c1;
656 const int result = cc_walk(t, sw1, r1, h ^ hc[c1]);
659 for (
int k = pb; k < pe; k++) bit_flip(S, mHT->
i[k]);
660 if (result == 1)
return 1;
666static void cc_walk_unit(
cc_walk_t *
const t,
const int col,
const int w_limit,
const int *
const fix,
const int nfix) {
668 const int pb = mHT->
p[col], pe = mHT->
p[col + 1];
669 for (
int k = pb; k < pe; k++) bit_flip(t->
S, mHT->
i[k]);
679 cc_walk(t, pe - pb, mHT->
i[pb], t->
hs->
hc[col]);
682 for (
int k = pb; k < pe; k++) bit_flip(t->
S, mHT->
i[k]);
703static inline void bnb_take(
cc_walk_t *
const t,
const int v) {
705 const int *
const ri = mHT->
i, ke = mHT->
p[v + 1];
706 int *
const acnt = t->
acnt;
707 t->
X[v >> 6] |= (uint64_t)1 << (v & 63);
708 for (
int k = mHT->
p[v]; k < ke; k++) acnt[ri[k]]--;
712static inline void bnb_give(
cc_walk_t *
const t,
const int v) {
714 const int *
const ri = mHT->
i, ke = mHT->
p[v + 1];
715 int *
const acnt = t->
acnt;
716 t->
X[v >> 6] &= ~((uint64_t)1 << (v & 63));
717 for (
int k = mHT->
p[v]; k < ke; k++) acnt[ri[k]]++;
720static inline bool bnb_avail(
const cc_walk_t *
const t,
const int u) {
return u > t->
col_min && !bit_get(t->
X, u); }
723static inline bool bnb_free(
const cc_walk_t *
const t,
const int u) {
return !bit_get(t->
X, u); }
726static inline uint32_t bnb_next_gen(
cc_walk_t *
const t) {
728 memset(t->
mark, 0, (
size_t)t->
mHT->
rows *
sizeof(uint32_t));
736static inline bool bnb_record_ok(
const cc_walk_t *
const t,
const int extra) {
738 if (atomic_load_explicit(&ctx->stop_flag, memory_order_relaxed))
return false;
739 const int cur_dmax = atomic_load_explicit(&ctx->dmax, memory_order_relaxed);
740 return !(cur_dmax > 0 && ctx->
p->
dW >= 0 && minint(t->
w_limit, cur_dmax + ctx->
p->
dW) < t->
w + extra);
746static int bnb_packing(
cc_walk_t *
const t,
const int R,
const int nu,
const int amin,
const int amax) {
747 int *
const bcnt = t->
bcnt;
748 const int *
const ulist = t->
ulist, *
const uacnt = t->
uacnt;
749 int *
const ord = t->
ord;
750 const int na = amax - amin + 1;
751 for (
int a = 0; a <= na; a++) bcnt[a] = 0;
752 for (
int i = 0; i < nu; i++) bcnt[uacnt[i] - amin + 1]++;
753 for (
int a = 1; a <= na; a++) bcnt[a] += bcnt[a - 1];
754 for (
int i = 0; i < nu; i++) ord[bcnt[uacnt[i] - amin]++] = ulist[i];
755 const uint32_t g = bnb_next_gen(t);
756 uint32_t *
const mark = t->
mark;
757 const uint64_t *
const X = t->
X;
758 const int *
const rp = t->
mH->
p, *
const ci = t->
mH->
i, *
const kf = t->
kfirst;
760 for (
int i = 0; i < nu; i++) {
761 const int r = ord[i], kb = kf[r], ke = rp[r + 1];
763 while (k < ke && !(mark[ci[k]] == g && !bit_get(X, ci[k]))) k++;
765 if (++cnt > R || cnt + (nu - 1 - i) <= R)
return cnt;
766 for (k = kb; k < ke; k++) mark[ci[k]] = g;
767 }
else if (cnt + (nu - 1 - i) <= R) {
777static bool bnb_cover(
cc_walk_t *
const t,
const int R,
const int nu) {
778 const csr_t *
const mH = t->
mH;
779 const uint32_t g = bnb_next_gen(t);
781 for (
int i = 0; i < nu; i++) {
782 const int r = t->
ulist[i];
783 for (
int k = t->
kfirst[r]; k < mH->
p[r + 1]; k++) {
784 const int u = mH->
i[k];
785 if (!bnb_free(t, u))
continue;
786 if (t->
mark[u] != g) {
795 int *
const hist = t->
hist;
796 for (
int c = 0; c <= D; c++) hist[c] = 0;
797 for (
int i = 0; i < nt; i++) hist[t->
cv[t->
touched[i]]]++;
798 int need = nu, tc = 0;
799 for (
int c = D; c >= 1 && need > 0; c--) {
800 if ((
long long)hist[c] * c >= need) {
801 tc += (need + c - 1) / c;
808 if (need > 0 || tc > R)
return true;
810 for (
int i = 0; i < nu; i++) {
811 const int r = t->
ulist[i];
813 for (
int k = t->
kfirst[r]; k < mH->
p[r + 1]; k++) {
814 const int u = mH->
i[k];
815 if (bnb_free(t, u) && t->
cv[u] > mx) mx = t->
cv[u];
817 if (mx == 0)
return true;
821 if (sum > R + 1e-9)
return true;
822 if (sum + (nu - 1 - i) < R + 1e-9 - 1e-7)
return false;
829 const uint64_t fb = key & hs->
bmask;
830 if (!((hs->
bits[fb >> 6] >> (fb & 63)) & 1))
return NULL;
831 int j = (int)(key >> hs->
tshift);
833 return (hs->
tab[j].
cnt > 0) ? &hs->
tab[j] : NULL;
838static int bnb_leaf(
cc_walk_t *
const t,
const int sw,
const uint64_t h) {
842 for (
int k = sl->
beg; k < sl->beg + sl->
cnt; k++) {
843 const int u = t->
hs->
cols[k];
844 if (!bnb_avail(t, u))
continue;
845 const int pb = mHT->
p[u];
846 if (mHT->
p[u + 1] - pb != sw)
continue;
848 while (x < sw && bit_get(t->
S, mHT->
i[pb + x])) x++;
849 if (x < sw)
continue;
850 if (!bnb_record_ok(t, 1))
return 0;
851 if (cc_walk_found(t, -1, u))
return 1;
861static int bnb_last2(
cc_walk_t *
const t,
const int sw,
const uint64_t h,
const int r) {
863 const csr_t *
const mH = t->
mH;
864 for (
int i = t->
kfirst[r]; i < mH->
p[r + 1]; i++) {
865 const int v = mH->
i[i];
866 if (!bnb_free(t, v))
continue;
869 for (
int k = sl->
beg; k < sl->beg + sl->
cnt; k++) {
870 const int u = hs->
cols[k];
871 if (u == v || !bnb_avail(t, u))
continue;
872 if (!cc_sum2_equal(t->
S, sw, t->
mHT, v, u))
continue;
873 if (!bnb_record_ok(t, 2))
return 0;
874 if (cc_walk_found(t, v, u))
return 1;
883static int cc_bnb(
cc_walk_t *
const t,
const int sw,
const uint64_t h) {
885 if (atomic_load_explicit(&ctx->stop_flag, memory_order_relaxed))
return 0;
888 int current_limit = t->
w_limit;
889 const int cur_dmax = atomic_load_explicit(&ctx->dmax, memory_order_relaxed);
890 if (cur_dmax > 0 &&
p->dW >= 0) current_limit = minint(t->
w_limit, cur_dmax +
p->dW);
891 const int R = current_limit - w;
892 if (R <= 0 || sw > R * t->
max_col_wt)
return 0;
894 const bool fixed = (w <= t->
nfix);
895 if (R == 1 && !fixed)
return bnb_leaf(t, sw, h);
897 const int nwords = (t->
m + 63) >> 6;
898 const int *
const acnt = t->
acnt;
899 int nu = 0, rbest = -1, abest = INT_MAX, amax = 0;
900 if (R == 2 && !fixed) {
901 for (
int k = 0; k < nwords; k++) {
902 uint64_t x = t->
S[k];
904 const int r = (k << 6) + __builtin_ctzll(x);
906 const int a = acnt[r];
908 if (a == 0)
return 0;
914 return bnb_last2(t, sw, h, rbest);
916 int *
const ulist = t->
ulist, *
const uacnt = t->
uacnt;
917 for (
int k = 0; k < nwords; k++) {
918 uint64_t x = t->
S[k];
920 const int r = (k << 6) + __builtin_ctzll(x);
922 const int a = acnt[r];
929 if (a > amax) amax = a;
932 if (abest == 0)
return 0;
933 if (sw > R && R >= 3) {
934 if (bnb_packing(t, R, nu, abest, amax) > R)
return 0;
936 if (t->
prune >= 2 && R >= 4 && bnb_cover(t, R, nu))
return 0;
939 const csr_t *
const mH = t->
mH;
941 int *
const brl = t->
brl + (size_t)w * (
size_t)t->
max_row_wt;
943 for (
int k = t->
kfirst[rbest]; k < mH->
p[rbest + 1]; k++) {
944 const int u = mH->
i[k];
945 if (bnb_free(t, u)) brl[q++] = u;
947 const int fsp0 = t->
fsp;
948 int i_beg = 0, i_end = q;
950 const int d = t->
fix[w - 1];
951 if (d >= q)
return 0;
952 for (
int i = 0; i < d; i++) {
960 const uint64_t *
const hc = t->
hs->
hc;
961 uint64_t *
const S = t->
S;
963 for (
int i = i_beg; i < i_end && !res; i++) {
964 const int v = brl[i];
965 const int pb = mHT->
p[v], pe = mHT->
p[v + 1];
968 if (sw - (pe - pb) > reach)
continue;
970 for (
int k = pb; k < pe; k++) nset += bit_get(S, mHT->
i[k]);
971 const int sw1 = sw + (pe - pb) - 2 * nset;
973 if (R == 1 && bnb_record_ok(t, 1)) res = cc_walk_found(t, -1, v);
976 if (sw1 > reach)
continue;
977 for (
int k = pb; k < pe; k++) bit_flip(S, mHT->
i[k]);
979 res = cc_bnb(t, sw1, h ^ hc[v]);
981 for (
int k = pb; k < pe; k++) bit_flip(S, mHT->
i[k]);
983 while (t->
fsp > fsp0) bnb_give(t, t->
fstk[--t->
fsp]);
991static void bnb_acnt_setup(
cc_walk_t *
const t,
const int c) {
995 for (
int u = t->
acnt_col + 1; u <= c; u++)
996 for (
int k = mHT->
p[u]; k < mHT->
p[u + 1]; k++) {
997 t->
acnt[mHT->
i[k]]--;
1001 const csr_t *
const mH = t->
mH;
1002 for (
int r = 0; r < t->
m; r++) {
1004 while (k < mH->
p[r + 1] && mH->
i[k] <= c) k++;
1005 t->
acnt[r] = mH->
p[r + 1] - k;
1017static void cc_bnb_unit(
cc_walk_t *
const t,
const int col,
const int idx,
const int w_limit,
const int *
const fix,
1021 const bool list = (
p->start_num > 0 && !
p->start_scan);
1023 bnb_acnt_setup(t, t->
col_min);
1026 for (
int i = 0; i < idx; i++) {
1027 bnb_take(t,
p->start_list[i]);
1028 t->
fstk[t->
fsp++] =
p->start_list[i];
1032 const int pb = mHT->
p[col], pe = mHT->
p[col + 1];
1033 for (
int k = pb; k < pe; k++) bit_flip(t->
S, mHT->
i[k]);
1041 cc_bnb(t, pe - pb, t->
hs->
hc[col]);
1043 for (
int k = pb; k < pe; k++) bit_flip(t->
S, mHT->
i[k]);
1044 if (list) bnb_give(t, col);
1045 while (t->
fsp > 0) bnb_give(t, t->
fstk[--t->
fsp]);
1052 const int w_limit,
const int max_col_wt,
1053 const csr_t *
const mH,
const csr_t *
const mHT,
1054 worker_arg_t *warg,
const int *
const fix,
const int nfix) {
1056 if (atomic_load_explicit(&ctx->stop_flag, memory_order_relaxed)) {
1060 const int w = err->
wei;
1061 int current_limit = w_limit;
1062 int cur_dmax = atomic_load_explicit(&ctx->dmax, memory_order_relaxed);
1063 if (cur_dmax > 0 &&
p->dW >= 0) {
1064 current_limit = minint(w_limit, cur_dmax +
p->dW);
1066 if (w >= current_limit) {
1071 const int syn_w_wei = syn_w->
wei;
1072 const int row = syn_w->
vec[0];
1074 int i1_beg = mH->
p[row], i1_end = mH->
p[row + 1];
1076 i1_beg += fix[w - 1];
1077 if (i1_beg >= i1_end)
return 0;
1078 i1_end = i1_beg + 1;
1084 const int col_min = (
p->start_num > 0 && !
p->start_scan) ? -1 : urr->vec[0];
1087 if (w + 1 == current_limit) {
1088 int max_leaf_swei = 0;
1089 if (
p->smax > 0 && current_limit <
MAX_W) {
1090 int cur_min = warg->
min_swei[current_limit] - 1;
1091 max_leaf_swei = (cur_min <
p->smax) ? (cur_min > 0 ? cur_min : 0) :
p->smax;
1094 for (
int i1 = i1_beg; i1 < i1_end; i1++) {
1095 const int col = mH->
i[i1];
1096 if (col <= col_min)
continue;
1098 const int p_beg = mHT->
p[col];
1099 const int col_wt = mHT->
p[col + 1] - p_beg;
1100 if (abs(syn_w_wei - col_wt) > max_leaf_swei)
continue;
1103 if (max_leaf_swei == 0) {
1105 if (mHT->
i[p_beg] != row ||
1106 mHT->
i[p_beg + col_wt - 1] != syn_w->
vec[col_wt - 1]) {
1109 if (memcmp(syn_w->
vec, &mHT->
i[p_beg], (
size_t)col_wt *
sizeof(
int)) != 0) {
1112 if (one_ordered_search(err, col) != -1)
continue;
1115 if (one_ordered_search(err, col) != -1)
continue;
1116 syn[w + 1]->
wei = 0;
1117 swei = one_csr_row_combine(syn[w + 1], syn_w, mHT, col);
1119 if (swei <= max_leaf_swei) {
1121 int cur_min = swei - 1;
1122 max_leaf_swei = (cur_min <
p->smax) ? (cur_min > 0 ? cur_min : 0) :
p->smax;
1129 int pos = one_ordered_ins(err, col);
1130 int nz = (!mL) || colmask_syndrome_non_zero(mL, err->
wei, err->
vec);
1131 if (nz && cc_record(ctx, warg, err)) {
1132 one_ordered_pos_del(err, col, pos);
1135 one_ordered_pos_del(err, col, pos);
1141 const int rem = current_limit - (w + 1);
1142 int max_s_needed = 0;
1143 if (
p->smax > 0 && current_limit <
MAX_W) {
1144 int cur_min = warg->
min_swei[current_limit] - 1;
1145 max_s_needed = (cur_min <
p->smax) ? (cur_min > 0 ? cur_min : 0) :
p->smax;
1147 const int max_reach = rem * max_col_wt + max_s_needed;
1149 for (
int i1 = i1_beg; i1 < i1_end; i1++) {
1150 const int col = mH->
i[i1];
1151 if (col > col_min) {
1152 const int col_wt = mHT->
p[col + 1] - mHT->
p[col];
1153 if (syn_w_wei - col_wt > max_reach)
continue;
1155 int pos = one_ordered_search(err, col);
1157 syn[w + 1]->
wei = 0;
1158 int swei = one_csr_row_combine(syn[w + 1], syn_w, mHT, col);
1160 if (
p->smax && swei > 0 && swei <= p->smax && (w + 1 <
MAX_W)) {
1161 if (swei < warg->min_swei[w + 1]) {
1166 if (swei > 0 && swei <= max_reach) {
1169 pos = one_ordered_ins(err, col);
1170 int result = start_CC_recurs_mt(err, urr, syn, w_limit, max_col_wt,
1171 mH, mHT, warg, fix, nfix);
1173 one_ordered_pos_del(err, col, pos);
1186static inline int hybrid_window(
const params_t *
const p,
const int nvar) {
1187 return rw_hybrid_window(
p, nvar);
1191static inline void rw_time_publish(
distfork_ctx_t *ctx,
double *acc_t,
int *acc_n) {
1193 atomic_fetch_add_explicit(&ctx->rw_busy_ns, (
long long)(*acc_t * 1e9), memory_order_relaxed);
1194 atomic_fetch_add_explicit(&ctx->rw_timed_steps, *acc_n, memory_order_relaxed);
1202static inline void rw_time_step(
distfork_ctx_t *ctx,
double *t_last,
double *acc_t,
int *acc_n,
const bool first) {
1203 const double t_now = get_time_sec();
1204 *acc_t += t_now - *t_last;
1207 if (first || *acc_t >= 0.002) rw_time_publish(ctx, acc_t, acc_n);
1212 return codeword_rw_limit(ctx->
p, atomic_load_explicit(&ctx->dmax, memory_order_relaxed),
1213 atomic_load_explicit(&ctx->cw_feed_limit, memory_order_relaxed));
1219static void rw_record_codeword(
distfork_ctx_t *
const ctx,
const rci_t *
const ee,
const int cnt,
const int tid,
1220 const int ksub,
const int src) {
1224 const bool collecting = (
p->outC != NULL) || (
p->maxC > 0);
1225 pthread_mutex_lock(&ctx->cw_mutex);
1227 ctx_update_feed_limit(ctx);
1228 const int best =
p->min_w;
1229 const int old_dmax = atomic_load(&ctx->dmax);
1230 if (old_dmax == 0 || best < old_dmax) {
1231 atomic_store(&ctx->dmax, best);
1234 : minint(ctx->rw_threads, ctx->num_threads - atomic_load(&ctx->cc_target_workers));
1235 if (num_rw < 1) num_rw = 1;
1237 fprintf(stderr,
"# [thread %d] RW (ksub=%d) found new upper bound cw of weight %d (using %d RW threads)\n",
1238 tid, ksub, best, num_rw);
1240 fprintf(stderr,
"# [thread %d] RW found new upper bound cw of weight %d (using %d RW threads)\n",
1246 snprintf(prefix,
sizeof(prefix),
"# [thread %d] RW codeword", tid);
1249 const int cur_dmin = atomic_load(&ctx->dmin);
1250 if (!collecting && cur_dmin > 0 && best <= cur_dmin) {
1254 if (!collecting &&
p->wmin > 0 && best <= p->wmin) {
1261 if ((
p->debug &
DBG_PROGRESS) && !atomic_load(&ctx->stop_flag) && !atomic_load(&ctx->rw_stop_flag)) {
1265 "# RW convergence reached: <n>=%.2f >= min_hits=%d average hits per codeword (%lld cws of weight "
1266 "%d..%d; w=%d: %lld cws, %lld hits), P_fail ~ exp(-<n>) = %.2g\n",
1269 if (
p->method == 1) {
1272 atomic_store(&ctx->rw_stop_flag,
true);
1275 pthread_mutex_unlock(&ctx->cw_mutex);
1279static inline void rw_row_xor(word * __restrict dst,
const word * __restrict src,
const wi_t width) {
1280 for (wi_t w = 0; w < width; w++) dst[w] ^= src[w];
1282static inline void rw_row_xor3(word * __restrict dst,
const word * __restrict a,
const word * __restrict b,
1284 for (wi_t w = 0; w < width; w++) dst[w] = a[w] ^ b[w];
1295static void rw_block_params(
const int rows,
const wi_t width,
int *
const k,
int *
const nt) {
1298 if (rows < 128)
return;
1299 *k = (rows < 512) ? 5 : 6;
1301 while (*k > 3 && ((
size_t)*nt << *k) * (
size_t)width *
sizeof(word) > ((
size_t)2 << 20)) {
1311#define RW_TUNE_STEPS 4
1336__attribute__((noinline))
1337static int rw_eliminate(
distfork_ctx_t *
const ctx, mzd_t *
const M,
const int nrows_eff,
const int nvar,
1338 const rci_t *
const order, rci_t *
const pivs, word *
const piv_mask,
1340 word *
const T = el->
table;
1341 const size_t tstride = el->
tstride;
1342 const int K = el->
k, nt = el->
nt;
1343 const wi_t width = M->width;
1344 const int P = K * nt;
1348 while (i < nvar && rank < nrows_eff) {
1349 const int r0 = rank;
1352 while (kk < P && i < nvar && rank < nrows_eff) {
1353 if ((i & 15) == 15 && (atomic_load_explicit(&ctx->stop_flag, memory_order_relaxed) ||
1354 atomic_load_explicit(&ctx->rw_stop_flag, memory_order_relaxed))) {
1357 const int col = order[i++];
1358 const int cw = col >> 6, cb = col & 63;
1360 for (
int l = 0; l < kk; l++) mask |= (uint64_t)((mzd_row(M, r0 + l)[cw] >> cb) & 1) << l;
1362 for (
int r = rank; r < nrows_eff; r++) {
1363 const word *
const row = mzd_row(M, r);
1364 word b = (row[cw] >> cb) & 1;
1365 for (uint64_t m2 = mask; m2; m2 &= m2 - 1) {
1366 const int l = __builtin_ctzll(m2);
1367 b ^= (row[bw[l]] >> bs[l]) & 1;
1374 if (found < 0)
continue;
1375 if (found != rank) mzd_row_swap(M, rank, found);
1376 word *
const prow = mzd_row(M, rank);
1377 for (
int l = 0; l < kk; l++) {
1378 if ((prow[bw[l]] >> bs[l]) & 1) rw_row_xor(prow, mzd_row(M, r0 + l), width);
1380 for (uint64_t m2 = mask; m2; m2 &= m2 - 1)
1381 rw_row_xor(mzd_row(M, r0 + __builtin_ctzll(m2)), prow, width);
1386 piv_mask[cw] |= (word)1 << cb;
1391 const int ntab = (kk + K - 1) / K;
1392 for (
int t = 0; t < ntab; t++) {
1393 word *
const Tt = T + ((size_t)t << K) * tstride;
1394 const int kt = (kk - t * K < K) ? kk - t * K : K;
1395 for (
unsigned g = 1; g < (1u << kt); g++) {
1396 const unsigned gc = g ^ (g >> 1), gp = (g - 1) ^ ((g - 1) >> 1);
1397 rw_row_xor3(Tt + gc * tstride, Tt + gp * tstride, mzd_row(M, r0 + t * K + __builtin_ctz(g)), width);
1401 for (
int r = 0; r < nrows_eff; r++) {
1406 word *
const row = mzd_row(M, r);
1408 for (
int t = 0; t < ntab; t++) {
1409 const int lo = t * K, hi = (lo + K < kk) ? lo + K : kk;
1411 for (
int l = lo; l < hi; l++) xt |= (
unsigned)((row[bw[l]] >> bs[l]) & 1) << (l - lo);
1414 for (
int t = 0; t < ntab; t++) {
1415 if (x[t]) rw_row_xor(row, T + (((
size_t)t << K) + x[t]) * tstride, width);
1439static void rw_radix_sort(uint64_t *
const a, uint64_t *
const tmp,
const int n,
const int bits) {
1440 uint64_t *src = a, *dst = tmp;
1441 for (
int sh = 0; sh < bits; sh += 8) {
1443 memset(cnt, 0,
sizeof(cnt));
1444 for (
int i = 0; i < n; i++) cnt[(src[i] >> sh) & 255]++;
1445 for (
int d = 0, sum = 0; d < 256; d++) {
1446 const int c = cnt[d];
1450 for (
int i = 0; i < n; i++) dst[cnt[(src[i] >> sh) & 255]++] = src[i];
1451 uint64_t *
const t = src;
1455 if (src != a) memcpy(a, src, (
size_t)n *
sizeof(uint64_t));
1472static uint64_t *rw_lhash_init(
const params_t *
const p) {
1474 const int nvar =
p->spaH->cols;
1475 if (
p->classical || !cm || cm->
cols < nvar)
return NULL;
1476 uint64_t *
const h = malloc((
size_t)nvar *
sizeof(uint64_t));
1477 if (!h)
ERROR(
"memory allocation");
1479 for (
int j = 0; j < nvar; j++) h[j] = cm->
mask[j];
1481 uint64_t *
const R = malloc((
size_t)cm->
kw * 64 *
sizeof(uint64_t));
1482 if (!R)
ERROR(
"memory allocation");
1483 uint64_t st = mix64((uint64_t)
p->seed ^ 0x2545f4914f6cdd1dULL);
1484 for (
int t = 0; t < cm->
kw * 64; t++) R[t] = splitmix64(&st);
1485 for (
int j = 0; j < nvar; j++) {
1487 for (
int w = 0; w < cm->
kw; w++) {
1488 for (word m = cm->
mask[(
size_t)j * cm->
kw + w]; m; m &= m - 1) s ^= R[w * 64 + __builtin_ctzll(m)];
1500 memset(ws, 0,
sizeof(*ws));
1501 if (ctx->
p->
pairs == 0)
return;
1503 ws->
srt = malloc((
size_t)2 * nvar *
sizeof(uint64_t));
1504 if (!ws->
ent || !ws->
srt)
ERROR(
"memory allocation");
1506 if (ctx->rw_lhash && cm->
kw == 1 && cm->
rows > 0) {
1508 ws->
width = mH->width;
1509 ws->
lrows = calloc((
size_t)ws->
nl * ws->
width,
sizeof(word));
1510 ws->
srows = malloc((
size_t)ws->
nl * ws->
width *
sizeof(word));
1512 for (
int j = 0; j < nvar; j++) {
1513 for (word m = cm->
mask[j]; m; m &= m - 1)
1514 ws->
lrows[(size_t)__builtin_ctzll(m) * ws->
width + (j >> 6)] |= (word)1 << (j & 63);
1524 memset(ws, 0,
sizeof(*ws));
1528static inline bool rw_is_hit(
distfork_ctx_t *
const ctx,
const int cnt) {
1529 const int dmax = atomic_load_explicit(&ctx->dmax, memory_order_relaxed);
1530 return (dmax <= 0) || (cnt <= dmax);
1538 const int dmax = atomic_load_explicit(&ctx->dmax, memory_order_relaxed);
1539 if (dmax <= 0)
return 0;
1540 long long int cap = (
long long int)dmax + 1;
1541 if ((
p->outC != NULL ||
p->maxC > 0) &&
p->dW > 0) cap +=
p->dW;
1542 const int lim = rw_cw_limit(ctx);
1543 return (lim < cap) ? lim : (int)cap;
1549#define RW_PAIRS_POLL 65536
1557static int rw_pairs_step(
distfork_ctx_t *
const ctx, mzd_t *
const mH, mzd_t *
const mHT,
1558 const rci_t *
const pivs,
const word *
const piv_mask,
const int rank,
int l,
1559 rw_pairs_ws_t *
const ws, rci_t *
const ee, uint64_t *
const rng_state,
const int tid,
1560 const int src,
long long *
const checked,
long long *
const hits) {
1563 const int classical =
p->classical;
1564 const uint64_t *
const lhash = ctx->rw_lhash;
1565 const int nvar =
p->spaH->cols;
1567 uint64_t *
const srt = ws->
srt;
1568 int lim = rw_pairs_limit(ctx);
1569 if (lim < 3 || l < 1 || l > rank)
return 0;
1573 while ((1L << wb) <= (
long)rank) wb++;
1574 while ((1L << ib) < (
long)(nvar - rank)) ib++;
1575 const int kb = (l + 8 < 63 - wb - ib) ? l + 8 : 63 - wb - ib;
1576 if (kb < 1)
return 0;
1577 const int sh_grp = ib + wb, sh_key = sh_grp + 1;
1578 const uint64_t wmask = ((uint64_t)1 << wb) - 1, imask = ((uint64_t)1 << ib) - 1;
1579 const int aw = (rank + 63) >> 6;
1580 const word last_mask = (rank & 63) ? (((word)1 << (rank & 63)) - 1) : ~(word)0;
1584 for (
int i = 0; i < l; i++) {
1587 r = rand_uniform_thread(rank, rng_state);
1588 for (j = 0; j < nk && kword[j] != (r >> 6); j++)
continue;
1589 }
while (j < nk && ((kmask[j] >> (r & 63)) & 1));
1594 kmask[j] |= (word)1 << (r & 63);
1598 const int nl = ws->
nl;
1599 const wi_t width = ws->
width;
1601 memcpy(ws->
srows, ws->
lrows, (
size_t)nl * width *
sizeof(word));
1602 for (
int i = 0; i < rank; i++) {
1603 word g = lhash[pivs[i]];
1605 const word *
const ri = mzd_row(mH, i);
1606 for (; g; g &= g - 1) rw_row_xor(ws->
srows + (
size_t)__builtin_ctzll(g) * width, ri, width);
1609 const int wsig = lim + 1;
1611 for (
int col = 0; col < nvar; col++) {
1612 if ((piv_mask[col >> 6] >> (col & 63)) & 1)
continue;
1613 const word *
const row = mzd_row(mHT, col);
1615 for (
int iw = 0; iw < aw - 1; iw++) wt += __builtin_popcountll(row[iw]);
1616 wt += __builtin_popcountll(row[aw - 1] & last_mask);
1618 for (
int j = 0; j < nk; j++) key = (key ^ (row[kword[j]] & kmask[j])) * 0x9e3779b97f4a7c15ULL;
1623 const word *
const sc = ws->
srows + (col >> 6);
1624 for (
int t = 0; t < nl; t++) sig |= ((sc[(
size_t)t * width] >> (col & 63)) & 1) << t;
1626 }
else if (lhash && wt <= wsig) {
1628 for (
int iw = 0; iw < aw; iw++) {
1629 for (word x = (iw < aw - 1) ? row[iw] : (row[iw] & last_mask); x; x &= x - 1)
1630 sig ^= lhash[pivs[(iw << 6) + __builtin_ctzll(x)]];
1634 const uint64_t grp = !known || (sig != 0);
1638 ent[K].
known = known;
1639 srt[K] = ((uint64_t)key << sh_key) | (grp << sh_grp) | ((uint64_t)wt << ib) | (uint64_t)K;
1642 rw_radix_sort(srt, srt + nvar, K, sh_key + kb);
1646 const bool other_pairs = (nl != 1);
1647 const bool irred = (
p->irred != 0);
1648 long long int nchk = 0, nhits = 0, nvis = 0, next_poll =
RW_PAIRS_POLL;
1649 long long int nred = 0;
1650 for (
int b0 = 0; b0 < K;) {
1651 const uint64_t bkey = srt[b0] >> sh_key;
1653 while (b1 < K && (srt[b1] >> sh_key) == bkey) b1++;
1655 if (atomic_load_explicit(&ctx->stop_flag, memory_order_relaxed) ||
1656 atomic_load_explicit(&ctx->rw_stop_flag, memory_order_relaxed))
break;
1657 lim = rw_pairs_limit(ctx);
1661 while (g0 < b1 && !((srt[g0] >> sh_grp) & 1)) g0++;
1663 const int x_end = other_pairs ? b1 - 1 : g0;
1664 for (
int x = b0; x < x_end; x++) {
1665 if (nvis >= next_poll) {
1667 if (atomic_load_explicit(&ctx->stop_flag, memory_order_relaxed) ||
1668 atomic_load_explicit(&ctx->rw_stop_flag, memory_order_relaxed))
goto done;
1669 lim = rw_pairs_limit(ctx);
1670 if (lim < 3)
goto done;
1673 const int wx = (int)((srt[x] >> ib) & wmask);
1674 const rw_pair_t *
const ex = ent + (srt[x] & imask);
1677 while (lo < b1 && (
int)((srt[lo] >> ib) & wmask) < wx - lx) lo++;
1681 for (; y < b1; y++) {
1682 const int wy = (int)((srt[y] >> ib) & wmask);
1683 if (wy - wx > lx)
break;
1684 const rw_pair_t *
const ey = ent + (srt[y] & imask);
1686 const word *
const ra = ex->
row;
1687 const word *
const rb = ey->
row;
1690 for (
int iw = 0; iw < aw - 1 && c <= lx; iw++) c += __builtin_popcountll(ra[iw] ^ rb[iw]);
1691 if (c <= lx) c += __builtin_popcountll((ra[aw - 1] ^ rb[aw - 1]) & last_mask);
1692 if (c > lx)
continue;
1696 if (irred && c == wx + wy) {
1701 ee[cnt++] = ex->
col;
1702 ee[cnt++] = ey->
col;
1703 for (
int iw = 0; iw < aw; iw++) {
1704 for (word z = (ra[iw] ^ rb[iw]) & ((iw < aw - 1) ? ~(word)0 : last_mask); z; z &= z - 1)
1705 ee[cnt++] = pivs[(iw << 6) + __builtin_ctzll(z)];
1707 if (classical || colmask_syndrome_non_zero(maskL, cnt, ee)) {
1708 rci_quick_sort(ee, cnt);
1709 if (rw_is_hit(ctx, cnt)) nhits++;
1710 rw_record_codeword(ctx, ee, cnt, tid, 0, src);
1711 lim = rw_pairs_limit(ctx);
1712 if (lim < 3)
goto done;
1724 if (nred > 0) atomic_fetch_add_explicit(&ctx->cw_red_rw, nred, memory_order_relaxed);
1731static int run_rw_steps(
distfork_ctx_t *ctx,
long step0,
int n_steps,
1732 mzd_t *mH, mzd_t *mHT, rci_t *ee,
1735 int *visited_cols,
int *visited_checks,
int *col_queue,
1736 int *visit_marker, uint64_t *rng_state,
int tid) {
1739 const int nvar =
p->spaH->cols;
1740 const int classical =
p->classical;
1741 const int kwin =
p->kwin;
1742 const int win_mode =
p->win_mode;
1743 int eff_nrows = *eff_nrows_ptr;
1746 long long int n_pairs = 0, hits1 = 0, hits2 = 0;
1747 double t_last = get_time_sec(), acc_t = 0.0;
1750 for (
int step = 0; step < n_steps; step++) {
1751 if (atomic_load_explicit(&ctx->stop_flag, memory_order_relaxed) ||
1752 atomic_load_explicit(&ctx->rw_stop_flag, memory_order_relaxed)) {
1756 int eff_kwin = kwin;
1757 int eff_win_mode = win_mode;
1758 if (eff_kwin == 0 && nvar >= 500 && ((step0 + step) & 1) == 1) {
1759 eff_kwin = hybrid_window(
p, nvar);
1762 const bool uniform = !(eff_kwin > 0 && eff_kwin < nvar);
1764 if (eff_kwin > 0 && eff_kwin < nvar) {
1765 int seed_col = rand_uniform_thread(nvar, rng_state);
1767 p->spaH, ctx->mHT_cc,
1768 visited_cols, visited_checks, col_queue,
1769 visit_marker, rng_state);
1771 pivs = mzp_rand_thread(pivs, nvar, rng_state);
1772 mzp_set_ui(perm, 1);
1776 memset(piv_mask, 0, mH->width *
sizeof(word));
1780 const double t_elim = (timed >= 0) ? get_time_sec() : 0.0;
1782 if (el->
choice == 0 || timed == 0) {
1783 for (
int i = 0; i < nvar && rank < eff_nrows; i++) {
1785 if ((i & 15) == 15 && (atomic_load_explicit(&ctx->stop_flag, memory_order_relaxed) ||
1786 atomic_load_explicit(&ctx->rw_stop_flag, memory_order_relaxed))) {
1790 int col = perm->values[i];
1791 if (gauss_one_rows(mH, col, rank, eff_nrows)) {
1792 pivs->values[rank++] = col;
1793 piv_mask[col >> 6] |= (word)1 << (col & 63);
1797 rank = rw_eliminate(ctx, mH, eff_nrows, nvar, perm->values, pivs->values, piv_mask, el);
1800 if (rank < 0)
break;
1802 if (timed >= 0) el->
t[timed] += get_time_sec() - t_elim;
1804 el->
choice = (el->
t[1] < el->
t[0]) ? 1 : 0;
1806 fprintf(stderr,
"# [thread 0] RW elimination: %s (blocked: %.2fx the time of column by column)\n",
1807 el->
choice ?
"blocked" :
"column by column", el->
t[1] / (el->
t[0] > 0.0 ? el->
t[0] : 1e-9));
1812 *eff_nrows_ptr = eff_nrows;
1813 if (atomic_load_explicit(&ctx->rw_rank, memory_order_relaxed) != rank) {
1814 atomic_store_explicit(&ctx->rw_rank, rank, memory_order_relaxed);
1817 mzd_transpose(mHT, mH);
1819 const int active_width = (rank + 63) >> 6;
1820 const word last_mask = (rank & 63) ? (((word)1 << (rank & 63)) - 1) : ~(word)0;
1821 for (
int col = 0; col < nvar; col++) {
1822 if ((piv_mask[col >> 6] >> (col & 63)) & 1)
continue;
1823 const int limit = rw_cw_limit(ctx);
1824 word *rawrow = mzd_row(mHT, col);
1828 for (
int iw = 0; iw < active_width - 1 && wt < limit; iw++) wt += __builtin_popcountll(rawrow[iw]);
1829 if (wt < limit && active_width > 0) wt += __builtin_popcountll(rawrow[active_width - 1] & last_mask);
1830 if (wt >= limit)
continue;
1836 j = nextelement(rawrow, active_width, j);
1837 if (j == -1 || j >= rank)
break;
1838 ee[cnt++] = pivs->values[j++];
1842 if (classical || colmask_syndrome_non_zero(maskL, cnt, ee)) {
1843 rci_quick_sort(ee, cnt);
1844 if (rw_is_hit(ctx, cnt)) hits1++;
1849 int l = rw_pairs_bits(
p, nvar, rank, atomic_load_explicit(&ctx->dmax, memory_order_relaxed));
1851 l = rw_pairs_step(ctx, mH, mHT, pivs->values, piv_mask, rank, l, pws, ee, rng_state, tid,
1853 if (l > 0 && atomic_load_explicit(&ctx->rw_pairs_l, memory_order_relaxed) != l)
1854 atomic_store_explicit(&ctx->rw_pairs_l, l, memory_order_relaxed);
1856 atomic_fetch_add(&ctx->rw_steps_completed, 1);
1858 if (uniform) n_unif++;
1859 rw_time_step(ctx, &t_last, &acc_t, &acc_n, n_done == 1);
1861 rw_time_publish(ctx, &acc_t, &acc_n);
1862 atomic_fetch_add_explicit(&ctx->rw_unif_steps, n_unif, memory_order_relaxed);
1863 if (n_pairs > 0) atomic_fetch_add_explicit(&ctx->rw_pairs_checked, n_pairs, memory_order_relaxed);
1864 if (hits1 > 0) atomic_fetch_add_explicit(&ctx->rw_hits1, hits1, memory_order_relaxed);
1865 if (hits2 > 0) atomic_fetch_add_explicit(&ctx->rw_hits2, hits2, memory_order_relaxed);
1872static void rw_shrink_matrices(mzd_t **mH, mzd_t **mHT,
const int rank) {
1873 pthread_mutex_lock(&m4ri_mem_mutex);
1874 mzd_t *
const top = mzd_init_window(*mH, 0, 0, rank, (*mH)->ncols);
1875 mzd_t *
const small = mzd_copy(NULL, top);
1876 mzd_free_window(top);
1880 *mHT = mzd_init(small->ncols, rank);
1881 pthread_mutex_unlock(&m4ri_mem_mutex);
1885static inline bool ksub_row_in_window(
const mzd_t *
const N,
const int r,
const int nvar,
1886 const int *
const visited_cols,
const int marker) {
1887 const word *
const raw_n = mzd_row_cons(N, r);
1888 for (
int j = nextelement(raw_n, N->width, -1); j >= 0 && j < nvar; j = nextelement(raw_n, N->width, j + 1)) {
1889 if (visited_cols[j] == marker)
return true;
1897static int run_rw_steps_ksub(
distfork_ctx_t *ctx,
long step0,
int n_steps,
1898 mzd_t *M_sub, rci_t *ee, mzp_t *perm, mzp_t *pivs,
int *nidx,
1899 int *visited_cols,
int *visited_checks,
int *col_queue,
1900 int *visit_marker, uint64_t *rng_state,
int tid) {
1903 const int nvar =
p->spaH->cols;
1904 const int classical =
p->classical;
1905 const int nu = ctx->nu;
1906 const int ksub = M_sub->nrows;
1907 const int kwin =
p->kwin;
1908 const int win_mode =
p->win_mode;
1910 if (nu <= 0 || ksub <= 0 || !ctx->N_global) {
1911 atomic_fetch_add(&ctx->rw_steps_completed, n_steps);
1916 double t_last = get_time_sec(), acc_t = 0.0;
1918 pthread_rwlock_rdlock(&ctx->basis_rwlock);
1919 const mzd_t *
const N = ctx->N_global;
1921 for (
int step = 0; step < n_steps; step++) {
1922 if (atomic_load_explicit(&ctx->stop_flag, memory_order_relaxed) ||
1923 atomic_load_explicit(&ctx->rw_stop_flag, memory_order_relaxed)) {
1928 int eff_kwin = kwin;
1929 int eff_win_mode = win_mode;
1930 if (eff_kwin == 0 && nvar >= 500 && ((step0 + step) & 1) == 1) {
1931 eff_kwin = hybrid_window(
p, nvar);
1935 if (eff_kwin > 0 && eff_kwin < nvar) {
1936 int seed_col = rand_uniform_thread(nvar, rng_state);
1938 p->spaH, ctx->mHT_cc,
1939 visited_cols, visited_checks, col_queue,
1940 visit_marker, rng_state);
1942 pivs = mzp_rand_thread(pivs, nvar, rng_state);
1943 mzp_set_ui(perm, 1);
1949 const int marker = *visit_marker;
1950 const bool in_window = (eff_kwin > 0 && eff_kwin < nvar && visited_cols);
1951 for (
int i = 0; i < ksub; i++) {
1952 int j = i + rand_uniform_thread(nu - i, rng_state);
1953 for (
int attempt = 0; in_window && attempt < 4 && !ksub_row_in_window(N, nidx[j], nvar, visited_cols, marker);
1955 j = i + rand_uniform_thread(nu - i, rng_state);
1958 mzd_copy_row(M_sub, i, N, nidx[i]);
1963 for (
int i = 0; i < nvar && rank < ksub; i++) {
1964 int col = perm->values[i];
1965 if (gauss_one(M_sub, col, rank)) {
1971 for (
int ir = 0; ir < rank; ir++) {
1973 const int limit = rw_cw_limit(ctx);
1975 word *rawrow = mzd_row(M_sub, ir);
1977 while (cnt < limit) {
1978 j = nextelement(rawrow, M_sub->width, j);
1979 if (j == -1 || j >= nvar)
break;
1983 if (cnt > 0 && cnt < limit && (classical || colmask_syndrome_non_zero(maskL, cnt, ee))) {
1985 if (!cw_irreducible(ctx, ctx->wargs[tid].irr_ws, cnt, ee)) {
1986 atomic_fetch_add_explicit(&ctx->cw_red_rw, 1, memory_order_relaxed);
1989 rw_record_codeword(ctx, ee, cnt, tid, ksub,
1993 atomic_fetch_add(&ctx->rw_steps_completed, 1);
1995 rw_time_step(ctx, &t_last, &acc_t, &acc_n, n_done == 1);
1997 rw_time_publish(ctx, &acc_t, &acc_n);
1999 pthread_rwlock_unlock(&ctx->basis_rwlock);
2002 if (
p->refresh > 0 && !atomic_load_explicit(&ctx->stop_flag, memory_order_relaxed) &&
2003 !atomic_load_explicit(&ctx->rw_stop_flag, memory_order_relaxed)) {
2004 long done = atomic_load_explicit(&ctx->rw_steps_completed, memory_order_relaxed);
2005 long next_ref = atomic_load_explicit(&ctx->next_refresh_step, memory_order_relaxed);
2006 if (done >= next_ref && next_ref > 0) {
2007 if (atomic_compare_exchange_strong(&ctx->next_refresh_step, &next_ref,
2008 done +
p->refresh)) {
2010 pthread_mutex_lock(&ctx->cw_mutex);
2011 if (
p->codewords &&
p->min_w < nvar) {
2016 for (
int k = 0; k < cw_wt; k++) ee[k] = cw->
arr[k];
2021 pthread_mutex_unlock(&ctx->cw_mutex);
2023 pthread_rwlock_wrlock(&ctx->basis_rwlock);
2024 pthread_mutex_lock(&m4ri_mem_mutex);
2026 pthread_mutex_unlock(&m4ri_mem_mutex);
2027 pthread_rwlock_unlock(&ctx->basis_rwlock);
2035static void *worker_thread_func(
void *arg) {
2038 int tid = warg->
tid;
2041 const bool enable_rw = ((ctx->
p->
method & 1) != 0) && (tid < ctx->rw_threads);
2042 const bool use_ksub = enable_rw && (ctx->
p->
ksub > 0);
2043 const int ksub_eff = (use_ksub && ctx->nu > 0) ? minint(ctx->
p->
ksub, ctx->nu) : 0;
2048 mzd_t *mHT_rw = NULL;
2049 mzd_t *M_sub = NULL;
2054 word *piv_mask = NULL;
2055 rw_elim_t rw_el = {NULL, 0, 0, 0, 0, {0.0, 0.0}, 0};
2057 memset(&rw_pws, 0,
sizeof(rw_pws));
2059 int *visited_cols = NULL;
2060 int *visited_checks = NULL;
2061 int *col_queue = NULL;
2062 int visit_marker = 0;
2065 uint64_t rng_state = mix64((uint64_t)ctx->
p->
seed ^ mix64((uint64_t)tid + 0x517cc1b727220a95ULL));
2068 ee = malloc((nvar + 2) *
sizeof(rci_t));
2069 perm = safe_mzp_init(nvar);
2070 pivs = safe_mzp_init(nvar);
2071 if (ctx->
p->
kwin > 0 || nvar >= 500) {
2072 visited_cols = calloc(nvar,
sizeof(
int));
2073 visited_checks = calloc(ctx->
p->
spaH->
rows,
sizeof(
int));
2074 col_queue = calloc(nvar,
sizeof(
int));
2078 M_sub = safe_mzd_init(ksub_eff, nvar);
2079 nidx = malloc((
size_t)ctx->nu *
sizeof(
int));
2080 if (!nidx)
ERROR(
"memory allocation");
2081 for (
int i = 0; i < ctx->nu; i++) nidx[i] = i;
2084 mH = safe_mzd_from_csr(NULL, ctx->
p->
spaH);
2085 mHT_rw = safe_mzd_init(nvar, ctx->
p->
spaH->
rows);
2086 piv_mask = calloc(mH->width,
sizeof(word));
2088 rw_el.
tstride = ((size_t)mH->width + 7) & ~(
size_t)7;
2089 rw_block_params(mH->nrows, mH->width, &rw_el.
k, &rw_el.
nt);
2090 rw_el.
choice = (rw_el.
nt == 0) ? 0 : ((mH->nrows >= 512) ? 1 : -1);
2091 const size_t tbytes = (rw_el.
nt > 0 ? ((size_t)rw_el.
nt << rw_el.
k) : 1) * rw_el.
tstride *
sizeof(word);
2092 rw_el.
table = aligned_alloc(64, tbytes);
2093 if (!piv_mask || !rw_el.
table)
ERROR(
"memory allocation");
2094 memset(rw_el.
table, 0, tbytes);
2095 rw_pairs_ws_init(&rw_pws, ctx, mH, nvar);
2100 const int wmax_alloc =
MAX_W - 1;
2101 one_vec_t *err = NULL, *urr = NULL, **syn = NULL;
2103 memset(&walk, 0,
sizeof(walk));
2105 err = calloc(1,
sizeof(
one_vec_t) +
sizeof(
int) * (wmax_alloc + 2));
2106 urr = calloc(1,
sizeof(
one_vec_t) +
sizeof(
int) * (wmax_alloc + 2));
2107 syn = calloc(wmax_alloc + 3,
sizeof(
one_vec_t *));
2108 if (!err || !urr || !syn)
ERROR(
"memory allocation");
2109 for (
int i = 0; i <= wmax_alloc + 2; i++) {
2111 if (!syn[i])
ERROR(
"memory allocation");
2117 walk.
mHT = ctx->mHT_cc;
2121 walk.
S = calloc((
size_t)(walk.
m / 64 + 2),
sizeof(uint64_t));
2122 walk.
E = calloc((
size_t)(nvar / 64 + 2),
sizeof(uint64_t));
2123 walk.
cols = calloc(wmax_alloc + 3,
sizeof(
int));
2124 walk.
cw = calloc(1,
sizeof(
one_vec_t) +
sizeof(
int) * (wmax_alloc + 3));
2125 if (!walk.
S || !walk.
E || !walk.
cols || !walk.
cw)
ERROR(
"memory allocation");
2127 if (walk.
prune > 0) {
2128 const int B = (ctx->cc_B > 0) ? ctx->cc_B : 1;
2131 walk.
X = calloc((
size_t)(nvar / 64 + 2),
sizeof(uint64_t));
2132 walk.
acnt = calloc((
size_t)walk.
m + 1,
sizeof(
int));
2133 walk.
kfirst = calloc((
size_t)walk.
m + 1,
sizeof(
int));
2134 walk.
fstk = malloc(((
size_t)nvar + 1) *
sizeof(
int));
2135 walk.
brl = malloc((
size_t)(wmax_alloc + 3) * (
size_t)B *
sizeof(
int));
2136 walk.
ulist = malloc(((
size_t)walk.
m + 1) *
sizeof(
int));
2137 walk.
uacnt = malloc(((
size_t)walk.
m + 1) *
sizeof(
int));
2138 walk.
ord = malloc(((
size_t)walk.
m + 1) *
sizeof(
int));
2139 walk.
bcnt = malloc(((
size_t)B + 2) *
sizeof(
int));
2140 walk.
mark = calloc((
size_t)nvar + 1,
sizeof(uint32_t));
2143 ERROR(
"memory allocation");
2144 if (walk.
prune >= 2) {
2145 walk.
cv = malloc(((
size_t)nvar + 1) *
sizeof(
int));
2146 walk.
touched = malloc(((
size_t)nvar + 1) *
sizeof(
int));
2157 while (!atomic_load_explicit(&ctx->stop_flag, memory_order_relaxed)) {
2163 bool did_work =
false;
2166 if (ctx->
p->
method >= 2 && atomic_load_explicit(&ctx->cc_round_active, memory_order_acquire)) {
2167 int active = atomic_load_explicit(&ctx->cc_active_workers, memory_order_relaxed);
2168 int target = atomic_load_explicit(&ctx->cc_target_workers, memory_order_relaxed);
2171 if (!enable_rw || atomic_load_explicit(&ctx->rw_stop_flag, memory_order_relaxed) ||
2172 atomic_load_explicit(&ctx->rw_steps_started, memory_order_relaxed) >= ctx->total_rw_steps) {
2175 const long long next = atomic_load_explicit(&ctx->cc_next, memory_order_relaxed);
2176 if (active < target && cc_unpack_idx(next) <= cc_unit_last(ctx, cc_unpack_w(next), cc_unpack_lev(next))) {
2177 if (atomic_compare_exchange_weak(&ctx->cc_active_workers, &active, active + 1)) {
2178 const double t_join = get_time_sec();
2179 while (!atomic_load_explicit(&ctx->stop_flag, memory_order_relaxed)) {
2185 const long long claim = atomic_fetch_add_explicit(&ctx->cc_next, 1, memory_order_relaxed);
2186 const int w = cc_unpack_w(claim);
2187 const int lev = cc_unpack_lev(claim);
2188 const int unit = cc_unpack_idx(claim);
2189 if (unit > cc_unit_last(ctx, w, lev))
break;
2191 const int idx = unit / ctx->cc_R[lev];
2193 for (
int l = 0, k = unit - idx * ctx->cc_R[lev]; l < lev; l++, k /= ctx->cc_B) fix[l] = k % ctx->cc_B;
2198 const int swei = ctx->mHT_cc->
p[col + 1] - ctx->mHT_cc->
p[col];
2200 if (swei > 0 && swei <= (w - 1) * ctx->max_col_W) {
2201 if (walk.
prune > 0) cc_bnb_unit(&walk, col, idx, w, fix, lev);
2202 else cc_walk_unit(&walk, col, w, fix, lev);
2206 if (swei > 0)
continue;
2209 err->
vec[0] = urr->vec[0] = col;
2210 err->
wei = urr->wei = 1;
2212 int swei = walk.
S ? 0 : one_csr_row_combine(syn[1], syn[0], ctx->mHT_cc, col);
2214 if (ctx->
p->
smax && swei > 0 && swei <= ctx->
p->smax) {
2215 if (swei < warg->min_swei[1]) {
2221 if (swei > 0 && swei <= (w - 1) * ctx->max_col_W + ctx->
p->
smax) {
2223 if (walk.
prune > 0) cc_bnb_unit(&walk, col, idx, w, fix, lev);
2224 else cc_walk_unit(&walk, col, w, fix, lev);
2226 start_CC_recurs_mt(err, urr, syn, w, ctx->max_col_W,
2227 ctx->
p->
spaH, ctx->mHT_cc, warg, fix, lev);
2232 int nz = (!ctx->
p->
maskL) || colmask_syndrome_non_zero(ctx->
p->
maskL, 1, err->
vec);
2235 pthread_mutex_lock(&ctx->cw_mutex);
2237 ctx_update_feed_limit(ctx);
2238 if ((
p->debug &
DBG_CODEWORDS) && atomic_load(&ctx->dmax) != 1) {
2240 snprintf(prefix,
sizeof(prefix),
"# [thread %d] CC codeword", tid);
2243 atomic_store(&ctx->cc_found_weight, 1);
2244 atomic_store(&ctx->cc_exact,
true);
2245 atomic_store(&ctx->dmin, 1);
2246 atomic_store(&ctx->dmax, 1);
2250 }
else if (!
p->outC &&
p->maxC == 0) {
2253 pthread_mutex_unlock(&ctx->cw_mutex);
2257 err->
wei = urr->wei = 0;
2260 atomic_fetch_add_explicit(&ctx->cc_busy_ns, (
long long)((get_time_sec() - t_join) * 1e9),
2261 memory_order_relaxed);
2262 atomic_fetch_add_explicit(&ctx->cc_nodes, walk.
nodes, memory_order_relaxed);
2264 atomic_fetch_sub_explicit(&ctx->cc_active_workers, 1, memory_order_release);
2272 if (enable_rw && !atomic_load(&ctx->stop_flag) && !atomic_load(&ctx->rw_stop_flag)) {
2273 long cur_s = atomic_load(&ctx->rw_steps_started);
2274 if (cur_s < ctx->total_rw_steps) {
2279 int nvar = ctx->
p->
nvar;
2281 if (ctx->total_rw_steps >= 50000) batch_size = 500;
2282 else if (ctx->total_rw_steps >= 1000) batch_size = 250;
2283 else batch_size = 50;
2284 }
else if (nvar < 5000) {
2285 if (ctx->total_rw_steps >= 10000) batch_size = 100;
2286 else batch_size = 50;
2289 if (ctx->total_rw_steps >= 10000) batch_size = 50;
2290 else batch_size = 25;
2296 const long guided = (ctx->total_rw_steps - cur_s + 4L * ctx->
rw_threads - 1) / (4L * ctx->
rw_threads);
2297 if (batch_size > guided) batch_size = (guided > 0) ? guided : 1;
2299 long target_s = cur_s + batch_size;
2300 if (target_s > ctx->total_rw_steps) target_s = ctx->total_rw_steps;
2301 if (atomic_compare_exchange_weak(&ctx->rw_steps_started, &cur_s, target_s)) {
2302 int n_steps = (int)(target_s - cur_s);
2305 run_rw_steps_ksub(ctx, cur_s, n_steps, M_sub, ee, perm, pivs, nidx,
2306 visited_cols, visited_checks, col_queue,
2307 &visit_marker, &rng_state, tid);
2309 atomic_fetch_add(&ctx->rw_steps_completed, n_steps);
2312 run_rw_steps(ctx, cur_s, n_steps, mH, mHT_rw, ee, perm, pivs,
2313 piv_mask, &rw_el, &rw_pws, &eff_nrows, visited_cols, visited_checks,
2314 col_queue, &visit_marker, &rng_state, tid);
2315 if (eff_nrows > 0 && eff_nrows < mH->nrows) rw_shrink_matrices(&mH, &mHT_rw, eff_nrows);
2331 rw_pairs_ws_free(&rw_pws);
2332 safe_mzp_free(perm);
2333 safe_mzp_free(pivs);
2336 free(visited_checks);
2339 safe_mzd_free(M_sub);
2340 safe_mzd_free(mHT_rw);
2345 for (
int i = 0; i <= wmax_alloc + 2; i++) free(syn[i]);
2374 const long long beg = (
long long)ctx->round_beg * ctx->round_R;
2375 const long long end = ((
long long)ctx->round_end + 1) * ctx->round_R;
2376 if (end <= beg)
return 100.0;
2377 long long idx = cc_unpack_idx(atomic_load(&ctx->cc_next));
2378 if (idx > end) idx = end;
2379 if (idx < beg) idx = beg;
2380 return 100.0 * (double)(idx - beg) / (double)(end - beg);
2385 if (atomic_load(&ctx->rw_stop_flag))
return "converged";
2386 if (atomic_load(&ctx->rw_steps_completed) >= ctx->total_rw_steps)
return "all steps done";
2394 const double t = get_time_sec() - ctx->
start_time;
2395 if (t < ctx->status_next)
return;
2396 ctx->status_next += ctx->status_dt;
2397 if (ctx->status_next <= t) ctx->status_next = t + ctx->status_dt;
2398 ctx->status_dt = (2.0 * ctx->status_dt < 60.0) ? 2.0 * ctx->status_dt : 60.0;
2400 char cc_str[160] =
"", rw_str[224] =
"";
2401 if (
p->method >= 2) {
2402 if (ctx->round_open) {
2403 int len = snprintf(cc_str,
sizeof(cc_str),
"; CC round w=%d: %.1f%% of start columns claimed, %.3gs",
2404 ctx->round_w, cc_round_claimed(ctx), get_time_sec() - ctx->round_t0);
2405 if (ctx->round_est > 0.0 && ctx->round_threads > 0 && len > 0 && len < (
int)
sizeof(cc_str))
2406 snprintf(cc_str + len,
sizeof(cc_str) - len,
" (predicted %.3gs on %d threads)",
2407 ctx->round_est / ctx->round_threads, ctx->round_threads);
2409 snprintf(cc_str,
sizeof(cc_str),
"; CC %s", ctx->cc_idle ? ctx->cc_idle :
"between rounds");
2413 const long steps = atomic_load(&ctx->rw_steps_completed);
2414 const double rate = (t > ctx->status_t) ? (
double)(steps - ctx->status_steps) / (t - ctx->status_t) : 0.0;
2415 ctx->status_steps = steps;
2417 const bool rw_on = !atomic_load(&ctx->rw_stop_flag) && steps < ctx->total_rw_steps;
2418 int len = snprintf(rw_str,
sizeof(rw_str),
"; RW: %ld of %ld steps (%.3g/s)%s%s", steps, ctx->total_rw_steps,
2419 rate, rw_on ?
"" :
", ", rw_on ?
"" : rw_end_state(ctx));
2420 if (
p->min_hits > 0 && len > 0 && len < (
int)
sizeof(rw_str)) {
2422 pthread_mutex_lock(&ctx->cw_mutex);
2424 pthread_mutex_unlock(&ctx->cw_mutex);
2426 snprintf(rw_str + len,
sizeof(rw_str) - len,
", <n>=%.2f over %lld cws of weight %d..%d", st.
avg,
2430 fprintf(stderr,
"# status %.1fs: bounds [%d, %d]%s%s\n", t, atomic_load(&ctx->dmin), atomic_load(&ctx->dmax),
2437 for (
int i = ctx->spawned; i < n && !ctx->spawn_failed; i++) {
2438 const int err = pthread_create(&ctx->threads[i], NULL, worker_thread_func, &ctx->wargs[i]);
2440 if (i == 0)
ERROR(
"cannot create a worker thread (error %d)", err);
2441 fprintf(stderr,
"# Warning: cannot create worker thread %d (error %d), running with %d threads\n", i, err, i);
2442 ctx->spawn_failed =
true;
2445 ctx->spawned = i + 1;
2453#define CC_SPAWN_INITIAL 4
2454#define CC_SPAWN_WORK 2e-3
2455#define CC_SPAWN_TIME 5e-3
2458static inline bool m2_can_spawn(
const distfork_ctx_t *
const ctx) {
2459 return ctx->spawned < ctx->
num_threads && !ctx->spawn_failed;
2463static void m2_spawn_rest(
distfork_ctx_t *
const ctx,
const int w,
const char *
const why) {
2464 const int n0 = ctx->spawned;
2466 ctx->round_threads = ctx->spawned;
2468 fprintf(stderr,
"# CC round w=%d: started %d more threads, %d in total (%s)\n", w, ctx->spawned - n0, ctx->spawned,
2475 while (!atomic_load(&ctx->stop_flag)) {
2480 if (atomic_load(&ctx->rw_steps_completed) >= ctx->total_rw_steps) {
2493static double cc_work_estimate(
const distfork_ctx_t *
const ctx,
const int w,
double *
const growth) {
2494 if (ctx->even_weight_only && !(w & 1)) {
2496 if (w <= 2)
return 1e-4;
2497 if (w >
MAX_W)
return 0.0;
2498 const double prev = ctx->cc_time_per_weight[w - 2];
2499 if (!(prev > 0.0))
return 0.0;
2500 if (w >= 6 && ctx->cc_time_per_weight[w - 4] > 1e-4) {
2501 const double g = prev / ctx->cc_time_per_weight[w - 4];
2502 *growth = (g < 4.0) ? 4.0 : ((g > 100.0) ? 100.0 : g);
2504 return ((prev > 1e-4) ? prev : 1e-4) * (*growth);
2507 if (w <= 1)
return 1e-4;
2508 if (w >
MAX_W)
return 0.0;
2509 const double prev = ctx->cc_time_per_weight[w - 1];
2510 if (!(prev > 0.0))
return 0.0;
2511 if (w >= 3 && ctx->cc_time_per_weight[w - 2] > 1e-4) {
2512 const double g = prev / ctx->cc_time_per_weight[w - 2];
2513 *growth = (g < 2.0) ? 2.0 : ((g > 10.0) ? 10.0 : g);
2515 return ((prev > 1e-4) ? prev : 1e-4) * (*growth);
2520 pthread_mutex_lock(&ctx->cw_mutex);
2522 pthread_mutex_unlock(&ctx->cw_mutex);
2528 const int wmax = ctx->
p->
wmax;
2529 const int w_start = ctx->
p->
noscan ? wmax : (ctx->
p->
dmin > 1 ? ctx->
p->
dmin : 1);
2531 const bool collecting = (ctx->
p->
outC != NULL) || (ctx->
p->
maxC > 0);
2533 const int extra_w = (ctx->
p->
outC && ctx->
p->
dW > 0) ? ctx->
p->
dW : 0;
2536 for (
int w = w_start; ; w++) {
2537 if (atomic_load(&ctx->stop_flag))
break;
2541 const int cur_dmax = atomic_load(&ctx->dmax);
2543 if (!collecting && atomic_load(&ctx->dmin) >= cur_dmax) {
2545 fprintf(stderr,
"# bounds coincide: dmin = dmax = %d (CC round w=%d not needed)\n", cur_dmax, w);
2550 w_limit = minint(w_limit, cur_dmax + extra_w);
2554 const int dstop = ctx->
p->
dstop;
2555 if (dstop > 0 && (cur_dmax == 0 || dstop < cur_dmax)) {
2556 if (!collecting && atomic_load(&ctx->dmin) >= dstop) {
2558 fprintf(stderr,
"# lower bound dmin=%d reached dstop=%d (CC round w=%d not needed)\n",
2559 atomic_load(&ctx->dmin), dstop, w);
2564 w_limit = minint(w_limit, collecting ? dstop + extra_w : dstop - 1);
2567 ctx->stop_w = w_limit;
2571 if (ctx->even_weight_only && (w & 1)) {
2572 if (w == atomic_load(&ctx->dmin)) atomic_store(&ctx->dmin, w + 1);
2576 double now = get_time_sec();
2578 if (ctx->
timeout > 0.0 && remaining_time <= 0.0) {
2586 const double t_cc_work = cc_work_estimate(ctx, w, &growth);
2588 const double t_cc_est = t_cc_work / ctx->
num_threads;
2589 if (t_cc_est > remaining_time) {
2590 fprintf(stderr,
"# CC for w=%d (est %.2fs) may not finish in the remaining time %.2fs, searching anyway "
2591 "(dmin=%d)\n", w, t_cc_est, remaining_time, atomic_load(&ctx->dmin));
2595 const int beg = cc_round_beg(ctx->
p);
2596 const int end = cc_round_end(ctx->
p, w);
2598 const bool certified = (w == atomic_load(&ctx->dmin));
2600 if (m2_can_spawn(ctx) && !(t_cc_work > 0.0 && t_cc_work <= CC_SPAWN_WORK * ctx->spawned)) {
2602 if (t_cc_work > 0.0) snprintf(why,
sizeof(why),
"predicted %.3g thread-s", t_cc_work);
2603 else snprintf(why,
sizeof(why),
"work not known");
2604 m2_spawn_rest(ctx, w, why);
2606 const int lev = cc_round_levels(ctx, w, beg, end, t_cc_work);
2607 const int last = cc_unit_last(ctx, w, lev);
2609 atomic_store(&ctx->cc_target_workers, ctx->
num_threads);
2610 const long long busy_ns0 = atomic_load(&ctx->cc_busy_ns);
2611 const long long nodes0 = atomic_load(&ctx->cc_nodes);
2612 atomic_store(&ctx->cc_next, cc_pack(w, lev, beg * ctx->cc_R[lev]));
2613 atomic_store(&ctx->cc_round_active, 1);
2615 double cc_start = get_time_sec();
2616 ctx->round_open = 1;
2618 ctx->round_beg = beg;
2619 ctx->round_end = end;
2620 ctx->round_R = ctx->cc_R[lev];
2621 ctx->round_threads = ctx->spawned;
2622 ctx->round_t0 = cc_start;
2623 ctx->round_est = t_cc_work;
2626 const char *note = (certified || atomic_load(&ctx->cc_exact)) ?
"" :
" (lower weights not scanned)";
2627 char units[64] =
"";
2628 if (lev > 0) snprintf(units,
sizeof(units),
", %d work units per start column", ctx->cc_R[lev]);
2630 fprintf(stderr,
"# searching w=%d with %d CC threads, start list (%d columns%s)%s%s\n",
2633 fprintf(stderr,
"# searching w=%d with %d CC threads, columns [%d, %d]%s%s\n",
2634 w, ctx->spawned, beg, end, units, note);
2638 bool round_completed =
false;
2639 while (!atomic_load(&ctx->stop_flag)) {
2640 const double t = get_time_sec();
2645 if (cc_unpack_idx(atomic_load(&ctx->cc_next)) > last && atomic_load(&ctx->cc_active_workers) == 0) {
2646 round_completed =
true;
2651 snprintf(why,
sizeof(why),
"running for %.3fs", t - cc_start);
2652 m2_spawn_rest(ctx, w, why);
2658 atomic_store(&ctx->cc_round_active, 0);
2659 if (round_completed) ctx->round_open = 0;
2661 double cc_dur = get_time_sec() - cc_start;
2663 ctx->cc_time_per_weight[w] = 1e-9 * (double)(atomic_load(&ctx->cc_busy_ns) - busy_ns0);
2665 char est_str[48] =
"unknown";
2666 if (t_cc_work > 0.0) snprintf(est_str,
sizeof(est_str),
"%.3g thread-s", t_cc_work);
2667 fprintf(stderr,
"# CC round w=%d work: %.3g thread-s measured, %s predicted (%.3fs on %d threads), %lld "
2668 "nodes\n", w, ctx->cc_time_per_weight[w], est_str, cc_dur, ctx->spawned,
2669 atomic_load(&ctx->cc_nodes) - nodes0);
2673 int cw_found = atomic_load(&ctx->cc_found_weight);
2674 if (cw_found > 0 && certified && cw_found == w && !atomic_load(&ctx->cc_exact)) {
2675 atomic_store(&ctx->cc_exact,
true);
2677 const bool exact = (cw_found > 0) && atomic_load(&ctx->cc_exact);
2679 atomic_store(&ctx->dmin, cw_found);
2680 atomic_store(&ctx->dmax, cw_found);
2682 w_limit = minint(w_limit, cw_found + extra_w);
2683 const int next_w = (ctx->even_weight_only && !(w & 1)) ? w + 2 : w + 1;
2684 const bool more_rounds = round_completed && (next_w <= w_limit);
2686 if (w == cw_found && more_rounds) {
2688 "# CC round w=%d finished in %.3fs (%d CC threads): found min-weight codewords "
2689 "(dmin=%d, continuing up to w=%d for dW=%d, total %lld cws)\n",
2690 w, cc_dur, ctx->spawned, cw_found, w_limit, ctx->
p->
dW, ctx_num_cws(ctx));
2691 }
else if (w == cw_found) {
2692 fprintf(stderr,
"# CC found min-weight codeword: d=%d (using %d CC threads, total %lld cws)\n",
2693 cw_found, ctx->spawned, ctx_num_cws(ctx));
2694 }
else if (round_completed) {
2696 "# CC round w=%d finished in %.3fs (%d CC threads): extra dW round completed "
2697 "(dmin=%d, total %lld cws)\n",
2698 w, cc_dur, ctx->spawned, cw_found, ctx_num_cws(ctx));
2706 }
else if (cw_found > 0) {
2710 "# CC round w=%d finished in %.3fs (%d CC threads): found codeword of weight %d -> dmax=%d "
2711 "(dmin=%d not certified: lower weights not scanned)\n",
2712 w, cc_dur, ctx->spawned, cw_found, atomic_load(&ctx->dmax), atomic_load(&ctx->dmin));
2717 if (!round_completed) {
2722 const int new_dmin = ctx->even_weight_only ? w + 2 : w + 1;
2723 atomic_store(&ctx->dmin, new_dmin);
2725 fprintf(stderr,
"# CC w=%d completed in %.3fs (%d CC threads): no codewords found -> dmin=%d\n",
2726 w, cc_dur, ctx->spawned, new_dmin);
2729 fprintf(stderr,
"# CC w=%d completed in %.3fs (%d CC threads): no codewords found "
2730 "(dmin=%d not raised: lower weights not scanned)\n",
2731 w, cc_dur, ctx->spawned, atomic_load(&ctx->dmin));
2739 return ctx->
rw_threads > 0 && !atomic_load(&ctx->rw_stop_flag) &&
2740 atomic_load(&ctx->rw_steps_completed) < ctx->total_rw_steps;
2746 const long n = atomic_load(&ctx->rw_timed_steps);
2747 if (n > 0)
return 1e-9 * (double)atomic_load(&ctx->rw_busy_ns) / (double)n;
2748 const double elapsed = get_time_sec() - ctx->
start_time;
2749 return (elapsed > 5e-5) ? elapsed : 5e-5;
2755static int m3_cc_target_w(
const distfork_ctx_t *
const ctx,
const int cur_dmin,
const int cur_dmax) {
2757 const int t = ctx_target(ctx, cur_dmax);
2760 if (
p->outC && (
p->dW > 0 || cur_dmin >= t)) {
2761 target = t + (
p->dW > 0 ?
p->dW : 0);
2766 target = (
p->wmax > 0) ?
p->wmax :
p->spaH->
cols;
2768 const int max_allowed_w = (
p->wmax > 0) ? minint(
p->wmax,
MAX_W - 2) : (
MAX_W - 2);
2769 int res = minint(target, max_allowed_w);
2770 if (ctx->even_weight_only && (res & 1)) res--;
2774#define M3_DEADLINE_FRACTION 0.75
2775#define M3_OVERRUN_FACTOR 1.5
2780static int m3_plan_cc_threads(
distfork_ctx_t *
const ctx,
const int w,
const double est,
const double growth,
2781 const double remaining_time) {
2783 if (nthr <= 1)
return 1;
2785 if (!m3_rw_running(ctx) || atomic_load(&ctx->rw_steps_started) >= ctx->total_rw_steps)
return nthr;
2786 const int cur_dmin = atomic_load(&ctx->dmin);
2787 const int cur_dmax = atomic_load(&ctx->dmax);
2791 const int t = ctx_target(ctx, cur_dmax);
2792 const int cert_w = ctx->even_weight_only ? (t - ((t & 1) ? 1 : 2)) : (t - 1);
2793 if (t > 0 && (w >= cert_w || cur_dmin >= t))
return nthr;
2801 }
else if (est < 0.005) {
2802 n_cc = (nthr >= 4) ? 2 : 1;
2807 int horizon = m3_cc_target_w(ctx, cur_dmin, cur_dmax);
2808 if (cur_dmax == 0 && ctx->
dexp > 0 && ctx->
dexp < horizon) horizon = ctx->
dexp;
2809 double t_cc = est, t_k = est;
2810 const int step = ctx->even_weight_only ? 2 : 1;
2811 for (
int k = w + step; k <= horizon && k <= w + 2 * step; k += step) {
2815 const long steps_rem = ctx->total_rw_steps - atomic_load(&ctx->rw_steps_completed);
2816 const long eff_steps = (cur_dmax > 0 && steps_rem > 2000) ? 2000 : steps_rem;
2817 double t_rw = (double)eff_steps * m3_rw_step_time(ctx);
2819 n_cc = (int)round((
double)nthr * t_cc / (t_cc + t_rw));
2820 if (n_cc < 1) n_cc = 1;
2821 if (n_cc > nthr - 1) n_cc = nthr - 1;
2824 if (ctx->
timeout > 0.0 && est > 0.0 && remaining_time > 0.0) {
2826 if (n_min > n_cc) n_cc = (n_min < nthr) ? (
int)n_min : nthr;
2835static void m3_wait_rw(
distfork_ctx_t *
const ctx,
const int dmax0,
const char *
const why) {
2837 while (!atomic_load(&ctx->stop_flag) && m3_rw_running(ctx) && atomic_load(&ctx->dmax) == dmax0) {
2845 ctx->cc_idle = NULL;
2854 int init_dmax = atomic_load(&ctx->dmax);
2855 int init_dmin = atomic_load(&ctx->dmin);
2856 if (init_dmax > 0 && init_dmin >= init_dmax && !ctx->
p->
outC) {
2865 while (!atomic_load(&ctx->stop_flag)) {
2866 if (ctx->even_weight_only && (w & 1)) {
2867 if (w == atomic_load(&ctx->dmin)) atomic_store(&ctx->dmin, w + 1);
2871 double now = get_time_sec();
2873 if (ctx->
timeout > 0.0 && remaining_time <= 0.0) {
2878 int cur_dmax = atomic_load(&ctx->dmax);
2879 int cur_dmin = atomic_load(&ctx->dmin);
2880 const bool rw_on = m3_rw_running(ctx);
2883 const int target_cc_w = m3_cc_target_w(ctx, cur_dmin, cur_dmax);
2885 if (cur_dmax > 0 && cur_dmin >= cur_dmax && w > target_cc_w) {
2887 atomic_store(&ctx->dmin, cur_dmax);
2891 if (ctx->
p->
dstop > 0 && cur_dmin >= ctx->
p->
dstop && w > target_cc_w) {
2897 if (w > target_cc_w) {
2901 ctx->stop_w = (ctx->even_weight_only && target_cc_w + 1 == max_allowed_w) ? max_allowed_w : target_cc_w;
2907 fprintf(stderr,
"# CC done up to w=%d, waiting for RW (bounds [%d, %d])\n", target_cc_w, cur_dmin, cur_dmax);
2909 m3_wait_rw(ctx, cur_dmax,
"done up to wmax, waiting for RW");
2917 const double t_cc_est = cc_work_estimate(ctx, w, &growth);
2918 if (ctx->
timeout > 0.0 && t_cc_est / nthr > remaining_time) {
2922 fprintf(stderr,
"# CC for w=%d (est %.2fs) exceeds remaining timeout %.2fs, devoting %d threads to RW\n",
2923 w, t_cc_est / nthr, remaining_time, ctx->
rw_threads);
2925 m3_wait_rw(ctx, cur_dmax,
"round predicted to exceed the timeout, waiting for RW");
2930 fprintf(stderr,
"# CC for w=%d (est %.2fs) exceeds remaining timeout %.2fs, RW ended: terminating early "
2931 "(dmin=%d)\n", w, t_cc_est / nthr, remaining_time, cur_dmin);
2939 fprintf(stderr,
"# CC for w=%d (est %.2fs) may not finish in the remaining time %.2fs, searching anyway "
2940 "(dmin=%d)\n", w, t_cc_est / nthr, remaining_time, cur_dmin);
2945 int n_cc = m3_plan_cc_threads(ctx, w, t_cc_est, growth, remaining_time);
2949 fprintf(stderr,
"# CC paused before w=%d > dexp=%d until RW finds a codeword (or ends)\n", w, ctx->
dexp);
2951 m3_wait_rw(ctx, cur_dmax,
"paused (w > dexp) until RW finds a codeword");
2954 const int n_cc0 = n_cc;
2955 int n_rw = nthr - n_cc;
2956 const long steps_rem = rw_on ? ctx->total_rw_steps - atomic_load(&ctx->rw_steps_completed) : 0;
2958 const int beg = cc_round_beg(ctx->
p);
2959 const int end = cc_round_end(ctx->
p, w);
2961 const bool certified = (w == atomic_load(&ctx->dmin));
2962 const int lev = cc_round_levels(ctx, w, beg, end, t_cc_est);
2963 const int last = cc_unit_last(ctx, w, lev);
2965 atomic_store(&ctx->cc_target_workers, n_cc);
2966 const long long busy_ns0 = atomic_load(&ctx->cc_busy_ns);
2967 const long long nodes0 = atomic_load(&ctx->cc_nodes);
2968 atomic_store(&ctx->cc_next, cc_pack(w, lev, beg * ctx->cc_R[lev]));
2969 atomic_store(&ctx->cc_round_active, 1);
2972 char est_str[64] =
"unknown";
2973 char rw_str[64] =
"";
2974 char units[64] =
"";
2975 if (t_cc_est > 0.0) snprintf(est_str,
sizeof(est_str),
"%.3g thread-s", t_cc_est);
2976 if (rw_on) snprintf(rw_str,
sizeof(rw_str),
", RW step %.3g s", m3_rw_step_time(ctx));
2977 if (lev > 0) snprintf(units,
sizeof(units),
", %d work units per start column", ctx->cc_R[lev]);
2979 "# CC round w=%d started: %d CC threads, %d RW threads "
2980 "(bounds [%d, %d], rem_rw=%ld, rem_time=%.2fs, est %s%s%s)\n",
2981 w, n_cc, n_rw, cur_dmin, cur_dmax, steps_rem, remaining_time, est_str, rw_str, units);
2984 double cc_start = get_time_sec();
2985 bool round_completed =
false;
2986 int dmax_seen = cur_dmax;
2987 unsigned int poll = 0;
2988 ctx->round_open = 1;
2990 ctx->round_beg = beg;
2991 ctx->round_end = end;
2992 ctx->round_R = ctx->cc_R[lev];
2993 ctx->round_threads = n_cc;
2994 ctx->round_t0 = cc_start;
2995 ctx->round_est = t_cc_est;
2997 while (!atomic_load(&ctx->stop_flag)) {
2998 const double t = get_time_sec();
3003 if (cc_unpack_idx(atomic_load(&ctx->cc_next)) > last && atomic_load(&ctx->cc_active_workers) == 0) {
3004 round_completed =
true;
3009 if ((++poll & 15) == 0 && n_cc < nthr) {
3011 const char *why =
"";
3012 const int d_now = atomic_load(&ctx->dmax);
3013 if (d_now != dmax_seen) {
3016 n_new = m3_plan_cc_threads(ctx, w, t_cc_est, growth, rem);
3017 why =
"new upper bound";
3019 const bool paused = (d_now == 0 && ctx->
dexp > 0 && w > ctx->
dexp && m3_rw_running(ctx));
3020 if (!paused && t_cc_est > 0.0 && t - cc_start >
M3_OVERRUN_FACTOR * t_cc_est / n_cc0 + 0.02) {
3022 why =
"longer than predicted";
3026 fprintf(stderr,
"# CC round w=%d: %d -> %d CC threads (%s, bounds [%d, %d], %.3fs into the round)\n",
3027 w, n_cc, n_new, why, atomic_load(&ctx->dmin), d_now, t - cc_start);
3031 ctx->round_threads = n_cc;
3032 atomic_store(&ctx->cc_target_workers, n_cc);
3039 atomic_store(&ctx->cc_round_active, 0);
3040 if (round_completed) ctx->round_open = 0;
3042 double cc_dur = get_time_sec() - cc_start;
3046 ctx->cc_time_per_weight[w] = 1e-9 * (double)(atomic_load(&ctx->cc_busy_ns) - busy_ns0);
3048 char est_str[48] =
"unknown";
3049 if (t_cc_est > 0.0) snprintf(est_str,
sizeof(est_str),
"%.3g thread-s", t_cc_est);
3050 fprintf(stderr,
"# CC round w=%d work: %.3g thread-s measured, %s predicted (%.3fs, %d CC threads at the "
3051 "end), %lld nodes\n", w, ctx->cc_time_per_weight[w], est_str, cc_dur, n_cc,
3052 atomic_load(&ctx->cc_nodes) - nodes0);
3056 int cw_found = atomic_load(&ctx->cc_found_weight);
3057 if (cw_found > 0 && certified && cw_found == w && !atomic_load(&ctx->cc_exact)) {
3058 atomic_store(&ctx->cc_exact,
true);
3060 const bool exact = (cw_found > 0) && atomic_load(&ctx->cc_exact);
3062 atomic_store(&ctx->dmin, cw_found);
3063 atomic_store(&ctx->dmax, cw_found);
3065 int max_w_lim = minint(ctx->
p->
wmax > 0 ? ctx->
p->
wmax : nvar, cw_found + ctx->
p->
dW);
3068 max_w_lim = minint(max_w_lim, ctx->
p->
dstop + (ctx->
p->
dW > 0 ? ctx->
p->
dW : 0));
3070 if (ctx->even_weight_only && (max_w_lim & 1)) max_w_lim--;
3071 if (ctx->
p->
outC && ctx->
p->
dW > 0 && w < max_w_lim) {
3073 if (w == cw_found) {
3075 "# CC round w=%d finished in %.3fs (%d CC threads, %d RW threads): found codewords "
3076 "(dmin=%d, continuing up to w=%d for dW=%d, total %lld cws)\n",
3077 w, cc_dur, n_cc, n_rw, cw_found, max_w_lim, ctx->
p->
dW, ctx_num_cws(ctx));
3078 }
else if (round_completed) {
3080 "# CC round w=%d finished in %.3fs (%d CC threads, %d RW threads): "
3081 "extra dW round completed (dmin=%d, total %lld cws)\n",
3082 w, cc_dur, n_cc, n_rw, cw_found, ctx_num_cws(ctx));
3088 if (round_completed) {
3090 "# CC round w=%d finished in %.3fs (%d CC threads, %d RW threads): "
3091 "extra dW round completed (dmin=%d, total %lld cws)\n",
3092 w, cc_dur, n_cc, n_rw, cw_found, ctx_num_cws(ctx));
3095 fprintf(stderr,
"# CC found min-weight codeword: d=%d (using %d CC threads, total %lld cws)\n",
3096 cw_found, n_cc, ctx_num_cws(ctx));
3103 }
else if (cur_dmax > 0 && cur_dmin >= cur_dmax) {
3107 "# CC round w=%d finished in %.3fs (%d CC threads, %d RW threads): "
3108 "extra dW round completed (dmin=%d, total %lld cws)\n",
3109 w, cc_dur, n_cc, n_rw, cur_dmin, ctx_num_cws(ctx));
3111 if (!round_completed) {
3115 if (!round_completed) {
3120 int new_dmin = ctx->even_weight_only ? w + 2 : w + 1;
3121 atomic_store(&ctx->dmin, new_dmin);
3123 fprintf(stderr,
"# CC round w=%d finished in %.3fs (%d CC threads, %d RW threads): no codewords -> dmin=%d\n",
3124 w, cc_dur, n_cc, n_rw, new_dmin);
3127 cur_dmax = atomic_load(&ctx->dmax);
3128 if (cur_dmax > 0 && new_dmin >= cur_dmax) {
3129 atomic_store(&ctx->dmin, cur_dmax);
3131 const int w_last = ctx->
p->
outC ? m3_cc_target_w(ctx, cur_dmax, cur_dmax) : 0;
3134 fprintf(stderr,
"# bracketing bounds coincide: dmin = dmax = %d (continuing up to w=%d for dW=%d to export "
3135 "codewords)\n", cur_dmax, w_last, ctx->
p->
dW > 0 ? ctx->
p->
dW : 0);
3140 fprintf(stderr,
"# bracketing bounds coincide: dmin = dmax = %d\n", cur_dmax);
3144 }
else if (ctx->
p->
dstop > 0 && new_dmin >= ctx->
p->
dstop) {
3147 const int w_last = ctx->
p->
outC ? m3_cc_target_w(ctx, new_dmin, cur_dmax) : 0;
3150 fprintf(stderr,
"# lower bound dmin=%d reached dstop=%d (continuing up to w=%d for dW=%d to export "
3151 "codewords)\n", new_dmin, ctx->
p->
dstop, w_last, ctx->
p->
dW > 0 ? ctx->
p->
dW : 0);
3156 fprintf(stderr,
"# lower bound dmin=%d reached dstop=%d\n", new_dmin, ctx->
p->
dstop);
3169static int available_cpus(
void) {
3173 if (sched_getaffinity(0,
sizeof(set), &set) == 0) {
3174 const int cnt = CPU_COUNT(&set);
3175 if (cnt > 0)
return cnt;
3178 const long nprocs = sysconf(_SC_NPROCESSORS_ONLN);
3179 return (nprocs > 0) ? (int)nprocs : 4;
3183static unsigned long long physical_memory_bytes(
void) {
3184#if defined(_SC_PHYS_PAGES) && defined(_SC_PAGESIZE)
3185 const long pages = sysconf(_SC_PHYS_PAGES), psize = sysconf(_SC_PAGESIZE);
3186 if (pages > 0 && psize > 0)
return (
unsigned long long)pages * (
unsigned long long)psize;
3198static int rw_thread_cap(
const params_t *
const p) {
3200 const int nvar =
p->nvar;
3201 const int nrows =
p->spaH ?
p->spaH->rows : 0;
3202 if (nrows > 0 && nvar > 0) {
3203 const size_t words_n = ((size_t)nvar + 63) / 64;
3204 size_t dense_bytes_per_thread;
3206 dense_bytes_per_thread = (size_t)minint(
p->ksub, nrows) * words_n * 8;
3208 dense_bytes_per_thread = (size_t)nrows * words_n * 8 + (
size_t)nvar * (((size_t)nrows + 63) / 64) * 8;
3210 if (dense_bytes_per_thread > 15ULL * 1024 * 1024) {
3211 const unsigned long long ram = physical_memory_bytes();
3212 const unsigned long long budget = ram ? ram / 4 : 1536ULL * 1024 * 1024;
3213 const unsigned long long max_mem_threads = budget / dense_bytes_per_thread;
3214 cap = (max_mem_threads < 2) ? 2 : ((max_mem_threads > 32) ? 32 : (int)max_mem_threads);
3218 const int max_step_threads = (int)(((
long)
p->steps + 9) / 10);
3219 cap = minint(cap, max_step_threads);
3225static void print_run_plan(
const distfork_ctx_t *
const ctx,
const int init_dmax) {
3227 fprintf(stderr,
"# run plan: method=%d (%s), %d threads",
p->method,
3228 (
p->method == 1) ?
"RW" : ((
p->method == 2) ?
"CC" :
"bracketing"), ctx->
num_threads);
3229 if (ctx->spawn_initial < ctx->
num_threads) fprintf(stderr,
" (%d at first)", ctx->spawn_initial);
3230 if (
p->method == 3) fprintf(stderr,
" (RW on up to %d)", ctx->
rw_threads);
3232 fprintf(stderr,
", steps=%ld, min_hits=%d, ksub=%d", ctx->total_rw_steps,
p->min_hits,
p->ksub);
3233 if (
p->method & 2) {
3234 fprintf(stderr,
", CC from w=%d",
p->noscan ?
p->wmax : (
p->dmin > 1 ?
p->dmin : 1));
3235 if (
p->wmax > 0) fprintf(stderr,
" to wmax=%d",
p->wmax);
3236 if (
p->smax == 0) fprintf(stderr,
" (cc_prune=%d)",
p->cc_prune);
3237 if (
p->method == 3 && ctx->
dexp > 0) fprintf(stderr,
", dexp=%d", ctx->
dexp);
3239 if (init_dmax > 0) fprintf(stderr,
", dmax=%d", init_dmax);
3240 if (
p->dstop > 0) fprintf(stderr,
", dstop=%d",
p->dstop);
3241 if (ctx->
timeout > 0.0) fprintf(stderr,
", timeout=%gs", ctx->
timeout);
3242 else fprintf(stderr,
", no timeout");
3243 fprintf(stderr,
", seed=%d\n",
p->seed);
3247static void print_stop_reason(
distfork_ctx_t *
const ctx,
const int final_dmax,
const bool cc_exact) {
3249 const int cc_found = atomic_load(&ctx->cc_found_weight);
3250 const bool rw_ran = (
p->method & 1) && ctx->
rw_threads > 0;
3251 fprintf(stderr,
"# stopped after %.3fs: ", get_time_sec() - ctx->
start_time);
3252 switch (atomic_load(&ctx->stop_reason)) {
3254 fprintf(stderr,
"bounds coincide: dmin = dmax = %d", final_dmax);
3258 fprintf(stderr,
"CC found min-weight codeword: d=%d", cc_found);
3260 fprintf(stderr,
"CC found a codeword of weight %d (upper bound only: lower weights not scanned)", cc_found);
3265 fprintf(stderr,
"RW convergence reached (<n>=%.2f >= min_hits=%d after %ld RW steps)", st.
avg,
p->min_hits,
3266 atomic_load(&ctx->rw_steps_completed));
3270 fprintf(stderr,
"all %ld RW steps done", ctx->total_rw_steps);
3273 fprintf(stderr,
"timeout=%gs reached", ctx->
timeout);
3274 if (ctx->round_open)
3275 fprintf(stderr,
" during CC round w=%d (%.1f%% of start columns claimed)", ctx->round_w, cc_round_claimed(ctx));
3277 fprintf(stderr,
", %ld of %ld RW steps done", atomic_load(&ctx->rw_steps_completed), ctx->total_rw_steps);
3280 fprintf(stderr,
"CC done up to w=%d", ctx->stop_w);
3281 if (
p->method == 3 && rw_ran) fprintf(stderr,
", RW ended (%s)", rw_end_state(ctx));
3284 fprintf(stderr,
"CC round w=%d predicted not to finish before the timeout, RW ended (%s)", ctx->stop_w,
3288 fprintf(stderr,
"found a codeword of weight %d <= wmin=%d", final_dmax,
p->wmin);
3291 fprintf(stderr,
"maxC=%lld codewords collected",
p->maxC);
3295 int w_hi = final_dmax;
3296 if (
p->outC &&
p->dW > 0) {
3297 w_hi = final_dmax +
p->dW;
3298 if (
p->dstop > 0 &&
p->dstop < final_dmax) w_hi = minint(w_hi,
p->dstop +
p->dW);
3299 if (
p->wmax > 0) w_hi = minint(w_hi,
p->wmax);
3301 fprintf(stderr,
"CC enumerated all codewords of weight %d", final_dmax);
3302 if (w_hi > final_dmax) fprintf(stderr,
"..%d", w_hi);
3303 if (
p->outC) fprintf(stderr,
" (for outC)");
3307 fprintf(stderr,
"lower bound dmin=%d reached dstop=%d", atomic_load(&ctx->dmin),
p->dstop);
3310 fprintf(stderr,
"search ended");
3312 fprintf(stderr,
"\n");
3320static int cc_hash_col_cmp(
const void *a,
const void *b) {
3322 if (x->
h != y->h)
return (x->
h < y->h) ? -1 : 1;
3323 return (x->
c > y->c) - (x->
c < y->c);
3327static cc_hash_t *cc_hash_new(
const csr_t *
const mHT,
const int m) {
3328 const int n = mHT->
rows;
3331 if (!hs || !tmp)
ERROR(
"memory allocation");
3332 hs->
zr = malloc((
size_t)(m > 0 ? m : 1) *
sizeof(uint64_t));
3333 hs->
hc = calloc((
size_t)(n > 0 ? n : 1),
sizeof(uint64_t));
3334 if (!hs->
zr || !hs->
hc)
ERROR(
"memory allocation");
3335 uint64_t state = 0x243f6a8885a308d3ULL;
3336 for (
int r = 0; r < m; r++) hs->
zr[r] = splitmix64(&state);
3338 for (
int c = 0; c < n; c++) {
3339 if (mHT->
p[c + 1] == mHT->
p[c])
continue;
3341 for (
int k = mHT->
p[c]; k < mHT->
p[c + 1]; k++) h ^= hs->
zr[mHT->
i[k]];
3355 for (
int k = 0; k <
nz; k++) nkeys += (k == 0 || tmp[k].h != tmp[k - 1].h);
3357 while ((1LL << lg) < nkeys) lg++;
3358 const int tbits = lg + 1;
3359 const int fbits = (lg + 4 > 6) ? lg + 4 : 6;
3361 hs->
tmask = (int)((1ULL << tbits) - 1);
3363 hs->
bmask = ((uint64_t)1 << fbits) - 1;
3364 hs->
bits = calloc((
size_t)1 << (fbits - 6),
sizeof(uint64_t));
3365 hs->
cols = malloc((
size_t)nz *
sizeof(
int));
3367 for (
int k = 0; k <
nz; k++) hs->
cols[k] = tmp[k].c;
3368 for (
int k = 0; k <
nz;) {
3369 const uint64_t key = tmp[k].
h;
3371 while (k2 < nz && tmp[k2].h == key) k2++;
3372 int j = (int)(key >> hs->
tshift);
3373 while (hs->
tab[j].
cnt > 0) j = (j + 1) & hs->
tmask;
3377 const uint64_t fb = key & hs->
bmask;
3378 hs->
bits[fb >> 6] |= (uint64_t)1 << (fb & 63);
3387static void cc_hash_free(
cc_hash_t *
const hs) {
3397int main(
int argc,
char **argv) {
3407 if ((
p->method & 1) &&
p->ksub > 0 &&
p->spaH) {
3408 int nrows =
p->spaH->rows;
3409 int nvar =
p->spaH->cols;
3410 if (2 * nrows < nvar) {
3413 "# Warning: ksub=%d requested, but m=%d < n-m=%d (<= nu); "
3414 "falling back to full-matrix RW (ksub=0)\n",
3415 p->ksub, nrows, nvar - nrows);
3422 int requested_threads =
p->threads;
3423 int num_threads =
p->threads;
3424 if (num_threads <= 0) num_threads = available_cpus();
3429 if (!
p->nothrottle) {
3432 int nrows =
p->spaH ?
p->spaH->rows : 0;
3433 unsigned long long n_elements = (
unsigned long long)nrows * (
unsigned long long)nvar;
3438 if (
p->method & 1) {
3439 const double rw_work = (double)n_elements * (
double)(
p->steps > 0 ?
p->steps : 1);
3440 if (nvar < 60 || (n_elements < 20000ULL && rw_work < 5.0e7)) {
3441 if (num_threads > 4) num_threads = 4;
3442 }
else if ((nvar < 150 || n_elements < 100000ULL) && rw_work < 2.0e8) {
3443 if (num_threads > 16) num_threads = 16;
3449 if (
p->method & 1) {
3450 const int rw_cap = rw_thread_cap(
p);
3451 if (
p->method == 1 && num_threads > rw_cap) num_threads = rw_cap;
3452 rw_threads = minint(num_threads, rw_cap);
3455 if (requested_threads > 0 && num_threads != requested_threads && (
p->debug &
DBG_TIMING)) {
3456 fprintf(stderr,
"# Note: throttled threads from %d to %d (n=%d, r=%d)\n",
3457 requested_threads, num_threads, nvar, nrows);
3460 if (
p->method & 1) rw_threads = num_threads;
3462 fprintf(stderr,
"# Thread throttling disabled (nothrottle=1); running with %d threads\n", num_threads);
3465 if (
p->method == 3 &&
p->steps == 0) {
3468 if (
p->method == 3 && rw_threads < num_threads && (
p->debug &
DBG_TIMING)) {
3469 if (rw_threads == 0) {
3470 fprintf(stderr,
"# Note: steps=0, no RW; CC rounds use all %d threads\n", num_threads);
3472 fprintf(stderr,
"# Note: RW limited to %d of %d threads (memory, steps); CC rounds can use all threads\n",
3473 rw_threads, num_threads);
3477 double timeout = (
p->timeout > 0.0) ?
p->timeout : 0.0;
3480 memset(&ctx, 0,
sizeof(ctx));
3487 ctx.total_rw_steps = (
p->steps >= 0) ?
p->steps : 1;
3490 atomic_init(&ctx.dmin,
p->dmin > 1 ?
p->dmin : 1);
3493 init_dmax =
p->dmax;
3495 if (
p->min_w != INT_MAX) {
3496 if (init_dmax == 0 ||
p->min_w < init_dmax) {
3497 init_dmax =
p->min_w;
3500 atomic_init(&ctx.dmax, init_dmax);
3502 if (init_dmax > 0 &&
p->wmin > 0 && init_dmax <= p->wmin && !
p->outC &&
p->maxC == 0) {
3504 fprintf(stderr,
"# stopped: known upper bound dmax=%d <= wmin=%d (no search)\n", init_dmax,
p->wmin);
3506 printf(
"%d %d 0\n",
p->dmin > 1 ?
p->dmin : 1, init_dmax);
3511 if (
p->method == 3 && init_dmax > 0 &&
p->dmin > 1 &&
p->dmin >= init_dmax && !
p->outC) {
3513 fprintf(stderr,
"# stopped: bounds coincide: dmin = dmax = %d (supplied, no search)\n", init_dmax);
3515 printf(
"%d %d 0\n",
p->dmin, init_dmax);
3522 if (
p->dstop > 0 &&
p->dmin >=
p->dstop && !
p->outC &&
p->maxC == 0) {
3524 fprintf(stderr,
"# stopped: lower bound dmin=%d >= dstop=%d (supplied, no search)\n",
p->dmin,
p->dstop);
3526 printf(
"%d %d 0\n", (init_dmax > 0 &&
p->dmin >= init_dmax) ? init_dmax :
p->dmin, init_dmax);
3531 atomic_init(&ctx.cc_found_weight, 0);
3532 atomic_init(&ctx.cc_exact,
false);
3533 atomic_init(&ctx.stop_flag,
false);
3534 atomic_init(&ctx.rw_stop_flag,
false);
3535 atomic_init(&ctx.stop_reason,
STOP_NONE);
3536 ctx.status_next = 1.0;
3537 ctx.status_dt = 1.0;
3539 atomic_init(&ctx.rw_steps_started, 0);
3540 atomic_init(&ctx.rw_steps_completed, 0);
3541 atomic_init(&ctx.rw_busy_ns, 0);
3542 atomic_init(&ctx.rw_timed_steps, 0);
3543 atomic_init(&ctx.rw_rank, 0);
3544 atomic_init(&ctx.rw_unif_steps, 0);
3545 atomic_init(&ctx.rw_pairs_l, 0);
3546 atomic_init(&ctx.rw_pairs_checked, 0);
3547 atomic_init(&ctx.rw_hits1, 0);
3548 atomic_init(&ctx.rw_hits2, 0);
3549 uint64_t *
const rw_lhash = (
p->pairs != 0 && (
p->method & 1) &&
p->ksub <= 0) ? rw_lhash_init(
p) : NULL;
3550 ctx.rw_lhash = rw_lhash;
3551 atomic_init(&ctx.cc_next, cc_pack(0, 0, 0));
3552 atomic_init(&ctx.cc_active_workers, 0);
3553 atomic_init(&ctx.cc_target_workers, 0);
3554 atomic_init(&ctx.cc_round_active, 0);
3555 atomic_init(&ctx.cc_busy_ns, 0);
3556 atomic_init(&ctx.cc_nodes, 0);
3557 atomic_init(&ctx.cw_red_cc, 0);
3558 atomic_init(&ctx.cw_red_rw, 0);
3559 atomic_init(&ctx.next_refresh_step,
p->refresh > 0 ?
p->refresh : 0);
3561 pthread_mutex_init(&ctx.cw_mutex, NULL);
3564 pthread_rwlockattr_t rwattr;
3565 pthread_rwlockattr_init(&rwattr);
3567 pthread_rwlockattr_setkind_np(&rwattr, PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP);
3569 pthread_rwlock_init(&ctx.basis_rwlock, &rwattr);
3570 pthread_rwlockattr_destroy(&rwattr);
3577 cc_hash_t *
const cc_hash = ((
p->method & 2) &&
p->smax == 0) ? cc_hash_new(ctx.mHT_cc,
p->spaH->rows) : NULL;
3579 ctx.even_weight_only = ((
p->method & 2) &&
p->smax == 0 && !
p->noscan &&
p->spaH &&
3582 fprintf(stderr,
"# CC: %s, bitset syndromes, hash of %d nonzero columns (%d distinct) for the last two levels"
3583 " (%d table slots)%s\n", (
p->cc_prune == 0) ?
"walk of the first unsatisfied row (cc_prune=0)"
3584 : ((
p->cc_prune == 1) ?
"branch and bound (cc_prune=1)" :
"branch and bound with the cover bounds "
3586 ctx.even_weight_only ?
", even weights only (1 in rowspan(H))" :
"");
3590 ctx.cc_branch = (
p->spaH->rows > 0) ? fmax(1.0, (
double)
p->spaH->p[
p->spaH->rows] /
p->spaH->rows - 1.0) : 1.0;
3593 const int prev = ctx.cc_R[l - 1];
3594 ctx.cc_R[l] = (prev > 0 && ctx.cc_B > 0 && prev <= INT_MAX / ctx.cc_B) ? prev * ctx.cc_B : 0;
3598 if ((
p->method & 1) &&
p->ksub > 0 && ctx.
rw_threads > 0) {
3599 double t_ker0 = get_time_sec();
3601 ctx.nu = ctx.N_global ? ctx.N_global->nrows : 0;
3602 if (
p->spaH->rows < ctx.nu || ctx.nu <= 0) {
3605 "# Warning: ksub=%d requested, but m=%d < nu=%d; "
3606 "falling back to full-matrix RW (ksub=0)\n",
3607 p->ksub,
p->spaH->rows, ctx.nu);
3609 safe_mzd_free(ctx.N_global);
3610 ctx.N_global = NULL;
3613 if (!
p->nothrottle) {
3614 const int rw_cap = rw_thread_cap(
p);
3615 if (
p->method == 1 && num_threads > rw_cap) {
3616 num_threads = rw_cap;
3621 fprintf(stderr,
"# Note: %d threads, %d of them for RW (full-matrix RW)\n", num_threads, ctx.
rw_threads);
3626 "# computed ker(H) in %.4fs: nu=%d, n=%d, ksub=%d (eff=%d)\n",
3627 get_time_sec() - t_ker0, ctx.nu,
p->spaH->cols,
p->ksub,
3628 minint(
p->ksub, ctx.nu));
3630 if (ctx.nu > 0) atomic_store(&ctx.rw_rank,
p->spaH->cols - ctx.nu);
3636 if (ctx.spawn_initial < num_threads && (
p->debug &
DBG_TIMING)) {
3637 fprintf(stderr,
"# Note: CC starts %d of %d threads, the others when a CC round takes longer\n",
3638 ctx.spawn_initial, num_threads);
3641 if (
p->debug &
DBG_PROGRESS) print_run_plan(&ctx, init_dmax);
3644 ctx.threads = malloc(num_threads *
sizeof(pthread_t));
3646 if (!ctx.threads || !args)
ERROR(
"memory allocation");
3649 for (
int i = 0; i < num_threads; i++) {
3653 for (
int k = 0; k <
MAX_W; k++) {
3654 args[i].
min_swei[k] =
p->spaH->rows + 1;
3657 ctx_spawn(&ctx, ctx.spawn_initial);
3659 if (
p->method == 1) {
3660 run_method1_coordinator(&ctx);
3661 }
else if (
p->method == 2) {
3662 run_method2_coordinator(&ctx);
3663 }
else if (
p->method == 3) {
3664 run_method3_coordinator(&ctx);
3666 ERROR(
"invalid method %d\n",
p->method);
3670 atomic_store(&ctx.stop_flag,
true);
3671 for (
int i = 0; i < ctx.spawned; i++) {
3672 pthread_join(ctx.threads[i], NULL);
3675 int final_dmin = atomic_load(&ctx.dmin);
3676 int final_dmax = atomic_load(&ctx.dmax);
3677 int cc_found = atomic_load(&ctx.cc_found_weight);
3680 const bool cc_exact = (cc_found > 0) && atomic_load(&ctx.cc_exact) &&
3681 (final_dmax == 0 || cc_found <= final_dmax);
3684 final_dmin = cc_found;
3685 final_dmax = cc_found;
3686 }
else if (final_dmax > 0 && final_dmin >= final_dmax) {
3687 final_dmin = final_dmax;
3691 if (
p->debug &
DBG_SUMMARY) print_stop_reason(&ctx, final_dmax, cc_exact);
3694 if (
p->smax &&
p->method >= 2) {
3695 int max_w_analyzed = (final_dmin > 1) ? (final_dmin - 1) : ((
p->wmax > 0) ?
p->wmax : 0);
3696 if (cc_exact) max_w_analyzed = cc_found;
3697 if (max_w_analyzed > 0) {
3698 int global_swei[
MAX_W];
3699 for (
int i = 0; i <
MAX_W; i++) global_swei[i] =
p->spaH->rows + 1;
3700 for (
int t = 0; t < ctx.spawned; t++) {
3701 for (
int i = 1; i <= max_w_analyzed; i++) {
3702 if (args[t].min_swei[i] < global_swei[i]) {
3703 global_swei[i] = args[t].
min_swei[i];
3709 for (
int i = 1; i <= max_w_analyzed; i++) {
3710 if (global_swei[i] <=
p->spaH->rows) {
3711 fprintf(stderr,
"# w=%d min non-zero syndrome weight %d\n", i, global_swei[i]);
3717 fprintf(stderr,
"# confinement: ");
3718 for (
int i = 1; i <= max_w_analyzed; i++) {
3719 if (global_swei[i] <=
p->spaH->rows) {
3720 fprintf(stderr,
"%d%s", global_swei[i], i < max_w_analyzed ?
"," :
"");
3723 fprintf(stderr,
"?%s", i < max_w_analyzed ?
"," :
"");
3726 fprintf(stderr,
"\n");
3730 "# Note: Some weights were skipped in confinement profile. "
3731 "Try increasing smax (current: %d)\n",
p->smax);
3736 long reported_rw_steps = 0;
3737 if (
p->method != 2 && !cc_exact) {
3738 reported_rw_steps = atomic_load(&ctx.rw_steps_completed);
3743 const long long red_cc = atomic_load(&ctx.cw_red_cc), red_rw = atomic_load(&ctx.cw_red_rw);
3744 if (red_cc > 0 || red_rw > 0)
3745 fprintf(stderr,
"# reducible codewords skipped (irred=1, a codeword plus a disjoint nonzero vector with zero "
3746 "syndrome): %lld times in CC, %lld times in RW\n", red_cc, red_rw);
3748 const long rw_done = atomic_load(&ctx.rw_steps_completed);
3749 const long rw_unif = atomic_load(&ctx.rw_unif_steps);
3750 const int rw_rank = atomic_load(&ctx.rw_rank);
3751 const long rw_timed = atomic_load(&ctx.rw_timed_steps);
3752 const double t_step = (rw_timed > 0) ? 1e-9 * (
double)atomic_load(&ctx.rw_busy_ns) / (double)rw_timed : 0.0;
3753 const int rw_pairs_l = atomic_load(&ctx.rw_pairs_l);
3754 if (rw_done > 0 && rw_rank > 0) {
3755 fprintf(stderr,
"# RW information sets: n=%d, rank(H)=%d, steps=%ld (uniform permutations: %ld), ksub=%d, "
3756 "pairs=%d, %.3g s/step per thread, %d RW threads\n",
p->nvar, rw_rank, rw_done, rw_unif,
p->ksub,
3759 fprintf(stderr,
"# RW pairs (p=2, buckets of l=%d random pivot rows): %.3g pairs checked per step; codewords "
3760 "recorded with weight up to the upper bound: %lld from pairs, %lld from single columns\n", rw_pairs_l,
3761 (
double)atomic_load(&ctx.rw_pairs_checked) / (
double)rw_done, (
long long)atomic_load(&ctx.rw_hits2),
3762 (
long long)atomic_load(&ctx.rw_hits1));
3763 if (rw_unif > 0 && rw_unif < rw_done)
3770 if (reported_rw_steps > 0 && final_dmin < final_dmax && atomic_load(&ctx.stop_reason) !=
STOP_DSTOP &&
3778 printf(
"%d %d %ld\n", final_dmin, final_dmax, reported_rw_steps);
3784 sprintf(comment,
"generated by dist_m4ri");
3787 fprintf(stderr,
"# exported %lld codewords to %s\n", num_written,
p->outC);
3793 fprintf(stderr,
"# all %lld codewords in hash (1-based columns, cnt: number of hits, of which u in RW steps with "
3794 "uniform permutations and w in RW steps with windows):\n",
p->num_cws);
3796 fprintf(stderr,
"# cw: [ ");
3797 for (
int i = 0; i < cw->
weight; i++) fprintf(stderr,
"%d ", 1 + col_orig_index(
p, cw->
arr[i]));
3798 fprintf(stderr,
"] cnt=%d (u=%d, w=%d)\n", cw->
cnt, cw->
cnt_u, cw->
cnt_w);
3804 safe_mzd_free(ctx.N_global);
3805 cc_hash_free(cc_hash);
3809 pthread_rwlock_destroy(&ctx.basis_rwlock);
3810 pthread_mutex_destroy(&ctx.cw_mutex);
#define M3_DEADLINE_FRACTION
#define CC_SPLIT_UNITS_PER_THREAD
#define CC_SPLIT_CLAIMS_UNKNOWN
#define M3_OVERRUN_FACTOR
int main(int argc, char **argv)
struct worker_arg worker_arg_t
#define CC_SPLIT_CLAIMS_PER_WORK
#define CC_SPLIT_MIN_WORK
#define CC_SPLIT_MAX_LEVELS
Hit statistics for the min_hits stopping criterion, see codeword_hit_stats().
const cc_hash_t * cc_hash
#define HASH_ITER(hh, head, el, tmp)
Utility functions for use with uthash.h.
struct ONE_VEC_T one_vec_t
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,...
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.
int check_min_hits_convergence(const params_t *const p)
Check whether the QDistRnd-style hit count stopping condition is met.
void var_kill(params_t *const p)
Clean up and free memory allocated in the params_t structure.
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.
int codeword_maxc_reached(const params_t *const p)
Returns 1 if maxC > 0 and maxC codewords of the collection window are collected.
int codeword_feed_limit(const params_t *const p)
Weight limit (exclusive) of RW codewords needed for the min_hits statistic.
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...
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.
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.
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...
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.
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.
void print_codeword_stats(FILE *stream, const params_t *const p)
Print accumulated codeword and hit statistics to the given stream.
void var_init(int argc, char **argv, params_t *const p)
Initialize parameters and load matrices from command line arguments.
#define RW_PAIRS_MAX_BITS
The largest number of random pivot rows of the buckets of the RW pairs (pairs, a 64-bit key).
mzd_t * mzd_nullspace(const csr_t *const H)
Compute the null space basis N = ker(H) of dimension nu x n.
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.
cols_ws_t * cols_ws_free(cols_ws_t *ws)
Free the workspace allocated by cols_ws_init().
int csr_cols_rank(const csr_t *const HT, const int cnt, const int cols[], cols_ws_t *const ws)
Rank of a set of columns of a sparse binary matrix H.
csr_t * csr_transpose(csr_t *dst, const csr_t *const p)
Transpose a compressed CSR sparse matrix.
mzd_t * mzd_from_csr(mzd_t *dst, const csr_t *p)
Convert a CSR sparse matrix to an MZD dense matrix.
mzp_t * perm_p_trans(mzp_t *q, const mzp_t *p, const rci_t start)
Apply transposed pivot permutation p to permutation q in-place.
csr_t * csr_free(csr_t *p)
Free memory allocated for a CSR sparse matrix.
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
void localized_window_perm(mzp_t *perm, int nvar, int seed_col, int kwin, int win_mode, const csr_t *spaH, const csr_t *mHT, int *visited_cols, int *visited_checks, int *col_queue, int *visit_marker, uint64_t *rng_state)
Construct a column permutation with a localized window at the front.
void refresh_nullspace_basis(mzd_t *N, const int *cw_arr, int cw_wt, uint64_t *rng_state)
Refresh the null space basis N under a random column permutation and optionally inject a low-weight c...
int csr_all_ones_in_rowspan(const csr_t *const H)
Check whether the all-ones vector lies in the row span of a binary CSR matrix H over GF(2).