sonobe/folding-schemes/src/arith/r1cs.rs
folding-schemes/src/arith/r1cs.rsRaw
1use ark_ff::PrimeField;
2use ark_relations::r1cs::ConstraintSystem;
3use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
4use ark_std::rand::Rng;
5
6use super::ccs::CCS;
7use super::{Arith, ArithSerializer};
8use crate::utils::vec::{
9 hadamard, is_zero_vec, mat_vec_mul, vec_scalar_mul, vec_sub, SparseMatrix,
10};
11use crate::Error;
12
13#[derive(Debug, Clone, Eq, PartialEq, CanonicalSerialize, CanonicalDeserialize)]
14pub struct R1CS<F: PrimeField> {
15 pub l: usize, // io len
16 pub A: SparseMatrix<F>,
17 pub B: SparseMatrix<F>,
18 pub C: SparseMatrix<F>,
19}
20
21impl<F: PrimeField> R1CS<F> {
22 /// Evaluates the CCS relation at a given vector of variables `z`
23 pub fn eval_at_z(&self, z: &[F]) -> Result<Vec<F>, Error> {
24 if z.len() != self.A.n_cols {
25 return Err(Error::NotSameLength(
26 "z.len()".to_string(),
27 z.len(),
28 "number of variables in R1CS".to_string(),
29 self.A.n_cols,
30 ));
31 }
32
33 let Az = mat_vec_mul(&self.A, z)?;
34 let Bz = mat_vec_mul(&self.B, z)?;
35 let Cz = mat_vec_mul(&self.C, z)?;
36 // Multiply Cz by z[0] (u) here, allowing this method to be reused for
37 // both relaxed and plain R1CS.
38 let uCz = vec_scalar_mul(&Cz, &z[0]);
39 let AzBz = hadamard(&Az, &Bz)?;
40 vec_sub(&AzBz, &uCz)
41 }
42}
43
44impl<F: PrimeField, W: AsRef<[F]>, U: AsRef<[F]>> Arith<W, U> for R1CS<F> {
45 type Evaluation = Vec<F>;
46
47 fn eval_relation(&self, w: &W, u: &U) -> Result<Self::Evaluation, Error> {
48 self.eval_at_z(&[&[F::one()], u.as_ref(), w.as_ref()].concat())
49 }
50
51 fn check_evaluation(_w: &W, _u: &U, e: Self::Evaluation) -> Result<(), Error> {
52 is_zero_vec(&e).then_some(()).ok_or(Error::NotSatisfied)
53 }
54}
55
56impl<F: PrimeField> ArithSerializer for R1CS<F> {
57 fn params_to_le_bytes(&self) -> Vec<u8> {
58 [
59 self.l.to_le_bytes(),
60 self.A.n_rows.to_le_bytes(),
61 self.A.n_cols.to_le_bytes(),
62 ]
63 .concat()
64 }
65}
66
67impl<F: PrimeField> R1CS<F> {
68 pub fn empty() -> Self {
69 R1CS {
70 l: 0,
71 A: SparseMatrix::empty(),
72 B: SparseMatrix::empty(),
73 C: SparseMatrix::empty(),
74 }
75 }
76 pub fn rand<R: Rng>(rng: &mut R, n_rows: usize, n_cols: usize) -> Self {
77 Self {
78 l: 1,
79 A: SparseMatrix::rand(rng, n_rows, n_cols),
80 B: SparseMatrix::rand(rng, n_rows, n_cols),
81 C: SparseMatrix::rand(rng, n_rows, n_cols),
82 }
83 }
84
85 #[inline]
86 pub fn num_constraints(&self) -> usize {
87 self.A.n_rows
88 }
89
90 #[inline]
91 pub fn num_public_inputs(&self) -> usize {
92 self.l
93 }
94
95 #[inline]
96 pub fn num_variables(&self) -> usize {
97 self.A.n_cols
98 }
99
100 #[inline]
101 pub fn num_witnesses(&self) -> usize {
102 self.num_variables() - self.num_public_inputs() - 1
103 }
104
105 /// returns a tuple containing (w, x) (witness and public inputs respectively)
106 pub fn split_z(&self, z: &[F]) -> (Vec<F>, Vec<F>) {
107 (z[self.l + 1..].to_vec(), z[1..self.l + 1].to_vec())
108 }
109}
110
111impl<F: PrimeField> From<CCS<F>> for R1CS<F> {
112 fn from(ccs: CCS<F>) -> Self {
113 R1CS::<F> {
114 l: ccs.l,
115 A: ccs.M[0].clone(),
116 B: ccs.M[1].clone(),
117 C: ccs.M[2].clone(),
118 }
119 }
120}
121
122/// extracts arkworks ConstraintSystem matrices into crate::utils::vec::SparseMatrix format as R1CS
123/// struct.
124pub fn extract_r1cs<F: PrimeField>(cs: &ConstraintSystem<F>) -> R1CS<F> {
125 let m = cs.to_matrices().unwrap();
126
127 let n_rows = cs.num_constraints;
128 let n_cols = cs.num_instance_variables + cs.num_witness_variables; // cs.num_instance_variables already counts the 1
129
130 let A = SparseMatrix::<F> {
131 n_rows,
132 n_cols,
133 coeffs: m.a,
134 };
135 let B = SparseMatrix::<F> {
136 n_rows,
137 n_cols,
138 coeffs: m.b,
139 };
140 let C = SparseMatrix::<F> {
141 n_rows,
142 n_cols,
143 coeffs: m.c,
144 };
145
146 R1CS::<F> {
147 l: cs.num_instance_variables - 1, // -1 to subtract the first '1'
148 A,
149 B,
150 C,
151 }
152}
153
154/// extracts the witness and the public inputs from arkworks ConstraintSystem.
155pub fn extract_w_x<F: PrimeField>(cs: &ConstraintSystem<F>) -> (Vec<F>, Vec<F>) {
156 (
157 cs.witness_assignment.clone(),
158 // skip the first element which is '1'
159 cs.instance_assignment[1..].to_vec(),
160 )
161}
162
163#[cfg(test)]
164pub mod tests {
165 use super::*;
166 use crate::utils::vec::{
167 is_zero_vec,
168 tests::{to_F_matrix, to_F_vec},
169 };
170
171 use ark_pallas::Fr;
172
173 pub fn get_test_r1cs<F: PrimeField>() -> R1CS<F> {
174 // R1CS for: x^3 + x + 5 = y (example from article
175 // https://www.vitalik.ca/general/2016/12/10/qap.html )
176 let A = to_F_matrix::<F>(vec![
177 vec![0, 1, 0, 0, 0, 0],
178 vec![0, 0, 0, 1, 0, 0],
179 vec![0, 1, 0, 0, 1, 0],
180 vec![5, 0, 0, 0, 0, 1],
181 ]);
182 let B = to_F_matrix::<F>(vec![
183 vec![0, 1, 0, 0, 0, 0],
184 vec![0, 1, 0, 0, 0, 0],
185 vec![1, 0, 0, 0, 0, 0],
186 vec![1, 0, 0, 0, 0, 0],
187 ]);
188 let C = to_F_matrix::<F>(vec![
189 vec![0, 0, 0, 1, 0, 0],
190 vec![0, 0, 0, 0, 1, 0],
191 vec![0, 0, 0, 0, 0, 1],
192 vec![0, 0, 1, 0, 0, 0],
193 ]);
194
195 R1CS::<F> { l: 1, A, B, C }
196 }
197
198 pub fn get_test_z<F: PrimeField>(input: usize) -> Vec<F> {
199 // z = (1, io, w)
200 to_F_vec(vec![
201 1,
202 input, // io
203 input * input * input + input + 5, // x^3 + x + 5
204 input * input, // x^2
205 input * input * input, // x^2 * x
206 input * input * input + input, // x^3 + x
207 ])
208 }
209
210 pub fn get_test_z_split<F: PrimeField>(input: usize) -> (F, Vec<F>, Vec<F>) {
211 // z = (1, io, w)
212 (
213 F::one(),
214 to_F_vec(vec![
215 input, // io
216 ]),
217 to_F_vec(vec![
218 input * input * input + input + 5, // x^3 + x + 5
219 input * input, // x^2
220 input * input * input, // x^2 * x
221 input * input * input + input, // x^3 + x
222 ]),
223 )
224 }
225
226 #[test]
227 fn test_eval_r1cs_relation() {
228 let mut rng = ark_std::test_rng();
229 let r1cs = get_test_r1cs::<Fr>();
230 let (_, x, mut w) = get_test_z_split::<Fr>(rng.gen::<u16>() as usize);
231
232 let f_w = r1cs.eval_relation(&w, &x).unwrap();
233 assert!(is_zero_vec(&f_w));
234
235 w[1] = Fr::from(111);
236 let f_w = r1cs.eval_relation(&w, &x).unwrap();
237 assert!(!is_zero_vec(&f_w));
238 }
239
240 #[test]
241 fn test_check_r1cs_relation() {
242 let r1cs = get_test_r1cs::<Fr>();
243 let (_, x, w) = get_test_z_split(5);
244 r1cs.check_relation(&w, &x).unwrap();
245 }
246}