Skip to content

Commit

Permalink
Add polynomial interfaces
Browse files Browse the repository at this point in the history
  • Loading branch information
ryanleh committed Oct 6, 2020
1 parent d9bc3c9 commit 06bf1f1
Show file tree
Hide file tree
Showing 87 changed files with 3,351 additions and 697 deletions.
7 changes: 6 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,12 @@ jobs:
uses: actions-rs/cargo@v1
with:
command: test
args: --all --all-features -- --skip dpc --skip integration_test
args: "--all \
--all-features \
--exclude cp-benches \
--exclude algebra-benches \
--exclude ff-fft-benches \
-- --skip dpc --skip integration_test"

check_no_std:
name: Check no_std
Expand Down
2 changes: 2 additions & 0 deletions algebra-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ pub extern crate alloc;
pub use alloc::{
borrow::{Cow, ToOwned},
boxed::Box,
collections::btree_map::BTreeMap,
format,
string::String,
vec,
Expand All @@ -40,6 +41,7 @@ pub use alloc::{
pub use std::{
borrow::{Cow, ToOwned},
boxed::Box,
collections::btree_map::BTreeMap,
format,
string::String,
vec,
Expand Down
58 changes: 54 additions & 4 deletions algebra-core/src/serialize/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ pub use flags::*;
#[doc(hidden)]
pub use algebra_core_derive::*;

use crate::{Cow, ToOwned, Vec};
use crate::{BTreeMap, Cow, ToOwned, Vec};
use core::convert::TryFrom;

/// Serializer in little endian format allowing to encode flags.
Expand Down Expand Up @@ -61,9 +61,9 @@ pub trait CanonicalSerialize {
self.serialize(writer)
}

/// Serializes `self` into `writer` without compression, and without performing
/// validity checks. Should be used *only* when there is no danger of
/// adversarial manipulation of the output.
/// Serializes `self` into `writer` without compression, and without
/// performing validity checks. Should be used *only* when there is no
/// danger of adversarial manipulation of the output.
#[inline]
fn serialize_unchecked<W: Write>(&self, writer: W) -> Result<(), SerializationError> {
self.serialize_uncompressed(writer)
Expand Down Expand Up @@ -793,6 +793,44 @@ impl CanonicalDeserialize for bool {
}
}

impl<K, V> CanonicalSerialize for BTreeMap<K, V>
where
K: CanonicalSerialize,
V: CanonicalSerialize,
{
fn serialize<W: Write>(&self, mut writer: W) -> Result<(), SerializationError> {
let len = self.len() as u64;
len.serialize(&mut writer)?;
for (k, v) in self.iter() {
k.serialize(&mut writer)?;
v.serialize(&mut writer)?;
}
Ok(())
}

fn serialized_size(&self) -> usize {
8 + self
.iter()
.map(|(k, v)| k.serialized_size() + v.serialized_size())
.sum::<usize>()
}
}

impl<K, V> CanonicalDeserialize for BTreeMap<K, V>
where
K: Ord + CanonicalDeserialize,
V: CanonicalDeserialize,
{
fn deserialize<R: Read>(mut reader: R) -> Result<Self, SerializationError> {
let len = u64::deserialize(&mut reader)?;
let mut map = BTreeMap::new();
for _ in 0..len {
map.insert(K::deserialize(&mut reader)?, V::deserialize(&mut reader)?);
}
Ok(map)
}
}

#[cfg(test)]
mod test {
use super::*;
Expand Down Expand Up @@ -859,6 +897,18 @@ mod test {
test_serialize(false);
}

#[test]
fn test_btreemap() {
let mut map = BTreeMap::new();
map.insert(0u64, true);
map.insert(5u64, false);
test_serialize(map);
let mut map = BTreeMap::new();
map.insert(10u64, vec![1u8, 2u8, 3u8]);
map.insert(50u64, vec![4u8, 5u8, 6u8]);
test_serialize(map);
}

#[test]
fn test_phantomdata() {
test_serialize(core::marker::PhantomData::<u64>);
Expand Down
2 changes: 1 addition & 1 deletion crypto-primitives/src/merkle_tree/constraints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ where
// proof.
let leaf_bits = leaf.to_bytes()?;
let leaf_hash = CRHGadget::evaluate(parameters, &leaf_bits)?;
let cs = leaf_hash.cs().or(root.cs()).unwrap();
let cs = leaf_hash.cs().or(root.cs());

// Check if leaf is one of the bottom-most siblings.
let leaf_is_left = Boolean::new_witness(r1cs_core::ns!(cs, "leaf_is_left"), || {
Expand Down
2 changes: 1 addition & 1 deletion crypto-primitives/src/prf/blake2s/constraints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -350,7 +350,7 @@ impl<ConstraintF: PrimeField> AllocVar<[u8; 32], ConstraintF> for OutputVar<Cons
impl<F: PrimeField> R1CSVar<F> for OutputVar<F> {
type Value = [u8; 32];

fn cs(&self) -> Option<ConstraintSystemRef<F>> {
fn cs(&self) -> ConstraintSystemRef<F> {
self.0.cs()
}

Expand Down
4 changes: 3 additions & 1 deletion ff-fft/src/evaluations.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
//! A polynomial represented in evaluations form.
use crate::{univariate::DensePolynomial, EvaluationDomain, GeneralEvaluationDomain, Vec};
use crate::{
univariate::DensePolynomial, EvaluationDomain, GeneralEvaluationDomain, UVPolynomial, Vec,
};
use algebra_core::FftField;
use core::ops::{Add, AddAssign, Div, DivAssign, Index, Mul, MulAssign, Sub, SubAssign};

Expand Down
2 changes: 1 addition & 1 deletion ff-fft/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ pub use domain::{
EvaluationDomain, GeneralEvaluationDomain, MixedRadixEvaluationDomain, Radix2EvaluationDomain,
};
pub use evaluations::Evaluations;
pub use polynomial::{multivariate, univariate};
pub use polynomial::{multivariate, univariate, MVPolynomial, Polynomial, UVPolynomial};

#[cfg(test)]
mod test;
87 changes: 87 additions & 0 deletions ff-fft/src/polynomial/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,91 @@
//! Modules for working with univariate or multivariate polynomials.
use crate::Vec;
use algebra_core::Field;
use core::{
fmt::Debug,
hash::Hash,
ops::{AddAssign, Index, SubAssign},
};
use rand::Rng;

pub mod multivariate;
pub mod univariate;

/// Describes the common interface for univariate and multivariate polynomials
pub trait Polynomial<F: Field>:
Sized
+ Clone
+ Debug
+ Default
+ Hash
+ PartialEq
+ Eq
+ Send
+ Sync
+ for<'a> AddAssign<&'a Self>
+ for<'a> AddAssign<(F, &'a Self)>
+ for<'a> SubAssign<&'a Self>
{
/// The domain of the polynomial.
type Domain: Sized + Clone + Ord + Debug;

/// Returns the zero polynomial.
fn zero() -> Self;

/// Checks if the given polynomial is zero.
fn is_zero(&self) -> bool;

/// Returns the total degree of the polynomial
fn degree(&self) -> usize;

/// Evaluates `self` at the given `point` in `Self::Domain`.
fn evaluate(&self, point: &Self::Domain) -> F;

/// If `num_vars` is `None`, outputs a polynomial a univariate polynomial
/// of degree `d` where each coefficient is sampled uniformly at random.
///
/// If `num_vars` is `Some(l)`, outputs an `l`-variate polynomial which
/// is the sum of `l` `d`-degree univariate polynomials where each coefficient
/// is sampled uniformly at random.
fn rand<R: Rng>(d: usize, num_vars: Option<usize>, rng: &mut R) -> Self;

/// Sample a random point from `Self::Domain`.
fn rand_domain_point<R: Rng>(domain_size: Option<usize>, rng: &mut R) -> Self::Domain;
}

/// Describes the interface for univariate polynomials
pub trait UVPolynomial<F: Field>: Polynomial<F> {
/// Constructs a new polynomial from a list of coefficients.
fn from_coefficients_slice(coeffs: &[F]) -> Self;

/// Constructs a new polynomial from a list of coefficients.
fn from_coefficients_vec(coeffs: Vec<F>) -> Self;

/// Returns the coefficients of `self`
fn coeffs(&self) -> &[F];
}

/// Describes the interface for univariate polynomials
pub trait MVPolynomial<F: Field>: Polynomial<F>
where
Self::Domain: Index<usize, Output = F>,
{
/// The type of the terms of `self`
type Term: multivariate::Term;

/// Constructs a new polynomial from a list of tuples of the form `(Self::Term, coeff)`
fn from_coefficients_slice(num_vars: usize, terms: &[(Self::Term, F)]) -> Self;

/// Constructs a new polynomial from a list of tuples of the form `(Self::Term, coeff)`
fn from_coefficients_vec(num_vars: usize, terms: Vec<(Self::Term, F)>) -> Self;

/// Returns the terms of a `self` as a list of tuples of the form `(Self::Term, coeff)`
fn terms(&self) -> &[(Self::Term, F)];

/// Given some point `z`, compute the quotients `w_i(X)` s.t
///
/// `p(X) - p(z) = (X_1-z_1)*w_1(X) + (X_2-z_2)*w_2(X) + ... + (X_l-z_l)*w_l(X)`
///
/// These quotients can always be found with no remainder.
fn divide_at_point(&self, point: &Self::Domain) -> Vec<Self>;
}
145 changes: 142 additions & 3 deletions ff-fft/src/polynomial/multivariate/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,145 @@
//! Work with multivariate polynomials.
//! Work with sparse multivariate polynomials.
use crate::Vec;
use algebra_core::Field;
use core::{
cmp::Ordering,
fmt::{Debug, Error, Formatter},
hash::Hash,
ops::Deref,
};

mod sparse;
#[cfg(feature = "parallel")]
use rayon::prelude::*;

pub use sparse::PolyVars;
mod sparse;
pub use sparse::SparsePolynomial;

/// Describes the interface for a term of a multivariate polynomial.
pub trait Term:
Clone + PartialOrd + Ord + PartialEq + Eq + Hash + Default + Debug + Deref + Send + Sync
{
/// Create a new Term from a list of tuples of the form `(variable, power)`
fn new(term: Vec<(usize, usize)>) -> Self;

/// Returns the sum of all variable powers in `self`
fn degree(&self) -> usize;

/// Returns a list of variables in `self`
fn vars(&self) -> Vec<usize>;

/// Returns whether `self` is a constant
fn is_constant(&self) -> bool;

/// Evaluates `self` at the given `point` in the field.
fn evaluate<F: Field>(&self, point: &[F]) -> F;
}

/// Stores a single term in a multivariate polynomial. Each element
/// is of the form `(variable, power)`.
#[derive(Clone, PartialOrd, PartialEq, Eq, Hash, Default)]
pub struct SparseTerm(Vec<(usize, usize)>);

impl SparseTerm {
/// Sums the powers of any duplicated variables. Assumes `term` is sorted.
fn combine(term: &[(usize, usize)]) -> Vec<(usize, usize)> {
let mut term_dedup: Vec<(usize, usize)> = Vec::new();
for (var, pow) in term {
match term_dedup.last_mut() {
Some(prev) => {
if prev.0 == *var {
prev.1 += pow;
continue;
}
}
_ => {}
};
term_dedup.push((*var, *pow));
}
term_dedup
}
}

impl Term for SparseTerm {
/// Create a new Term from a list of tuples of the form `(variable, power)`
fn new(mut term: Vec<(usize, usize)>) -> Self {
// Remove any terms with power 0
term.retain(|(_, pow)| *pow != 0);
// If there are more than one variables, make sure they are
// in order and combine any duplicates
if term.len() > 1 {
term.sort_by(|(v1, _), (v2, _)| v1.cmp(v2));
term = Self::combine(&term);
}
Self(term)
}

/// Returns the sum of all variable powers in `self`
fn degree(&self) -> usize {
self.iter().fold(0, |sum, acc| sum + acc.1)
}

/// Returns a list of variables in `self`
fn vars(&self) -> Vec<usize> {
self.iter().map(|(v, _)| *v).collect()
}

/// Returns whether `self` is a constant
fn is_constant(&self) -> bool {
self.len() == 0
}

/// Evaluates `self` at the given `point` in the field.
fn evaluate<F: Field>(&self, point: &[F]) -> F {
cfg_into_iter!(self)
.map(|var| {
let term = point.get(var.0).unwrap();
term.pow(&[var.1 as u64])
})
.product()
}
}

impl Debug for SparseTerm {
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
for variable in self.iter() {
if variable.1 == 1 {
write!(f, " * x_{}", variable.0)?;
} else {
write!(f, " * x_{}^{}", variable.0, variable.1)?;
}
}
Ok(())
}
}

impl Deref for SparseTerm {
type Target = [(usize, usize)];

fn deref(&self) -> &[(usize, usize)] {
&self.0
}
}

impl Ord for SparseTerm {
/// Sort by total degree. If total degree is equal then ordering
/// is given by exponent weight in lower-numbered variables
/// ie. `x_1 > x_2`, `x_1^2 > x_1 * x_2`, etc.
fn cmp(&self, other: &Self) -> Ordering {
if self.degree() != other.degree() {
self.degree().cmp(&other.degree())
} else {
// Iterate through all variables and return the corresponding ordering
// if they differ in variable numbering or power
for (cur, other) in self.iter().zip(other.iter()) {
if other.0 == cur.0 {
if cur.1 != other.1 {
return (cur.1).cmp(&other.1);
}
} else {
return (other.0).cmp(&cur.0);
}
}
Ordering::Equal
}
}
}
Loading

0 comments on commit 06bf1f1

Please sign in to comment.