nova_snark/spartan/polys/
univariate.rs1use crate::{
5 errors::NovaError,
6 traits::{
7 evm_serde::{CustomSerdeTrait, EvmCompatSerde},
8 AbsorbInRO2Trait, Engine, Group, ROTrait, TranscriptReprTrait,
9 },
10};
11use ff::PrimeField;
12use rayon::prelude::{IntoParallelIterator, ParallelIterator};
13use serde::{Deserialize, Serialize};
14use serde_with::serde_as;
15
16#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
20pub struct UniPoly<Scalar: PrimeField> {
21 pub coeffs: Vec<Scalar>,
23}
24
25#[serde_as]
29#[derive(Clone, Debug, Serialize, Deserialize)]
30pub struct CompressedUniPoly<Scalar: PrimeField + CustomSerdeTrait> {
31 #[serde_as(as = "Vec<EvmCompatSerde>")]
32 coeffs_except_linear_term: Vec<Scalar>,
33}
34
35impl<Scalar: PrimeField + CustomSerdeTrait> UniPoly<Scalar> {
36 pub fn from_coeffs(mut coeffs: Vec<Scalar>) -> Result<Self, NovaError> {
46 if coeffs.is_empty() {
47 return Err(NovaError::InvalidInputLength);
48 }
49 while coeffs.len() > 1 && coeffs.last().is_some_and(|c| bool::from(c.is_zero())) {
51 coeffs.pop();
52 }
53 Ok(Self { coeffs })
54 }
55
56 pub fn from_evals(evals: &[Scalar]) -> Self {
59 let n = evals.len();
60
61 if n == 1 {
63 return Self {
64 coeffs: vec![evals[0]],
65 };
66 }
67
68 let xs: Vec<Scalar> = (0..n).map(|x| Scalar::from(x as u64)).collect();
69
70 let mut matrix: Vec<Vec<Scalar>> = Vec::with_capacity(n);
71 for i in 0..n {
72 let mut row = Vec::with_capacity(n);
73 let x = xs[i];
74 row.push(Scalar::ONE);
75 row.push(x);
76 for j in 2..n {
77 row.push(row[j - 1] * x);
78 }
79 row.push(evals[i]);
80 matrix.push(row);
81 }
82
83 let coeffs = gaussian_elimination(&mut matrix);
84 Self { coeffs }
85 }
86
87 pub fn from_evals_deg2(evals: &[Scalar]) -> Self {
90 let c = evals[0];
91 let a = evals[2];
92 let a_b_c = evals[1];
93 let b = a_b_c - a - c;
94 Self {
95 coeffs: vec![c, b, a],
96 }
97 }
98
99 pub fn from_evals_deg3(evals: &[Scalar]) -> Self {
102 let d = evals[0];
103 let a = evals[2];
104 let a_b_c_d = evals[1];
105 let b2_d2 = a_b_c_d + evals[3];
106 let b = b2_d2 * Scalar::TWO_INV - d;
107 let c = a_b_c_d - a - d - b;
108 Self {
109 coeffs: vec![d, c, b, a],
110 }
111 }
112
113 pub fn coeffs(&self) -> &[Scalar] {
115 &self.coeffs
116 }
117
118 pub fn degree(&self) -> usize {
120 self.coeffs.len() - 1
121 }
122
123 pub fn eval_at_zero(&self) -> Scalar {
125 self.coeffs[0]
126 }
127
128 pub fn eval_at_one(&self) -> Scalar {
130 (0..self.coeffs.len())
131 .into_par_iter()
132 .map(|i| self.coeffs[i])
133 .sum()
134 }
135
136 pub fn evaluate(&self, r: &Scalar) -> Scalar {
138 let mut eval = self.coeffs[0];
139 let mut power = *r;
140 for coeff in self.coeffs.iter().skip(1) {
141 eval += power * coeff;
142 power *= r;
143 }
144 eval
145 }
146
147 pub fn compress(&self) -> CompressedUniPoly<Scalar> {
149 let coeffs_except_linear_term = [&self.coeffs[0..1], &self.coeffs[2..]].concat();
150 assert_eq!(coeffs_except_linear_term.len() + 1, self.coeffs.len());
151 CompressedUniPoly {
152 coeffs_except_linear_term,
153 }
154 }
155}
156
157impl<Scalar: PrimeField + CustomSerdeTrait> CompressedUniPoly<Scalar> {
158 pub fn decompress(&self, hint: &Scalar) -> UniPoly<Scalar> {
162 let mut linear_term =
163 *hint - self.coeffs_except_linear_term[0] - self.coeffs_except_linear_term[0];
164 for i in 1..self.coeffs_except_linear_term.len() {
165 linear_term -= self.coeffs_except_linear_term[i];
166 }
167
168 let mut coeffs: Vec<Scalar> = Vec::new();
169 coeffs.push(self.coeffs_except_linear_term[0]);
170 coeffs.push(linear_term);
171 coeffs.extend(&self.coeffs_except_linear_term[1..]);
172 assert_eq!(self.coeffs_except_linear_term.len() + 1, coeffs.len());
173 UniPoly { coeffs }
174 }
175}
176
177impl<G: Group> TranscriptReprTrait<G> for UniPoly<G::Scalar>
178where
179 G::Scalar: CustomSerdeTrait,
180{
181 fn to_transcript_bytes(&self) -> Vec<u8> {
182 let coeffs = self.compress().coeffs_except_linear_term;
183 #[cfg(not(feature = "evm"))]
184 {
185 coeffs
186 .iter()
187 .flat_map(|&t| t.to_repr().as_ref().to_vec())
188 .collect::<Vec<u8>>()
189 }
190 #[cfg(feature = "evm")]
191 {
192 coeffs
193 .iter()
194 .flat_map(|&t| {
195 t.to_repr()
196 .as_ref()
197 .iter()
198 .rev()
199 .cloned()
200 .collect::<Vec<u8>>()
201 })
202 .collect::<Vec<u8>>()
203 }
204 }
205}
206
207impl<E: Engine> AbsorbInRO2Trait<E> for UniPoly<E::Scalar> {
208 fn absorb_in_ro2(&self, ro: &mut E::RO2) {
209 for coeff in &self.coeffs {
210 ro.absorb(*coeff);
211 }
212 }
213}
214
215pub fn gaussian_elimination<F: PrimeField>(matrix: &mut [Vec<F>]) -> Vec<F> {
219 let size = matrix.len();
220 assert_eq!(size, matrix[0].len() - 1);
221
222 for i in 0..size - 1 {
223 for j in i..size - 1 {
224 echelon(matrix, i, j);
225 }
226 }
227
228 for i in (1..size).rev() {
229 eliminate(matrix, i);
230 }
231
232 let mut result: Vec<F> = vec![F::ZERO; size];
233 for i in 0..size {
234 result[i] = div_f(matrix[i][size], matrix[i][i]);
235 }
236
237 result
238}
239
240fn echelon<F: PrimeField>(matrix: &mut [Vec<F>], i: usize, j: usize) {
241 let size = matrix.len();
242 if matrix[i][i] != F::ZERO {
243 let factor = div_f(matrix[j + 1][i], matrix[i][i]);
244 (i..size + 1).for_each(|k| {
245 let tmp = matrix[i][k];
246 matrix[j + 1][k] -= factor * tmp;
247 });
248 }
249}
250
251fn eliminate<F: PrimeField>(matrix: &mut [Vec<F>], i: usize) {
252 let size = matrix.len();
253 if matrix[i][i] != F::ZERO {
254 for j in (1..i + 1).rev() {
255 let factor = div_f(matrix[j - 1][i], matrix[i][i]);
256 for k in (0..size + 1).rev() {
257 let tmp = matrix[i][k];
258 matrix[j - 1][k] -= factor * tmp;
259 }
260 }
261 }
262}
263
264pub fn div_f<F: PrimeField>(a: F, b: F) -> F {
270 let inverse_b = b.invert();
271
272 if inverse_b.into_option().is_none() {
273 panic!("Division by zero");
274 }
275
276 a * inverse_b.unwrap()
277}
278
279#[cfg(test)]
280mod tests {
281 use super::*;
282 use crate::provider::{bn256_grumpkin::bn256, pasta::pallas, secp_secq::secp256k1};
283
284 fn test_from_evals_quad_with<F: PrimeField + CustomSerdeTrait>() {
285 let e0 = F::ONE;
287 let e1 = F::from(6);
288 let e2 = F::from(2);
289 let evals = vec![e0, e1, e2];
290 let poly = UniPoly::from_evals_deg2(&evals);
291
292 assert_eq!(poly.eval_at_zero(), e0);
293 assert_eq!(poly.eval_at_one(), e1);
294 assert_eq!(poly.coeffs.len(), 3);
295 assert_eq!(poly.coeffs[0], F::ONE);
296 assert_eq!(poly.coeffs[1], F::from(3));
297 assert_eq!(poly.coeffs[2], F::from(2));
298
299 let hint = e0 + e1;
300 let compressed_poly = poly.compress();
301 let decompressed_poly = compressed_poly.decompress(&hint);
302 for i in 0..decompressed_poly.coeffs.len() {
303 assert_eq!(decompressed_poly.coeffs[i], poly.coeffs[i]);
304 }
305
306 let e3 = F::from(28);
307 assert_eq!(poly.evaluate(&F::from(3)), e3);
308 }
309
310 #[test]
311 fn test_from_evals_quad() {
312 test_from_evals_quad_with::<pallas::Scalar>();
313 test_from_evals_quad_with::<bn256::Scalar>();
314 test_from_evals_quad_with::<secp256k1::Scalar>();
315 }
316
317 fn test_from_evals_cubic_with<F: PrimeField + CustomSerdeTrait>() {
318 let e0 = F::ONE; let e1 = F::from(7); let e2 = F::ONE; let e3 = -F::ONE; let evals = vec![e0, e1, e2, e3];
324 let poly = UniPoly::from_evals_deg3(&evals);
325
326 assert_eq!(poly.eval_at_zero(), e0);
327 assert_eq!(poly.eval_at_one(), e1);
328 assert_eq!(poly.coeffs.len(), 4);
329
330 assert_eq!(poly.coeffs[1], F::from(3));
331 assert_eq!(poly.coeffs[2], F::from(2));
332 assert_eq!(poly.coeffs[3], F::from(1));
333
334 let hint = e0 + e1;
335 let compressed_poly = poly.compress();
336 let decompressed_poly = compressed_poly.decompress(&hint);
337 for i in 0..decompressed_poly.coeffs.len() {
338 assert_eq!(decompressed_poly.coeffs[i], poly.coeffs[i]);
339 }
340
341 let e4 = F::from(109);
342 assert_eq!(poly.evaluate(&F::from(4)), e4);
343 }
344
345 #[test]
346 fn test_from_evals_cubic() {
347 test_from_evals_cubic_with::<pallas::Scalar>();
348 test_from_evals_cubic_with::<bn256::Scalar>();
349 test_from_evals_cubic_with::<secp256k1::Scalar>()
350 }
351
352 fn test_from_evals_constant_with<F: PrimeField + CustomSerdeTrait>() {
353 let evals = vec![F::from(5)];
355 let poly = UniPoly::from_evals(&evals);
356
357 assert_eq!(poly.coeffs.len(), 1);
358 assert_eq!(poly.coeffs[0], F::from(5));
359 assert_eq!(poly.evaluate(&F::ZERO), F::from(5));
360 assert_eq!(poly.evaluate(&F::ONE), F::from(5));
361 assert_eq!(poly.evaluate(&F::from(100)), F::from(5));
362 }
363
364 #[test]
365 fn test_from_evals_constant() {
366 test_from_evals_constant_with::<pallas::Scalar>();
367 test_from_evals_constant_with::<bn256::Scalar>();
368 test_from_evals_constant_with::<secp256k1::Scalar>();
369 }
370
371 fn test_from_evals_linear_with<F: PrimeField + CustomSerdeTrait>() {
372 let e0 = F::from(2); let e1 = F::from(5); let evals = vec![e0, e1];
376 let poly = UniPoly::from_evals(&evals);
377
378 assert_eq!(poly.coeffs.len(), 2);
379 assert_eq!(poly.coeffs[0], F::from(2)); assert_eq!(poly.coeffs[1], F::from(3)); assert_eq!(poly.evaluate(&F::ZERO), e0);
382 assert_eq!(poly.evaluate(&F::ONE), e1);
383 assert_eq!(poly.evaluate(&F::from(2)), F::from(8)); }
385
386 #[test]
387 fn test_from_evals_linear() {
388 test_from_evals_linear_with::<pallas::Scalar>();
389 test_from_evals_linear_with::<bn256::Scalar>();
390 test_from_evals_linear_with::<secp256k1::Scalar>();
391 }
392
393 fn test_from_evals_quadratic_with<F: PrimeField + CustomSerdeTrait>() {
394 let e0 = F::ONE; let e1 = F::from(6); let e2 = F::from(15); let evals = vec![e0, e1, e2];
399 let poly = UniPoly::from_evals(&evals);
400
401 assert_eq!(poly.coeffs.len(), 3);
402 assert_eq!(poly.coeffs[0], F::ONE);
403 assert_eq!(poly.coeffs[1], F::from(3));
404 assert_eq!(poly.coeffs[2], F::from(2));
405 assert_eq!(poly.evaluate(&F::ZERO), e0);
406 assert_eq!(poly.evaluate(&F::ONE), e1);
407 assert_eq!(poly.evaluate(&F::from(2)), e2);
408 assert_eq!(poly.evaluate(&F::from(3)), F::from(28)); }
410
411 #[test]
412 fn test_from_evals_quadratic() {
413 test_from_evals_quadratic_with::<pallas::Scalar>();
414 test_from_evals_quadratic_with::<bn256::Scalar>();
415 test_from_evals_quadratic_with::<secp256k1::Scalar>();
416 }
417
418 fn test_from_evals_quartic_with<F: PrimeField + CustomSerdeTrait>() {
419 let e0 = F::from(5); let e1 = F::from(15); let e2 = F::from(57); let e3 = F::from(179); let e4 = F::from(453); let evals = vec![e0, e1, e2, e3, e4];
426 let poly = UniPoly::from_evals(&evals);
427
428 assert_eq!(poly.coeffs.len(), 5);
429 assert_eq!(poly.coeffs[0], F::from(5)); assert_eq!(poly.coeffs[1], F::from(4)); assert_eq!(poly.coeffs[2], F::from(3)); assert_eq!(poly.coeffs[3], F::from(2)); assert_eq!(poly.coeffs[4], F::from(1)); assert_eq!(poly.evaluate(&F::ZERO), e0);
435 assert_eq!(poly.evaluate(&F::ONE), e1);
436 assert_eq!(poly.evaluate(&F::from(2)), e2);
437 assert_eq!(poly.evaluate(&F::from(3)), e3);
438 }
439
440 #[test]
441 fn test_from_evals_quartic() {
442 test_from_evals_quartic_with::<pallas::Scalar>();
443 test_from_evals_quartic_with::<bn256::Scalar>();
444 test_from_evals_quartic_with::<secp256k1::Scalar>();
445 }
446
447 fn test_from_coeffs_with<F: PrimeField + CustomSerdeTrait>() {
448 let poly = UniPoly::from_coeffs(vec![F::ONE, F::from(3), F::from(2)]).unwrap();
450
451 assert_eq!(poly.coeffs.len(), 3);
453 assert_eq!(poly.coeffs[0], F::ONE); assert_eq!(poly.coeffs[1], F::from(3)); assert_eq!(poly.coeffs[2], F::from(2)); assert_eq!(poly.eval_at_zero(), F::ONE); assert_eq!(poly.eval_at_one(), F::from(6)); assert_eq!(poly.evaluate(&F::from(2)), F::from(15)); assert_eq!(poly.evaluate(&F::from(3)), F::from(28)); }
463
464 #[test]
465 fn test_from_coeffs() {
466 test_from_coeffs_with::<pallas::Scalar>();
467 test_from_coeffs_with::<bn256::Scalar>();
468 test_from_coeffs_with::<secp256k1::Scalar>();
469 }
470
471 fn test_from_coeffs_edge_cases_with<F: PrimeField + CustomSerdeTrait>() {
472 let result: Result<UniPoly<F>, _> = UniPoly::from_coeffs(vec![]);
474 assert!(result.is_err());
475
476 let poly = UniPoly::from_coeffs(vec![F::ONE, F::from(3), F::from(2), F::ZERO]).unwrap();
479 assert_eq!(poly.coeffs.len(), 3); assert_eq!(poly.coeffs[2], F::from(2)); assert_eq!(poly.eval_at_zero(), F::ONE); assert_eq!(poly.eval_at_one(), F::from(6)); assert_eq!(poly.evaluate(&F::from(2)), F::from(15)); let zero_poly = UniPoly::from_coeffs(vec![F::ZERO, F::ZERO, F::ZERO]).unwrap();
489 assert_eq!(zero_poly.coeffs.len(), 1);
490 assert!(bool::from(zero_poly.coeffs[0].is_zero()));
491 }
492
493 #[test]
494 fn test_from_coeffs_edge_cases() {
495 test_from_coeffs_edge_cases_with::<pallas::Scalar>();
496 test_from_coeffs_edge_cases_with::<bn256::Scalar>();
497 test_from_coeffs_edge_cases_with::<secp256k1::Scalar>();
498 }
499}