Orbiter 2022
Combinatorial Objects
mindist.cpp
Go to the documentation of this file.
1// mindist.cpp
2//
3// The algorithm for computing the minimum distance implemented here
4// is due to Brouwer. It has been described in
5// Betten, Fripertinger et al.~\cite{BettenCodes98}.
6
7#include "foundations.h"
8
9using namespace std;
10
11
12
13namespace orbiter {
14namespace layer1_foundations {
15namespace coding_theory {
16
17
18typedef struct mindist MINDIST;
19
21
22
23struct mindist {
25 int f_v, f_vv, f_vvv;
26 int k, n, d, q;
27 int p, f;
28 int **G;
29 int ***S;
30 int M;
31 int K0;
32 int ZC;
33 int *Size;
34 int *ff_mult; // [q][q]
35 int *ff_add; // [q][q]
36 int *ff_inv; // [q]
41};
42//Local data structure for the mindist computation.
43//Contains tables for the finite field structure.
44
45
46static void print_matrix(MINDIST *MD, int **G);
47static int min_weight(MINDIST *MD);
48static void create_systematic_generator_matrices(MINDIST *MD);
49static int weight_of_linear_combinations(MINDIST *MD, int t);
50static int weight(int *v, int n, int idx_zero);
51static void padic(int ind, int *v, int L, int A);
52static int nextsub(int k, int l, int *sub);
53static void vmmult(MINDIST *MD, int *v, int **mx, int *cv);
54
55int coding_theory_domain::mindist(int n, int k, int q, int *G,
56 int verbose_level, int idx_zero, int idx_one,
57 int *add_table, int *mult_table)
58//Main routine for the code minimum distance computation.
59//The tables are only needed if $q = p^f$ with $f > 1$.
60//In the GF(p) case, just pass a NULL pointer.
61{
62 MINDIST MD;
63 int i, j, a, d;
64 int p, e;
65 //vector vp, ve;
66 int wt_rows, w;
68
69 NT.factor_prime_power(q, p, e);
70 MD.verbose_level = verbose_level;
71 MD.f_v = (verbose_level >= 1);
72 MD.f_vv = (verbose_level >= 2);
73 MD.f_vvv = FALSE;
74 MD.k = k;
75 MD.n = n;
76 MD.q = q;
77 MD.p = (int) p;
78 MD.f = (int) e;
79 MD.idx_zero = idx_zero;
80 MD.idx_one = idx_one;
81 MD.idx_mone = 0; // will be computed when we print the tables next
82 MD.ff_mult = (int *) malloc(sizeof(int) * q * q);
83 MD.ff_add = (int *) malloc(sizeof(int) * q * q);
84 MD.ff_inv = (int *) malloc(sizeof(int) * q);
86 if (MD.f > 1) {
87 if (MD.f_v) {
88 cout << "multiplication table:" << endl;
89 }
90 for (i = 0; i < q; i++) {
91 for (j = 0; j < q; j++) {
92 a = (int) mult_table[i * q + j];
93 MD.ff_mult[i * q + j] = a;
94 if (a == idx_one) {
95 MD.ff_inv[i] = j;
96 if (i == j)
97 MD.idx_mone = i;
98 }
99 if (MD.f_v) {
100 cout << a << " ";
101 }
102 }
103 if (MD.f_v) {
104 cout << endl;
105 }
106
107 }
108 if (MD.f_v) {
109 cout << "addition table:" << endl;
110 }
111 for (i = 0; i < q; i++) {
112 for (j = 0; j < q; j++) {
113 a = (int) add_table[i * q + j];
114 MD.ff_add[i * q + j] = a;
115 if (MD.f_v) {
116 cout << a << " ";
117 }
118 }
119 if (MD.f_v) {
120 cout << endl;
121 }
122 }
123 }
124 else {
125 if (MD.f_v) {
126 cout << "multiplication table:" << endl;
127 }
128 for (i = 0; i < q; i++) {
129 for (j = 0; j < q; j++) {
130 a = (i * j) % q;
131 MD.ff_mult[i * q + j] = a;
132 if (a == idx_one) {
133 MD.ff_inv[i] = j;
134 if (i == j)
135 MD.idx_mone = i;
136 }
137 if (MD.f_v) {
138 cout << a << " ";
139 }
140 }
141 if (MD.f_v) {
142 cout << endl;
143 }
144 }
145 if (MD.f_v) {
146 cout << "addition table:" << endl;
147 }
148 for (i = 0; i < q; i++) {
149 for (j = 0; j < q; j++) {
150 a = (i + j) % q;
151 MD.ff_add[i * q + j] = a;
152 if (MD.f_v) {
153 cout << a << " ";
154 }
155 }
156 if (MD.f_v) {
157 cout << endl;
158 }
159 }
160 }
161 if (MD.f_v) {
162 cout << "the field: GF(" << MD.q << ") = GF(" << MD.p << "^" << MD.f << ")" << endl;
163 cout << "idx_zero = " << MD.idx_zero << ", idx_one = " << MD.idx_one << ", idx_mone = " << MD.idx_mone << endl;
164 }
165
166 if (MD.idx_zero != 0) {
167 cout << "at the moment, we assume that idx_zero == 0" << endl;
168 exit(1);
169 }
170
171 MD.G = (int **) malloc((sizeof (int *))*(k+2));
172 for (i = 1; i <= k; i++) {
173 MD.G[i] = (int *)calloc(n+2,sizeof(int));
174 }
175 wt_rows = n;
176 for (i = 0; i < k; i++) {
177 w = 0;
178 for (j = 0; j < n; j++) {
179 if (G[i * n + j])
180 w++;
181 }
182 wt_rows = MINIMUM(wt_rows, w);
183 }
184
185 for (i = 1; i <= k; i++) {
186 for (j = 1; j <= n; j++) {
187 a = G[(i-1)*n + j - 1];
188 MD.G[i][j] = a;
189 }
190 }
191 if (MD.f_v) {
192 cout << "(" << n << "," << k << ") code over GF(" << q << "), generated by" << endl;
193 print_matrix(&MD, MD.G);
194 }
195
196 MD.ZC = 0;
197 d = min_weight(&MD);
198
199 if (MD.f_v) {
200 cout << "the minimum distance is " << d << endl;
201 cout << "This was determined by looking at "
203 << " codewords\n(rather than "
204 << NT.i_power_j(q, k) << " codewords)" << endl;
205 }
206
207 if (d != wt_rows) {
208 if (MD.f_v) {
209 cout << "min weight = " << d <<
210 " is less than the weight of the vectors in "
211 "the rows, which is " << wt_rows << endl;
212 print_matrix(&MD, MD.G);
213 }
214 //cin >> i;
215 }
216
217 for (i = 1; i <= k; i++) {
218 free(MD.G[i]);
219 }
220 free(MD.G);
221 free(MD.ff_mult);
222 free(MD.ff_add);
223 free(MD.ff_inv);
224 return d;
225}
226
227static void print_matrix(MINDIST *MD, int **G)
228{
229 int i, j, a;
230
231 for (i = 1; i <= MD->k; i++) {
232 for (j = 1; j <= MD->n; j++) {
233 a = G[i][j];
234 cout << a << " ";
235 }
236 cout << endl;
237 }
238
239}
240
241
242static int min_weight(MINDIST *MD)
243// Calculate the minimum-weight of the code which is created by the generator matrix G.
244// Main routine, loop with the two bounds (upper and lower)
245// for the minimum distance.
246{
247 int n = MD->n;
248 int k = MD->k;
249 int i, j, t;
250 int a, b;
251 int w_c, w_t, w_r; /* minimum-weight of code /regarded /not regarded codevectors */
252 int size = 0; /* number of information subsets */
253 //int w_1 = -1;
254
255 /* allocate base pointer for the (k,n)-generator matrices */
256 MD->S = (int ***)malloc((sizeof (int **))*(n+2));
257 create_systematic_generator_matrices(MD);
258
259 /* evaluate minimum weight of code created by generator matrix G */
260 for (i = 1; i <= MD->M; i++) {
261 size = size + MD->Size[i];
262 }
263 w_c = n;
264 w_r = 0;
265 t = 0;
266 while (w_c > w_r && t < k) {
267 t = t + 1;
268
269 /* evaluate minimumweight of codevectors created by linearcombination
270 of t rows of the systematic generator matrices S[1],...,S[M] */
271 w_t = weight_of_linear_combinations(MD, t);
272#if 0
273 if (t == 1) {
274 w_1 = w_t;
275 }
276#endif
277 if (MD->f_v) {
278 cout << "\\bar{d}_" << t << "=" << w_t << endl;
279 }
280 w_c = MINIMUM(w_c,w_t);
281 if (MD->f_v) {
282 cout << "mindist(C_{\\le " << t << "})=" << w_c << endl;
283 }
284
285 if (MD->f_v) {
286 cout << "\\bar{d}_" << t << "=";
287 }
288 w_r = 0;
289 for (i = 1; i <= MD->M; i++) {
290 a = k - MD->Size[i];
291 b = t + 1 - a;
292 if (b > 0) {
293 w_r += b;
294 }
295 if (MD->f_v) {
296 cout << " +" << t + 1 << "-(" << k << "-" << MD->Size[i] << ")" << endl;
297 }
298 }
299 if (MD->f_v) {
300 cout << "=" << w_r << endl;
301 }
302
303 } /* while */
304#if 0
305 if (w_c != w_1) {
306 cout << "min weight = " << w_c <<
307 " is less than the weight of the vectors in the rows, which is " << w_1 << endl;
308 print_matrix(MD, MD->G);
309 for (i = 1; i <= MD->M; i++) {
310 cout << i << "-th matrix:" << endl;
311 print_matrix(MD, MD->S[MD->M]);
312 }
313 }
314#endif
315
316 for (i = 1; i <= MD->M; i++){
317 for (j = 1; j <= k; j++) {
318 free(MD->S[i][j]);
319 }
320 free(MD->S[i]);
321 }
322 free(MD->S);
323 free(MD->Size);
324 return(w_c);
325}
326
327
328static void create_systematic_generator_matrices(MINDIST *MD)
329//create systematic generator matrices
330//$(S[1]=(I,*),...,S[z]=(*,I,*),...,S[M]=(*,I))$
331//with identity-matrix $I$ beginning at $P+1 = k*(z-1)+1$
332//(k = \# lines, m = \# systematic generator matrices),
333//by elementary row-transformations and permutations in
334//generator matrix G
335//allocates S[u] for $1 \le u \le M$,
336//allocates S[u][i] for $1 \le u \le M, 1 \le i \le k$,
337//allocates Size
338{
339 int n = MD->n;
340 int k = MD->k;
341 int q = MD->q;
342
343 int i, I = 0, j, J, l;
344 int P, h, h1, h2, h3, pivot, pivot_inv;
345 int K;
346 int M;
347
348 /* allocate memory for systematic generator matrix S[1] */
349 MD->S[1] = (int **)calloc(k+2,sizeof(int *));
350 for (i = 1; i <= k; i++) {
351 MD->S[1][i] = (int *)calloc(n+2,sizeof(int));
352 }
353
354 /* S[1] := G */
355 for (i = 1; i <= k; i++) {
356 for (j = 1; j <= n; j++) {
357 MD->S[1][i][j] = MD->G[i][j];
358 }
359 }
360 /* allocate memory for size of information subsets of S[1] */
361 MD->Size = (int *)calloc(n+1,sizeof(int ));
362
363 M = 1;
364 P = 0;
365 K = k;
366
367 while (1) {
368 if (MD->f_vvv) {
369 cout << "loop with M = " << M << ", P = " << P << " K = " << K << endl;
370 }
371
372 /* create identity matrix, columns P+1,...,P+k */
373
374 for (i = 1; i <= K; i++) {
375 if (MD->f_vvv) {
376 cout << "i = " << i << " ";
377 cout << " (M = " << M << ", P = " << P << " K = " << K << endl;
378 }
379 /* search for pivot:
380 if the entry is 0 at (i,P+i),
381 first check the entries below
382 (-> row-permutation necessary)
383 then check the columns behind
384 (-> column-permutation necessary) */
385 /* printf("i=%d, P=%d, S[M][i][P+i]=%d\n",i,P,S[M][i][P+i]); */
386 for (J = P + i; J <= n; J++) {
387 for (I = i; I <= K; I++) {
388 if (MD->S[M][I][J] != MD->idx_zero) {
389 break;
390 }
391 }
392 if (I <= K) {
393 /* pivot found ? */
394 break;
395 }
396 } // next J
397 if (MD->f_vvv) {
398 cout << "I=" << I << ", J=" << J << endl;
399 }
400
401 /* if pivot found but end of columns reached: */
402 if ((I <= K) && (J == n)) {
403 if (MD->f_vvv) {
404 cout << "end reached, I = " << I << endl;
405 }
406 if (P+K >= n) {
407 K = n - P;
408 }
409 }
410 if (MD->f_vvv) {
411 cout << "pivot in I=" << I << " J=" << J << endl;
412 }
413
414 /* if there doesn't exist a pivot: */
415 /* P(s,t)=0 for s>=i and t>=P+i */
416 if (I > K && J > n) {
417 K = i - 1;
418 if (MD->f_vvv) {
419 cout << "no pivot" << endl;
420 }
421 break;
422 }
423
424 /* if necessary: row-permutation i<->I */
425 if (I != i) {
426 if (MD->f_vvv) {
427 cout << endl << "swapping rows: " << i << "<->" << I << endl;
428 }
429 for (j = 1; j <= n; j++) {
430 h = MD->S[M][i][j];
431 MD->S[M][i][j] = MD->S[M][I][j];
432 MD->S[M][I][j] = h;
433 }
434 }
435
436 /* if necessary: column-permutation P+i<->J */
437 if (J != P + i) {
438 if (MD->f_vvv) {
439 cout << endl << "swapping columns: " << P + i << "<->" << J << endl;
440 }
441 for (j = 1; j <= k; j++) {
442 h = MD->S[M][j][i+P];
443 MD->S[M][j][i+P] = MD->S[M][j][J];
444 MD->S[M][j][J] = h;
445 }
446 }
447
448 pivot = MD->S[M][i][P + i];
449 if (pivot == MD->idx_zero) {
450 cout << "pivot is 0, exiting!" << endl;
451 }
452 pivot_inv = MD->ff_inv[pivot];
453 if (MD->f_vvv) {
454 cout << "pivot = " << pivot << ", pivot_inv = " << pivot_inv << endl;
455 }
456 /* replace pivot by 1 [multiply pivot-row by inv(pivot)] */
457 if (MD->S[M][i][P+i] != MD->idx_one) {
458 for (j = 1; j <= n; j++) {
459 MD->S[M][i][j] = MD->ff_mult[MD->S[M][i][j] * q + pivot_inv];
460 }
461 }
462 if (MD->f_vvv) {
463 cout << "pivot row normalized" << endl;
464 }
465
466 /* replace all elements of pivot-column, except pivot
467 element, by 0 by elementary row-trans. */
468 for (l = 1; l <= k; l++) {
469 if (l != i) {
470 if ((h = MD->S[M][l][i+P]) != MD->idx_zero)
471 for (j = 1; j <= n; j++) {
472 h1 = MD->ff_mult[MD->S[M][i][j] * q + h];
473 h2 = MD->ff_mult[h1 * q + MD->idx_mone];
474 h3 = MD->ff_add[MD->S[M][l][j] * q + h2];
475 MD->S[M][l][j] = h3;
476
477
478#if 0
479 MD->S[M][l][j] =
480 mod_p(MD->S[M][l][j] - MD->S[M][i][j] * h);
481#endif
482 } // next j
483 } /* if */
484 } /* l */
485 if (MD->f_vvv) {
486 cout << "pivot col normalized" << endl;
487 }
488
489 } /* i */
490
491 if (MD->f_v) {
492 cout << endl << "systematic generator matrix s[" << M << "]:" << endl;
493 print_matrix(MD, MD->S[M]);
494 //printf("K = %d\n",K);
495 }
496 MD->Size[M] = K;
497
498 if (K == 0) {
499 if (MD->f_v) {
500 cout << "K = 0, the generator matrix has " << n - P << " zero columns" << endl;
501 }
502 }
503 if (P + K >= n || K == 0) {
504 if (K == 0) {
505 MD->ZC = n - P; /* number of zero columns in G */
506 }
507 break;
508 }
509 else {
510 P = P + K;
511 }
512
513 M++;
514 /* find first column P+1 */
515 MD->S[M] = (int **)calloc(k+2,sizeof(int *));
516
517 for (i = 1; i <= k; i++) {
518 MD->S[M][i] = (int *)calloc(n+2,sizeof(int));
519 }
520
521 /* S[M] := S[M-1] */
522 for (i = 1; i <= k; i++) {
523 for (j = 1; j <= n; j++) {
524 MD->S[M][i][j] = MD->S[M-1][i][j];
525 }
526 }
527 } /* infinite loop */
528
529 if (MD->f_v) {
530 //printf("M = %d\n", M);
531 //printf("ZC = %d\n", MD->ZC);
532 cout << "size of information subsets:" << endl;
533 for (i = 1; i <= M; i++) {
534 cout << MD->Size[i] << " ";
535 }
536 cout << endl;
537 }
538 MD->M = M;
539
540}
541
542static int weight_of_linear_combinations(MINDIST *MD, int l)
543//evaluate minimum-weight of all codevectors that are constructed
544//by linearcombinations of $l$ rows of matrix $S[1],...,S[M]$
545//algorithm: create all possibilities for combining $l$ rows out of $k$
546//possible matrix rows by constructing the $l$-element-
547//subsets over $\{1,...,k\}$;
548//create all possibilities for filling an $l$-element-subset by
549//$\{1,...,p\}$ while construct the p-adic numbers of $0,...,lc$
550//($lc$ = number of possibilities for filling);
551//combining the two results gives all possibilities of linear-
552//combinations of $l$ matrix-rows;
553//out of this construct the codevectors and
554//calculate minimumweight
555//$l$ = \# rows taken for linearcombination
556// needed: weight(int *p, n, idx\_zero)
557{
558 int n = MD->n;
559 int k = MD->k;
560 int q = MD->q;
561 int M = MD->M;
562 int d1;
563 int d_l; /* minimum-weight by combining l rows */
564 int lc, dec, h;
565 int i, j, z, w;
566 int *v,*linc,*lcv;
567 int *sub;
568 number_theory::number_theory_domain NT;
569
570 /* for l = 1 the weight of a multiple of a generating codevector
571 is equal to the weight of that codevector (p = prim) */
572
573 /* allocate array for l-subset of a k-set */
574 sub = (int *)calloc(k+1,sizeof (int));
575
576 d_l = n;
577 if (l == 1) {
578 for (z = 1; z <= M; z++) {
579 for (i = 1; i <= k; i++) {
580
581 if (MD->Size[z] > 0) {
582 d1 = weight(MD->S[z][i], n, MD->idx_zero);
583 MD->weight_computations++;
584 if (d1 > 0) {
585 d_l = MINIMUM(d_l,d1);
586 if (MD->f_vv) {
587 cout << "matrix " << z << " row " << i << " is ";
588 for (j = 1; j <= n; j++) {
589 cout << MD->S[z][i][j] << " ";
590 }
591 cout << " of weight " << d1 << " minimum is " << d_l << endl;
592 }
593 }
594 } // if
595 } // next z
596 } // next i
597 free(sub);
598 return(d_l);
599 }
600
601 /* construct codevectors by linear combinations of l rows
602 of the generator matrices and calculate their weight */
603 else {
604 /* allocate memory for help-pointers */
605 v = (int*)calloc(l+2,sizeof(int));
606 linc = (int*)calloc(k+2,sizeof(int));
607 lcv = (int*)calloc(n+2,sizeof(int));
608
609 lc = NT.i_power_j(q-1, l);
610#if 0
611 lc = expo(p-1,l); /* number of possibilities to replace one
612 * subset with entries 1,...,p-1 */
613#endif
614
615 /* If the l-subset is b_1,...,b_l, then form the
616 linear combination of the rows b_i times v[i] */
617 for (dec = 1; dec <= lc; dec++) {
618 padic(dec, v, l, q-1);
619
620 /* for each systematic-generator-matrix S[z] */
621 for (z = 1; z <= M; z++) {
622 int K = MD->Size[z];
623 if (K < l) {
624 continue;
625 }
626
627 /* initialize with set: 1,2,...,l */
628 for (i = 1; i <= l; i++) {
629 sub[i] = i;
630 }
631 /* for (i=1;i<=l;i++)
632 printf("%d ",sub[i]); printf("\n"); */
633
634 do {
635 /* for (i=1;i<=l;i++) printf("%d ",sub[i]); */
636 h = 1;
637 for (j = 1; j <= K; j++) {
638 if (j == sub[h]) {
639 linc[j] = v[h-1]+1;
640 if (h != l) {
641 h = h+1;
642 }
643 } /* if */
644 else {
645 linc[j] = 0;
646 }
647 } /* j */
648 /* construct the corresponding codevector lcv */
649 vmmult(MD, linc, MD->S[z], lcv);
650
651 /* calculate its weight and store if minimal */
652 w = weight(lcv, n, MD->idx_zero);
653
654 MD->weight_computations++;
655
656 d_l = MINIMUM(d_l,w);
657 if (MD->f_vv) {
658 for (i = 1; i <= k; i++) {
659 cout << linc[i] << " ";
660 }
661 cout << " : ";
662 for (i = 1; i <= n; i++) {
663 cout << lcv[i] << " ";
664 }
665 cout << " of weight " << w << " minimum is " << d_l << endl;
666 }
667 } while (nextsub(K,l,sub)); /* next l-subset of K-set */
668 } /* z */
669 } /* dec */
670 free(v);
671 free(linc);
672 free(lcv);
673 free(sub);
674 return(d_l);
675 } /* else */
676
677}
678
679static int weight(int *v, int n, int idx_zero)
680/* calculate weight of vector v ( = number or non-zero elements of v ) */
681//Note that in any case, 0 stands for the zero element of the field
682//(either $GF(p)$ or $GF(q)$).
683{
684 int i, w=0;
685
686 for (i = 1; i <= n; i++) {
687 if (v[i] != idx_zero) {
688 w++;
689 }
690 }
691 return(w);
692}
693
694static void padic(int ind, int *v, int L, int A)
695/* convert ind of radix 10 to a L digit number of radix A, and store it at v */
696{
697 int i;
698 int z = ind;
699 for (i = 0; i < L; i++) {
700 v[i] = z % A;
701 z = (z - v[i]) / A;
702 }
703}
704
705
706static int nextsub(int k, int l, int *sub)
707/* return 0 if lexicographically largest subset reached, otherwise 1 */
708{
709 int a, i, j;
710
711 if (sub[l] != k) {
712 sub[l]++;
713 return 1;
714 }
715 else {
716 i = 1;
717 while (i < l && sub[l - i] == k-i) {
718 i++;
719 }
720 if (i < l) {
721 a = sub[l - i];
722 for (j = l - i; j <= l; j++) {
723 sub[j] = a + 1 + j - (l - i);
724 }
725 return 1;
726 }
727 else {
728 return 0;
729 }
730 }
731}
732
733
734
735static void vmmult(MINDIST *MD, int *v, int **mx, int *cv)
736/* multiply vector v with matrix mx and store it at cv */
737{
738 int k = MD->k;
739 int q = MD->q;
740 int i, j;
741 int h1, h2;
742
743 for (j = 1; j <= MD->n; j++) {
744 cv[j] = 0;
745 for (i = 1; i <= k; i++) {
746 h1 = MD->ff_mult[v[i] * q + mx[i][j]];
747 h2 = MD->ff_add[cv[j] * q + h1];
748 cv[j] = h2;
749 // cv[j] = mod_p(cv[j] + v[i] * mx[i][j]);
750 }
751 }
752}
753
754}}}
755
int mindist(int n, int k, int q, int *G, int f_verbose_level, int idx_zero, int idx_one, int *add_table, int *mult_table)
Definition: mindist.cpp:55
#define MINIMUM(x, y)
Definition: foundations.h:216
#define FALSE
Definition: foundations.h:234
the orbiter library for the classification of combinatorial objects
internal class for the algorithm to compute the minimum distance of a linear code
Definition: mindist.cpp:23