source: CIVL/docs/examples.md@ 6b26bbb

1.23 2.0 main
Last change on this file since 6b26bbb was 135e8cf, checked in by Youngjun Lee <youngjunlee7@…>, 3 weeks ago

Initial Markdown documents

  • Property mode set to 100644
File size: 9.3 KB
Line 
1## Sparse Matrix-Vector Multiplication
2
3In this example, we verify the functional correctness of a matrix-vector multiplication
4routine where the matrix is represented in a sparse (Compressed Sparse Row)
5representation.
6
7```c title="csr.h"
8#ifndef _CSR_H
9#define _CSR_H
10/* Compressed Sparse Row (CSR) sparse matrix representation. */
11
12struct csr_matrix {
13 double *val; // length NZ (number of non-0s)
14 int *col_ind; // length NZ
15 int *row_ptr; // length N+1. row_ptr[N]=NZ.
16 int rows, cols; // number of rows (N), number of columns
17};
18
19/* Multiplies matrix m and (dense) vector v, storing result in already
20 allocated vector p. If m has dimensions NxM then v has length M
21 and p has length N. */
22void csr_matrix_vector_multiply(struct csr_matrix *m, double *v, double *p);
23
24#endif
25```
26
27```c title="csr.c"
28/* Implementation of CSR matrix-vector multiplication from "LAProof: A
29 Library of Formal Proofs of Accuracy and Correctness for Linear
30 Algebra Programs" by Kellison, Appel, Tekriwal, and Bindel, IEEE
31 30th Symposium on Computer Arithmetic, 2023. That paper cites
32 Barrett et al., "Templates for the Solution of Linear Systems:
33 Building Blocks for Iterative Methods", SIAM, 1994, as the source
34 of the algorithm. */
35#include "csr.h"
36
37#define fma(x,y,s) (x*y+s)
38
39void csr_matrix_vector_multiply(struct csr_matrix *m, double *v, double *p) {
40 int i, rows=m->rows;
41 double *val = m->val;
42 int *col_ind = m->col_ind;
43 int *row_ptr = m->row_ptr;
44 int next=row_ptr[0];
45 for (i=0; i<rows; i++) {
46 double s=0.0;
47 int h=next;
48 next=row_ptr[i+1];
49 for (; h<next; h++) {
50 double x = val[h];
51 int j = col_ind[h];
52 double y = v[j];
53 s = fma(x,y,s);
54 }
55 p[i]=s;
56 }
57}
58```
59
60```civl title="csr_driver.cvl"
61/* Filename : csr_driver.cvl
62 Author : Stephen F. Siegel
63 Created : 2023-08-03
64 Modified : 2023-08-07
65
66 Verification of matrix-vector multiplication routine using
67 Compressed Sparse Row representation for the matrix. Start with an
68 arbitrary CSR matrix with N and M under specified bounds. Take an
69 arbitrary vector v of length M. Call sparse matrix-vector
70 multiplication routine to compute "actual" resulting vector.
71 Convert the sparse matrix to a dense matrix and compute dense
72 matrix-vector product to get "expected" result. Check the actual
73 and expected agree.
74*/
75#include <stdio.h>
76#include <stdlib.h>
77#include <pointer.cvh>
78#include "csr.h"
79
80typedef struct $mat {
81 int n, m; // num rows, columns;
82 double data[][];
83} $mat;
84
85void $mat_vec_mul($mat mat, double * v, double *p) {
86 int n = mat.n, m = mat.m;
87 for (int i=0; i<n; i++) {
88 double s = 0.0;
89 for (int j=0; j<m; j++) s += mat.data[i][j]*v[j];
90 p[i] = s;
91 }
92}
93
94$mat $mat_csr(struct csr_matrix csr) {
95 int n = csr.rows, m = csr.cols;
96 $mat mat;
97 mat.n = n;
98 mat.m = m;
99 mat.data = (double[n][m])$lambda(int i,j) 0.0;
100 for (int i=0; i<n; i++) {
101 int r = csr.row_ptr[i], rnxt = csr.row_ptr[i+1];
102 for (int k=r; k<rnxt; k++)
103 mat.data[i][csr.col_ind[k]] = csr.val[k];
104 }
105 return mat;
106}
107
108// N,M=number of rows,columns. N_B,M_B=upper bounds on N,M
109$input int N_B = 3, M_B = 3, N, M;
110$assume (1<=N && N<=N_B && 1<=M && M<=M_B);
111// the non-0 entries for the matrix and the (dense) vector
112$input double A[N*M], V[M];
113
114/* Fills in p[0],...,p[len-1] with a strictly increasing sequence
115 of integers in [0,max]. Precondition: 0 <= len <= max+1 */
116void strict_inc(int * p, int len, int max) {
117 //$assert(0<=len && len <= max+1);
118 for (int i=0; i<len; i++) {
119 int a = (i == 0 ? 0 : p[i-1]+1), b = max - len + i + 1;
120 p[i] = a + $choose_int(b-a+1); // choose in a..b
121 }
122}
123
124/* Nondeterministically create a CSR matrix with n rows and
125 m columns */
126struct csr_matrix make_csr(int n, int m) {
127 int * row_ptr = malloc((n+1)*sizeof(int));
128 row_ptr[0] = 0;
129 for (int i=1; i<=n; i++)
130 row_ptr[i] = row_ptr[i-1]+$choose_int(m+1);
131 int NZ = row_ptr[n];
132 $assert(0<=NZ && NZ<=n*m);
133 int * col_ind = malloc(NZ*sizeof(int));
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 double * val = malloc(NZ*sizeof(double));
137 for (int i=0; i<NZ; i++) val[i] = A[i];
138 return (struct csr_matrix){ val, col_ind, row_ptr, n, m };
139}
140
141void print_int_array(int * p, int n) {
142 printf("{ ");
143 for (int i=0; i<n; i++) printf("%d ", p[i]);
144 printf("}");
145}
146
147void print_dbl_array(double * p, int n) {
148 printf("{ ");
149 for (int i=0; i<n; i++) printf("%lf ", p[i]);
150 printf("}");
151}
152
153void print_csr(struct csr_matrix mat) {
154 int nz = mat.row_ptr[mat.rows];
155 printf("begin CSR[nrow=%d, ncol=%d, nz=%d]",
156 mat.rows, mat.cols, nz);
157 printf("\n row_ptr = ");
158 print_int_array(mat.row_ptr, mat.rows+1);
159 printf("\n col_ind = ");
160 print_int_array(mat.col_ind, nz);
161 printf("\n val = ");
162 print_dbl_array(mat.val, nz);
163 printf("\nend CSR\n");
164}
165
166void destroy_csr(struct csr_matrix mat) {
167 free(mat.row_ptr);
168 free(mat.col_ind);
169 free(mat.val);
170}
171
172int main(void) {
173 double v[M], actual[N], expected[N];
174 for (int i=0; i<M; i++) v[i] = V[i];
175 struct csr_matrix mat = make_csr(N, M);
176 csr_matrix_vector_multiply(&mat, v, actual);
177 $mat dense = $mat_csr(mat);
178 $mat_vec_mul(dense, v, expected);
179#ifdef VERBOSE
180 printf("\n");
181 print_csr(mat);
182 printf("v = ");
183 print_dbl_array(v, M);
184 printf("\nactual = ");
185 print_dbl_array(actual, N);
186 printf("\nexpected = ");
187 print_dbl_array(expected, N);
188 printf("\n");
189#endif
190 destroy_csr(mat);
191 $assert($equals(actual, expected));
192}
193```
194
195```make title="Makefile"
196small: csr.h csr.c csr_driver.cvl
197 civl verify -DVERBOSE csr.c csr_driver.cvl
198
199medium: csr.h csr.c csr_driver.cvl
200 civl verify -inputN_B=3 -inputM_B=4 csr.c csr_driver.cvl
201
202medium2: csr.h csr.c csr_driver.cvl
203 civl verify -inputN_B=4 -inputM_B=3 csr.c csr_driver.cvl
204
205large: csr.h csr.c csr_driver.cvl
206 civl verify -inputN_B=4 -inputM_B=4 csr.c csr_driver.cvl
207
208clean:
209 rm -rf CIVLREP *~ *.out *.tmp
210```
211
212Output from `make` (excerpted):
213```
214civl verify -DVERBOSE csr.c csr_driver.cvl
215CIVL v1.22+ of 2023-10-09 -- http://vsl.cis.udel.edu/civl
216
217begin CSR[nrow=3, ncol=3, nz=0]
218 row_ptr = { 0 0 0 0 }
219 col_ind = { }
220 val = { }
221end CSR
222v = { X_V[0] X_V[1] X_V[2] }
223actual = { 0 0 0 }
224expected = { 0 0 0 }
225
226begin CSR[nrow=3, ncol=3, nz=1]
227 row_ptr = { 0 0 0 1 }
228 col_ind = { 0 }
229 val = { X_A[0] }
230end CSR
231v = { X_V[0] X_V[1] X_V[2] }
232actual = { 0 0 X_V[0]*X_A[0] }
233expected = { 0 0 X_V[0]*X_A[0] }
234
235begin CSR[nrow=3, ncol=3, nz=1]
236 row_ptr = { 0 0 0 1 }
237 col_ind = { 1 }
238 val = { X_A[0] }
239end CSR
240v = { X_V[0] X_V[1] X_V[2] }
241actual = { 0 0 X_V[1]*X_A[0] }
242expected = { 0 0 X_V[1]*X_A[0] }
243
244begin CSR[nrow=3, ncol=3, nz=1]
245 row_ptr = { 0 0 0 1 }
246 col_ind = { 2 }
247 val = { X_A[0] }
248end CSR
249v = { X_V[0] X_V[1] X_V[2] }
250actual = { 0 0 X_V[2]*X_A[0] }
251expected = { 0 0 X_V[2]*X_A[0] }
252
253begin CSR[nrow=3, ncol=3, nz=2]
254 row_ptr = { 0 0 0 2 }
255 col_ind = { 0 1 }
256 val = { X_A[0] X_A[1] }
257end CSR
258v = { X_V[0] X_V[1] X_V[2] }
259actual = { 0 0 (X_V[1]*X_A[1])+(X_V[0]*X_A[0]) }
260expected = { 0 0 (X_V[1]*X_A[1])+(X_V[0]*X_A[0]) }
261
262begin CSR[nrow=3, ncol=3, nz=2]
263 row_ptr = { 0 0 0 2 }
264 col_ind = { 0 2 }
265 val = { X_A[0] X_A[1] }
266end CSR
267v = { X_V[0] X_V[1] X_V[2] }
268actual = { 0 0 (X_V[2]*X_A[1])+(X_V[0]*X_A[0]) }
269expected = { 0 0 (X_V[2]*X_A[1])+(X_V[0]*X_A[0]) }
270.
271.
272.
273begin CSR[nrow=3, ncol=2, nz=6]
274 row_ptr = { 0 2 4 6 }
275 col_ind = { 0 1 0 1 0 1 }
276 val = { X_A[0] X_A[1] X_A[2] X_A[3] X_A[4] X_A[5] }
277end CSR
278v = { X_V[0] X_V[1] }
279actual = { (X_V[1]*X_A[1])+(X_V[0]*X_A[0]) (X_V[1]*X_A[3])+(X_V[0]*X_A[2]) (X_V[1]*X_A[5])+(X_V[0]*X_A[4]) }
280expected = { (X_V[1]*X_A[1])+(X_V[0]*X_A[0]) (X_V[1]*X_A[3])+(X_V[0]*X_A[2]) (X_V[1]*X_A[5])+(X_V[0]*X_A[4]) }
281.
282.
283.
284begin CSR[nrow=1, ncol=1, nz=1]
285 row_ptr = { 0 1 }
286 col_ind = { 0 }
287 val = { X_A[0] }
288end CSR
289v = { X_V[0] }
290actual = { X_A[0]*X_V[0] }
291expected = { X_A[0]*X_V[0] }
292
293=== Source files ===
294csr.c (csr.c)
295csr.h (csr.h)
296csr_driver.cvl (csr_driver.cvl)
297
298=== Command ===
299civl verify -DVERBOSE csr.c csr_driver.cvl
300
301=== Stats ===
302 time (s) : 9.22 transitions : 215091
303 memory (bytes) : 6.62700032E8 trace steps : 175869
304 max process count : 1 valid calls : 661988
305 states : 175197 provers : cvc4, z3
306 states saved : 246990 prover calls : 19
307 state matches : 673
308
309=== Result ===
310All errors marked with '+' are absent on all executions.
311 + Dereference errors + Functional equivalence violations
312 + Internal errors + Library loading errors
313 + Other errors + Assertion violations
314 + Communication errors + Writes to constant variables
315 + Absolute deadlocks + Division by zero
316 + Writes to $input variables + Invalid casts
317 + Malloc errors + Memory leaks
318 + Memory management errors + MPI usage errors
319 + Out of bounds errors + Reads from $output variables
320 + Pointer errors + Process leaks
321 + Sequence errors - Non-termination
322 + Use of undefined values + Union errors
323```
324
325Times on a 2020 M1 MacBook Pro:
326
327| Matrices | Time |
328|-----------|-------|
329| Up to 3x3 | 6s |
330| Up to 3x4 | 17s |
331| Up to 4x3 | 17s |
332| Up to 4x4 | 19min |
Note: See TracBrowser for help on using the repository browser.