ark_groth16/
r1cs_to_qap.rs1use ark_ff::PrimeField;
2use ark_poly::EvaluationDomain;
3use ark_std::{cfg_iter_mut, vec};
4
5use crate::Vec;
6use ark_relations::gr1cs::{
7 ConstraintSystemRef, Matrix, Result as R1CSResult, SynthesisError, R1CS_PREDICATE_LABEL,
8};
9use core::ops::Deref;
10
11#[cfg(feature = "parallel")]
12use rayon::prelude::*;
13
14#[inline]
15pub fn evaluate_constraint<F: PrimeField>(terms: &[(F, usize)], assignment: &[F]) -> F {
32 #[cfg(feature = "parallel")]
33 if terms.len() < 100 {
34 serial_evaluate_constraint(terms, assignment)
35 } else {
36 terms
37 .par_iter()
38 .map(|(coeff, index)| {
39 let val = assignment[*index];
40 if coeff.is_one() {
41 val
42 } else {
43 val * coeff
44 }
45 })
46 .sum()
47 }
48 #[cfg(not(feature = "parallel"))]
49 serial_evaluate_constraint(terms, assignment)
50}
51
52fn serial_evaluate_constraint<F: PrimeField>(terms: &[(F, usize)], assignment: &[F]) -> F {
53 let mut sum = F::zero();
54 for chunk in terms.chunks(4) {
56 let chunk_sum = chunk
57 .iter()
58 .map(|(coeff, index)| {
59 let val = assignment[*index];
60 if coeff.is_one() {
61 val
62 } else {
63 val * coeff
64 }
65 })
66 .sum::<F>();
67 sum += chunk_sum;
68 }
69 sum
70}
71
72pub trait R1CSToQAP {
75 fn instance_map_with_evaluation<F: PrimeField, D: EvaluationDomain<F>>(
78 cs: ConstraintSystemRef<F>,
79 t: &F,
80 ) -> Result<(Vec<F>, Vec<F>, Vec<F>, F, usize, usize), SynthesisError>;
81
82 #[inline]
83 fn witness_map<F: PrimeField, D: EvaluationDomain<F>>(
86 prover: ConstraintSystemRef<F>,
87 ) -> Result<Vec<F>, SynthesisError> {
88 let matrices = &prover.to_matrices().unwrap()[R1CS_PREDICATE_LABEL];
89 let num_inputs = prover.num_instance_variables();
90 let num_constraints = prover.num_constraints();
91
92 let cs = prover.borrow().unwrap();
93 let prover = cs.deref();
94
95 let full_assignment = [
96 prover.instance_assignment().unwrap(),
97 prover.witness_assignment().unwrap(),
98 ]
99 .concat();
100
101 Self::witness_map_from_matrices::<F, D>(
102 &matrices,
103 num_inputs,
104 num_constraints,
105 &full_assignment,
106 )
107 }
108
109 fn witness_map_from_matrices<F: PrimeField, D: EvaluationDomain<F>>(
112 matrices: &[Matrix<F>],
113 num_inputs: usize,
114 num_constraints: usize,
115 full_assignment: &[F],
116 ) -> R1CSResult<Vec<F>>;
117
118 fn h_query_scalars<F: PrimeField, D: EvaluationDomain<F>>(
121 max_power: usize,
122 t: F,
123 zt: F,
124 delta_inverse: F,
125 ) -> Result<Vec<F>, SynthesisError>;
126}
127
128pub struct LibsnarkReduction;
130
131impl R1CSToQAP for LibsnarkReduction {
132 #[inline]
133 #[allow(clippy::type_complexity)]
134 fn instance_map_with_evaluation<F: PrimeField, D: EvaluationDomain<F>>(
135 cs: ConstraintSystemRef<F>,
136 t: &F,
137 ) -> R1CSResult<(Vec<F>, Vec<F>, Vec<F>, F, usize, usize)> {
138 let matrices = &cs.to_matrices().unwrap()[R1CS_PREDICATE_LABEL];
139 let domain_size = cs.num_constraints() + cs.num_instance_variables();
140 let domain = D::new(domain_size).ok_or(SynthesisError::PolynomialDegreeTooLarge)?;
141 let domain_size = domain.size();
142
143 let zt = domain.evaluate_vanishing_polynomial(*t);
144
145 let coefficients_time = start_timer!(|| "Evaluate Lagrange coefficients");
147 let u = domain.evaluate_all_lagrange_coefficients(*t);
148 end_timer!(coefficients_time);
149
150 let qap_num_variables = (cs.num_instance_variables() - 1) + cs.num_witness_variables();
151
152 let mut a = vec![F::zero(); qap_num_variables + 1];
153 let mut b = vec![F::zero(); qap_num_variables + 1];
154 let mut c = vec![F::zero(); qap_num_variables + 1];
155
156 {
157 let start = 0;
158 let end = cs.num_instance_variables();
159 let num_constraints = cs.num_constraints();
160 a[start..end].copy_from_slice(&u[(start + num_constraints)..(end + num_constraints)]);
161 }
162
163 for (i, u_i) in u.iter().enumerate().take(cs.num_constraints()) {
164 for &(ref coeff, index) in &matrices[0][i] {
165 a[index] += &(*u_i * coeff);
166 }
167 for &(ref coeff, index) in &matrices[1][i] {
168 b[index] += &(*u_i * coeff);
169 }
170 for &(ref coeff, index) in &matrices[2][i] {
171 c[index] += &(*u_i * coeff);
172 }
173 }
174
175 Ok((a, b, c, zt, qap_num_variables, domain_size))
176 }
177
178 fn witness_map_from_matrices<F: PrimeField, D: EvaluationDomain<F>>(
179 matrices: &[Matrix<F>],
180 num_inputs: usize,
181 num_constraints: usize,
182 full_assignment: &[F],
183 ) -> R1CSResult<Vec<F>> {
184 let domain =
185 D::new(num_constraints + num_inputs).ok_or(SynthesisError::PolynomialDegreeTooLarge)?;
186 let domain_size = domain.size();
187 let zero = F::zero();
188
189 let mut a = vec![zero; domain_size];
190 let mut b = vec![zero; domain_size];
191
192 cfg_iter_mut!(a[..num_constraints])
193 .zip(&mut b[..num_constraints])
194 .zip(&matrices[0])
195 .zip(&matrices[1])
196 .for_each(|(((a, b), at_i), bt_i)| {
197 *a = evaluate_constraint(&at_i, &full_assignment);
198 *b = evaluate_constraint(&bt_i, &full_assignment);
199 });
200
201 {
202 let start = num_constraints;
203 let end = start + num_inputs;
204 a[start..end].clone_from_slice(&full_assignment[..num_inputs]);
205 }
206
207 domain.ifft_in_place(&mut a);
208 domain.ifft_in_place(&mut b);
209
210 let coset_domain = domain.get_coset(F::GENERATOR).unwrap();
211
212 coset_domain.fft_in_place(&mut a);
213 coset_domain.fft_in_place(&mut b);
214
215 let mut ab = domain.mul_polynomials_in_evaluation_domain(&a, &b);
216 drop(a);
217 drop(b);
218
219 let mut c = vec![zero; domain_size];
220 cfg_iter_mut!(c[..num_constraints])
221 .enumerate()
222 .for_each(|(i, c)| {
223 *c = evaluate_constraint(&matrices[2][i], &full_assignment);
224 });
225
226 domain.ifft_in_place(&mut c);
227 coset_domain.fft_in_place(&mut c);
228
229 let vanishing_polynomial_over_coset = domain
230 .evaluate_vanishing_polynomial(F::GENERATOR)
231 .inverse()
232 .unwrap();
233 cfg_iter_mut!(ab).zip(c).for_each(|(ab_i, c_i)| {
234 *ab_i -= &c_i;
235 *ab_i *= &vanishing_polynomial_over_coset;
236 });
237
238 coset_domain.ifft_in_place(&mut ab);
239
240 Ok(ab)
241 }
242
243 fn h_query_scalars<F: PrimeField, D: EvaluationDomain<F>>(
244 max_power: usize,
245 t: F,
246 zt: F,
247 delta_inverse: F,
248 ) -> Result<Vec<F>, SynthesisError> {
249 let scalars = cfg_into_iter!(0..max_power)
250 .map(|i| zt * &delta_inverse * &t.pow([i as u64]))
251 .collect::<Vec<_>>();
252 Ok(scalars)
253 }
254}