/ algorithms / src / polycommit / sonic_pc / polynomial.rs
polynomial.rs
  1  // Copyright (c) 2025-2026 ACDC Network
  2  // This file is part of the alphavm library.
  3  //
  4  // Alpha Chain | Delta Chain Protocol
  5  // International Monetary Graphite.
  6  //
  7  // Derived from Aleo (https://aleo.org) and ProvableHQ (https://provable.com).
  8  // They built world-class ZK infrastructure. We installed the EASY button.
  9  // Their cryptography: elegant. Our modifications: bureaucracy-compatible.
 10  // Original brilliance: theirs. Robert's Rules: ours. Bugs: definitely ours.
 11  //
 12  // Original Aleo/ProvableHQ code subject to Apache 2.0 https://www.apache.org/licenses/LICENSE-2.0
 13  // All modifications and new work: CC0 1.0 Universal Public Domain Dedication.
 14  // No rights reserved. No permission required. No warranty. No refunds.
 15  //
 16  // https://creativecommons.org/publicdomain/zero/1.0/
 17  // SPDX-License-Identifier: CC0-1.0
 18  
 19  use super::PolynomialLabel;
 20  use crate::fft::{DensePolynomial, EvaluationDomain, Evaluations as EvaluationsOnDomain, Polynomial, SparsePolynomial};
 21  use alphavm_fields::{Field, PrimeField};
 22  use alphavm_utilities::{cfg_iter, cfg_iter_mut, CanonicalDeserialize, CanonicalSerialize};
 23  
 24  use anyhow::Result;
 25  use std::borrow::Cow;
 26  
 27  #[cfg(feature = "serial")]
 28  use itertools::Itertools;
 29  #[cfg(not(feature = "serial"))]
 30  use rayon::prelude::*;
 31  
 32  #[derive(Clone, Debug, CanonicalSerialize, CanonicalDeserialize, Eq, PartialEq)]
 33  pub struct PolynomialInfo {
 34      label: PolynomialLabel,
 35      degree_bound: Option<usize>,
 36      hiding_bound: Option<usize>,
 37  }
 38  
 39  impl PolynomialInfo {
 40      /// Construct a new labeled polynomial by consuming `polynomial`.
 41      pub fn new(label: PolynomialLabel, degree_bound: Option<usize>, hiding_bound: Option<usize>) -> Self {
 42          Self { label, degree_bound, hiding_bound }
 43      }
 44  
 45      /// Return the label for `self`.
 46      pub fn label(&self) -> &str {
 47          &self.label
 48      }
 49  
 50      /// Retrieve the degree bound in `self`.
 51      pub fn degree_bound(&self) -> Option<usize> {
 52          self.degree_bound
 53      }
 54  
 55      /// Retrieve whether the polynomial in `self` should be hidden.
 56      pub fn is_hiding(&self) -> bool {
 57          self.hiding_bound.is_some()
 58      }
 59  
 60      /// Retrieve the hiding bound for the polynomial in `self`.
 61      pub fn hiding_bound(&self) -> Option<usize> {
 62          self.hiding_bound
 63      }
 64  }
 65  
 66  /// A polynomial along with information about its degree bound (if any), and the
 67  /// maximum number of queries that will be made to it. This latter number
 68  /// determines the amount of protection that will be provided to a commitment
 69  /// for this polynomial.
 70  #[derive(Debug, Clone, CanonicalSerialize, CanonicalDeserialize, PartialEq, Eq)]
 71  pub struct LabeledPolynomial<F: Field> {
 72      pub info: PolynomialInfo,
 73      pub polynomial: Polynomial<'static, F>,
 74  }
 75  
 76  impl<F: Field> core::ops::Deref for LabeledPolynomial<F> {
 77      type Target = Polynomial<'static, F>;
 78  
 79      fn deref(&self) -> &Self::Target {
 80          &self.polynomial
 81      }
 82  }
 83  
 84  impl<F: Field> LabeledPolynomial<F> {
 85      /// Construct a new labeled polynomial by consuming `polynomial`.
 86      pub fn new(
 87          label: impl Into<PolynomialLabel>,
 88          polynomial: impl Into<Polynomial<'static, F>>,
 89          degree_bound: impl Into<Option<usize>>,
 90          hiding_bound: impl Into<Option<usize>>,
 91      ) -> Self {
 92          let info = PolynomialInfo::new(label.into(), degree_bound.into(), hiding_bound.into());
 93          Self { info, polynomial: polynomial.into() }
 94      }
 95  
 96      pub fn info(&self) -> &PolynomialInfo {
 97          &self.info
 98      }
 99  
100      /// Return the label for `self`.
101      pub fn label(&self) -> &str {
102          &self.info.label
103      }
104  
105      /// Return the label for `self`.
106      pub fn to_label(&self) -> String {
107          self.info.label.clone()
108      }
109  
110      /// Retrieve the polynomial from `self`.
111      pub fn polynomial(&self) -> &Polynomial<'_, F> {
112          &self.polynomial
113      }
114  
115      /// Retrieve a mutable reference to the enclosed polynomial.
116      pub fn polynomial_mut(&mut self) -> &mut Polynomial<'static, F> {
117          &mut self.polynomial
118      }
119  
120      /// Evaluate the polynomial in `self`.
121      pub fn evaluate(&self, point: F) -> F {
122          self.polynomial.evaluate(point)
123      }
124  
125      /// Retrieve the degree bound in `self`.
126      pub fn degree_bound(&self) -> Option<usize> {
127          self.info.degree_bound
128      }
129  
130      /// Retrieve whether the polynomial in `self` should be hidden.
131      pub fn is_hiding(&self) -> bool {
132          self.info.hiding_bound.is_some()
133      }
134  
135      /// Retrieve the hiding bound for the polynomial in `self`.
136      pub fn hiding_bound(&self) -> Option<usize> {
137          self.info.hiding_bound
138      }
139  }
140  
141  /////////////////////////////////////////////////////////////////////////////////////
142  
143  #[derive(Debug, Clone)]
144  pub struct LabeledPolynomialWithBasis<'a, F: PrimeField> {
145      pub info: PolynomialInfo,
146      pub polynomial: PolynomialWithBasis<'a, F>,
147  }
148  
149  impl<'a, F: PrimeField> LabeledPolynomialWithBasis<'a, F> {
150      /// Construct a new labeled polynomial by consuming `polynomial`.
151      pub fn new_monomial_basis(
152          label: PolynomialLabel,
153          polynomial: &'a Polynomial<F>,
154          degree_bound: Option<usize>,
155          hiding_bound: Option<usize>,
156      ) -> Self {
157          let polynomial = PolynomialWithBasis::new_monomial_basis_ref(polynomial, degree_bound);
158          let info = PolynomialInfo::new(label, degree_bound, hiding_bound);
159          Self { info, polynomial }
160      }
161  
162      pub fn new_lagrange_basis(
163          label: PolynomialLabel,
164          polynomial: EvaluationsOnDomain<F>,
165          hiding_bound: Option<usize>,
166      ) -> Self {
167          let polynomial = PolynomialWithBasis::new_lagrange_basis(polynomial);
168          let info = PolynomialInfo::new(label, None, hiding_bound);
169          Self { info, polynomial }
170      }
171  
172      pub fn new_lagrange_basis_ref(
173          label: PolynomialLabel,
174          polynomial: &'a EvaluationsOnDomain<F>,
175          hiding_bound: Option<usize>,
176      ) -> Self {
177          let polynomial = PolynomialWithBasis::new_lagrange_basis_ref(polynomial);
178          let info = PolynomialInfo::new(label, None, hiding_bound);
179          Self { info, polynomial }
180      }
181  
182      /// Return the label for `self`.
183      pub fn label(&self) -> &str {
184          &self.info.label
185      }
186  
187      /// Return the information about the label, degree bound, and hiding bound
188      /// of `self`.
189      pub fn info(&self) -> &PolynomialInfo {
190          &self.info
191      }
192  
193      pub fn degree(&self) -> usize {
194          match &self.polynomial {
195              PolynomialWithBasis::Lagrange { evaluations } => evaluations.domain().size() - 1,
196              PolynomialWithBasis::Monomial { polynomial, .. } => polynomial.degree(),
197          }
198      }
199  
200      /// Evaluate the polynomial in `self`.
201      pub fn evaluate(&self, point: F) -> F {
202          self.polynomial.evaluate(point)
203      }
204  
205      /// Retrieve the degree bound in `self`.
206      pub fn degree_bound(&self) -> Option<usize> {
207          match self.polynomial {
208              PolynomialWithBasis::Monomial { degree_bound, .. } => degree_bound,
209              _ => None,
210          }
211      }
212  
213      /// Retrieve whether the polynomial in `self` should be hidden.
214      pub fn is_hiding(&self) -> bool {
215          self.info.hiding_bound.is_some()
216      }
217  
218      /// Retrieve the hiding bound for the polynomial in `self`.
219      pub fn hiding_bound(&self) -> Option<usize> {
220          self.info.hiding_bound
221      }
222  }
223  
224  impl<'a, F: PrimeField> From<&'a LabeledPolynomial<F>> for LabeledPolynomialWithBasis<'a, F> {
225      fn from(other: &'a LabeledPolynomial<F>) -> Self {
226          let polynomial = PolynomialWithBasis::Monomial {
227              polynomial: Cow::Borrowed(other.polynomial()),
228              degree_bound: other.degree_bound(),
229          };
230          Self { info: other.info.clone(), polynomial }
231      }
232  }
233  
234  impl<F: PrimeField> From<LabeledPolynomial<F>> for LabeledPolynomialWithBasis<'_, F> {
235      fn from(other: LabeledPolynomial<F>) -> Self {
236          let polynomial = PolynomialWithBasis::Monomial {
237              polynomial: Cow::Owned(other.polynomial),
238              degree_bound: other.info.degree_bound,
239          };
240          Self { info: other.info.clone(), polynomial }
241      }
242  }
243  
244  #[derive(Debug, Clone)]
245  pub enum PolynomialWithBasis<'a, F: PrimeField> {
246      /// A polynomial in monomial basis, along with information about
247      /// its degree bound (if any).
248      Monomial { polynomial: Cow<'a, Polynomial<'a, F>>, degree_bound: Option<usize> },
249  
250      /// A polynomial in Lagrange basis, along with information about
251      /// its degree bound (if any).
252      Lagrange { evaluations: Cow<'a, EvaluationsOnDomain<F>> },
253  }
254  
255  impl<'a, F: PrimeField> PolynomialWithBasis<'a, F> {
256      pub fn new_monomial_basis_ref(polynomial: &'a Polynomial<F>, degree_bound: Option<usize>) -> Self {
257          Self::Monomial { polynomial: Cow::Borrowed(polynomial), degree_bound }
258      }
259  
260      pub fn new_monomial_basis(polynomial: Polynomial<'a, F>, degree_bound: Option<usize>) -> Self {
261          Self::Monomial { polynomial: Cow::Owned(polynomial), degree_bound }
262      }
263  
264      pub fn new_dense_monomial_basis_ref(polynomial: &'a DensePolynomial<F>, degree_bound: Option<usize>) -> Self {
265          let polynomial = Polynomial::Dense(Cow::Borrowed(polynomial));
266          Self::Monomial { polynomial: Cow::Owned(polynomial), degree_bound }
267      }
268  
269      pub fn new_dense_monomial_basis(polynomial: DensePolynomial<F>, degree_bound: Option<usize>) -> Self {
270          let polynomial = Polynomial::from(polynomial);
271          Self::Monomial { polynomial: Cow::Owned(polynomial), degree_bound }
272      }
273  
274      pub fn new_sparse_monomial_basis_ref(polynomial: &'a SparsePolynomial<F>, degree_bound: Option<usize>) -> Self {
275          let polynomial = Polynomial::Sparse(Cow::Borrowed(polynomial));
276          Self::Monomial { polynomial: Cow::Owned(polynomial), degree_bound }
277      }
278  
279      pub fn new_sparse_monomial_basis(polynomial: SparsePolynomial<F>, degree_bound: Option<usize>) -> Self {
280          let polynomial = Polynomial::from(polynomial);
281          Self::Monomial { polynomial: Cow::Owned(polynomial), degree_bound }
282      }
283  
284      pub fn new_lagrange_basis(evaluations: EvaluationsOnDomain<F>) -> Self {
285          Self::Lagrange { evaluations: Cow::Owned(evaluations) }
286      }
287  
288      pub fn new_lagrange_basis_ref(evaluations: &'a EvaluationsOnDomain<F>) -> Self {
289          Self::Lagrange { evaluations: Cow::Borrowed(evaluations) }
290      }
291  
292      pub fn is_in_monomial_basis(&self) -> bool {
293          matches!(self, Self::Monomial { .. })
294      }
295  
296      /// Retrieve the degree bound in `self`.
297      pub fn degree_bound(&self) -> Option<usize> {
298          match self {
299              Self::Monomial { degree_bound, .. } => *degree_bound,
300              _ => None,
301          }
302      }
303  
304      /// Retrieve the degree bound in `self`.
305      pub fn is_sparse(&self) -> bool {
306          match self {
307              Self::Monomial { polynomial, .. } => matches!(polynomial.as_ref(), Polynomial::Sparse(_)),
308              _ => false,
309          }
310      }
311  
312      pub fn is_in_lagrange_basis(&self) -> bool {
313          matches!(self, Self::Lagrange { .. })
314      }
315  
316      pub fn domain(&self) -> Option<EvaluationDomain<F>> {
317          match self {
318              Self::Lagrange { evaluations } => Some(evaluations.domain()),
319              _ => None,
320          }
321      }
322  
323      pub fn evaluate(&self, point: F) -> F {
324          match self {
325              Self::Monomial { polynomial, .. } => polynomial.evaluate(point),
326              Self::Lagrange { evaluations } => {
327                  let domain = evaluations.domain();
328                  let degree = domain.size() as u64;
329                  let multiplier = (point.pow([degree]) - F::one()) / F::from(degree);
330                  let powers: Vec<_> = domain.elements().collect();
331                  let mut denominators = cfg_iter!(powers).map(|pow| point - pow).collect::<Vec<_>>();
332                  alphavm_fields::batch_inversion(&mut denominators);
333                  cfg_iter_mut!(denominators)
334                      .zip_eq(powers)
335                      .zip_eq(&evaluations.evaluations)
336                      .map(|((denom, power), coeff)| *denom * power * coeff)
337                      .sum::<F>()
338                      * multiplier
339              }
340          }
341      }
342  }