This commit is contained in:
Daniel Lubarov 2021-02-27 21:42:12 -08:00
parent 5d6da4f94a
commit 58425eb548
5 changed files with 120 additions and 26 deletions

View File

@ -2,7 +2,7 @@ use std::collections::{HashMap, HashSet};
use std::hash::{Hash, Hasher};
use std::iter::{Product, Sum};
use std::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign};
use std::ptr;
use std::{ptr, fmt};
use std::rc::Rc;
use num::{BigUint, FromPrimitive, One, Zero};
@ -11,6 +11,7 @@ use crate::field::field::Field;
use crate::wire::Wire;
use crate::gates::output_graph::GateOutputLocation;
use std::borrow::Borrow;
use std::fmt::{Display, Formatter, Debug};
pub(crate) struct EvaluationVars<'a, F: Field> {
pub(crate) local_constants: &'a [F],
@ -279,6 +280,12 @@ impl<F: Field> ConstraintPolynomial<F> {
}
}
impl<F: Field> Display for ConstraintPolynomial<F> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
Display::fmt(&self.0, f)
}
}
impl<F: Field> PartialEq for ConstraintPolynomial<F> {
fn eq(&self, other: &Self) -> bool {
ptr::eq(&*self.0, &*other.0)
@ -480,6 +487,29 @@ pub(crate) enum ConstraintPolynomialInner<F: Field> {
},
}
impl<F: Field> Display for ConstraintPolynomialInner<F> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
ConstraintPolynomialInner::Constant(c) =>
write!(f, "{}", c),
ConstraintPolynomialInner::LocalConstant(i) =>
write!(f, "local_const_{}", i),
ConstraintPolynomialInner::NextConstant(i) =>
write!(f, "next_const_{}", i),
ConstraintPolynomialInner::LocalWireValue(i) =>
write!(f, "local_wire_{}", i),
ConstraintPolynomialInner::NextWireValue(i) =>
write!(f, "next_wire_{}", i),
ConstraintPolynomialInner::Sum { lhs, rhs } =>
write!(f, "({} + {})", lhs, rhs),
ConstraintPolynomialInner::Product { lhs, rhs } =>
write!(f, "({} * {})", lhs, rhs),
ConstraintPolynomialInner::Exponentiation { base, exponent } =>
write!(f, "({} ^ {})", base, exponent),
}
}
}
impl<F: Field> ConstraintPolynomialInner<F> {
fn add_dependencies(&self, gate: usize, deps: &mut HashSet<Wire>) {
match self {

View File

@ -1,4 +1,4 @@
use std::fmt::{Debug, Formatter};
use std::fmt::{Debug, Formatter, Display};
use std::fmt;
use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};
@ -29,9 +29,15 @@ impl PartialEq for CrandallField {
impl Eq for CrandallField {}
impl Display for CrandallField {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
Display::fmt(&self.0, f)
}
}
impl Debug for CrandallField {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
Debug::fmt(&self.0, f)
}
}

View File

@ -1,4 +1,4 @@
use std::fmt::Debug;
use std::fmt::{Debug, Display};
use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};
/// A finite field with prime order less than 2^64.
@ -16,6 +16,7 @@ pub trait Field: 'static
+ Div<Self, Output=Self>
+ DivAssign<Self>
+ Debug
+ Display
+ Send
+ Sync {
const ZERO: Self;

View File

@ -123,10 +123,11 @@ mod tests {
use crate::gates::deterministic_gate::DeterministicGate;
#[test]
#[ignore]
fn degree() {
type F = CrandallField;
const W: usize = 12;
const R: usize = 101;
const R: usize = 20;
let gate = GMiMCGate::<F, W, R> { constants: Arc::new([F::TWO; R]) };
let config = CircuitConfig {
num_wires: 200,

View File

@ -1,10 +1,11 @@
use std::collections::HashMap;
use std::iter;
use std::{iter, fmt};
use num::{BigUint, FromPrimitive, One};
use num::{BigUint, FromPrimitive, One, ToPrimitive};
use crate::constraint_polynomial::ConstraintPolynomial;
use crate::field::field::Field;
use std::fmt::{Display, Formatter};
/// Represents a set of deterministic gate outputs, expressed as polynomials over witness
/// values.
@ -31,32 +32,39 @@ impl<F: Field> OutputGraph<F> {
let mut current_graph = self.clone();
while current_graph.count_high_degree_polys(max_degree) > 0 {
'shrinker: while current_graph.count_high_degree_polys(max_degree) > 0 {
// Find polynomials with a degree between 2 and the max, inclusive.
// These are candidates for becoming new wires.
let mut candidates = current_graph.degree_map().into_iter()
.filter(|(_poly, deg)| deg > &BigUint::one() && deg <= &max_degree_biguint)
.map(|(poly, _deg)| poly);
let degrees = current_graph.degree_map();
let current_high_deg_count = current_graph.count_high_degree_polys(max_degree);
let mut candidate_degrees: Vec<(ConstraintPolynomial<F>, usize)> = degrees
.iter()
.filter(|(poly, deg)| *deg > &BigUint::one() && *deg <= &max_degree_biguint)
.map(|(poly, deg)| (poly.clone(), deg.to_usize().unwrap()))
.collect();
candidate_degrees.sort_unstable_by_key(|(poly, deg)| *deg);
candidate_degrees.reverse();
// Pick the candidate that minimizes the number of high-degree polynomials in our graph.
// This is just a simple heuristic; it won't always give an optimal wire count.
let mut first = candidates.next().expect("No candidate; cannot reduce degree further");
let mut leader_graph = current_graph.allocate_wire(first);
let mut leader_high_deg_count = leader_graph.count_high_degree_polys(max_degree);
for candidate in candidates {
let candidate_graph = current_graph.allocate_wire(candidate);
for (poly, _deg) in &candidate_degrees {
let candidate_graph = current_graph.allocate_wire(poly.clone());
let candidate_high_deg_count = candidate_graph.count_high_degree_polys(max_degree);
if candidate_high_deg_count < leader_high_deg_count {
leader_graph = candidate_graph;
leader_high_deg_count = candidate_high_deg_count;
if candidate_high_deg_count < current_high_deg_count {
// println!("before {}", &current_graph);
// println!("after {}", &candidate_graph);
current_graph = candidate_graph;
println!("Reduced high degree polys to {}", candidate_high_deg_count);
continue 'shrinker;
}
}
// println!("before {:?}", current_graph);
// println!("after {:?}", leader_graph);
current_graph = leader_graph;
println!("{}", leader_high_deg_count);
println!("No good candidates; cannot reduce high degree polys");
for (poly, _deg) in candidate_degrees {
let candidate_graph = current_graph.allocate_wire(poly);
current_graph = candidate_graph;
continue 'shrinker;
}
panic!("No candidate; cannot make progress");
}
current_graph
@ -101,6 +109,15 @@ impl<F: Field> OutputGraph<F> {
}
}
impl<F: Field> Display for OutputGraph<F> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
for (loc, out) in &self.outputs {
write!(f, "{} := {}, ", loc, out)?;
}
Ok(())
}
}
/// Represents an output location of a deterministic gate.
#[derive(Copy, Clone, Debug)]
pub enum GateOutputLocation {
@ -110,12 +127,51 @@ pub enum GateOutputLocation {
NextWire(usize),
}
impl Display for GateOutputLocation {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
GateOutputLocation::LocalWire(i) => write!(f, "local_wire_{}", i),
GateOutputLocation::NextWire(i) => write!(f, "next_wire_{}", i),
}
}
}
#[cfg(test)]
mod tests {
use crate::constraint_polynomial::ConstraintPolynomial;
use crate::field::crandall_field::CrandallField;
use crate::gates::output_graph::{GateOutputLocation, OutputGraph};
#[test]
fn shrink_mimc() {
// This is like a simplified version of GMiMC, for easy debugging.
type F = CrandallField;
let switch = ConstraintPolynomial::<F>::local_wire_value(0);
let x = ConstraintPolynomial::<F>::local_wire_value(1);
let y = ConstraintPolynomial::<F>::local_wire_value(2);
// deg 2
let delta = &switch * (&y - &x);
let l0 = &x + &delta;
let r0 = &y - &delta;
let s0 = &l0 + &r0;
// 2*3
let l1 = s0.cube(); let r1 = r0.cube(); let s1 = &l1 + &r1;
// 2*3*3
let l2 = s1.cube(); let r2 = r1.cube(); let s2 = &l2 + &r2;
// 2*3*3*3
let l3 = s2.cube(); let r3 = r2.cube(); let s3 = &l3 + &r3;
let og = OutputGraph { outputs: vec![
(GateOutputLocation::NextWire(0), l3),
(GateOutputLocation::NextWire(1), r3),
] };
let shrunk = og.shrink_degree(9);
assert_eq!(shrunk.max_wire_input_index(), Some(4));
}
#[test]
fn shrink_squaring_graph() {
type F = CrandallField;