| [6d5b8a3] | 1 | #include<assert.h>
|
|---|
| 2 | #include<mem.cvh>
|
|---|
| 3 |
|
|---|
| 4 | #pragma CIVL ACSL
|
|---|
| 5 | /* Filename : vss_example.cvl
|
|---|
| 6 | Author : Stephen F. Siegel
|
|---|
| 7 | Created : 2023-08-03
|
|---|
| 8 | Modified : 2023-08-07
|
|---|
| 9 |
|
|---|
| 10 | Verification of matrix-vector multiplication routine using
|
|---|
| 11 | Compressed Sparse Row representation for the matrix. Start with an
|
|---|
| 12 | arbitrary CSR matrix with N and M under specified bounds. Take an
|
|---|
| 13 | arbitrary vector v of length M. Call sparse matrix-vector
|
|---|
| 14 | multiplication routine to compute "actual" resulting vector.
|
|---|
| 15 | Convert the sparse matrix to a dense matrix and compute dense
|
|---|
| 16 | matrix-vector product to get "expected" result. Check the actual
|
|---|
| 17 | and expected agree.
|
|---|
| 18 | */
|
|---|
| 19 | #include <stdio.h>
|
|---|
| 20 | #include <stdlib.h>
|
|---|
| 21 | #include <pointer.cvh>
|
|---|
| 22 |
|
|---|
| 23 | /* Compressed Sparse Row (CSR) sparse matrix representation. */
|
|---|
| 24 |
|
|---|
| 25 | struct csr_matrix {
|
|---|
| 26 | double *val; // length NZ (number of non-0s)
|
|---|
| 27 | int *col_ind; // length NZ
|
|---|
| 28 | int *row_ptr; // length N+1. row_ptr[N]=NZ.
|
|---|
| 29 | int rows, cols; // number of rows (N), number of columns
|
|---|
| 30 | };
|
|---|
| 31 |
|
|---|
| 32 | /* Multiplies matrix m and (dense) vector v, storing result in already
|
|---|
| 33 | allocated vector p. If m has dimensions NxM then v has length M
|
|---|
| 34 | and p has length N. */
|
|---|
| 35 | /* Implementation of CSR matrix-vector multiplication from "LAProof: A
|
|---|
| 36 | Library of Formal Proofs of Accuracy and Correctness for Linear
|
|---|
| 37 | Algebra Programs" by Kellison, Appel, Tekriwal, and Bindel, IEEE
|
|---|
| 38 | 30th Symposium on Computer Arithmetic, 2023. That paper cites
|
|---|
| 39 | Barrett et al., "Templates for the Solution of Linear Systems:
|
|---|
| 40 | Building Blocks for Iterative Methods", SIAM, 1994, as the source
|
|---|
| 41 | of the algorithm. */
|
|---|
| 42 | void csr_matrix_vector_multiply(struct csr_matrix *m, double *v, double *p) {
|
|---|
| 43 | int i, rows=m->rows;
|
|---|
| 44 | double *val = m->val;
|
|---|
| 45 | int *col_ind = m->col_ind;
|
|---|
| 46 | int *row_ptr = m->row_ptr;
|
|---|
| 47 | int next=row_ptr[0];
|
|---|
| 48 | /*@ loop invariant next == row_ptr[i];
|
|---|
| 49 | @ loop assigns p[0..rows-1], next;
|
|---|
| 50 | @ focus F | p[F];
|
|---|
| 51 | @*/
|
|---|
| 52 | for (i=0; i<rows; i++) {
|
|---|
| 53 | double s=0.0;
|
|---|
| 54 | int h=next;
|
|---|
| 55 | next=row_ptr[i+1];
|
|---|
| 56 | for (; h<next; h++) {
|
|---|
| 57 | double x = val[h];
|
|---|
| 58 | int j = col_ind[h];
|
|---|
| 59 | double y = v[j];
|
|---|
| 60 | s = x*y+s;
|
|---|
| 61 | }
|
|---|
| 62 | p[i]=s;
|
|---|
| 63 | }
|
|---|
| 64 | }
|
|---|
| 65 |
|
|---|
| 66 | typedef struct $mat {
|
|---|
| 67 | int n, m; // num rows, columns;
|
|---|
| 68 | double data[][];
|
|---|
| 69 | } $mat;
|
|---|
| 70 |
|
|---|
| 71 | void $mat_vec_mul($mat mat, double * v, double *p) {
|
|---|
| 72 | int n = mat.n, m = mat.m;
|
|---|
| 73 | /*@ loop assigns p[0..n-1];
|
|---|
| 74 | @ focus F | p[F];
|
|---|
| 75 | @*/
|
|---|
| 76 | for (int i=0; i<n; i++) {
|
|---|
| 77 | double s = 0.0;
|
|---|
| 78 | for (int j=0; j<m; j++) s += mat.data[i][j]*v[j];
|
|---|
| 79 | p[i] = s;
|
|---|
| 80 | }
|
|---|
| 81 | }
|
|---|
| 82 |
|
|---|
| 83 | $mat $mat_csr(struct csr_matrix csr) {
|
|---|
| 84 | int n = csr.rows, m = csr.cols;
|
|---|
| 85 | $mat mat;
|
|---|
| 86 | mat.n = n;
|
|---|
| 87 | mat.m = m;
|
|---|
| 88 | mat.data = (double[n][m])$lambda(int i,j) 0.0;
|
|---|
| 89 | /*@ loop assigns mat.data[0..n-1][0..m-1];
|
|---|
| 90 | @ focus F | mat.data[F][0..m-1];
|
|---|
| 91 | @*/
|
|---|
| 92 | for (int i=0; i<n; i++) {
|
|---|
| 93 | int r = csr.row_ptr[i], rnxt = csr.row_ptr[i+1];
|
|---|
| 94 | for (int k=r; k<rnxt; k++)
|
|---|
| 95 | mat.data[i][csr.col_ind[k]] = csr.val[k];
|
|---|
| 96 | }
|
|---|
| 97 | return mat;
|
|---|
| 98 | }
|
|---|
| 99 |
|
|---|
| 100 | // N,M=number of rows,columns. N_B,M_B=upper bounds on N,M
|
|---|
| 101 | $input int N_B, M_B = 3, N, M;
|
|---|
| 102 | $assume (1<=N && N<=N_B && 1<=M && M<=M_B);
|
|---|
| 103 | // the non-0 entries for the matrix and the (dense) vector
|
|---|
| 104 | $input double A[N*M], V[M];
|
|---|
| 105 |
|
|---|
| 106 | /* Fills in p[0],...,p[len-1] with a strictly increasing sequence
|
|---|
| 107 | of integers in [0,max]. Precondition: 0 <= len <= max+1 */
|
|---|
| 108 | void strict_inc(int * p, int len, int max) {
|
|---|
| 109 | for (int i=0; i<len; i++) {
|
|---|
| 110 | int a = (i == 0 ? 0 : p[i-1]+1), b = max - len + i + 1;
|
|---|
| 111 | p[i] = a + $choose_int(b-a+1); // choose in a..b
|
|---|
| 112 | }
|
|---|
| 113 | }
|
|---|
| 114 |
|
|---|
| 115 | /* Nondeterministically create a CSR matrix with n rows and
|
|---|
| 116 | m columns */
|
|---|
| 117 | struct csr_matrix make_csr(int n, int m) {
|
|---|
| 118 | int * row_ptr = malloc((n+1)*sizeof(int));
|
|---|
| 119 | row_ptr[0] = 0;
|
|---|
| 120 | /*@ loop invariant 0 <= row_ptr[i-1] && row_ptr[i-1] <= (i-1)*m;
|
|---|
| 121 | @ loop assigns row_ptr[1..n];
|
|---|
| 122 | @ focus F+{0..1} | row_ptr[F..F+1];
|
|---|
| 123 | @*/
|
|---|
| 124 | for (int i=1; i<=n; i++) {
|
|---|
| 125 | row_ptr[i] = row_ptr[i-1]+$choose_int(m+1);
|
|---|
| 126 | }
|
|---|
| 127 | //@ focus ordered(<=, F : 0..n | row_ptr[F]);
|
|---|
| 128 | int NZ = row_ptr[n];
|
|---|
| 129 | $assert(0<=NZ && NZ<=n*m);
|
|---|
| 130 | int * col_ind = malloc(NZ*sizeof(int));
|
|---|
| 131 | /*@ loop assigns col_ind[0..NZ-1];
|
|---|
| 132 | @ focus F | col_ind[row_ptr[F]..row_ptr[F+1]-1];
|
|---|
| 133 | @*/
|
|---|
| 134 | for (int i=0; i<n; i++) {
|
|---|
| 135 | strict_inc(col_ind+row_ptr[i], row_ptr[i+1]-row_ptr[i], m-1);
|
|---|
| 136 | }
|
|---|
| 137 | double * val = malloc(NZ*sizeof(double));
|
|---|
| 138 | /*@ loop assigns val[0..NZ-1];
|
|---|
| 139 | @ focus Z | val[Z];
|
|---|
| 140 | @*/
|
|---|
| 141 | for (int i=0; i<NZ; i++) val[i] = A[i];
|
|---|
| 142 | //@ focus Z;
|
|---|
| 143 | $assert($forall(int i:0..NZ-1) val[i] == A[i]);
|
|---|
| 144 | return (struct csr_matrix){ val, col_ind, row_ptr, n, m };
|
|---|
| 145 | }
|
|---|
| 146 |
|
|---|
| 147 | void print_int_array(int * p, int n) {
|
|---|
| 148 | printf("{ ");
|
|---|
| 149 | for (int i=0; i<n; i++) printf("%d ", p[i]);
|
|---|
| 150 | printf("}");
|
|---|
| 151 | }
|
|---|
| 152 |
|
|---|
| 153 | void print_dbl_array(double * p, int n) {
|
|---|
| 154 | printf("{ ");
|
|---|
| 155 | for (int i=0; i<n; i++) printf("%lf ", p[i]);
|
|---|
| 156 | printf("}");
|
|---|
| 157 | }
|
|---|
| 158 |
|
|---|
| 159 | void print_csr(struct csr_matrix mat) {
|
|---|
| 160 | int nz = mat.row_ptr[mat.rows];
|
|---|
| 161 | printf("begin CSR[nrow=%d, ncol=%d, nz=%d]",
|
|---|
| 162 | mat.rows, mat.cols, nz);
|
|---|
| 163 | printf("\n row_ptr = ");
|
|---|
| 164 | print_int_array(mat.row_ptr, mat.rows+1);
|
|---|
| 165 | printf("\n col_ind = ");
|
|---|
| 166 | print_int_array(mat.col_ind, nz);
|
|---|
| 167 | printf("\n val = ");
|
|---|
| 168 | print_dbl_array(mat.val, nz);
|
|---|
| 169 | printf("\nend CSR\n");
|
|---|
| 170 | }
|
|---|
| 171 |
|
|---|
| 172 | void destroy_csr(struct csr_matrix mat) {
|
|---|
| 173 | free(mat.row_ptr);
|
|---|
| 174 | free(mat.col_ind);
|
|---|
| 175 | free(mat.val);
|
|---|
| 176 | }
|
|---|
| 177 |
|
|---|
| 178 | int main(void) {
|
|---|
| 179 | double v[M], actual[N], expected[N];
|
|---|
| 180 | for (int i=0; i<M; i++) v[i] = V[i];
|
|---|
| 181 | struct csr_matrix mat = make_csr(N, M);
|
|---|
| 182 | csr_matrix_vector_multiply(&mat, v, actual);
|
|---|
| 183 | $mat dense = $mat_csr(mat);
|
|---|
| 184 | $mat_vec_mul(dense, v, expected);
|
|---|
| 185 | #ifdef VERBOSE
|
|---|
| 186 | printf("\n");
|
|---|
| 187 | print_csr(mat);
|
|---|
| 188 | printf("v = ");
|
|---|
| 189 | print_dbl_array(v, M);
|
|---|
| 190 | printf("\nactual = ");
|
|---|
| 191 | print_dbl_array(actual, N);
|
|---|
| 192 | printf("\nexpected = ");
|
|---|
| 193 | print_dbl_array(expected, N);
|
|---|
| 194 | printf("\n");
|
|---|
| 195 | #endif
|
|---|
| 196 | destroy_csr(mat);
|
|---|
| 197 |
|
|---|
| 198 | //@ focus F;
|
|---|
| 199 | $assert($forall(int i:0..N-1) actual[i] == expected[i]);
|
|---|
| 200 | }
|
|---|