ark_r1cs_std/poly/evaluations/univariate/
mod.rs1pub mod lagrange_interpolator;
2
3use crate::{
4 alloc::AllocVar,
5 eq::EqGadget,
6 fields::{fp::FpVar, FieldVar},
7 poly::{
8 domain::Radix2DomainVar,
9 evaluations::univariate::lagrange_interpolator::LagrangeInterpolator,
10 },
11 R1CSVar,
12};
13use ark_ff::{batch_inversion, PrimeField};
14use ark_relations::r1cs::SynthesisError;
15use ark_std::{
16 ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign},
17 vec::Vec,
18};
19
20#[derive(Clone)]
21pub struct EvaluationsVar<F: PrimeField> {
23 pub evals: Vec<FpVar<F>>,
25 pub lagrange_interpolator: Option<LagrangeInterpolator<F>>,
27 domain: Radix2DomainVar<F>,
28 subgroup_points: Option<Vec<F>>,
34}
35
36impl<F: PrimeField> EvaluationsVar<F> {
37 pub fn from_vec_and_domain(
41 evaluations: Vec<FpVar<F>>,
42 domain: Radix2DomainVar<F>,
43 interpolate: bool,
44 ) -> Self {
45 assert_eq!(
46 evaluations.len(),
47 1 << domain.dim,
48 "evaluations and domain has different dimensions"
49 );
50
51 let mut ev = Self {
52 evals: evaluations,
53 lagrange_interpolator: None,
54 domain,
55 subgroup_points: None,
56 };
57 if interpolate {
58 ev.generate_interpolation_cache();
59 }
60 ev
61 }
62
63 pub fn generate_interpolation_cache(&mut self) {
66 if self.domain.offset().is_constant() {
67 let poly_evaluations_val: Vec<_> = self
68 .evals
69 .iter()
70 .map(|v| v.value().unwrap_or_default())
71 .collect();
72 let domain = &self.domain;
73 let lagrange_interpolator = if let &FpVar::Constant(x) = domain.offset() {
74 LagrangeInterpolator::new(x, domain.gen, domain.dim, poly_evaluations_val)
75 } else {
76 panic!("Domain offset needs to be constant.")
77 };
78 self.lagrange_interpolator = Some(lagrange_interpolator)
79 } else {
80 let mut subgroup_points = Vec::with_capacity(self.domain.size() as usize);
83 subgroup_points.push(F::one());
84 for i in 1..self.domain.size() as usize {
85 subgroup_points.push(subgroup_points[i - 1] * self.domain.gen)
86 }
87 self.subgroup_points = Some(subgroup_points)
88 }
89 }
90
91 fn compute_lagrange_coefficients(
94 &self,
95 interpolation_point: &FpVar<F>,
96 ) -> Result<Vec<FpVar<F>>, SynthesisError> {
97 let cs = interpolation_point.cs();
99 let t = interpolation_point;
100 let lagrange_interpolator = self
101 .lagrange_interpolator
102 .as_ref()
103 .expect("lagrange interpolator has not been initialized. \
104 Call `self.generate_interpolation_cache` first or set `interpolate` to true in constructor. ");
105 let lagrange_coeffs =
106 lagrange_interpolator.compute_lagrange_coefficients(t.value().unwrap_or_default());
107 let mut lagrange_coeffs_fg = Vec::new();
108 let vp_t = lagrange_interpolator.domain_vp.evaluate_constraints(t)?;
112 for i in 0..lagrange_interpolator.domain_order {
114 let constant: F =
115 (-lagrange_interpolator.all_domain_elems[i]) * lagrange_interpolator.v_inv_elems[i];
116 let mut a_element: FpVar<F> =
117 t * &FpVar::constant(lagrange_interpolator.v_inv_elems[i]);
118 a_element += FpVar::constant(constant);
119
120 let lag_coeff: FpVar<F> =
121 FpVar::new_witness(ns!(cs, "generate lagrange coefficient"), || {
122 Ok(lagrange_coeffs[i])
123 })?;
124 if !cs.is_in_setup_mode() {
126 assert_eq!(
127 (lagrange_interpolator.v_inv_elems[i] * t.value().unwrap_or_default()
128 - lagrange_interpolator.v_inv_elems[i]
129 * lagrange_interpolator.all_domain_elems[i])
130 * lagrange_coeffs[i],
131 vp_t.value().unwrap_or_default()
132 );
133 }
134 a_element.mul_equals(&lag_coeff, &vp_t)?;
135 lagrange_coeffs_fg.push(lag_coeff);
136 }
137 Ok(lagrange_coeffs_fg)
138 }
139
140 pub fn interpolate_and_evaluate(
143 &self,
144 interpolation_point: &FpVar<F>,
145 ) -> Result<FpVar<F>, SynthesisError> {
146 if self.domain.offset().is_constant() {
149 self.lagrange_interpolate_with_constant_offset(interpolation_point)
150 } else {
151 self.lagrange_interpolate_with_non_constant_offset(interpolation_point)
154 }
155 }
156
157 fn lagrange_interpolate_with_constant_offset(
158 &self,
159 interpolation_point: &FpVar<F>,
160 ) -> Result<FpVar<F>, SynthesisError> {
161 let lagrange_interpolator = self
162 .lagrange_interpolator
163 .as_ref()
164 .expect("lagrange interpolator has not been initialized. ");
165 let lagrange_coeffs = self.compute_lagrange_coefficients(interpolation_point)?;
166
167 let interpolation = lagrange_coeffs
168 .iter()
169 .zip(&self.evals)
170 .take(lagrange_interpolator.domain_order)
171 .map(|(coeff, eval)| coeff * eval)
172 .sum::<FpVar<F>>();
173
174 Ok(interpolation)
175 }
176
177 fn lagrange_interpolate_with_non_constant_offset(
180 &self,
181 interpolation_point: &FpVar<F>, ) -> Result<FpVar<F>, SynthesisError> {
183 let subgroup_points = self.subgroup_points.as_ref()
185 .expect("lagrange interpolator has not been initialized. \
186 Call `self.generate_interpolation_cache` first or set `interpolate` to true in constructor. ");
187 let coset_offset_to_size = self
196 .domain
197 .offset()
198 .pow_by_constant(&[self.domain.size()])?; let alpha_to_size = interpolation_point.pow_by_constant(&[self.domain.size()])?;
200 let lhs_numerator = &alpha_to_size - &coset_offset_to_size;
201 lhs_numerator.enforce_not_equal(&FpVar::zero())?;
204
205 let lhs_denominator = &coset_offset_to_size * FpVar::constant(F::from(self.domain.size()));
208
209 let lhs = lhs_numerator.mul_by_inverse_unchecked(&lhs_denominator)?;
211
212 let alpha_coset_offset_inv =
216 interpolation_point.mul_by_inverse_unchecked(&self.domain.offset())?;
217
218 let domain_size = self.domain.size() as usize;
219
220 let evals = (0..domain_size)
222 .map(|i| {
223 let subgroup_point_inv = subgroup_points[(domain_size - i) % domain_size];
225 debug_assert_eq!(subgroup_points[i] * subgroup_point_inv, F::one());
226 let lag_denom = &alpha_coset_offset_inv * subgroup_point_inv - F::one();
228 let lag_coeff = lhs.mul_by_inverse_unchecked(&lag_denom)?;
239
240 Ok(&self.evals[i] * lag_coeff)
241 })
242 .collect::<Result<Vec<_>, _>>()?;
243
244 let res = evals.iter().sum();
245
246 Ok(res)
247 }
248}
249
250impl<'a, 'b, F: PrimeField> Add<&'a EvaluationsVar<F>> for &'b EvaluationsVar<F> {
251 type Output = EvaluationsVar<F>;
252
253 fn add(self, rhs: &'a EvaluationsVar<F>) -> Self::Output {
254 let mut result = self.clone();
255 result += rhs;
256 result
257 }
258}
259
260impl<'a, F: PrimeField> AddAssign<&'a EvaluationsVar<F>> for EvaluationsVar<F> {
261 fn add_assign(&mut self, other: &'a EvaluationsVar<F>) {
263 assert!(
265 self.domain.gen == other.domain.gen && self.domain.dim == other.domain.dim,
266 "domains are unequal"
267 );
268
269 self.lagrange_interpolator = None;
270 self.evals
271 .iter_mut()
272 .zip(&other.evals)
273 .for_each(|(a, b)| *a = &*a + b)
274 }
275}
276
277impl<'a, 'b, F: PrimeField> Sub<&'a EvaluationsVar<F>> for &'b EvaluationsVar<F> {
278 type Output = EvaluationsVar<F>;
279
280 fn sub(self, rhs: &'a EvaluationsVar<F>) -> Self::Output {
281 let mut result = self.clone();
282 result -= rhs;
283 result
284 }
285}
286
287impl<'a, F: PrimeField> SubAssign<&'a EvaluationsVar<F>> for EvaluationsVar<F> {
288 fn sub_assign(&mut self, other: &'a EvaluationsVar<F>) {
290 assert!(
292 self.domain.gen == other.domain.gen && self.domain.dim == other.domain.dim,
293 "domains are unequal"
294 );
295
296 self.lagrange_interpolator = None;
297 self.evals
298 .iter_mut()
299 .zip(&other.evals)
300 .for_each(|(a, b)| *a = &*a - b)
301 }
302}
303
304impl<'a, 'b, F: PrimeField> Mul<&'a EvaluationsVar<F>> for &'b EvaluationsVar<F> {
305 type Output = EvaluationsVar<F>;
306
307 fn mul(self, rhs: &'a EvaluationsVar<F>) -> Self::Output {
309 let mut result = self.clone();
310 result *= rhs;
311 result
312 }
313}
314
315impl<'a, F: PrimeField> MulAssign<&'a EvaluationsVar<F>> for EvaluationsVar<F> {
316 fn mul_assign(&mut self, other: &'a EvaluationsVar<F>) {
318 assert!(
320 self.domain.gen == other.domain.gen && self.domain.dim == other.domain.dim,
321 "domains are unequal"
322 );
323
324 self.lagrange_interpolator = None;
325 self.evals
326 .iter_mut()
327 .zip(&other.evals)
328 .for_each(|(a, b)| *a = &*a * b)
329 }
330}
331
332impl<'a, 'b, F: PrimeField> Div<&'a EvaluationsVar<F>> for &'b EvaluationsVar<F> {
333 type Output = EvaluationsVar<F>;
334
335 fn div(self, rhs: &'a EvaluationsVar<F>) -> Self::Output {
336 let mut result = self.clone();
337 result /= rhs;
338 result
339 }
340}
341
342impl<'a, F: PrimeField> DivAssign<&'a EvaluationsVar<F>> for EvaluationsVar<F> {
343 fn div_assign(&mut self, other: &'a EvaluationsVar<F>) {
345 assert!(
347 self.domain.gen == other.domain.gen && self.domain.dim == other.domain.dim,
348 "domains are unequal"
349 );
350 let cs = self.evals[0].cs();
351 let mut result_val: Vec<_> = other
353 .evals
354 .iter()
355 .map(|x| x.value().unwrap_or_default())
356 .collect();
357 batch_inversion(&mut result_val);
358 result_val
359 .iter_mut()
360 .zip(&self.evals)
361 .for_each(|(a, self_var)| *a *= self_var.value().unwrap_or_default());
362 let result_var: Vec<_> = result_val
363 .iter()
364 .map(|x| FpVar::new_witness(ns!(cs, "div result"), || Ok(*x)).unwrap())
365 .collect();
366 for i in 0..result_var.len() {
368 result_var[i]
369 .mul_equals(&other.evals[i], &self.evals[i])
370 .unwrap();
371 }
372
373 self.lagrange_interpolator = None;
374 self.evals = result_var
375 }
376}
377
378#[cfg(test)]
379mod tests {
380 use crate::{
381 alloc::AllocVar,
382 fields::{fp::FpVar, FieldVar},
383 poly::{domain::Radix2DomainVar, evaluations::univariate::EvaluationsVar},
384 R1CSVar,
385 };
386 use ark_ff::{FftField, Field, One, UniformRand};
387 use ark_poly::{polynomial::univariate::DensePolynomial, DenseUVPolynomial, Polynomial};
388 use ark_relations::r1cs::ConstraintSystem;
389 use ark_std::test_rng;
390 use ark_test_curves::bls12_381::Fr;
391
392 #[test]
393 fn test_interpolate_constant_offset() {
394 for n in [11, 12, 13, 14] {
395 let mut rng = test_rng();
396
397 let poly = DensePolynomial::rand((1 << n) - 1, &mut rng);
398 let gen = Fr::get_root_of_unity(1 << n).unwrap();
399 assert_eq!(gen.pow(&[1 << n]), Fr::one());
400 let domain = Radix2DomainVar::new(gen, n, FpVar::constant(Fr::rand(&mut rng))).unwrap();
401 let mut coset_point = domain.offset().value().unwrap();
402 let mut oracle_evals = Vec::new();
403 for _ in 0..(1 << n) {
404 oracle_evals.push(poly.evaluate(&coset_point));
405 coset_point *= gen;
406 }
407 let cs = ConstraintSystem::new_ref();
408 let evaluations_fp: Vec<_> = oracle_evals
409 .iter()
410 .map(|x| FpVar::new_input(ns!(cs, "evaluations"), || Ok(x)).unwrap())
411 .collect();
412 let evaluations_var = EvaluationsVar::from_vec_and_domain(evaluations_fp, domain, true);
413
414 let interpolate_point = Fr::rand(&mut rng);
415 let interpolate_point_fp =
416 FpVar::new_input(ns!(cs, "interpolate point"), || Ok(interpolate_point)).unwrap();
417
418 let expected = poly.evaluate(&interpolate_point);
419
420 let actual = evaluations_var
421 .interpolate_and_evaluate(&interpolate_point_fp)
422 .unwrap()
423 .value()
424 .unwrap();
425
426 assert_eq!(actual, expected);
427 assert!(cs.is_satisfied().unwrap());
428 println!("number of constraints: {}", cs.num_constraints());
429 }
430 }
431
432 #[test]
433 fn test_interpolate_non_constant_offset() {
434 for n in [11, 12, 13, 14] {
435 let mut rng = test_rng();
436 let poly = DensePolynomial::rand((1 << n) - 1, &mut rng);
437 let gen = Fr::get_root_of_unity(1 << n).unwrap();
438 assert_eq!(gen.pow(&[1 << n]), Fr::one());
439 let cs = ConstraintSystem::new_ref();
440 let domain = Radix2DomainVar::new(
441 gen,
442 n,
443 FpVar::new_witness(ns!(cs, "offset"), || Ok(Fr::rand(&mut rng))).unwrap(),
444 )
445 .unwrap();
446 let mut coset_point = domain.offset().value().unwrap();
447 let mut oracle_evals = Vec::new();
448 for _ in 0..(1 << n) {
449 oracle_evals.push(poly.evaluate(&coset_point));
450 coset_point *= gen;
451 }
452
453 let evaluations_fp: Vec<_> = oracle_evals
454 .iter()
455 .map(|x| FpVar::new_input(ns!(cs, "evaluations"), || Ok(x)).unwrap())
456 .collect();
457 let evaluations_var = EvaluationsVar::from_vec_and_domain(evaluations_fp, domain, true);
458
459 let interpolate_point = Fr::rand(&mut rng);
460 let interpolate_point_fp =
461 FpVar::new_input(ns!(cs, "interpolate point"), || Ok(interpolate_point)).unwrap();
462
463 let expected = poly.evaluate(&interpolate_point);
464
465 let actual = evaluations_var
466 .interpolate_and_evaluate(&interpolate_point_fp)
467 .unwrap()
468 .value()
469 .unwrap();
470
471 assert_eq!(actual, expected);
472 assert!(cs.is_satisfied().unwrap());
473 println!("number of constraints: {}", cs.num_constraints());
474 }
475 }
476
477 #[test]
478 fn test_division() {
479 let mut rng = test_rng();
480 let gen = Fr::get_root_of_unity(1 << 4).unwrap();
481 assert_eq!(gen.pow(&[1 << 4]), Fr::one());
482 let domain = Radix2DomainVar::new(
483 gen,
484 4, FpVar::constant(Fr::GENERATOR),
486 )
487 .unwrap();
488 let cs = ConstraintSystem::new_ref();
489
490 let ev_a = EvaluationsVar::from_vec_and_domain(
491 (0..16)
492 .map(|_| FpVar::new_input(ns!(cs, "poly_a"), || Ok(Fr::rand(&mut rng))).unwrap())
493 .collect(),
494 domain.clone(),
495 false,
496 );
497 let ev_b = EvaluationsVar::from_vec_and_domain(
498 (0..16)
499 .map(|_| FpVar::new_input(ns!(cs, "poly_a"), || Ok(Fr::rand(&mut rng))).unwrap())
500 .collect(),
501 domain.clone(),
502 false,
503 );
504
505 let a_div_b = (&ev_a) / (&ev_b);
506 assert!(cs.is_satisfied().unwrap());
507 let b_div_a = (&ev_b) / (&ev_a);
508
509 let one = &a_div_b * &b_div_a;
510 for ev in one.evals.iter() {
511 assert!(Fr::is_one(&ev.value().unwrap()))
512 }
513
514 assert!(cs.is_satisfied().unwrap());
515 }
516}