Degree-3 GMiMC gate

This commit is contained in:
Daniel Lubarov 2021-03-01 12:35:02 -08:00
parent 194c4864d2
commit ec0632bf16
3 changed files with 90 additions and 105 deletions

View File

@ -1,17 +1,17 @@
use std::{fmt, ptr};
use std::borrow::Borrow;
use std::collections::{HashMap, HashSet};
use std::fmt::{Debug, Display, Formatter};
use std::hash::{Hash, Hasher};
use std::iter::{Product, Sum};
use std::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign};
use std::{ptr, fmt};
use std::rc::Rc;
use num::{BigUint, FromPrimitive, One, Zero};
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};
use crate::wire::Wire;
pub(crate) struct EvaluationVars<'a, F: Field> {
pub(crate) local_constants: &'a [F],
@ -121,41 +121,6 @@ impl<F: Field> ConstraintPolynomial<F> {
self.0.degree()
}
pub(crate) fn populate_degree_map(&self, degrees: &mut HashMap<Self, BigUint>) {
if degrees.contains_key(self) {
// Already visited this node in the polynomial graph.
return;
}
match self.0.as_ref() {
ConstraintPolynomialInner::Constant(_) =>
degrees.insert(self.clone(), BigUint::zero()),
ConstraintPolynomialInner::LocalConstant(_) =>
degrees.insert(self.clone(), BigUint::one()),
ConstraintPolynomialInner::NextConstant(_) =>
degrees.insert(self.clone(), BigUint::one()),
ConstraintPolynomialInner::LocalWireValue(_) =>
degrees.insert(self.clone(), BigUint::one()),
ConstraintPolynomialInner::NextWireValue(_) =>
degrees.insert(self.clone(), BigUint::one()),
ConstraintPolynomialInner::Sum { lhs, rhs } => {
lhs.populate_degree_map(degrees);
rhs.populate_degree_map(degrees);
degrees.insert(self.clone(), (&degrees[lhs]).max(&degrees[rhs]).clone())
}
ConstraintPolynomialInner::Product { lhs, rhs } => {
lhs.populate_degree_map(degrees);
rhs.populate_degree_map(degrees);
degrees.insert(self.clone(), &degrees[lhs] + &degrees[rhs])
}
ConstraintPolynomialInner::Exponentiation { base, exponent } => {
base.populate_degree_map(degrees);
degrees.insert(self.clone(),
&degrees[base] * BigUint::from_usize(*exponent).unwrap())
}
};
}
/// Returns the set of wires that this constraint would depend on if it were applied at a
/// certain gate index.
pub(crate) fn dependencies(&self, gate: usize) -> Vec<Wire> {
@ -271,13 +236,6 @@ impl<F: Field> ConstraintPolynomial<F> {
fn from_inner(inner: ConstraintPolynomialInner<F>) -> Self {
Self(Rc::new(inner))
}
/// The number of polynomials in this graph.
fn graph_size(&self) -> usize {
let mut degrees = HashMap::new();
self.populate_degree_map(&mut degrees);
degrees.len()
}
}
impl<F: Field> Display for ConstraintPolynomial<F> {
@ -611,37 +569,4 @@ mod tests {
// ConstraintPolynomialInner.
assert_eq!(wire0.clone(), wire0);
}
#[test]
fn replace_all() {
type F = CrandallField;
let wire0 = ConstraintPolynomial::<F>::local_wire_value(0);
let wire1 = ConstraintPolynomial::<F>::local_wire_value(1);
let wire2 = ConstraintPolynomial::<F>::local_wire_value(2);
let wire3 = ConstraintPolynomial::<F>::local_wire_value(3);
let wire4 = ConstraintPolynomial::<F>::local_wire_value(4);
let sum01 = &wire0 + &wire1;
let sum12 = &wire1 + &wire2;
let sum23 = &wire2 + &wire3;
let product = &sum01 * &sum12 * &sum23;
assert_eq!(
wire0.replace_all(wire0.clone(), wire1.clone()),
wire1);
assert_eq!(
wire0.replace_all(wire1.clone(), wire2.clone()),
wire0);
// This should be a no-op, since wire 4 is not present in the product.
assert_eq!(
product.replace_all(wire4.clone(), wire3.clone()).graph_size(),
product.graph_size());
// This shouldn't change the graph structure at all, since the replacement wire 4 was not
// previously present.
assert_eq!(
product.replace_all(wire3.clone(), wire4.clone()).graph_size(),
product.graph_size());
}
}

View File

@ -1,6 +1,8 @@
use std::convert::TryInto;
use std::sync::Arc;
use num::{BigUint, One};
use crate::circuit_data::CircuitConfig;
use crate::constraint_polynomial::ConstraintPolynomial;
use crate::field::field::Field;
@ -34,17 +36,31 @@ impl<F: Field, const W: usize, const R: usize> GMiMCGate<F, W, R> {
/// If this is set to 1, the first four inputs will be swapped with the next four inputs. This
/// is useful for ordering hashes in Merkle proofs. Otherwise, this should be set to 0.
// TODO: Assert binary.
pub const WIRE_SWITCH: usize = 0;
pub const WIRE_SWITCH: usize = W;
/// The wire index for the i'th input to the permutation.
pub fn wire_input(i: usize) -> usize {
i + 1
i
}
/// The wire index for the i'th output to the permutation.
/// Note that outputs are written to the next gate's wires.
pub fn wire_output(i: usize) -> usize {
i + 1
i
}
/// Adds a local wire output to this output graph. Returns a `ConstraintPolynomial` which
/// references the newly-created wire.
///
/// This may seem like it belongs in `OutputGraph`, but it is not that general, since it uses
/// a notion of "next available" local wire indices specific to this gate.
fn add_local(
outputs: &mut OutputGraph<F>,
poly: ConstraintPolynomial<F>,
) -> ConstraintPolynomial<F> {
let index = outputs.max_local_output_index().map_or(W + 1, |i| i + 1);
outputs.add(GateOutputLocation::LocalWire(index), poly);
ConstraintPolynomial::local_wire_value(index)
}
}
@ -59,6 +75,8 @@ impl<F: Field, const W: usize, const R: usize> DeterministicGate<F> for GMiMCGat
.map(|i| ConstraintPolynomial::local_wire_value(Self::wire_input(i)))
.collect::<Vec<_>>();
let mut outputs = OutputGraph::new();
// Conditionally switch inputs based on the (boolean) switch wire.
let switch = ConstraintPolynomial::local_wire_value(Self::WIRE_SWITCH);
let mut state = Vec::new();
@ -83,21 +101,20 @@ impl<F: Field, const W: usize, const R: usize> DeterministicGate<F> for GMiMCGat
for r in 0..R {
let active = r % W;
let round_constant = ConstraintPolynomial::constant(self.constants[r]);
let f = (&state[active] + &addition_buffer + round_constant).cube();
addition_buffer += &f;
state[active] -= f;
let mut f_input = &state[active] + &addition_buffer + round_constant;
if f_input.degree() > BigUint::one() {
f_input = Self::add_local(&mut outputs, f_input);
}
let f_output = f_input.cube();
addition_buffer += &f_output;
state[active] -= f_output;
}
for i in 0..W {
state[i] += &addition_buffer;
outputs.add(GateOutputLocation::NextWire(i), &state[i] + &addition_buffer);
}
let outputs = state.into_iter()
.enumerate()
.map(|(i, out)| (GateOutputLocation::NextWire(Self::wire_output(i)), out))
.collect();
OutputGraph { outputs }
outputs
}
fn additional_constraints(&self, _config: CircuitConfig) -> Vec<ConstraintPolynomial<F>> {
@ -111,25 +128,48 @@ impl<F: Field, const W: usize, const R: usize> DeterministicGate<F> for GMiMCGat
mod tests {
use std::sync::Arc;
use crate::field::crandall_field::CrandallField;
use crate::gates::gmimc::GMiMCGate;
use crate::field::field::Field;
use crate::circuit_data::CircuitConfig;
use crate::field::crandall_field::CrandallField;
use crate::field::field::Field;
use crate::gates::deterministic_gate::DeterministicGate;
use crate::gates::gmimc::GMiMCGate;
use crate::circuit_builder::CircuitBuilder2;
#[test]
#[ignore]
fn degree() {
type F = CrandallField;
const W: usize = 12;
const R: usize = 20;
const R: usize = 101;
let gate = GMiMCGate::<F, W, R> { constants: Arc::new([F::TWO; R]) };
let config = CircuitConfig {
num_wires: 200,
num_routed_wires: 200,
security_bits: 128
security_bits: 128,
};
let outs = gate.outputs(config);
assert_eq!(outs.max_wire_input_index(), Some(50));
assert_eq!(outs.degree(), 3);
assert_eq!(outs.max_local_output_index(), Some(57));
panic!();
}
#[test]
fn generated_output() {
type F = CrandallField;
const W: usize = 12;
const R: usize = 101;
let constants = Arc::new([F::TWO; R]);
let gate = GMiMCGate::<F, W, R>::with_constants(constants);
let config = CircuitConfig {
num_wires: 200,
num_routed_wires: 200,
security_bits: 128,
};
let mut builder = CircuitBuilder2::new(config);
builder.add_gate(gate, Vec::new());
// let circuit = builder.build();
// TODO: generate witness & compare output to normal GMiMC function.
}
}

View File

@ -1,11 +1,11 @@
use std::{fmt, iter};
use std::collections::HashMap;
use std::{iter, fmt};
use std::fmt::{Display, Formatter};
use num::{BigUint, FromPrimitive, One, ToPrimitive};
use crate::constraint_polynomial::ConstraintPolynomial;
use crate::constraint_polynomial::{ConstraintPolynomial, EvaluationVars};
use crate::field::field::Field;
use std::fmt::{Display, Formatter};
/// Represents a set of deterministic gate outputs, expressed as polynomials over witness
/// values.
@ -15,15 +15,35 @@ pub struct OutputGraph<F: Field> {
}
impl<F: Field> OutputGraph<F> {
/// Creates a new output graph with no outputs.
pub fn new() -> Self {
Self { outputs: Vec::new() }
}
/// Creates an output graph with a single output.
pub fn single_output(loc: GateOutputLocation, out: ConstraintPolynomial<F>) -> Self {
Self { outputs: vec![(loc, out)] }
}
/// The largest local wire index in this entire graph.
pub(crate) fn max_wire_input_index(&self) -> Option<usize> {
pub fn add(&mut self, location: GateOutputLocation, poly: ConstraintPolynomial<F>) {
self.outputs.push((location, poly))
}
/// The largest polynomial degree among all polynomials in this graph.
pub fn degree(&self) -> usize {
self.outputs.iter()
.flat_map(|(loc, out)| out.max_wire_input_index())
.map(|(loc, out)| out.degree().to_usize().unwrap())
.max()
.unwrap_or(0)
}
/// The largest local wire index among this graph's output locations.
pub fn max_local_output_index(&self) -> Option<usize> {
self.outputs.iter()
.filter_map(|(loc, out)| match loc {
GateOutputLocation::LocalWire(i) => Some(*i),
GateOutputLocation::NextWire(_) => None
})
.max()
}
}