/* MOD2SPARSE.C - Procedures for handling sparse mod2 matrices. */ /* Copyright (c) 2000, 2001 by Radford M. Neal * * Permission is granted for anyone to copy, use, modify, or distribute this * program and accompanying programs and documents for any purpose, provided * this copyright notice is retained and prominently displayed, along with * a note saying that the original programs are available from Radford Neal's * web page, and note is made of any changes made to the programs. The * programs and documents are distributed without any warranty, express or * implied. As the programs were written for research purposes only, they have * not been tested to the degree that would be advisable in any important * application. All use of these programs is entirely at the user's own risk. */ /* NOTE: See mod2sparse.html for documentation on these procedures. */ #include #include #include #include "alloc.h" #include "intio.h" #include "mod2sparse.h" /* ALLOCATE AN ENTRY WITHIN A MATRIX. This local procedure is used to allocate a new entry, representing a non-zero element, within a given matrix. Entries in this matrix that were previously allocated and then freed are re-used. If there are no such entries, a new block of entries is allocated. */ static mod2entry *alloc_entry ( mod2sparse *m ) { mod2block *b; mod2entry *e; int k; if (m->next_free==0) { b = chk_alloc (1, sizeof *b); b->next = m->blocks; m->blocks = b; for (k = 0; kentry[k].left = m->next_free; m->next_free = &b->entry[k]; } } e = m->next_free; m->next_free = e->left; e->pr = 0; e->lr = 0; return e; } /* ALLOCATE SPACE FOR A SPARSE MOD2 MATRIX. */ mod2sparse *mod2sparse_allocate ( int n_rows, /* Number of rows in matrix */ int n_cols /* Number of columns in matrix */ ) { mod2sparse *m; mod2entry *e; int i, j; if (n_rows<=0 || n_cols<=0) { fprintf(stderr,"mod2sparse_allocate: Invalid number of rows or columns\n"); exit(1); } m = chk_alloc (1, sizeof *m); m->n_rows = n_rows; m->n_cols = n_cols; m->rows = chk_alloc (n_rows, sizeof *m->rows); m->cols = chk_alloc (n_cols, sizeof *m->cols); m->blocks = 0; m->next_free = 0; for (i = 0; irows[i]; e->left = e->right = e->up = e->down = e; e->row = e->col = -1; } for (j = 0; jcols[j]; e->left = e->right = e->up = e->down = e; e->row = e->col = -1; } return m; } /* FREE SPACE OCCUPIED BY A SPARSE MOD2 MATRIX. */ void mod2sparse_free ( mod2sparse *m /* Matrix to free */ ) { mod2block *b; free(m->rows); free(m->cols); while (m->blocks!=0) { b = m->blocks; m->blocks = b->next; free(b); } } /* CLEAR A SPARSE MATRIX TO ALL ZEROS. */ void mod2sparse_clear ( mod2sparse *r ) { mod2block *b; mod2entry *e; int i, j; for (i = 0; irows[i]; e->left = e->right = e->up = e->down = e; } for (j = 0; jcols[j]; e->left = e->right = e->up = e->down = e; } while (r->blocks!=0) { b = r->blocks; r->blocks = b->next; free(b); } } /* COPY A SPARSE MATRIX. */ void mod2sparse_copy ( mod2sparse *m, /* Matrix to copy */ mod2sparse *r /* Place to store copy of matrix */ ) { mod2entry *e, *f; int i; if (mod2sparse_rows(m)>mod2sparse_rows(r) || mod2sparse_cols(m)>mod2sparse_cols(r)) { fprintf(stderr,"mod2sparse_copy: Destination matrix is too small\n"); exit(1); } mod2sparse_clear(r); for (i = 0; irow,e->col); f->lr = e->lr; f->pr = e->pr; e = mod2sparse_next_in_row(e); } } } /* COPY ROWS OF A SPARSE MOD2 MATRIX. */ void mod2sparse_copyrows ( mod2sparse *m, /* Matrix to copy */ mod2sparse *r, /* Place to store copy of matrix */ int *rows /* Indexes of rows to copy, from 0 */ ) { mod2entry *e; int i; if (mod2sparse_cols(m)>mod2sparse_cols(r)) { fprintf(stderr, "mod2sparse_copyrows: Destination matrix has fewer columns than source\n"); exit(1); } mod2sparse_clear(r); for (i = 0; i=mod2sparse_rows(m)) { fprintf(stderr,"mod2sparse_copyrows: Row index out of range\n"); exit(1); } e = mod2sparse_first_in_row(m,rows[i]); while (!mod2sparse_at_end(e)) { mod2sparse_insert(r,i,e->col); e = mod2sparse_next_in_row(e); } } } /* COPY COLUMNS OF A SPARSE MOD2 MATRIX. */ void mod2sparse_copycols ( mod2sparse *m, /* Matrix to copy */ mod2sparse *r, /* Place to store copy of matrix */ int *cols /* Indexes of columns to copy, from 0 */ ) { mod2entry *e; int j; if (mod2sparse_rows(m)>mod2sparse_rows(r)) { fprintf(stderr, "mod2sparse_copycols: Destination matrix has fewer rows than source\n"); exit(1); } mod2sparse_clear(r); for (j = 0; j=mod2sparse_cols(m)) { fprintf(stderr,"mod2sparse_copycols: Column index out of range\n"); exit(1); } e = mod2sparse_first_in_col(m,cols[j]); while (!mod2sparse_at_end(e)) { mod2sparse_insert(r,e->row,j); e = mod2sparse_next_in_col(e); } } } /* PRINT A SPARSE MOD2 MATRIX IN HUMAN-READABLE FORM. */ void mod2sparse_print ( FILE *f, mod2sparse *m ) { int rdigits, cdigits; mod2entry *e; int i; rdigits = mod2sparse_rows(m)<=10 ? 1 : mod2sparse_rows(m)<=100 ? 2 : mod2sparse_rows(m)<=1000 ? 3 : mod2sparse_rows(m)<=10000 ? 4 : mod2sparse_rows(m)<=100000 ? 5 : 6; cdigits = mod2sparse_cols(m)<=10 ? 1 : mod2sparse_cols(m)<=100 ? 2 : mod2sparse_cols(m)<=1000 ? 3 : mod2sparse_cols(m)<=10000 ? 4 : mod2sparse_cols(m)<=100000 ? 5 : 6; for (i = 0; in_rows); if (ferror(f)) return 0; intio_write(f,m->n_cols); if (ferror(f)) return 0; for (i = 0; i=n_rows) break; } else { col = v-1; if (col>=n_cols) break; if (row==-1) break; mod2sparse_insert(m,row,col); } } /* Error if we get here. */ mod2sparse_free(m); return 0; } /* LOOK FOR AN ENTRY WITH GIVEN ROW AND COLUMN. */ mod2entry *mod2sparse_find ( mod2sparse *m, int row, int col ) { mod2entry *re, *ce; if (row<0 || row>=mod2sparse_rows(m) || col<0 || col>=mod2sparse_cols(m)) { fprintf(stderr,"mod2sparse_find: row or column index out of bounds\n"); exit(1); } /* Check last entries in row and column. */ re = mod2sparse_last_in_row(m,row); if (mod2sparse_at_end(re) || mod2sparse_col(re)col) { return 0; } if (mod2sparse_col(re)==col) { return re; } if (mod2sparse_at_end(ce) || mod2sparse_row(ce)>row) { return 0; } if (mod2sparse_row(ce)==row) { return ce; } re = mod2sparse_next_in_row(re); ce = mod2sparse_next_in_col(ce); } } /* INSERT AN ENTRY WITH GIVEN ROW AND COLUMN. */ mod2entry *mod2sparse_insert ( mod2sparse *m, int row, int col ) { mod2entry *re, *ce, *ne; if (row<0 || row>=mod2sparse_rows(m) || col<0 || col>=mod2sparse_cols(m)) { fprintf(stderr,"mod2sparse_insert: row or column index out of bounds\n"); exit(1); } /* Find old entry and return it, or allocate new entry and insert into row. */ re = mod2sparse_last_in_row(m,row); if (!mod2sparse_at_end(re) && mod2sparse_col(re)==col) { return re; } if (mod2sparse_at_end(re) || mod2sparse_col(re)right; } else { re = mod2sparse_first_in_row(m,row); for (;;) { if (!mod2sparse_at_end(re) && mod2sparse_col(re)==col) { return re; } if (mod2sparse_at_end(re) || mod2sparse_col(re)>col) { break; } re = mod2sparse_next_in_row(re); } } ne = alloc_entry(m); ne->row = row; ne->col = col; ne->left = re->left; ne->right = re; ne->left->right = ne; ne->right->left = ne; /* Insert new entry into column. If we find an existing entry here, the matrix must be garbled, since we didn't find it in the row. */ ce = mod2sparse_last_in_col(m,col); if (!mod2sparse_at_end(ce) && mod2sparse_row(ce)==row) { fprintf(stderr,"mod2sparse_insert: Garbled matrix\n"); exit(1); } if (mod2sparse_at_end(ce) || mod2sparse_row(ce)down; } else { ce = mod2sparse_first_in_col(m,col); for (;;) { if (!mod2sparse_at_end(ce) && mod2sparse_row(ce)==row) { fprintf(stderr,"mod2sparse_insert: Garbled matrix\n"); exit(1); } if (mod2sparse_at_end(ce) || mod2sparse_row(ce)>row) { break; } ce = mod2sparse_next_in_col(ce); } } ne->up = ce->up; ne->down = ce; ne->up->down = ne; ne->down->up = ne; /* Return the new entry. */ return ne; } /* DELETE AN ENTRY FROM A SPARSE MATRIX. */ void mod2sparse_delete ( mod2sparse *m, mod2entry *e ) { if (e==0) { fprintf(stderr,"mod2sparse_delete: Trying to delete a null entry\n"); exit(1); } if (e->row<0 || e->col<0) { fprintf(stderr,"mod2sparse_delete: Trying to delete a header entry\n"); exit(1); } e->left->right = e->right; e->right->left = e->left; e->up->down = e->down; e->down->up = e->up; e->left = m->next_free; m->next_free = e; } /* TEST WHETHER TWO SPARSE MATRICES ARE EQUAL. */ int mod2sparse_equal ( mod2sparse *m1, mod2sparse *m2 ) { mod2entry *e1, *e2; int i; if (mod2sparse_rows(m1)!=mod2sparse_rows(m2) || mod2sparse_cols(m1)!=mod2sparse_cols(m2)) { fprintf(stderr,"mod2sparse_equal: Matrices have different dimensions\n"); exit(1); } for (i = 0; i=mod2sparse_rows(m)) { fprintf(stderr,"mod2sparse_count_row: row index out of bounds\n"); exit(1); } count = 0; for (e = mod2sparse_first_in_row(m,row); !mod2sparse_at_end(e); e = mod2sparse_next_in_row(e)) { count += 1; } return count; } /* COUNT ENTRIES IN A COLUMN. */ int mod2sparse_count_col ( mod2sparse *m, int col ) { mod2entry *e; int count; if (col<0 || col>=mod2sparse_cols(m)) { fprintf(stderr,"mod2sparse_count_col: column index out of bounds\n"); exit(1); } count = 0; for (e = mod2sparse_first_in_col(m,col); !mod2sparse_at_end(e); e = mod2sparse_next_in_col(e)) { count += 1; } return count; } /* ADD TO A ROW. */ void mod2sparse_add_row ( mod2sparse *m1, /* Matrix containing row to add to */ int row1, /* Index in this matrix of row to add to */ mod2sparse *m2, /* Matrix containing row to add from */ int row2 /* Index in this matrix of row to add from */ ) { mod2entry *f1, *f2, *ft; if (mod2sparse_cols(m1)=mod2sparse_rows(m1) || row2<0 || row2>=mod2sparse_rows(m2)) { fprintf (stderr,"mod2sparse_add_row: row index out of range\n"); exit(1); } f1 = mod2sparse_first_in_row(m1,row1); f2 = mod2sparse_first_in_row(m2,row2); while (!mod2sparse_at_end(f1) && !mod2sparse_at_end(f2)) { if (mod2sparse_col(f1)>mod2sparse_col(f2)) { mod2sparse_insert(m1,row1,mod2sparse_col(f2)); f2 = mod2sparse_next_in_row(f2); } else { ft = mod2sparse_next_in_row(f1); if (mod2sparse_col(f1)==mod2sparse_col(f2)) { mod2sparse_delete(m1,f1); f2 = mod2sparse_next_in_row(f2); } f1 = ft; } } while (!mod2sparse_at_end(f2)) { mod2sparse_insert(m1,row1,mod2sparse_col(f2)); f2 = mod2sparse_next_in_row(f2); } } /* ADD TO A COLUMN. */ void mod2sparse_add_col ( mod2sparse *m1, /* Matrix containing column to add to */ int col1, /* Index in this matrix of column to add to */ mod2sparse *m2, /* Matrix containing column to add from */ int col2 /* Index in this matrix of column to add from */ ) { mod2entry *f1, *f2, *ft; if (mod2sparse_rows(m1)=mod2sparse_cols(m1) || col2<0 || col2>=mod2sparse_cols(m2)) { fprintf (stderr,"mod2sparse_add_col: Column index out of range\n"); exit(1); } f1 = mod2sparse_first_in_col(m1,col1); f2 = mod2sparse_first_in_col(m2,col2); while (!mod2sparse_at_end(f1) && !mod2sparse_at_end(f2)) { if (mod2sparse_row(f1)>mod2sparse_row(f2)) { mod2sparse_insert(m1,mod2sparse_row(f2),col1); f2 = mod2sparse_next_in_col(f2); } else { ft = mod2sparse_next_in_col(f1); if (mod2sparse_row(f1)==mod2sparse_row(f2)) { mod2sparse_delete(m1,f1); f2 = mod2sparse_next_in_col(f2); } f1 = ft; } } while (!mod2sparse_at_end(f2)) { mod2sparse_insert(m1,mod2sparse_row(f2),col1); f2 = mod2sparse_next_in_col(f2); } } /* FIND AN LU DECOMPOSITION OF A SPARSE MATRIX. */ int mod2sparse_decomp ( mod2sparse *A, /* Input matrix, M by N */ int K, /* Size of sub-matrix to find LU decomposition of */ mod2sparse *L, /* Matrix in which L is stored, M by K */ mod2sparse *U, /* Matrix in which U is stored, K by N */ int *rows, /* Array where row indexes are stored, M long */ int *cols, /* Array where column indexes are stored, N long */ mod2sparse_strategy strategy, /* Strategy to follow in picking rows/columns */ int abandon_number, /* Number of columns to abandon at some point */ int abandon_when /* When to abandon these columns */ ) { int *rinv, *cinv, *acnt, *rcnt; mod2sparse *B; int M, N; mod2entry *e, *f, *fn, *e2; int i, j, k, cc, cc2, cc3, cr2, pr; int found, nnf; M = mod2sparse_rows(A); N = mod2sparse_cols(A); if (mod2sparse_cols(L)!=K || mod2sparse_rows(L)!=M || mod2sparse_cols(U)!=N || mod2sparse_rows(U)!=K) { fprintf (stderr, "mod2sparse_decomp: Matrices have incompatible dimensions\n"); exit(1); } if (abandon_number>N-K) { fprintf(stderr,"Trying to abandon more columns than allowed\n"); exit(1); } rinv = chk_alloc (M, sizeof *rinv); cinv = chk_alloc (N, sizeof *cinv); if (abandon_number>0) { acnt = chk_alloc (M+1, sizeof *acnt); } if (strategy==Mod2sparse_minprod) { rcnt = chk_alloc (M, sizeof *rcnt); } mod2sparse_clear(L); mod2sparse_clear(U); /* Copy A to B. B will be modified, then discarded. */ B = mod2sparse_allocate(M,N); mod2sparse_copy(A,B); /* Count 1s in rows of B, if using minprod strategy. */ if (strategy==Mod2sparse_minprod) { for (i = 0; i=i) { found = 1; goto out_first; } e = mod2sparse_next_in_col(e); } } out_first: break; } case Mod2sparse_mincol: { found = 0; for (j = i; j=i) { found = 1; cc = cc2; e = e2; k = j; break; } e2 = mod2sparse_next_in_col(e2); } } } break; } case Mod2sparse_minprod: { found = 0; for (j = i; j=i) { cr2 = rcnt[mod2sparse_row(e2)]; if (!found || cc2==1 || (cc2-1)*(cr2-1)i) { mod2sparse_add_row(B,k,B,mod2sparse_row(e)); if (strategy==Mod2sparse_minprod) { rcnt[k] = mod2sparse_count_row(B,k); } mod2sparse_insert(L,k,i); } else if (rinv[k]0 && i==abandon_when) { for (k = 0; kk || cc3==k && cc>0) { if (cc3==k) cc -= 1; for (;;) { f = mod2sparse_first_in_col(B,j); if (mod2sparse_at_end(f)) break; mod2sparse_delete(B,f); } cc2 += 1; } } if (cc2!=abandon_number) abort(); if (strategy==Mod2sparse_minprod) { for (j = 0; j0) free(acnt); return nnf; } /* SOLVE A LOWER-TRIANGULAR SYSTEM BY FORWARD SUBSTITUTION. */ int mod2sparse_forward_sub ( mod2sparse *L, /* Matrix that is lower triangular after reordering */ int *rows, /* Array of indexes (from 0) of rows for new order */ char *x, /* Vector on right of equation, also reordered */ char *y /* Place to store solution */ ) { int K, i, j, ii, b, d; mod2entry *e; K = mod2sparse_cols(L); /* Make sure that L is lower-triangular, after row re-ordering. */ for (i = 0; ii) { fprintf(stderr, "mod2sparse_forward_sub: Matrix is not lower-triangular\n"); exit(1); } } /* Solve system by forward substitution. */ for (i = 0; ii) { fprintf(stderr, "mod2sparse_backward_sub: Matrix is not upper-triangular\n"); exit(1); } } /* Solve system by backward substitution. */ for (i = K-1; i>=0; i--) { ii = cols ? cols[i] : i; /* Look at bits in this row, forming inner product with partial solution, and seeing if the diagonal is 1. */ d = 0; b = 0; for (e = mod2sparse_first_in_row(U,i); !mod2sparse_at_end(e); e = mod2sparse_next_in_row(e)) { j = mod2sparse_col(e); if (j==ii) { d = 1; } else { b ^= z[j]; } } /* Check for no solution if the diagonal isn't 1. */ if (!d && b!=y[i]) { return 0; } /* Set bit of solution, zero if arbitrary. */ z[ii] = b^y[i]; } return 1; }