diff --git a/src/bin/bench_recursion.rs b/src/bin/bench_recursion.rs index 9fab2456..59b65e51 100644 --- a/src/bin/bench_recursion.rs +++ b/src/bin/bench_recursion.rs @@ -18,12 +18,6 @@ fn main() { env_logger::Builder::from_env(Env::default().default_filter_or("debug")).init(); bench_prove::(); - - // bench_field_mul::(); - - // bench_fft(); - println!(); - // bench_gmimc::(); } fn bench_prove, const D: usize>() { @@ -31,7 +25,7 @@ fn bench_prove, const D: usize>() { let config = CircuitConfig { num_wires: 134, - num_routed_wires: 12, + num_routed_wires: 27, security_bits: 128, rate_bits: 3, num_challenges: 3, @@ -45,17 +39,17 @@ fn bench_prove, const D: usize>() { let mut builder = CircuitBuilder::::new(config); + let zero = builder.zero(); + let zero_ext = builder.zero_extension(); + + let mut state = [zero; 12]; for _ in 0..10000 { - builder.add_gate_no_constants(gmimc_gate.clone()); + state = builder.permute(state); } - builder.add_gate(ConstantGate::get(), vec![F::NEG_ONE]); - - // for _ in 0..(40 * 5) { - // builder.add_gate( - // FriConsistencyGate::new(2, 3, 13), - // vec![F::primitive_root_of_unity(13)]); - // } + // Random other gates. + builder.add(zero, zero); + builder.add_extension(zero_ext, zero_ext); let prover = builder.build_prover(); let inputs = PartialWitness::new(); diff --git a/src/circuit_builder.rs b/src/circuit_builder.rs index ec6f60be..1c42c4c1 100644 --- a/src/circuit_builder.rs +++ b/src/circuit_builder.rs @@ -11,7 +11,8 @@ use crate::field::cosets::get_unique_coset_shifts; use crate::field::extension_field::target::ExtensionTarget; use crate::field::extension_field::Extendable; use crate::gates::constant::ConstantGate; -use crate::gates::gate::{GateInstance, GateRef}; +use crate::gates::gate::{GateInstance, GateRef, PrefixedGate}; +use crate::gates::gate_tree::Tree; use crate::gates::noop::NoopGate; use crate::generator::{CopyGenerator, WitnessGenerator}; use crate::hash::hash_n_to_hash; @@ -234,22 +235,26 @@ impl, const D: usize> CircuitBuilder { } } - fn constant_polys(&self) -> Vec> { - let num_constants = self - .gate_instances + fn constant_polys(&self, gates: &[PrefixedGate]) -> Vec> { + let num_constants = gates .iter() - .map(|gate_inst| gate_inst.constants.len()) + .map(|gate| gate.gate.0.num_constants() + gate.prefix.len()) .max() .unwrap(); let constants_per_gate = self .gate_instances .iter() - .map(|gate_inst| { - let mut padded_constants = gate_inst.constants.clone(); - for _ in padded_constants.len()..num_constants { - padded_constants.push(F::ZERO); - } - padded_constants + .map(|gate| { + let prefix = &gates + .iter() + .find(|g| g.gate.0.id() == gate.gate_type.0.id()) + .unwrap() + .prefix; + let mut prefixed_constants = Vec::with_capacity(num_constants); + prefixed_constants.extend(prefix.iter().map(|&b| if b { F::ONE } else { F::ZERO })); + prefixed_constants.extend_from_slice(&gate.constants); + prefixed_constants.resize(num_constants, F::ZERO); + prefixed_constants }) .collect::>(); @@ -293,7 +298,11 @@ impl, const D: usize> CircuitBuilder { let degree = self.gate_instances.len(); info!("degree after blinding & padding: {}", degree); - let constant_vecs = self.constant_polys(); + let gates = self.gates.iter().cloned().collect(); + let gate_tree = Tree::from_gates(gates); + let prefixed_gates = PrefixedGate::from_tree(gate_tree); + + let constant_vecs = self.constant_polys(&prefixed_gates); let constants_commitment = ListPolynomialCommitment::new( constant_vecs.into_iter().map(|v| v.ifft()).collect(), self.config.fri_config.rate_bits, @@ -315,11 +324,12 @@ impl, const D: usize> CircuitBuilder { sigmas_root, }; - let generators = self.generators; let prover_only = ProverOnlyCircuitData { - generators, + generators: self.generators, constants_commitment, sigmas_commitment, + copy_constraints: self.copy_constraints, + gate_instances: self.gate_instances, }; // The HashSet of gates will have a non-deterministic order. When converting to a Vec, we @@ -342,7 +352,7 @@ impl, const D: usize> CircuitBuilder { let common = CommonCircuitData { config: self.config, degree_bits, - gates, + gates: prefixed_gates, num_gate_constraints, k_is, circuit_digest, diff --git a/src/circuit_data.rs b/src/circuit_data.rs index c6c27d35..9d6ebf7e 100644 --- a/src/circuit_data.rs +++ b/src/circuit_data.rs @@ -3,11 +3,12 @@ use anyhow::Result; use crate::field::extension_field::Extendable; use crate::field::field::Field; use crate::fri::FriConfig; -use crate::gates::gate::GateRef; +use crate::gates::gate::{GateInstance, PrefixedGate}; use crate::generator::WitnessGenerator; use crate::polynomial::commitment::ListPolynomialCommitment; use crate::proof::{Hash, HashTarget, Proof}; use crate::prover::prove; +use crate::target::Target; use crate::verifier::verify; use crate::witness::PartialWitness; @@ -67,7 +68,7 @@ impl CircuitConfig { /// Circuit data required by the prover or the verifier. pub struct CircuitData, const D: usize> { - pub(crate) prover_only: ProverOnlyCircuitData, + pub(crate) prover_only: ProverOnlyCircuitData, pub(crate) verifier_only: VerifierOnlyCircuitData, pub(crate) common: CommonCircuitData, } @@ -90,7 +91,7 @@ impl, const D: usize> CircuitData { /// required, like LDEs of preprocessed polynomials. If more succinctness was desired, we could /// construct a more minimal prover structure and convert back and forth. pub struct ProverCircuitData, const D: usize> { - pub(crate) prover_only: ProverOnlyCircuitData, + pub(crate) prover_only: ProverOnlyCircuitData, pub(crate) common: CommonCircuitData, } @@ -113,12 +114,16 @@ impl, const D: usize> VerifierCircuitData { } /// Circuit data required by the prover, but not the verifier. -pub(crate) struct ProverOnlyCircuitData { +pub(crate) struct ProverOnlyCircuitData, const D: usize> { pub generators: Vec>>, /// Commitments to the constants polynomial. pub constants_commitment: ListPolynomialCommitment, /// Commitments to the sigma polynomial. pub sigmas_commitment: ListPolynomialCommitment, + /// The circuit's copy constraints. + pub copy_constraints: Vec<(Target, Target)>, + /// The concrete placement of each gate in the circuit. + pub gate_instances: Vec>, } /// Circuit data required by the verifier, but not the prover. @@ -136,8 +141,8 @@ pub(crate) struct CommonCircuitData, const D: usize> { pub(crate) degree_bits: usize, - /// The types of gates used in this circuit. - pub(crate) gates: Vec>, + /// The types of gates used in this circuit, along with their prefixes. + pub(crate) gates: Vec>, /// The largest number of constraints imposed by any gate. pub(crate) num_gate_constraints: usize, @@ -166,7 +171,7 @@ impl, const D: usize> CommonCircuitData { pub fn constraint_degree(&self) -> usize { self.gates .iter() - .map(|g| g.0.degree()) + .map(|g| g.gate.0.degree()) .max() .expect("No gates?") } diff --git a/src/field/extension_field/mod.rs b/src/field/extension_field/mod.rs index 9caa7dc8..2a176fe9 100644 --- a/src/field/extension_field/mod.rs +++ b/src/field/extension_field/mod.rs @@ -1,6 +1,7 @@ -use crate::field::field::Field; use std::convert::TryInto; +use crate::field::field::Field; + pub mod algebra; pub mod quadratic; pub mod quartic; diff --git a/src/field/fft.rs b/src/field/fft.rs index 8bcde967..e1c1a097 100644 --- a/src/field/fft.rs +++ b/src/field/fft.rs @@ -126,32 +126,11 @@ pub(crate) fn fft_with_precomputation_power_of_2( PolynomialValues { values } } -pub(crate) fn coset_fft(poly: PolynomialCoeffs, shift: F) -> PolynomialValues { - let mut points = fft(poly); - let mut shift_exp_i = F::ONE; - for p in points.values.iter_mut() { - *p *= shift_exp_i; - shift_exp_i *= shift; - } - points -} - pub(crate) fn ifft(poly: PolynomialValues) -> PolynomialCoeffs { let precomputation = fft_precompute(poly.len()); ifft_with_precomputation_power_of_2(poly, &precomputation) } -pub(crate) fn coset_ifft(poly: PolynomialValues, shift: F) -> PolynomialCoeffs { - let shift_inv = shift.inverse(); - let mut shift_inv_exp_i = F::ONE; - let mut coeffs = ifft(poly); - for c in coeffs.coeffs.iter_mut() { - *c *= shift_inv_exp_i; - shift_inv_exp_i *= shift_inv; - } - coeffs -} - #[cfg(test)] mod tests { use crate::field::crandall_field::CrandallField; diff --git a/src/gates/gate.rs b/src/gates/gate.rs index 1765191e..4b37892c 100644 --- a/src/gates/gate.rs +++ b/src/gates/gate.rs @@ -1,9 +1,12 @@ +use std::fmt::{Debug, Error, Formatter}; use std::hash::{Hash, Hasher}; use std::sync::Arc; use crate::circuit_builder::CircuitBuilder; use crate::field::extension_field::target::ExtensionTarget; use crate::field::extension_field::{Extendable, FieldExtension}; +use crate::field::field::Field; +use crate::gates::gate_tree::Tree; use crate::generator::WitnessGenerator; use crate::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase}; @@ -51,15 +54,23 @@ pub trait Gate, const D: usize>: 'static + Send + Sync { vars: EvaluationTargets, ) -> Vec>; - fn eval_filtered(&self, vars: EvaluationVars) -> Vec { - // TODO: Filter + fn eval_filtered(&self, mut vars: EvaluationVars, prefix: &[bool]) -> Vec { + let filter = compute_filter(prefix, vars.local_constants); + vars.remove_prefix(prefix); self.eval_unfiltered(vars) + .into_iter() + .map(|c| filter * c) + .collect() } /// Like `eval_filtered`, but specialized for points in the base field. - fn eval_filtered_base(&self, vars: EvaluationVarsBase) -> Vec { - // TODO: Filter + fn eval_filtered_base(&self, mut vars: EvaluationVarsBase, prefix: &[bool]) -> Vec { + let filter = compute_filter(prefix, vars.local_constants); + vars.remove_prefix(prefix); self.eval_unfiltered_base(vars) + .into_iter() + .map(|c| c * filter) + .collect() } fn eval_filtered_recursively( @@ -113,8 +124,46 @@ impl, const D: usize> Hash for GateRef { impl, const D: usize> Eq for GateRef {} +impl, const D: usize> Debug for GateRef { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { + write!(f, "{}", self.0.id()) + } +} + /// A gate along with any constants used to configure it. pub struct GateInstance, const D: usize> { pub gate_type: GateRef, pub constants: Vec, } + +/// Map each gate to a boolean prefix used to construct the gate's selector polynomial. +#[derive(Debug, Clone)] +pub struct PrefixedGate, const D: usize> { + pub gate: GateRef, + pub prefix: Vec, +} + +impl, const D: usize> PrefixedGate { + pub fn from_tree(tree: Tree>) -> Vec { + tree.traversal() + .into_iter() + .map(|(gate, prefix)| PrefixedGate { gate, prefix }) + .collect() + } +} + +/// A gate's filter is computed as `prod b_i*c_i + (1-b_i)*(1-c_i)`, with `(b_i)` the prefix and +/// `(c_i)` the local constants, which is one if the prefix of `constants` matches `prefix`. +fn compute_filter(prefix: &[bool], constants: &[K]) -> K { + prefix + .iter() + .enumerate() + .map(|(i, &b)| { + if b { + constants[i] + } else { + K::ONE - constants[i] + } + }) + .product() +} diff --git a/src/gates/gate_tree.rs b/src/gates/gate_tree.rs new file mode 100644 index 00000000..1423de76 --- /dev/null +++ b/src/gates/gate_tree.rs @@ -0,0 +1,257 @@ +use log::info; + +use crate::field::extension_field::Extendable; +use crate::gates::gate::GateRef; + +/// A binary tree where leaves hold some type `T` and other nodes are empty. +#[derive(Debug, Clone)] +pub enum Tree { + Leaf(T), + Bifurcation(Option>>, Option>>), +} + +impl Default for Tree { + fn default() -> Self { + Self::Bifurcation(None, None) + } +} + +impl Tree { + /// Traverse a tree using a depth-first traversal and collect data and position for each leaf. + /// A leaf's position is represented by its left/right path, where `false` means left and `true` means right. + pub fn traversal(&self) -> Vec<(T, Vec)> { + let mut res = Vec::new(); + let prefix = []; + self.traverse(&prefix, &mut res); + res + } + + /// Utility function to traverse the tree. + fn traverse(&self, prefix: &[bool], current: &mut Vec<(T, Vec)>) { + match &self { + // If node is a leaf, collect the data and position. + Tree::Leaf(t) => { + current.push((t.clone(), prefix.to_vec())); + } + // Otherwise, traverse the left subtree and then the right subtree. + Tree::Bifurcation(left, right) => { + if let Some(l) = left { + let mut left_prefix = prefix.to_vec(); + left_prefix.push(false); + l.traverse(&left_prefix, current); + } + if let Some(r) = right { + let mut right_prefix = prefix.to_vec(); + right_prefix.push(true); + r.traverse(&right_prefix, current); + } + } + } + } +} + +impl, const D: usize> Tree> { + /// The binary gate tree influences the degree `D` of the constraint polynomial and the number `C` + /// of constant wires in the circuit. We want to construct a tree minimizing both values. To do so + /// we iterate over possible values of `(D, C)` and try to construct a tree with these values. + /// For this construction, we use the greedy algorithm in `Self::find_tree`. + /// This latter function greedily adds gates at the depth where + /// `filtered_deg(gate)=D, constant_wires(gate)=C` to ensure no space is wasted. + /// We return the first tree found in this manner. + pub fn from_gates(mut gates: Vec>) -> Self { + let timer = std::time::Instant::now(); + gates.sort_unstable_by_key(|g| (-(g.0.degree() as isize), -(g.0.num_constants() as isize))); + + for max_degree_bits in 1..10 { + // The constraint polynomials are padded to the next power in `compute_vanishig_polys`. + // So we can restrict our search space by setting `max_degree` to a power of 2. + let max_degree = 1 << max_degree_bits; + for max_constants in 1..100 { + if let Some(mut tree) = Self::find_tree(&gates, max_degree, max_constants) { + tree.shorten(); + info!( + "Found tree with max degree {} in {}s.", + max_degree, + timer.elapsed().as_secs_f32() + ); + return tree; + } + } + } + + panic!("Can't find a tree.") + } + + /// Greedily add gates wherever possible. Returns `None` if this fails. + fn find_tree(gates: &[GateRef], max_degree: usize, max_constants: usize) -> Option { + let mut tree = Tree::default(); + + for g in gates { + tree.try_add_gate(g, max_degree, max_constants)?; + } + Some(tree) + } + + /// Try to add a gate in the tree. Returns `None` if this fails. + fn try_add_gate( + &mut self, + g: &GateRef, + max_degree: usize, + max_constants: usize, + ) -> Option<()> { + // We want `gate.degree + depth <= max_degree` and `gate.num_constants + depth <= max_wires`. + let depth = max_degree + .checked_sub(g.0.degree())? + .min(max_constants.checked_sub(g.0.num_constants())?); + self.try_add_gate_at_depth(g, depth) + } + + /// Try to add a gate in the tree at a specified depth. Returns `None` if this fails. + fn try_add_gate_at_depth(&mut self, g: &GateRef, depth: usize) -> Option<()> { + // If depth is 0, we have to insert the gate here. + if depth == 0 { + return if let Tree::Bifurcation(None, None) = self { + // Insert the gate as a new leaf. + *self = Tree::Leaf(g.clone()); + Some(()) + } else { + // A leaf is already here. + None + }; + } + + // A leaf is already here so we cannot go deeper. + if let Tree::Leaf(_) = self { + return None; + } + + if let Tree::Bifurcation(left, right) = self { + if let Some(left) = left { + // Try to add the gate to the left if there's already a left subtree. + if left.try_add_gate_at_depth(g, depth - 1).is_some() { + return Some(()); + } + } else { + // Add a new left subtree and try to add the gate to it. + let mut new_left = Tree::default(); + if new_left.try_add_gate_at_depth(g, depth - 1).is_some() { + *left = Some(Box::new(new_left)); + return Some(()); + } + } + if let Some(right) = right { + // Try to add the gate to the right if there's already a right subtree. + if right.try_add_gate_at_depth(g, depth - 1).is_some() { + return Some(()); + } + } else { + // Add a new right subtree and try to add the gate to it. + let mut new_right = Tree::default(); + if new_right.try_add_gate_at_depth(g, depth - 1).is_some() { + *right = Some(Box::new(new_right)); + return Some(()); + } + } + } + + None + } + + /// `Self::find_tree` returns a tree where each gate has `F(gate)=M` (see `Self::from_gates` comment). + /// This can produce subtrees with more nodes than necessary. This function removes useless nodes, + /// i.e., nodes that have a left but no right subtree. + fn shorten(&mut self) { + if let Tree::Bifurcation(left, right) = self { + if let (Some(left), None) = (left, right) { + // If the node has a left but no right subtree, set the node to its (shortened) left subtree. + let mut new = *left.clone(); + new.shorten(); + *self = new; + } + } + if let Tree::Bifurcation(left, right) = self { + if let Some(left) = left { + // Shorten the left subtree if there is one. + left.shorten(); + } + if let Some(right) = right { + // Shorten the right subtree if there is one. + right.shorten(); + } + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::field::crandall_field::CrandallField; + use crate::gates::arithmetic::ArithmeticGate; + use crate::gates::base_sum::BaseSumGate; + use crate::gates::constant::ConstantGate; + use crate::gates::gmimc::GMiMCGate; + use crate::gates::interpolation::InterpolationGate; + use crate::gates::mul_extension::MulExtensionGate; + use crate::gates::noop::NoopGate; + use crate::hash::GMIMC_ROUNDS; + + #[test] + fn test_prefix_generation() { + env_logger::init(); + type F = CrandallField; + const D: usize = 4; + + let gates = vec![ + NoopGate::get::(), + ConstantGate::get(), + ArithmeticGate::new(), + BaseSumGate::<4>::new(4), + GMiMCGate::::with_automatic_constants(), + InterpolationGate::new(4), + MulExtensionGate::new(), + ]; + let len = gates.len(); + + let tree = Tree::from_gates(gates.clone()); + let mut gates_with_prefix = tree.traversal(); + for (g, p) in &gates_with_prefix { + info!( + "\nGate: {}, prefix: {:?}.\n\ + Filtered constraint degree: {}, Num constant wires: {}", + &g.0.id()[..20.min(g.0.id().len())], + p, + g.0.degree() + p.len(), + g.0.num_constants() + p.len() + ); + } + + assert_eq!( + gates_with_prefix.len(), + gates.len(), + "The tree has too much or too little gates." + ); + assert!( + gates + .iter() + .all(|g| gates_with_prefix.iter().map(|(gg, _)| gg).any(|gg| gg == g)), + "Some gates are not in the tree." + ); + assert!( + gates_with_prefix + .iter() + .all(|(g, p)| g.0.degree() + g.0.num_constants() + p.len() <= 8), + "Total degree is larger than 8." + ); + + gates_with_prefix.sort_unstable_by_key(|(g, p)| p.len()); + for i in 0..gates_with_prefix.len() { + for j in i + 1..gates_with_prefix.len() { + assert_ne!( + &gates_with_prefix[i].1, + &gates_with_prefix[j].1[0..gates_with_prefix[i].1.len()], + "Some gates share an overlapping prefix" + ); + } + } + } +} diff --git a/src/gates/gmimc.rs b/src/gates/gmimc.rs index 19042d57..bdfade7c 100644 --- a/src/gates/gmimc.rs +++ b/src/gates/gmimc.rs @@ -323,6 +323,8 @@ mod tests { use crate::gates::gmimc::{GMiMCGate, W}; use crate::generator::generate_partial_witness; use crate::gmimc::gmimc_permute_naive; + use crate::permutation_argument::TargetPartitions; + use crate::target::Target; use crate::wire::Wire; use crate::witness::PartialWitness; diff --git a/src/gates/mod.rs b/src/gates/mod.rs index d013c7cd..bb8b178b 100644 --- a/src/gates/mod.rs +++ b/src/gates/mod.rs @@ -2,6 +2,7 @@ pub(crate) mod arithmetic; pub mod base_sum; pub mod constant; pub(crate) mod gate; +pub mod gate_tree; pub mod gmimc; pub mod interpolation; pub mod mul_extension; diff --git a/src/generator.rs b/src/generator.rs index 443809b6..db81172f 100644 --- a/src/generator.rs +++ b/src/generator.rs @@ -2,6 +2,7 @@ use std::collections::{HashMap, HashSet}; use std::fmt::Debug; use crate::field::field::Field; +use crate::permutation_argument::TargetPartitions; use crate::target::Target; use crate::witness::PartialWitness; @@ -24,10 +25,7 @@ pub(crate) fn generate_partial_witness( // 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); - } + let mut pending_generator_indices: HashSet<_> = (0..generators.len()).collect(); // We also track a list of "expired" generators which have already returned false. let mut expired_generator_indices = HashSet::new(); @@ -58,6 +56,11 @@ pub(crate) fn generate_partial_witness( pending_generator_indices = next_pending_generator_indices; } + assert_eq!( + expired_generator_indices.len(), + generators.len(), + "Some generators weren't run." + ); } /// A generator participates in the generation of the witness. diff --git a/src/plonk_challenger.rs b/src/plonk_challenger.rs index b24bbde8..9af5e590 100644 --- a/src/plonk_challenger.rs +++ b/src/plonk_challenger.rs @@ -1,3 +1,5 @@ +use std::convert::TryInto; + use crate::circuit_builder::CircuitBuilder; use crate::field::extension_field::target::ExtensionTarget; use crate::field::extension_field::{Extendable, FieldExtension}; @@ -5,7 +7,6 @@ use crate::field::field::Field; use crate::hash::{permute, SPONGE_RATE, SPONGE_WIDTH}; use crate::proof::{Hash, HashTarget, OpeningSet}; use crate::target::Target; -use std::convert::TryInto; /// Observes prover messages, and generates challenges by hashing the transcript. #[derive(Clone)] @@ -320,6 +321,7 @@ mod tests { use crate::field::crandall_field::CrandallField; use crate::field::field::Field; use crate::generator::generate_partial_witness; + use crate::permutation_argument::TargetPartitions; use crate::plonk_challenger::{Challenger, RecursiveChallenger}; use crate::target::Target; use crate::witness::PartialWitness; diff --git a/src/plonk_common.rs b/src/plonk_common.rs index c9f11b74..617ba530 100644 --- a/src/plonk_common.rs +++ b/src/plonk_common.rs @@ -5,7 +5,7 @@ use crate::circuit_data::CommonCircuitData; use crate::field::extension_field::target::ExtensionTarget; use crate::field::extension_field::Extendable; use crate::field::field::Field; -use crate::gates::gate::GateRef; +use crate::gates::gate::{GateRef, PrefixedGate}; use crate::polynomial::commitment::SALT_SIZE; use crate::polynomial::polynomial::PolynomialCoeffs; use crate::target::Target; @@ -167,13 +167,13 @@ pub(crate) fn eval_vanishing_poly_base, const D: usize>( /// strictly necessary, but it helps performance by ensuring that we allocate a vector with exactly /// the capacity that we need. pub fn evaluate_gate_constraints, const D: usize>( - gates: &[GateRef], + gates: &[PrefixedGate], num_gate_constraints: usize, vars: EvaluationVars, ) -> Vec { let mut constraints = vec![F::Extension::ZERO; num_gate_constraints]; for gate in gates { - let gate_constraints = gate.0.eval_filtered(vars); + let gate_constraints = gate.gate.0.eval_filtered(vars, &gate.prefix); for (i, c) in gate_constraints.into_iter().enumerate() { debug_assert!( i < num_gate_constraints, @@ -186,13 +186,13 @@ pub fn evaluate_gate_constraints, const D: usize>( } pub fn evaluate_gate_constraints_base, const D: usize>( - gates: &[GateRef], + gates: &[PrefixedGate], num_gate_constraints: usize, vars: EvaluationVarsBase, ) -> Vec { let mut constraints = vec![F::ZERO; num_gate_constraints]; for gate in gates { - let gate_constraints = gate.0.eval_filtered_base(vars); + let gate_constraints = gate.gate.0.eval_filtered_base(vars, &gate.prefix); for (i, c) in gate_constraints.into_iter().enumerate() { debug_assert!( i < num_gate_constraints, diff --git a/src/prover.rs b/src/prover.rs index 5ea6ddfd..0d3f1be1 100644 --- a/src/prover.rs +++ b/src/prover.rs @@ -20,7 +20,7 @@ use crate::wire::Wire; use crate::witness::PartialWitness; pub(crate) fn prove, const D: usize>( - prover_data: &ProverOnlyCircuitData, + prover_data: &ProverOnlyCircuitData, common_data: &CommonCircuitData, inputs: PartialWitness, ) -> Proof { @@ -31,10 +31,17 @@ pub(crate) fn prove, const D: usize>( let mut witness = inputs; info!("Running {} generators", prover_data.generators.len()); timed!( - generate_partial_witness(&mut witness, &prover_data.generators), + generate_partial_witness(&mut witness, &prover_data.generators,), "to generate witness" ); + timed!( + witness + .check_copy_constraints(&prover_data.copy_constraints, &prover_data.gate_instances) + .unwrap(), // TODO: Change return value to `Result` and use `?` here. + "to check copy constraints" + ); + let config = &common_data.config; let num_wires = config.num_wires; let num_challenges = config.num_challenges; @@ -159,7 +166,7 @@ fn compute_z, const D: usize>( fn compute_vanishing_polys, const D: usize>( common_data: &CommonCircuitData, - prover_data: &ProverOnlyCircuitData, + prover_data: &ProverOnlyCircuitData, wires_commitment: &ListPolynomialCommitment, plonk_zs_commitment: &ListPolynomialCommitment, betas: &[F], diff --git a/src/target.rs b/src/target.rs index 423865fa..8aec0b5a 100644 --- a/src/target.rs +++ b/src/target.rs @@ -1,6 +1,7 @@ +use std::ops::Range; + use crate::circuit_data::CircuitConfig; use crate::wire::Wire; -use std::ops::Range; /// A location in the witness. #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] diff --git a/src/vars.rs b/src/vars.rs index 74f15f23..a8fa6527 100644 --- a/src/vars.rs +++ b/src/vars.rs @@ -27,6 +27,16 @@ impl<'a, F: Extendable, const D: usize> EvaluationVars<'a, F, D> { let arr = self.local_wires[wire_range].try_into().unwrap(); ExtensionAlgebra::from_basefield_array(arr) } + + pub fn remove_prefix(&mut self, prefix: &[bool]) { + self.local_constants = &self.local_constants[prefix.len()..]; + } +} + +impl<'a, F: Field> EvaluationVarsBase<'a, F> { + pub fn remove_prefix(&mut self, prefix: &[bool]) { + self.local_constants = &self.local_constants[prefix.len()..]; + } } #[derive(Copy, Clone)] diff --git a/src/wire.rs b/src/wire.rs index 02d43029..f63a19c1 100644 --- a/src/wire.rs +++ b/src/wire.rs @@ -1,6 +1,7 @@ -use crate::circuit_data::CircuitConfig; use std::ops::Range; +use crate::circuit_data::CircuitConfig; + /// Represents a wire in the circuit. #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] pub struct Wire { diff --git a/src/witness.rs b/src/witness.rs index e71b1cfb..989ec2bc 100644 --- a/src/witness.rs +++ b/src/witness.rs @@ -1,11 +1,14 @@ use std::collections::HashMap; +use std::convert::TryInto; + +use anyhow::{ensure, Result}; use crate::field::extension_field::target::ExtensionTarget; use crate::field::extension_field::{Extendable, FieldExtension}; use crate::field::field::Field; +use crate::gates::gate::GateInstance; use crate::target::Target; use crate::wire::Wire; -use std::convert::TryInto; #[derive(Clone, Debug)] pub struct PartialWitness { @@ -121,6 +124,31 @@ impl PartialWitness { self.set_target(target, value); } } + + /// Checks that the copy constraints are satisfied in the witness. + pub fn check_copy_constraints( + &self, + copy_constraints: &[(Target, Target)], + gate_instances: &[GateInstance], + ) -> Result<()> + where + F: Extendable, + { + for &(a, b) in copy_constraints { + // TODO: Take care of public inputs once they land. + if let (Target::Wire(wa), Target::Wire(wb)) = (a, b) { + let va = self.target_values.get(&a).copied().unwrap_or(F::ZERO); + let vb = self.target_values.get(&b).copied().unwrap_or(F::ZERO); + ensure!( + va == vb, + "Copy constraint between wire {} of gate #{} (`{}`) and wire {} of gate #{} (`{}`) is not satisfied. \ + Got values of {} and {} respectively.", + wa.input, wa.gate, gate_instances[wa.gate].gate_type.0.id(), wb.input, wb.gate, + gate_instances[wb.gate].gate_type.0.id(), va, vb); + } + } + Ok(()) + } } impl Default for PartialWitness {