diff --git a/src/circuit_builder.rs b/src/circuit_builder.rs index 4aaeb537..c0db18f4 100644 --- a/src/circuit_builder.rs +++ b/src/circuit_builder.rs @@ -3,7 +3,7 @@ use std::collections::HashSet; use crate::circuit_data::CircuitConfig; use crate::field::field::Field; use crate::gates::gate::{GateInstance, GateRef}; -use crate::generator::{CopyGenerator, WitnessGenerator2}; +use crate::generator::{CopyGenerator, WitnessGenerator}; use crate::target::Target; use crate::gates::constant::ConstantGate2; use crate::wire::Wire; @@ -12,7 +12,7 @@ pub struct CircuitBuilder2 { config: CircuitConfig, gates: HashSet>, gate_instances: Vec>, - generators: Vec>>, + generators: Vec>>, } impl CircuitBuilder2 { @@ -62,7 +62,7 @@ impl CircuitBuilder2 { assert!(y.is_routable(self.config)); } - pub fn add_generator>(&mut self, generator: G) { + pub fn add_generator>(&mut self, generator: G) { self.generators.push(Box::new(generator)); } diff --git a/src/gates/deterministic_gate.rs b/src/gates/deterministic_gate.rs index a70bcaa3..49b60049 100644 --- a/src/gates/deterministic_gate.rs +++ b/src/gates/deterministic_gate.rs @@ -4,7 +4,7 @@ use crate::circuit_data::CircuitConfig; use crate::constraint_polynomial::{ConstraintPolynomial, EvaluationVars}; use crate::field::field::Field; use crate::gates::gate::Gate; -use crate::generator::{SimpleGenerator, WitnessGenerator2}; +use crate::generator::{SimpleGenerator, WitnessGenerator}; use crate::target::Target; use crate::wire::Wire; use crate::witness::PartialWitness; @@ -35,7 +35,7 @@ pub trait DeterministicGate: 'static { &self, _config: CircuitConfig, _gate_index: usize, - ) -> Vec>> { + ) -> Vec>> { Vec::new() } } @@ -73,7 +73,7 @@ impl> Gate for DeterministicGateAdapter, next_constants: Vec, - ) -> Vec>> { + ) -> Vec>> { self.gate.outputs(config).outputs .into_iter() .map(|(location, out)| { @@ -87,7 +87,7 @@ impl> Gate for DeterministicGateAdapter`, rather // than a boxed `OutputGenerator`. - let b: Box::> = Box::new(og); + let b: Box::> = Box::new(og); b }) .chain(self.gate.additional_generators(config, gate_index)) diff --git a/src/gates/gate.rs b/src/gates/gate.rs index 10e11d15..f5495a4a 100644 --- a/src/gates/gate.rs +++ b/src/gates/gate.rs @@ -4,7 +4,7 @@ use std::rc::Rc; use crate::circuit_data::CircuitConfig; use crate::constraint_polynomial::ConstraintPolynomial; use crate::field::field::Field; -use crate::generator::WitnessGenerator2; +use crate::generator::WitnessGenerator; use num::ToPrimitive; /// A custom gate. @@ -21,7 +21,7 @@ pub trait Gate: 'static { gate_index: usize, local_constants: Vec, next_constants: Vec, - ) -> Vec>>; + ) -> Vec>>; /// The number of constants used by this gate. fn num_constants(&self, config: CircuitConfig) -> usize { diff --git a/src/gates/gmimc.rs b/src/gates/gmimc.rs index 23e90380..86058e9f 100644 --- a/src/gates/gmimc.rs +++ b/src/gates/gmimc.rs @@ -1,19 +1,13 @@ -use std::convert::TryInto; use std::sync::Arc; -use num::{BigUint, One}; +use num::{BigUint, FromPrimitive, One}; use crate::circuit_data::CircuitConfig; use crate::constraint_polynomial::ConstraintPolynomial; use crate::field::field::Field; use crate::gates::deterministic_gate::{DeterministicGate, DeterministicGateAdapter}; -use crate::gates::gate::{Gate, GateRef}; +use crate::gates::gate::GateRef; use crate::gates::output_graph::{GateOutputLocation, OutputGraph}; -use crate::generator::{SimpleGenerator, WitnessGenerator2}; -use crate::gmimc::{gmimc_permute, gmimc_permute_array}; -use crate::target::Target; -use crate::wire::Wire; -use crate::witness::PartialWitness; /// Evaluates a full GMiMC permutation, and writes the output to the next gate's first `width` /// wires (which could be the input of another `GMiMCGate`). @@ -126,14 +120,20 @@ impl DeterministicGate for GMiMCGat #[cfg(test)] mod tests { + use std::convert::TryInto; use std::sync::Arc; + use num::ToPrimitive; + 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; + use crate::generator::generate_partial_witness; + use crate::gmimc::gmimc_permute_naive; + use crate::wire::Wire; + use crate::witness::PartialWitness; #[test] fn degree() { @@ -147,10 +147,7 @@ mod tests { security_bits: 128, }; let outs = gate.outputs(config); - assert_eq!(outs.degree(), 3); - assert_eq!(outs.max_local_output_index(), Some(57)); - panic!(); } #[test] @@ -159,17 +156,38 @@ mod tests { const W: usize = 12; const R: usize = 101; let constants = Arc::new([F::TWO; R]); - let gate = GMiMCGate::::with_constants(constants); + type Gate = GMiMCGate::; + let gate = Gate::with_constants(constants.clone()); 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. + let permutation_inputs = (0..W) + .map(F::from_canonical_usize) + .collect::>(); + + let mut witness = PartialWitness::new(); + witness.set_wire(Wire { gate: 0, input: Gate::WIRE_SWITCH }, F::ZERO); + for i in 0..W { + witness.set_wire( + Wire { gate: 0, input: Gate::wire_input(i) }, + permutation_inputs[i]); + } + + let generators = gate.0.generators(config, 0, vec![], vec![]); + generate_partial_witness(&mut witness, generators); + + let expected_outputs: [F; W] = gmimc_permute_naive( + permutation_inputs.try_into().unwrap(), + constants); + + for i in 0..W { + let out = witness.get_wire( + Wire { gate: 1, input: Gate::wire_output(i) }); + assert_eq!(out, expected_outputs[i]); + } } } diff --git a/src/generator.rs b/src/generator.rs index fcea403e..9d5c4919 100644 --- a/src/generator.rs +++ b/src/generator.rs @@ -1,9 +1,65 @@ +use std::borrow::Borrow; +use std::collections::{HashMap, HashSet}; + use crate::field::field::Field; use crate::target::Target; use crate::witness::PartialWitness; +pub(crate) fn generate_partial_witness( + witness: &mut PartialWitness, + mut generators: Vec>>, +) { + // Index generator indices by their watched targets. + let mut generator_indices_by_watches: HashMap> = HashMap::new(); + for (i, generator) in generators.iter().enumerate() { + for watch in generator.watch_list() { + generator_indices_by_watches + .entry(watch) + .or_insert_with(Vec::new) + .push(i); + } + } + + // Build a list of "pending" generators which are queued to be run. Initially, all generators + // are queued. + let mut pending_generator_indices = HashSet::new(); + for i in 0..generators.len() { + pending_generator_indices.insert(i); + } + + // We also track a list of "expired" generators which have already returned false. + let mut expired_generator_indices = HashSet::new(); + + // Keep running generators until no generators are queued. + while !pending_generator_indices.is_empty() { + let mut next_pending_generator_indices = HashSet::new(); + + for &generator_idx in &pending_generator_indices { + let (result, finished) = generators[generator_idx].run(&witness); + if finished { + expired_generator_indices.insert(generator_idx); + } + + // Enqueue unfinished generators that were watching one of the newly populated targets. + for watch in result.target_values.keys() { + if let Some(watching_generator_indices) = generator_indices_by_watches.get(watch) { + for watching_generator_idx in watching_generator_indices { + if !expired_generator_indices.contains(watching_generator_idx) { + next_pending_generator_indices.insert(*watching_generator_idx); + } + } + } + } + + witness.extend(result); + } + + pending_generator_indices = next_pending_generator_indices; + } +} + /// A generator participates in the generation of the witness. -pub trait WitnessGenerator2: 'static { +pub trait WitnessGenerator: 'static { /// Targets to be "watched" by this generator. Whenever a target in the watch list is populated, /// the generator will be queued to run. fn watch_list(&self) -> Vec; @@ -22,7 +78,7 @@ pub trait SimpleGenerator: 'static { fn run_once(&mut self, witness: &PartialWitness) -> PartialWitness; } -impl> WitnessGenerator2 for SG { +impl> WitnessGenerator for SG { fn watch_list(&self) -> Vec { self.dependencies() } diff --git a/src/main.rs b/src/main.rs index a93db9f5..753301f5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -30,8 +30,8 @@ mod verifier; mod wire; mod witness; -// 12 wire polys, 3 Z polys, 4 parts of quotient poly. -const PROVER_POLYS: usize = 64 + 3 + (9 + 1); // TODO: Check +// 112 wire polys, 3 Z polys, 4 parts of quotient poly. +const PROVER_POLYS: usize = 113 + 3 + 4; fn main() { let overall_start = Instant::now(); @@ -56,7 +56,7 @@ fn bench_gmimc() { } const THREADS: usize = 12; - const LDE_BITS: i32 = 4; + const LDE_BITS: i32 = 3; const W: usize = 13; let hashes_per_poly = 1 << (13 + LDE_BITS); let threads = (0..THREADS).map(|_i| { diff --git a/src/witness.rs b/src/witness.rs index 07c54bf4..ae5abd70 100644 --- a/src/witness.rs +++ b/src/witness.rs @@ -4,9 +4,9 @@ use crate::field::field::Field; use crate::target::Target; use crate::wire::Wire; -#[derive(Debug)] +#[derive(Clone, Debug)] pub struct PartialWitness { - target_values: HashMap, + pub(crate) target_values: HashMap, } impl PartialWitness { @@ -53,4 +53,10 @@ impl PartialWitness { pub fn set_wire(&mut self, wire: Wire, value: F) { self.set_target(Target::Wire(wire), value) } + + pub fn extend(&mut self, other: PartialWitness) { + for (target, value) in other.target_values { + self.set_target(target, value); + } + } }