mirror of
https://github.com/logos-storage/plonky2.git
synced 2026-01-08 16:53:07 +00:00
Degree-3 GMiMC gate
This commit is contained in:
parent
194c4864d2
commit
ec0632bf16
@ -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(), (°rees[lhs]).max(°rees[rhs]).clone())
|
||||
}
|
||||
ConstraintPolynomialInner::Product { lhs, rhs } => {
|
||||
lhs.populate_degree_map(degrees);
|
||||
rhs.populate_degree_map(degrees);
|
||||
degrees.insert(self.clone(), °rees[lhs] + °rees[rhs])
|
||||
}
|
||||
ConstraintPolynomialInner::Exponentiation { base, exponent } => {
|
||||
base.populate_degree_map(degrees);
|
||||
degrees.insert(self.clone(),
|
||||
°rees[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());
|
||||
}
|
||||
}
|
||||
|
||||
@ -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.
|
||||
}
|
||||
}
|
||||
|
||||
@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user