From b0a855a9c3764acd5233ed0ba5e8d614bb550657 Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Mon, 23 Aug 2021 12:13:31 -0700 Subject: [PATCH 01/31] progress on permutation --- src/gadgets/arithmetic.rs | 2 +- src/gadgets/mod.rs | 1 + src/gadgets/permutation.rs | 115 ++++++++++++++++++++++++++++++++++++ src/gates/exponentiation.rs | 14 +++-- src/gates/switch.rs | 69 +++++++++++----------- 5 files changed, 163 insertions(+), 38 deletions(-) create mode 100644 src/gadgets/permutation.rs diff --git a/src/gadgets/arithmetic.rs b/src/gadgets/arithmetic.rs index 960f139f..2d354567 100644 --- a/src/gadgets/arithmetic.rs +++ b/src/gadgets/arithmetic.rs @@ -107,7 +107,7 @@ impl, const D: usize> CircuitBuilder { exponent_bits: impl IntoIterator>, ) -> Target { let _false = self._false(); - let gate = ExponentiationGate::new(self.config.clone()); + let gate = ExponentiationGate::new_from_config(self.config.clone()); let num_power_bits = gate.num_power_bits; let mut exp_bits_vec: Vec = exponent_bits.into_iter().map(|b| *b.borrow()).collect(); diff --git a/src/gadgets/mod.rs b/src/gadgets/mod.rs index 0eb42e27..4b3371ef 100644 --- a/src/gadgets/mod.rs +++ b/src/gadgets/mod.rs @@ -3,6 +3,7 @@ pub mod arithmetic_extension; pub mod hash; pub mod insert; pub mod interpolation; +pub mod permutation; pub mod polynomial; pub mod random_access; pub mod range_check; diff --git a/src/gadgets/permutation.rs b/src/gadgets/permutation.rs new file mode 100644 index 00000000..b477a553 --- /dev/null +++ b/src/gadgets/permutation.rs @@ -0,0 +1,115 @@ +use crate::field::{extension_field::Extendable, field_types::Field}; +use crate::gates::switch::SwitchGate; +use crate::iop::generator::{GeneratedValues, SimpleGenerator}; +use crate::iop::target::Target; +use crate::iop::witness::PartialWitness; +use crate::plonk::circuit_builder::CircuitBuilder; + +impl, const D: usize> CircuitBuilder { + /// Assert that two lists of expressions evaluate to permutations of one another. + pub fn assert_permutation( + &mut self, + a: Vec<[Target; CHUNK_SIZE]>, + b: Vec<[Target; CHUNK_SIZE]>, + ) { + assert_eq!( + a.len(), + b.len(), + "Permutation must have same number of inputs and outputs" + ); + assert_eq!(a[0].len(), b[0].len(), "Chunk sizes must be the same"); + + match a.len() { + // Two empty lists are permutations of one another, trivially. + 0 => (), + // Two singleton lists are permutations of one another as long as their items are equal. + 1 => { + for e in 0..CHUNK_SIZE { + self.assert_equal(a[0][e], b[0][e]) + } + } + 2 => self.assert_permutation_2x2(a[0], a[1], b[0], b[1]), + // For larger lists, we recursively use two smaller permutation networks. + //_ => self.assert_permutation_recursive(a, b) + _ => self.assert_permutation_recursive(a, b), + } + } + + /// Assert that [a, b] is a permutation of [c, d]. + fn assert_permutation_2x2( + &mut self, + a: [Target; CHUNK_SIZE], + b: [Target; CHUNK_SIZE], + c: [Target; CHUNK_SIZE], + d: [Target; CHUNK_SIZE], + ) { + let gate = SwitchGate::::new(1); + let gate_index = self.add_gate(gate.clone(), vec![]); + + for e in 0..CHUNK_SIZE { + self.route(a[e], Target::wire(gate_index, gate.wire_first_input(0, e))); + self.route(b[e], Target::wire(gate_index, gate.wire_second_input(0, e))); + self.route(c[e], Target::wire(gate_index, gate.wire_first_output(0, e))); + self.route( + d[e], + Target::wire(gate_index, gate.wire_second_output(0, e)), + ); + } + } + + fn assert_permutation_recursive( + &mut self, + a: Vec<[Target; CHUNK_SIZE]>, + b: Vec<[Target; CHUNK_SIZE]>, + ) { + } +} + +struct PermutationGenerator { + gate_index: usize, +} + +impl SimpleGenerator for PermutationGenerator { + fn dependencies(&self) -> Vec { + todo!() + } + + fn run_once(&self, witness: &PartialWitness, out_buffer: &mut GeneratedValues) { + todo!() + } +} + +#[cfg(test)] +mod tests { + use anyhow::Result; + + use super::*; + use crate::field::crandall_field::CrandallField; + use crate::field::extension_field::quartic::QuarticCrandallField; + use crate::field::field_types::Field; + use crate::iop::witness::PartialWitness; + use crate::plonk::circuit_data::CircuitConfig; + use crate::plonk::verifier::verify; + + fn test_permutation(size: usize) -> Result<()> { + type F = CrandallField; + type FF = QuarticCrandallField; + let len = 1 << len_log; + let config = CircuitConfig::large_config(); + let pw = PartialWitness::new(config.num_wires); + let mut builder = CircuitBuilder::::new(config); + let vec = FF::rand_vec(len); + let v: Vec<_> = vec.iter().map(|x| builder.constant_extension(*x)).collect(); + + for i in 0..len { + let it = builder.constant(F::from_canonical_usize(i)); + let elem = builder.constant_extension(vec[i]); + builder.random_access(it, elem, v.clone()); + } + + let data = builder.build(); + let proof = data.prove(pw)?; + + verify(proof, &data.verifier_only, &data.common) + } +} diff --git a/src/gates/exponentiation.rs b/src/gates/exponentiation.rs index d57c9001..1a3a6ea3 100644 --- a/src/gates/exponentiation.rs +++ b/src/gates/exponentiation.rs @@ -20,14 +20,18 @@ pub(crate) struct ExponentiationGate, const D: usize> { } impl, const D: usize> ExponentiationGate { - pub fn new(config: CircuitConfig) -> Self { - let num_power_bits = Self::max_power_bits(config.num_wires, config.num_routed_wires); + pub fn new(num_power_bits: usize) -> Self { Self { num_power_bits, _phantom: PhantomData, } } + pub fn new_from_config(config: CircuitConfig) -> Self { + let num_power_bits = Self::max_power_bits(config.num_wires, config.num_routed_wires); + Self::new(num_power_bits) + } + fn max_power_bits(num_wires: usize, num_routed_wires: usize) -> usize { // 2 wires are reserved for the base and output. let max_for_routed_wires = num_routed_wires - 2; @@ -296,12 +300,14 @@ mod tests { ..CircuitConfig::large_config() }; - test_low_degree::(ExponentiationGate::new(config)); + test_low_degree::(ExponentiationGate::new_from_config(config)); } #[test] fn eval_fns() -> Result<()> { - test_eval_fns::(ExponentiationGate::new(CircuitConfig::large_config())) + test_eval_fns::(ExponentiationGate::new_from_config( + CircuitConfig::large_config(), + )) } #[test] diff --git a/src/gates/switch.rs b/src/gates/switch.rs index 6ef29341..a48adb61 100644 --- a/src/gates/switch.rs +++ b/src/gates/switch.rs @@ -20,45 +20,49 @@ pub(crate) struct SwitchGate, const D: usize, const CHUNK_SIZE: } impl, const D: usize, const CHUNK_SIZE: usize> SwitchGate { - pub fn new(config: CircuitConfig) -> Self { - let num_copies = Self::max_num_copies(config.num_routed_wires); + pub fn new(num_copies: usize) -> Self { Self { num_copies, _phantom: PhantomData, } } - fn max_num_copies(num_routed_wires: usize) -> usize { - num_routed_wires / (4 * CHUNK_SIZE + 1) + pub fn new_from_config(config: CircuitConfig) -> Self { + let num_copies = Self::max_num_copies(config.num_routed_wires); + Self::new(num_copies) } - pub fn wire_switch_bool(&self, copy: usize) -> usize { - debug_assert!(copy < self.num_copies); - copy * (4 * CHUNK_SIZE + 1) + fn max_num_copies(num_routed_wires: usize) -> usize { + num_routed_wires / (4 * CHUNK_SIZE) } pub fn wire_first_input(&self, copy: usize, element: usize) -> usize { debug_assert!(copy < self.num_copies); debug_assert!(element < CHUNK_SIZE); - copy * (4 * CHUNK_SIZE + 1) + 1 + element + copy * (4 * CHUNK_SIZE) + element } pub fn wire_second_input(&self, copy: usize, element: usize) -> usize { debug_assert!(copy < self.num_copies); debug_assert!(element < CHUNK_SIZE); - copy * (4 * CHUNK_SIZE + 1) + 1 + CHUNK_SIZE + element + copy * (4 * CHUNK_SIZE) + CHUNK_SIZE + element } pub fn wire_first_output(&self, copy: usize, element: usize) -> usize { debug_assert!(copy < self.num_copies); debug_assert!(element < CHUNK_SIZE); - copy * (4 * CHUNK_SIZE + 1) + 1 + 2 * CHUNK_SIZE + element + copy * (4 * CHUNK_SIZE) + 2 * CHUNK_SIZE + element } pub fn wire_second_output(&self, copy: usize, element: usize) -> usize { debug_assert!(copy < self.num_copies); debug_assert!(element < CHUNK_SIZE); - copy * (4 * CHUNK_SIZE + 1) + 1 + 3 * CHUNK_SIZE + element + copy * (4 * CHUNK_SIZE) + 3 * CHUNK_SIZE + element + } + + pub fn wire_switch_bool(&self, copy: usize) -> usize { + debug_assert!(copy < self.num_copies); + self.num_copies * (4 * CHUNK_SIZE) + copy } } @@ -200,10 +204,11 @@ impl, const D: usize, const CHUNK_SIZE: usize> SimpleGenerator< let mut deps = Vec::new(); for c in 0..self.gate.num_copies { - deps.push(local_target(self.gate.wire_switch_bool(c))); for e in 0..CHUNK_SIZE { deps.push(local_target(self.gate.wire_first_input(c, e))); deps.push(local_target(self.gate.wire_second_input(c, e))); + deps.push(local_target(self.gate.wire_first_output(c, e))); + deps.push(local_target(self.gate.wire_second_output(c, e))); } } @@ -219,19 +224,17 @@ impl, const D: usize, const CHUNK_SIZE: usize> SimpleGenerator< let get_local_wire = |input| witness.get_wire(local_wire(input)); for c in 0..self.gate.num_copies { - let switch_bool = get_local_wire(self.gate.wire_switch_bool(c)); + let switch_bool_wire = local_wire(self.gate.wire_switch_bool(c)); for e in 0..CHUNK_SIZE { let first_input = get_local_wire(self.gate.wire_first_input(c, e)); let second_input = get_local_wire(self.gate.wire_second_input(c, e)); - let first_output_wire = local_wire(self.gate.wire_first_output(c, e)); - let second_output_wire = local_wire(self.gate.wire_second_output(c, e)); + let first_output = get_local_wire(self.gate.wire_first_output(c, e)); + let second_output = get_local_wire(self.gate.wire_second_output(c, e)); - if switch_bool == F::ONE { - out_buffer.set_wire(first_output_wire, second_input); - out_buffer.set_wire(second_output_wire, first_input); + if first_input == first_output { + out_buffer.set_wire(switch_bool_wire, F::ONE); } else { - out_buffer.set_wire(first_output_wire, first_input); - out_buffer.set_wire(second_output_wire, second_input); + out_buffer.set_wire(switch_bool_wire, F::ZERO); } } } @@ -261,19 +264,19 @@ mod tests { _phantom: PhantomData, }; - assert_eq!(gate.wire_switch_bool(0), 0); - assert_eq!(gate.wire_first_input(0, 0), 1); - assert_eq!(gate.wire_first_input(0, 2), 3); - assert_eq!(gate.wire_second_input(0, 0), 4); - assert_eq!(gate.wire_second_input(0, 2), 6); - assert_eq!(gate.wire_first_output(0, 0), 7); - assert_eq!(gate.wire_second_output(0, 2), 12); - assert_eq!(gate.wire_switch_bool(1), 13); - assert_eq!(gate.wire_first_input(1, 0), 14); - assert_eq!(gate.wire_second_output(1, 2), 25); - assert_eq!(gate.wire_switch_bool(2), 26); - assert_eq!(gate.wire_first_input(2, 0), 27); - assert_eq!(gate.wire_second_output(2, 2), 38); + assert_eq!(gate.wire_first_input(0, 0), 0); + assert_eq!(gate.wire_first_input(0, 2), 2); + assert_eq!(gate.wire_second_input(0, 0), 3); + assert_eq!(gate.wire_second_input(0, 2), 5); + assert_eq!(gate.wire_first_output(0, 0), 6); + assert_eq!(gate.wire_second_output(0, 2), 11); + assert_eq!(gate.wire_first_input(1, 0), 12); + assert_eq!(gate.wire_second_output(1, 2), 23); + assert_eq!(gate.wire_first_input(2, 0), 24); + assert_eq!(gate.wire_second_output(2, 2), 35); + assert_eq!(gate.wire_switch_bool(0), 36); + assert_eq!(gate.wire_switch_bool(1), 37); + assert_eq!(gate.wire_switch_bool(2), 38); } #[test] From 412ada762aee080992dc402f1fa9718be3caa54f Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Mon, 23 Aug 2021 14:03:34 -0700 Subject: [PATCH 02/31] permutation progress --- src/gadgets/permutation.rs | 42 +++++++++++++++++++++++++++++++++----- 1 file changed, 37 insertions(+), 5 deletions(-) diff --git a/src/gadgets/permutation.rs b/src/gadgets/permutation.rs index b477a553..22e3bb77 100644 --- a/src/gadgets/permutation.rs +++ b/src/gadgets/permutation.rs @@ -4,6 +4,7 @@ use crate::iop::generator::{GeneratedValues, SimpleGenerator}; use crate::iop::target::Target; use crate::iop::witness::PartialWitness; use crate::plonk::circuit_builder::CircuitBuilder; +use std::convert::TryInto; impl, const D: usize> CircuitBuilder { /// Assert that two lists of expressions evaluate to permutations of one another. @@ -43,18 +44,34 @@ impl, const D: usize> CircuitBuilder { c: [Target; CHUNK_SIZE], d: [Target; CHUNK_SIZE], ) { + let (_, _, gate_c, gate_d) = self.create_switch(a, b); + for e in 0..CHUNK_SIZE { + self.route(c[e], gate_c[e]); + self.route(d[e], gate_d[e]); + } + } + + fn create_switch( + &mut self, + a: [Target; CHUNK_SIZE], + b: [Target; CHUNK_SIZE], + ) -> (SwitchGate, usize, [Target; CHUNK_SIZE], [Target; CHUNK_SIZE]) { let gate = SwitchGate::::new(1); let gate_index = self.add_gate(gate.clone(), vec![]); + let mut c = Vec::new(); + let mut d = Vec::new(); for e in 0..CHUNK_SIZE { self.route(a[e], Target::wire(gate_index, gate.wire_first_input(0, e))); self.route(b[e], Target::wire(gate_index, gate.wire_second_input(0, e))); - self.route(c[e], Target::wire(gate_index, gate.wire_first_output(0, e))); - self.route( - d[e], - Target::wire(gate_index, gate.wire_second_output(0, e)), - ); + c.push(Target::wire(gate_index, gate.wire_first_output(0, e))); + d.push(Target::wire(gate_index, gate.wire_second_output(0, e))); } + + let c_arr: [Target; CHUNK_SIZE] = c.try_into().unwrap(); + let d_arr: [Target; CHUNK_SIZE] = d.try_into().unwrap(); + + (gate, gate_index, c_arr, d_arr) } fn assert_permutation_recursive( @@ -62,6 +79,21 @@ impl, const D: usize> CircuitBuilder { a: Vec<[Target; CHUNK_SIZE]>, b: Vec<[Target; CHUNK_SIZE]>, ) { + let n = a.len(); + let even = n % 2 == 0; + + let mut child_1_a = Vec::new(); + let mut child_1_b = Vec::new(); + let mut child_2_a = Vec::new(); + let mut child_2_b = Vec::new(); + + // See Figure 8 in the AS-Waksman paper. + let a_num_switches = n / 2; + let b_num_switches = if even { a_num_switches - 1 } else { a_num_switches }; + + for i in 0..a_num_switches { + let (gate, gate_index) = self.create_switch() + } } } From a574fecc4d220ea5541fd32e21fce5f07827256d Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Mon, 23 Aug 2021 14:53:32 -0700 Subject: [PATCH 03/31] permutation progress --- src/gadgets/permutation.rs | 37 +++++++++++++++++++++++++++++++++---- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/src/gadgets/permutation.rs b/src/gadgets/permutation.rs index 22e3bb77..e5f67ea6 100644 --- a/src/gadgets/permutation.rs +++ b/src/gadgets/permutation.rs @@ -1,10 +1,11 @@ +use std::convert::TryInto; + use crate::field::{extension_field::Extendable, field_types::Field}; use crate::gates::switch::SwitchGate; use crate::iop::generator::{GeneratedValues, SimpleGenerator}; use crate::iop::target::Target; use crate::iop::witness::PartialWitness; use crate::plonk::circuit_builder::CircuitBuilder; -use std::convert::TryInto; impl, const D: usize> CircuitBuilder { /// Assert that two lists of expressions evaluate to permutations of one another. @@ -55,7 +56,12 @@ impl, const D: usize> CircuitBuilder { &mut self, a: [Target; CHUNK_SIZE], b: [Target; CHUNK_SIZE], - ) -> (SwitchGate, usize, [Target; CHUNK_SIZE], [Target; CHUNK_SIZE]) { + ) -> ( + SwitchGate, + usize, + [Target; CHUNK_SIZE], + [Target; CHUNK_SIZE], + ) { let gate = SwitchGate::::new(1); let gate_index = self.add_gate(gate.clone(), vec![]); @@ -89,11 +95,34 @@ impl, const D: usize> CircuitBuilder { // See Figure 8 in the AS-Waksman paper. let a_num_switches = n / 2; - let b_num_switches = if even { a_num_switches - 1 } else { a_num_switches }; + let b_num_switches = if even { + a_num_switches - 1 + } else { + a_num_switches + }; for i in 0..a_num_switches { - let (gate, gate_index) = self.create_switch() + let (_, _, out_1, out_2) = self.create_switch(a[i * 2], a[i * 2 + 1]); + child_1_a.push(out_1); + child_2_a.push(out_2); } + for i in 0..b_num_switches { + let (_, _, out_1, out_2) = self.create_switch(b[i * 2], b[i * 2 + 1]); + child_1_b.push(out_1); + child_2_b.push(out_2); + } + + // See Figure 8 in the AS-Waksman paper. + if even { + child_1_b.push(b[n - 2].clone()); + child_2_b.push(b[n - 1].clone()); + } else { + child_2_a.push(a[n - 1].clone()); + child_2_b.push(b[n - 1].clone()); + } + + self.assert_permutation(child_1_a, child_1_b); + self.assert_permutation(child_2_a, child_2_b); } } From 2ab37e688f06edb02bfbd340d5a717c96e38730e Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Tue, 24 Aug 2021 18:35:30 -0700 Subject: [PATCH 04/31] progress --- src/gadgets/permutation.rs | 95 +++++++++++++++++++++------- src/gates/switch.rs | 117 +++++++++++++++++++---------------- src/plonk/circuit_builder.rs | 5 +- 3 files changed, 143 insertions(+), 74 deletions(-) diff --git a/src/gadgets/permutation.rs b/src/gadgets/permutation.rs index e5f67ea6..4b301c04 100644 --- a/src/gadgets/permutation.rs +++ b/src/gadgets/permutation.rs @@ -3,7 +3,7 @@ use std::convert::TryInto; use crate::field::{extension_field::Extendable, field_types::Field}; use crate::gates::switch::SwitchGate; use crate::iop::generator::{GeneratedValues, SimpleGenerator}; -use crate::iop::target::Target; +use crate::iop::target::{BoolTarget, Target}; use crate::iop::witness::PartialWitness; use crate::plonk::circuit_builder::CircuitBuilder; @@ -45,7 +45,7 @@ impl, const D: usize> CircuitBuilder { c: [Target; CHUNK_SIZE], d: [Target; CHUNK_SIZE], ) { - let (_, _, gate_c, gate_d) = self.create_switch(a, b); + let (_, gate_c, gate_d) = self.create_switch(a, b); for e in 0..CHUNK_SIZE { self.route(c[e], gate_c[e]); self.route(d[e], gate_d[e]); @@ -56,28 +56,64 @@ impl, const D: usize> CircuitBuilder { &mut self, a: [Target; CHUNK_SIZE], b: [Target; CHUNK_SIZE], - ) -> ( - SwitchGate, - usize, - [Target; CHUNK_SIZE], - [Target; CHUNK_SIZE], - ) { - let gate = SwitchGate::::new(1); - let gate_index = self.add_gate(gate.clone(), vec![]); + ) -> (usize, [Target; CHUNK_SIZE], [Target; CHUNK_SIZE]) { + if self.current_switch_gates.len() < CHUNK_SIZE { + self.current_switch_gates + .extend(vec![None; CHUNK_SIZE - self.current_switch_gates.len()]); + } + + let (gate_index, mut next_copy) = match self.current_switch_gates[CHUNK_SIZE - 1] { + None => { + let gate = SwitchGate::::new_from_config(self.config.clone()); + let gate_index = self.add_gate(gate.clone(), vec![]); + (gate_index, 0) + } + Some((idx, next_copy)) => (idx, next_copy), + }; + + let num_copies = + SwitchGate::::max_num_copies(self.config.num_routed_wires); let mut c = Vec::new(); let mut d = Vec::new(); for e in 0..CHUNK_SIZE { - self.route(a[e], Target::wire(gate_index, gate.wire_first_input(0, e))); - self.route(b[e], Target::wire(gate_index, gate.wire_second_input(0, e))); - c.push(Target::wire(gate_index, gate.wire_first_output(0, e))); - d.push(Target::wire(gate_index, gate.wire_second_output(0, e))); + self.route( + a[e], + Target::wire( + gate_index, + SwitchGate::::wire_first_input(0, e), + ), + ); + self.route( + b[e], + Target::wire( + gate_index, + SwitchGate::::wire_second_input(0, e), + ), + ); + c.push(Target::wire( + gate_index, + SwitchGate::::wire_first_output(0, e), + )); + d.push(Target::wire( + gate_index, + SwitchGate::::wire_second_output(0, e), + )); } let c_arr: [Target; CHUNK_SIZE] = c.try_into().unwrap(); let d_arr: [Target; CHUNK_SIZE] = d.try_into().unwrap(); - (gate, gate_index, c_arr, d_arr) + next_copy += 1; + if next_copy == num_copies { + let new_gate = SwitchGate::::new_from_config(self.config.clone()); + let new_gate_index = self.add_gate(new_gate.clone(), vec![]); + self.current_switch_gates[CHUNK_SIZE - 1] = Some((new_gate_index, 0)); + } else { + self.current_switch_gates[CHUNK_SIZE - 1] = Some((gate_index, next_copy)); + } + + (gate_index, c_arr, d_arr) } fn assert_permutation_recursive( @@ -102,12 +138,12 @@ impl, const D: usize> CircuitBuilder { }; for i in 0..a_num_switches { - let (_, _, out_1, out_2) = self.create_switch(a[i * 2], a[i * 2 + 1]); + let (_, out_1, out_2) = self.create_switch(a[i * 2], a[i * 2 + 1]); child_1_a.push(out_1); child_2_a.push(out_2); } for i in 0..b_num_switches { - let (_, _, out_1, out_2) = self.create_switch(b[i * 2], b[i * 2 + 1]); + let (_, out_1, out_2) = self.create_switch(b[i * 2], b[i * 2 + 1]); child_1_b.push(out_1); child_2_b.push(out_2); } @@ -126,13 +162,30 @@ impl, const D: usize> CircuitBuilder { } } -struct PermutationGenerator { - gate_index: usize, +fn route_one_layer( + a_values: Vec, + b_values: Vec, + a_wires: Vec<[Target; CHUNK_SIZE]>, + b_wires: Vec<[Target; CHUNK_SIZE]>, +) { + todo!() } -impl SimpleGenerator for PermutationGenerator { +struct PermutationGenerator { + a_values: Vec, + b_values: Vec, + a_wires: Vec<[Target; CHUNK_SIZE]>, + b_wires: Vec<[Target; CHUNK_SIZE]>, +} + +impl SimpleGenerator for PermutationGenerator { fn dependencies(&self) -> Vec { - todo!() + self.a_wires + .iter() + .map(|arr| arr.to_vec()) + .flatten() + .collect() + //.chain(self.b_wires.iter()).collect() } fn run_once(&self, witness: &PartialWitness, out_buffer: &mut GeneratedValues) { diff --git a/src/gates/switch.rs b/src/gates/switch.rs index a48adb61..70bfd45d 100644 --- a/src/gates/switch.rs +++ b/src/gates/switch.rs @@ -32,37 +32,32 @@ impl, const D: usize, const CHUNK_SIZE: usize> SwitchGate usize { + pub fn max_num_copies(num_routed_wires: usize) -> usize { num_routed_wires / (4 * CHUNK_SIZE) } - pub fn wire_first_input(&self, copy: usize, element: usize) -> usize { - debug_assert!(copy < self.num_copies); + pub fn wire_first_input(copy: usize, element: usize) -> usize { debug_assert!(element < CHUNK_SIZE); copy * (4 * CHUNK_SIZE) + element } - pub fn wire_second_input(&self, copy: usize, element: usize) -> usize { - debug_assert!(copy < self.num_copies); + pub fn wire_second_input(copy: usize, element: usize) -> usize { debug_assert!(element < CHUNK_SIZE); copy * (4 * CHUNK_SIZE) + CHUNK_SIZE + element } - pub fn wire_first_output(&self, copy: usize, element: usize) -> usize { - debug_assert!(copy < self.num_copies); + pub fn wire_first_output(copy: usize, element: usize) -> usize { debug_assert!(element < CHUNK_SIZE); copy * (4 * CHUNK_SIZE) + 2 * CHUNK_SIZE + element } - pub fn wire_second_output(&self, copy: usize, element: usize) -> usize { - debug_assert!(copy < self.num_copies); + pub fn wire_second_output(copy: usize, element: usize) -> usize { debug_assert!(element < CHUNK_SIZE); copy * (4 * CHUNK_SIZE) + 3 * CHUNK_SIZE + element } - pub fn wire_switch_bool(&self, copy: usize) -> usize { - debug_assert!(copy < self.num_copies); - self.num_copies * (4 * CHUNK_SIZE) + copy + pub fn wire_switch_bool(num_copies: usize, copy: usize) -> usize { + num_copies * (4 * CHUNK_SIZE) + copy } } @@ -77,14 +72,14 @@ impl, const D: usize, const CHUNK_SIZE: usize> Gate let mut constraints = Vec::with_capacity(self.num_constraints()); for c in 0..self.num_copies { - let switch_bool = vars.local_wires[self.wire_switch_bool(c)]; + let switch_bool = vars.local_wires[Self::wire_switch_bool(self.num_wires(), c)]; let not_switch = F::Extension::ONE - switch_bool; for e in 0..CHUNK_SIZE { - let first_input = vars.local_wires[self.wire_first_input(c, e)]; - let second_input = vars.local_wires[self.wire_second_input(c, e)]; - let first_output = vars.local_wires[self.wire_first_output(c, e)]; - let second_output = vars.local_wires[self.wire_second_output(c, e)]; + let first_input = vars.local_wires[Self::wire_first_input(c, e)]; + let second_input = vars.local_wires[Self::wire_second_input(c, e)]; + let first_output = vars.local_wires[Self::wire_first_output(c, e)]; + let second_output = vars.local_wires[Self::wire_second_output(c, e)]; constraints.push(switch_bool * (first_input - second_output)); constraints.push(switch_bool * (second_input - first_output)); @@ -100,14 +95,14 @@ impl, const D: usize, const CHUNK_SIZE: usize> Gate let mut constraints = Vec::with_capacity(self.num_constraints()); for c in 0..self.num_copies { - let switch_bool = vars.local_wires[self.wire_switch_bool(c)]; + let switch_bool = vars.local_wires[Self::wire_switch_bool(self.num_copies, c)]; let not_switch = F::ONE - switch_bool; for e in 0..CHUNK_SIZE { - let first_input = vars.local_wires[self.wire_first_input(c, e)]; - let second_input = vars.local_wires[self.wire_second_input(c, e)]; - let first_output = vars.local_wires[self.wire_first_output(c, e)]; - let second_output = vars.local_wires[self.wire_second_output(c, e)]; + let first_input = vars.local_wires[Self::wire_first_input(c, e)]; + let second_input = vars.local_wires[Self::wire_second_input(c, e)]; + let first_output = vars.local_wires[Self::wire_first_output(c, e)]; + let second_output = vars.local_wires[Self::wire_second_output(c, e)]; constraints.push(switch_bool * (first_input - second_output)); constraints.push(switch_bool * (second_input - first_output)); @@ -128,14 +123,14 @@ impl, const D: usize, const CHUNK_SIZE: usize> Gate let one = builder.one_extension(); for c in 0..self.num_copies { - let switch_bool = vars.local_wires[self.wire_switch_bool(c)]; + let switch_bool = vars.local_wires[Self::wire_switch_bool(self.num_copies, c)]; let not_switch = builder.sub_extension(one, switch_bool); for e in 0..CHUNK_SIZE { - let first_input = vars.local_wires[self.wire_first_input(c, e)]; - let second_input = vars.local_wires[self.wire_second_input(c, e)]; - let first_output = vars.local_wires[self.wire_first_output(c, e)]; - let second_output = vars.local_wires[self.wire_second_output(c, e)]; + let first_input = vars.local_wires[Self::wire_first_input(c, e)]; + let second_input = vars.local_wires[Self::wire_second_input(c, e)]; + let first_output = vars.local_wires[Self::wire_first_output(c, e)]; + let second_output = vars.local_wires[Self::wire_second_output(c, e)]; let first_switched = builder.sub_extension(first_input, second_output); let first_switched_constraint = builder.mul_extension(switch_bool, first_switched); @@ -174,7 +169,7 @@ impl, const D: usize, const CHUNK_SIZE: usize> Gate } fn num_wires(&self) -> usize { - self.wire_second_output(self.num_copies - 1, CHUNK_SIZE - 1) + 1 + Self::wire_second_output(self.num_copies - 1, CHUNK_SIZE - 1) + 1 } fn num_constants(&self) -> usize { @@ -205,10 +200,18 @@ impl, const D: usize, const CHUNK_SIZE: usize> SimpleGenerator< let mut deps = Vec::new(); for c in 0..self.gate.num_copies { for e in 0..CHUNK_SIZE { - deps.push(local_target(self.gate.wire_first_input(c, e))); - deps.push(local_target(self.gate.wire_second_input(c, e))); - deps.push(local_target(self.gate.wire_first_output(c, e))); - deps.push(local_target(self.gate.wire_second_output(c, e))); + deps.push(local_target( + SwitchGate::::wire_first_input(c, e), + )); + deps.push(local_target( + SwitchGate::::wire_second_input(c, e), + )); + deps.push(local_target( + SwitchGate::::wire_first_output(c, e), + )); + deps.push(local_target( + SwitchGate::::wire_second_output(c, e), + )); } } @@ -224,12 +227,19 @@ impl, const D: usize, const CHUNK_SIZE: usize> SimpleGenerator< let get_local_wire = |input| witness.get_wire(local_wire(input)); for c in 0..self.gate.num_copies { - let switch_bool_wire = local_wire(self.gate.wire_switch_bool(c)); + let switch_bool_wire = local_wire(SwitchGate::::wire_switch_bool( + self.gate.num_copies, + c, + )); for e in 0..CHUNK_SIZE { - let first_input = get_local_wire(self.gate.wire_first_input(c, e)); - let second_input = get_local_wire(self.gate.wire_second_input(c, e)); - let first_output = get_local_wire(self.gate.wire_first_output(c, e)); - let second_output = get_local_wire(self.gate.wire_second_output(c, e)); + let first_input = + get_local_wire(SwitchGate::::wire_first_input(c, e)); + let second_input = + get_local_wire(SwitchGate::::wire_second_input(c, e)); + let first_output = + get_local_wire(SwitchGate::::wire_first_output(c, e)); + let second_output = + get_local_wire(SwitchGate::::wire_second_output(c, e)); if first_input == first_output { out_buffer.set_wire(switch_bool_wire, F::ONE); @@ -259,24 +269,27 @@ mod tests { #[test] fn wire_indices() { - let gate = SwitchGate:: { - num_copies: 3, + type SG = SwitchGate; + let num_copies = 3; + + let gate = SG { + num_copies, _phantom: PhantomData, }; - assert_eq!(gate.wire_first_input(0, 0), 0); - assert_eq!(gate.wire_first_input(0, 2), 2); - assert_eq!(gate.wire_second_input(0, 0), 3); - assert_eq!(gate.wire_second_input(0, 2), 5); - assert_eq!(gate.wire_first_output(0, 0), 6); - assert_eq!(gate.wire_second_output(0, 2), 11); - assert_eq!(gate.wire_first_input(1, 0), 12); - assert_eq!(gate.wire_second_output(1, 2), 23); - assert_eq!(gate.wire_first_input(2, 0), 24); - assert_eq!(gate.wire_second_output(2, 2), 35); - assert_eq!(gate.wire_switch_bool(0), 36); - assert_eq!(gate.wire_switch_bool(1), 37); - assert_eq!(gate.wire_switch_bool(2), 38); + assert_eq!(SG::wire_first_input(0, 0), 0); + assert_eq!(SG::wire_first_input(0, 2), 2); + assert_eq!(SG::wire_second_input(0, 0), 3); + assert_eq!(SG::wire_second_input(0, 2), 5); + assert_eq!(SG::wire_first_output(0, 0), 6); + assert_eq!(SG::wire_second_output(0, 2), 11); + assert_eq!(SG::wire_first_input(1, 0), 12); + assert_eq!(SG::wire_second_output(1, 2), 23); + assert_eq!(SG::wire_first_input(2, 0), 24); + assert_eq!(SG::wire_second_output(2, 2), 35); + assert_eq!(SG::wire_switch_bool(num_copies, 0), 36); + assert_eq!(SG::wire_switch_bool(num_copies, 1), 37); + assert_eq!(SG::wire_switch_bool(num_copies, 2), 38); } #[test] diff --git a/src/plonk/circuit_builder.rs b/src/plonk/circuit_builder.rs index 7a5b57bc..0daa4943 100644 --- a/src/plonk/circuit_builder.rs +++ b/src/plonk/circuit_builder.rs @@ -40,7 +40,7 @@ pub struct CircuitBuilder, const D: usize> { gates: HashSet>, /// The concrete placement of each gate. - gate_instances: Vec>, + pub(crate) gate_instances: Vec>, /// Targets to be made public. public_inputs: Vec, @@ -65,6 +65,8 @@ pub struct CircuitBuilder, const D: usize> { /// A map `(c0, c1) -> (g, i)` from constants `(c0,c1)` to an available arithmetic gate using /// these constants with gate index `g` and already using `i` arithmetic operations. pub(crate) free_arithmetic: HashMap<(F, F), (usize, usize)>, + + pub(crate) current_switch_gates: Vec>, } impl, const D: usize> CircuitBuilder { @@ -82,6 +84,7 @@ impl, const D: usize> CircuitBuilder { constants_to_targets: HashMap::new(), targets_to_constants: HashMap::new(), free_arithmetic: HashMap::new(), + current_switch_gates: Vec::new(), } } From 013c8bb612354d5e9f1872d123902826c6644d09 Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Thu, 26 Aug 2021 14:23:30 -0700 Subject: [PATCH 05/31] progress --- Cargo.toml | 1 + src/gadgets/permutation.rs | 192 ++++++++++++++++++++++++++++++++++--- src/gates/switch.rs | 6 +- src/util/bimap.rs | 76 +++++++++++++++ src/util/mod.rs | 1 + 5 files changed, 260 insertions(+), 16 deletions(-) create mode 100644 src/util/bimap.rs diff --git a/Cargo.toml b/Cargo.toml index d8b84356..790ff6b9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,7 @@ edition = "2018" default-run = "bench_recursion" [dependencies] +bimap = "0.4.0" env_logger = "0.9.0" log = "0.4.14" itertools = "0.10.0" diff --git a/src/gadgets/permutation.rs b/src/gadgets/permutation.rs index 4b301c04..64e18f93 100644 --- a/src/gadgets/permutation.rs +++ b/src/gadgets/permutation.rs @@ -1,3 +1,4 @@ +use std::collections::BTreeMap; use std::convert::TryInto; use crate::field::{extension_field::Extendable, field_types::Field}; @@ -6,6 +7,7 @@ use crate::iop::generator::{GeneratedValues, SimpleGenerator}; use crate::iop::target::{BoolTarget, Target}; use crate::iop::witness::PartialWitness; use crate::plonk::circuit_builder::CircuitBuilder; +use crate::util::bimap::bimap_from_lists; impl, const D: usize> CircuitBuilder { /// Assert that two lists of expressions evaluate to permutations of one another. @@ -162,18 +164,150 @@ impl, const D: usize> CircuitBuilder { } } -fn route_one_layer( - a_values: Vec, - b_values: Vec, - a_wires: Vec<[Target; CHUNK_SIZE]>, - b_wires: Vec<[Target; CHUNK_SIZE]>, +fn route( + a_values: Vec<[F; CHUNK_SIZE]>, + b_values: Vec<[F; CHUNK_SIZE]>, + a_switches: Vec<[Target; CHUNK_SIZE]>, + b_switches: Vec<[Target; CHUNK_SIZE]>, + witness: &PartialWitness, + out_buffer: &mut GeneratedValues, ) { - todo!() + assert_eq!(a_values.len(), b_values.len()); + let n = a_values.len(); + let even = n % 2 == 0; + // Bimap: maps indices of values in a to indices of the same values in b + let ab_map = bimap_from_lists(a_values, b_values); + let switches = [a_switches, b_switches]; + + // Given a side and an index, returns the index in the other side that corresponds to the same value. + let ab_map_by_side = |side: usize, index: usize| -> usize { + *match side { + 0 => ab_map.get_by_left(&index), + 1 => ab_map.get_by_right(&index), + _ => panic!("Expected side to be 0 or 1"), + } + .unwrap() + }; + + // We maintain two maps for wires which have been routed to a particular subnetwork on one side + // of the network (left or right) but not the other. The keys are wire indices, and the values + // are subnetwork indices. + let mut partial_routes = [BTreeMap::new(), BTreeMap::new()]; + + // After we route a wire on one side, we find the corresponding wire on the other side and check + // if it still needs to be routed. If so, we add it to partial_routes. + let enqueue_other_side = |partial_routes: &mut [BTreeMap], + witness: &PartialWitness, + out_buffer: &mut GeneratedValues, + side: usize, + this_i: usize, + subnet: bool| { + let other_side = 1 - side; + let other_i = ab_map_by_side(side, this_i); + let other_switch_i = other_i / 2; + + if other_switch_i >= switches[other_side].len() { + // The other wire doesn't go through a switch, so there's no routing to be done. + // This happens in the case of the very last wire. + return; + } + + if witness.contains_all(&switches[other_side][other_switch_i]) { + // The other switch has already been routed. + return; + } + + let other_i_sibling = 4 * other_switch_i + 1 - other_i; + if let Some(&sibling_subnet) = partial_routes[other_side].get(&other_i_sibling) { + // The other switch's sibling is already pending routing. + assert_ne!(subnet, sibling_subnet); + } else { + let opt_old_subnet = partial_routes[other_side].insert(other_i, subnet); + if let Some(old_subnet) = opt_old_subnet { + assert_eq!(subnet, old_subnet, "Routing conflict (should never happen)"); + } + } + }; + + // See Figure 8 in the AS-Waksman paper. + if even { + enqueue_other_side(&mut partial_routes, witness, out_buffer, 1, n - 2, false); + enqueue_other_side(&mut partial_routes, witness, out_buffer, 1, n - 1, true); + } else { + enqueue_other_side(&mut partial_routes, witness, out_buffer, 0, n - 1, true); + enqueue_other_side(&mut partial_routes, witness, out_buffer, 1, n - 1, true); + } + + let route_switch = |partial_routes: &mut [BTreeMap], + witness: &PartialWitness, + out_buffer: &mut GeneratedValues, + side: usize, + switch_index: usize, + swap: bool| { + // First, we actually set the switch configuration. + for e in 0..CHUNK_SIZE { + out_buffer.set_target(switches[side][switch_index][e], F::from_bool(swap)); + } + + // Then, we enqueue the two corresponding wires on the other side of the network, to ensure + // that they get routed in the next step. + let this_i_1 = switch_index * 2; + let this_i_2 = this_i_1 + 1; + enqueue_other_side(partial_routes, witness, out_buffer, side, this_i_1, swap); + enqueue_other_side(partial_routes, witness, out_buffer, side, this_i_2, !swap); + }; + + // If {a,b}_only_routes is empty, then we can route any switch next. For efficiency, we will + // simply do top-down scans (one on the left side, one on the right side) for switches which + // have not yet been routed. These variables represent the positions of those two scans. + let mut scan_index = [0, 0]; + + // Until both scans complete, we alternate back and worth between the left and right switch + // layers. We process any partially routed wires for that side, or if there aren't any, we route + // the next switch in our scan. + while scan_index[0] < switches[0].len() || scan_index[1] < switches[1].len() { + for side in 0..=1 { + if !partial_routes[side].is_empty() { + for (this_i, subnet) in partial_routes[side].clone().into_iter() { + let this_first_switch_input = this_i % 2 == 0; + let swap = this_first_switch_input == subnet; + let this_switch_i = this_i / 2; + route_switch( + &mut partial_routes, + witness, + out_buffer, + side, + this_switch_i, + swap, + ); + } + partial_routes[side].clear(); + } else { + // We can route any switch next. Continue our scan for pending switches. + while scan_index[side] < switches[side].len() + && witness.contains_all(&switches[side][scan_index[side]]) + { + scan_index[side] += 1; + } + if scan_index[side] < switches[side].len() { + // Either switch configuration would work; we arbitrarily choose to not swap. + route_switch( + &mut partial_routes, + witness, + out_buffer, + side, + scan_index[side], + false, + ); + } + } + } + } } struct PermutationGenerator { - a_values: Vec, - b_values: Vec, + a_values: Vec<[F; CHUNK_SIZE]>, + b_values: Vec<[F; CHUNK_SIZE]>, a_wires: Vec<[Target; CHUNK_SIZE]>, b_wires: Vec<[Target; CHUNK_SIZE]>, } @@ -185,11 +319,17 @@ impl SimpleGenerator for PermutationGenera .map(|arr| arr.to_vec()) .flatten() .collect() - //.chain(self.b_wires.iter()).collect() } fn run_once(&self, witness: &PartialWitness, out_buffer: &mut GeneratedValues) { - todo!() + route( + self.a_values.clone(), + self.b_values.clone(), + self.a_wires.clone(), + self.b_wires.clone(), + witness, + out_buffer, + ); } } @@ -205,10 +345,36 @@ mod tests { use crate::plonk::circuit_data::CircuitConfig; use crate::plonk::verifier::verify; - fn test_permutation(size: usize) -> Result<()> { + #[test] + fn route_2x2() -> Result<()> { + type F = CrandallField; + type FF = QuarticCrandallField; + let config = CircuitConfig::large_config(); + let pw = PartialWitness::new(config.num_wires); + let mut builder = CircuitBuilder::::new(config); + + let one = F::ONE; + let two = F::from_canonical_usize(2); + let seven = F::from_canonical_usize(7); + let eight = F::from_canonical_usize(8); + + let one_two = [builder.constant(one), builder.constant(two)]; + let seven_eight = [builder.constant(seven), builder.constant(eight)]; + + let a = vec![one_two, seven_eight]; + let b = vec![seven_eight, one_two]; + + builder.assert_permutation(a, b); + + let data = builder.build(); + let proof = data.prove(pw).unwrap(); + + verify(proof, &data.verifier_only, &data.common) + } + + /*fn test_permutation(size: usize) -> Result<()> { type F = CrandallField; type FF = QuarticCrandallField; - let len = 1 << len_log; let config = CircuitConfig::large_config(); let pw = PartialWitness::new(config.num_wires); let mut builder = CircuitBuilder::::new(config); @@ -225,5 +391,5 @@ mod tests { let proof = data.prove(pw)?; verify(proof, &data.verifier_only, &data.common) - } + }*/ } diff --git a/src/gates/switch.rs b/src/gates/switch.rs index 70bfd45d..5ffc6525 100644 --- a/src/gates/switch.rs +++ b/src/gates/switch.rs @@ -169,7 +169,7 @@ impl, const D: usize, const CHUNK_SIZE: usize> Gate } fn num_wires(&self) -> usize { - Self::wire_second_output(self.num_copies - 1, CHUNK_SIZE - 1) + 1 + Self::wire_switch_bool(self.num_copies, self.num_copies - 1) + 1 } fn num_constants(&self) -> usize { @@ -294,14 +294,14 @@ mod tests { #[test] fn low_degree() { - test_low_degree::(SwitchGate::<_, 4, 3>::new( + test_low_degree::(SwitchGate::<_, 4, 3>::new_from_config( CircuitConfig::large_config(), )); } #[test] fn eval_fns() -> Result<()> { - test_eval_fns::(SwitchGate::<_, 4, 3>::new( + test_eval_fns::(SwitchGate::<_, 4, 3>::new_from_config( CircuitConfig::large_config(), )) } diff --git a/src/util/bimap.rs b/src/util/bimap.rs new file mode 100644 index 00000000..6fb46db7 --- /dev/null +++ b/src/util/bimap.rs @@ -0,0 +1,76 @@ +use std::collections::HashMap; +use std::hash::Hash; + +use bimap::BiMap; +use itertools::enumerate; + +/// Given two lists which are permutations of one another, creates a BiMap which maps an index in +/// one list to an index in the other list with the same associated value. +/// +/// If the lists contain duplicates, then multiple permutations with this property exist, and an +/// arbitrary one of them will be returned. +pub fn bimap_from_lists(a: Vec, b: Vec) -> BiMap { + assert_eq!(a.len(), b.len(), "Vectors differ in length"); + + let mut b_values_to_indices = HashMap::new(); + for (i, value) in enumerate(b) { + b_values_to_indices + .entry(value) + .or_insert_with(Vec::new) + .push(i); + } + + let mut bimap = BiMap::new(); + for (i, value) in enumerate(a) { + if let Some(j) = b_values_to_indices.get_mut(&value).and_then(Vec::pop) { + bimap.insert(i, j); + } else { + panic!("Value in first list not found in second list"); + } + } + + bimap +} + +#[cfg(test)] +mod tests { + use crate::util::bimap::bimap_from_lists; + + #[test] + fn empty_lists() { + let empty: Vec = Vec::new(); + let bimap = bimap_from_lists(empty.clone(), empty); + assert!(bimap.is_empty()); + } + + #[test] + fn without_duplicates() { + let bimap = bimap_from_lists(vec!['a', 'b', 'c'], vec!['b', 'c', 'a']); + assert_eq!(bimap.get_by_left(&0), Some(&2)); + assert_eq!(bimap.get_by_left(&1), Some(&0)); + assert_eq!(bimap.get_by_left(&2), Some(&1)); + } + + #[test] + fn with_duplicates() { + let first = vec!['a', 'a', 'b']; + let second = vec!['a', 'b', 'a']; + let bimap = bimap_from_lists(first.clone(), second.clone()); + for i in 0..3 { + let j = *bimap.get_by_left(&i).unwrap(); + assert_eq!(first[i], second[j]); + } + } + + #[test] + #[should_panic] + fn lengths_differ() { + bimap_from_lists(vec!['a', 'a', 'b'], vec!['a', 'b']); + } + + #[test] + #[should_panic] + fn not_a_permutation() { + bimap_from_lists(vec!['a', 'a', 'b'], vec!['a', 'b', 'b']); + } +} diff --git a/src/util/mod.rs b/src/util/mod.rs index cdd26ef8..daa6716b 100644 --- a/src/util/mod.rs +++ b/src/util/mod.rs @@ -1,6 +1,7 @@ use crate::field::field_types::Field; use crate::polynomial::polynomial::PolynomialValues; +pub(crate) mod bimap; pub(crate) mod context_tree; pub(crate) mod marking; pub(crate) mod partial_products; From 2d5f362c6a92a39e4ed66d8b461a03fab3a1bd97 Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Thu, 26 Aug 2021 14:27:42 -0700 Subject: [PATCH 06/31] fixes --- src/gates/switch.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/gates/switch.rs b/src/gates/switch.rs index 5ffc6525..506911e5 100644 --- a/src/gates/switch.rs +++ b/src/gates/switch.rs @@ -72,7 +72,7 @@ impl, const D: usize, const CHUNK_SIZE: usize> Gate let mut constraints = Vec::with_capacity(self.num_constraints()); for c in 0..self.num_copies { - let switch_bool = vars.local_wires[Self::wire_switch_bool(self.num_wires(), c)]; + let switch_bool = vars.local_wires[Self::wire_switch_bool(self.num_copies, c)]; let not_switch = F::Extension::ONE - switch_bool; for e in 0..CHUNK_SIZE { @@ -234,12 +234,8 @@ impl, const D: usize, const CHUNK_SIZE: usize> SimpleGenerator< for e in 0..CHUNK_SIZE { let first_input = get_local_wire(SwitchGate::::wire_first_input(c, e)); - let second_input = - get_local_wire(SwitchGate::::wire_second_input(c, e)); let first_output = get_local_wire(SwitchGate::::wire_first_output(c, e)); - let second_output = - get_local_wire(SwitchGate::::wire_second_output(c, e)); if first_input == first_output { out_buffer.set_wire(switch_bool_wire, F::ONE); From f9a47ade336f5e5a7ff9fec4a5701c676a292586 Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Thu, 26 Aug 2021 16:08:55 -0700 Subject: [PATCH 07/31] fixes --- src/gates/switch.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/gates/switch.rs b/src/gates/switch.rs index 506911e5..c421775e 100644 --- a/src/gates/switch.rs +++ b/src/gates/switch.rs @@ -318,10 +318,12 @@ mod tests { ) -> Vec { let num_copies = first_inputs.len(); + let mut switches = Vec::new(); let mut v = Vec::new(); for c in 0..num_copies { let switch = switch_bools[c]; - v.push(F::from_bool(switch)); + switches.push(F::from_bool(switch)); + let mut first_input_chunk = Vec::with_capacity(CHUNK_SIZE); let mut second_input_chunk = Vec::with_capacity(CHUNK_SIZE); let mut first_output_chunk = Vec::with_capacity(CHUNK_SIZE); @@ -341,6 +343,7 @@ mod tests { v.append(&mut first_output_chunk); v.append(&mut second_output_chunk); } + v.extend(switches); v.iter().map(|&x| x.into()).collect::>() } From a1d5f5b6fee4aa706880c916debcdc9495f5ff3c Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Fri, 27 Aug 2021 11:45:26 -0700 Subject: [PATCH 08/31] progress --- src/gadgets/permutation.rs | 47 +++++++++++++++++++++++++------------- src/gates/switch.rs | 5 ++-- 2 files changed, 34 insertions(+), 18 deletions(-) diff --git a/src/gadgets/permutation.rs b/src/gadgets/permutation.rs index 64e18f93..e94315b0 100644 --- a/src/gadgets/permutation.rs +++ b/src/gadgets/permutation.rs @@ -58,19 +58,19 @@ impl, const D: usize> CircuitBuilder { &mut self, a: [Target; CHUNK_SIZE], b: [Target; CHUNK_SIZE], - ) -> (usize, [Target; CHUNK_SIZE], [Target; CHUNK_SIZE]) { + ) -> (Target, [Target; CHUNK_SIZE], [Target; CHUNK_SIZE]) { if self.current_switch_gates.len() < CHUNK_SIZE { self.current_switch_gates .extend(vec![None; CHUNK_SIZE - self.current_switch_gates.len()]); } - let (gate_index, mut next_copy) = match self.current_switch_gates[CHUNK_SIZE - 1] { + let (gate, gate_index, mut next_copy) = match self.current_switch_gates[CHUNK_SIZE - 1] { None => { let gate = SwitchGate::::new_from_config(self.config.clone()); let gate_index = self.add_gate(gate.clone(), vec![]); - (gate_index, 0) + (gate, gate_index, 0) } - Some((idx, next_copy)) => (idx, next_copy), + Some((idx, next_copy)) => (self.gate_instances[idx], idx, next_copy), }; let num_copies = @@ -83,26 +83,31 @@ impl, const D: usize> CircuitBuilder { a[e], Target::wire( gate_index, - SwitchGate::::wire_first_input(0, e), + SwitchGate::::wire_first_input(next_copy, e), ), ); self.route( b[e], Target::wire( gate_index, - SwitchGate::::wire_second_input(0, e), + SwitchGate::::wire_second_input(next_copy, e), ), ); c.push(Target::wire( gate_index, - SwitchGate::::wire_first_output(0, e), + SwitchGate::::wire_first_output(next_copy, e), )); d.push(Target::wire( gate_index, - SwitchGate::::wire_second_output(0, e), + SwitchGate::::wire_second_output(next_copy, e), )); } + let switch = Target::wire( + gate_index, + SwitchGate::::wire_switch_bool(gate.num_copies, next_copy), + ); + let c_arr: [Target; CHUNK_SIZE] = c.try_into().unwrap(); let d_arr: [Target; CHUNK_SIZE] = d.try_into().unwrap(); @@ -115,7 +120,7 @@ impl, const D: usize> CircuitBuilder { self.current_switch_gates[CHUNK_SIZE - 1] = Some((gate_index, next_copy)); } - (gate_index, c_arr, d_arr) + (switch, c_arr, d_arr) } fn assert_permutation_recursive( @@ -140,12 +145,12 @@ impl, const D: usize> CircuitBuilder { }; for i in 0..a_num_switches { - let (_, out_1, out_2) = self.create_switch(a[i * 2], a[i * 2 + 1]); + let (a_switch, out_1, out_2) = self.create_switch(a[i * 2], a[i * 2 + 1]); child_1_a.push(out_1); child_2_a.push(out_2); } for i in 0..b_num_switches { - let (_, out_1, out_2) = self.create_switch(b[i * 2], b[i * 2 + 1]); + let (b_switch, out_1, out_2) = self.create_switch(b[i * 2], b[i * 2 + 1]); child_1_b.push(out_1); child_2_b.push(out_2); } @@ -161,6 +166,8 @@ impl, const D: usize> CircuitBuilder { self.assert_permutation(child_1_a, child_1_b); self.assert_permutation(child_2_a, child_2_b); + + self.add_generator(PermutationGenerator {}); } } @@ -198,7 +205,7 @@ fn route( // if it still needs to be routed. If so, we add it to partial_routes. let enqueue_other_side = |partial_routes: &mut [BTreeMap], witness: &PartialWitness, - out_buffer: &mut GeneratedValues, + _out_buffer: &mut GeneratedValues, side: usize, this_i: usize, subnet: bool| { @@ -306,8 +313,6 @@ fn route( } struct PermutationGenerator { - a_values: Vec<[F; CHUNK_SIZE]>, - b_values: Vec<[F; CHUNK_SIZE]>, a_wires: Vec<[Target; CHUNK_SIZE]>, b_wires: Vec<[Target; CHUNK_SIZE]>, } @@ -322,9 +327,19 @@ impl SimpleGenerator for PermutationGenera } fn run_once(&self, witness: &PartialWitness, out_buffer: &mut GeneratedValues) { + let wire_chunk_to_vals = |wire| { + let mut vals = [F::ZERO; CHUNK_SIZE]; + for e in 0..CHUNK_SIZE { + vals[e] = witness.get_target(wire[e]); + } + vals + }; + + let a_values = self.a_wires.iter().map(wire_chunk_to_vals).collect(); + let b_values = self.b_wires.iter().map(wire_chunk_to_vals).collect(); route( - self.a_values.clone(), - self.b_values.clone(), + a_values.clone(), + b_values.clone(), self.a_wires.clone(), self.b_wires.clone(), witness, diff --git a/src/gates/switch.rs b/src/gates/switch.rs index c421775e..85a26753 100644 --- a/src/gates/switch.rs +++ b/src/gates/switch.rs @@ -161,11 +161,12 @@ impl, const D: usize, const CHUNK_SIZE: usize> Gate gate_index: usize, _local_constants: &[F], ) -> Vec>> { - let gen = SwitchGenerator:: { + /*let gen = SwitchGenerator:: { gate_index, gate: self.clone(), }; - vec![Box::new(gen)] + vec![Box::new(gen)]*/ + vec![] } fn num_wires(&self) -> usize { From fe843db57f442e4ca1ecbbe2e9502767618edc2e Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Fri, 27 Aug 2021 14:34:53 -0700 Subject: [PATCH 09/31] many fixes --- src/gadgets/permutation.rs | 66 ++++++++++++++++++++++---------------- 1 file changed, 38 insertions(+), 28 deletions(-) diff --git a/src/gadgets/permutation.rs b/src/gadgets/permutation.rs index e94315b0..6d8e7d6d 100644 --- a/src/gadgets/permutation.rs +++ b/src/gadgets/permutation.rs @@ -1,5 +1,6 @@ use std::collections::BTreeMap; use std::convert::TryInto; +use std::marker::PhantomData; use crate::field::{extension_field::Extendable, field_types::Field}; use crate::gates::switch::SwitchGate; @@ -64,13 +65,13 @@ impl, const D: usize> CircuitBuilder { .extend(vec![None; CHUNK_SIZE - self.current_switch_gates.len()]); } - let (gate, gate_index, mut next_copy) = match self.current_switch_gates[CHUNK_SIZE - 1] { + let (gate_index, mut next_copy) = match self.current_switch_gates[CHUNK_SIZE - 1] { None => { let gate = SwitchGate::::new_from_config(self.config.clone()); let gate_index = self.add_gate(gate.clone(), vec![]); - (gate, gate_index, 0) + (gate_index, 0) } - Some((idx, next_copy)) => (self.gate_instances[idx], idx, next_copy), + Some((idx, next_copy)) => (idx, next_copy), }; let num_copies = @@ -105,7 +106,7 @@ impl, const D: usize> CircuitBuilder { let switch = Target::wire( gate_index, - SwitchGate::::wire_switch_bool(gate.num_copies, next_copy), + SwitchGate::::wire_switch_bool(num_copies, next_copy), ); let c_arr: [Target; CHUNK_SIZE] = c.try_into().unwrap(); @@ -144,13 +145,17 @@ impl, const D: usize> CircuitBuilder { a_num_switches }; + let mut a_switches = Vec::new(); + let mut b_switches = Vec::new(); for i in 0..a_num_switches { - let (a_switch, out_1, out_2) = self.create_switch(a[i * 2], a[i * 2 + 1]); + let (switch, out_1, out_2) = self.create_switch(a[i * 2], a[i * 2 + 1]); + a_switches.push(switch); child_1_a.push(out_1); child_2_a.push(out_2); } for i in 0..b_num_switches { - let (b_switch, out_1, out_2) = self.create_switch(b[i * 2], b[i * 2 + 1]); + let (switch, out_1, out_2) = self.create_switch(b[i * 2], b[i * 2 + 1]); + b_switches.push(switch); child_1_b.push(out_1); child_2_b.push(out_2); } @@ -167,15 +172,21 @@ impl, const D: usize> CircuitBuilder { self.assert_permutation(child_1_a, child_1_b); self.assert_permutation(child_2_a, child_2_b); - self.add_generator(PermutationGenerator {}); + self.add_generator(PermutationGenerator:: { + a, + b, + a_switches, + b_switches, + _phantom: PhantomData, + }); } } fn route( a_values: Vec<[F; CHUNK_SIZE]>, b_values: Vec<[F; CHUNK_SIZE]>, - a_switches: Vec<[Target; CHUNK_SIZE]>, - b_switches: Vec<[Target; CHUNK_SIZE]>, + a_switches: Vec, + b_switches: Vec, witness: &PartialWitness, out_buffer: &mut GeneratedValues, ) { @@ -219,7 +230,7 @@ fn route( return; } - if witness.contains_all(&switches[other_side][other_switch_i]) { + if witness.contains(switches[other_side][other_switch_i]) { // The other switch has already been routed. return; } @@ -252,9 +263,7 @@ fn route( switch_index: usize, swap: bool| { // First, we actually set the switch configuration. - for e in 0..CHUNK_SIZE { - out_buffer.set_target(switches[side][switch_index][e], F::from_bool(swap)); - } + out_buffer.set_target(switches[side][switch_index], F::from_bool(swap)); // Then, we enqueue the two corresponding wires on the other side of the network, to ensure // that they get routed in the next step. @@ -292,7 +301,7 @@ fn route( } else { // We can route any switch next. Continue our scan for pending switches. while scan_index[side] < switches[side].len() - && witness.contains_all(&switches[side][scan_index[side]]) + && witness.contains(switches[side][scan_index[side]]) { scan_index[side] += 1; } @@ -313,21 +322,22 @@ fn route( } struct PermutationGenerator { - a_wires: Vec<[Target; CHUNK_SIZE]>, - b_wires: Vec<[Target; CHUNK_SIZE]>, + a: Vec<[Target; CHUNK_SIZE]>, + b: Vec<[Target; CHUNK_SIZE]>, + a_switches: Vec, + b_switches: Vec, + _phantom: PhantomData, } impl SimpleGenerator for PermutationGenerator { fn dependencies(&self) -> Vec { - self.a_wires - .iter() - .map(|arr| arr.to_vec()) - .flatten() - .collect() + let mut deps = self.a_switches.clone(); + deps.extend(self.b_switches.clone()); + deps } fn run_once(&self, witness: &PartialWitness, out_buffer: &mut GeneratedValues) { - let wire_chunk_to_vals = |wire| { + let wire_chunk_to_vals = |wire: [Target; CHUNK_SIZE]| { let mut vals = [F::ZERO; CHUNK_SIZE]; for e in 0..CHUNK_SIZE { vals[e] = witness.get_target(wire[e]); @@ -335,13 +345,13 @@ impl SimpleGenerator for PermutationGenera vals }; - let a_values = self.a_wires.iter().map(wire_chunk_to_vals).collect(); - let b_values = self.b_wires.iter().map(wire_chunk_to_vals).collect(); + let a_values = self.a.iter().map(|chunk| wire_chunk_to_vals(*chunk)).collect(); + let b_values = self.b.iter().map(|chunk| wire_chunk_to_vals(*chunk)).collect(); route( - a_values.clone(), - b_values.clone(), - self.a_wires.clone(), - self.b_wires.clone(), + a_values, + b_values, + self.a_switches.clone(), + self.b_switches.clone(), witness, out_buffer, ); From f7607dddd4b25242872059a1928beba2e291a105 Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Fri, 27 Aug 2021 15:55:13 -0700 Subject: [PATCH 10/31] fmt --- src/gadgets/permutation.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/gadgets/permutation.rs b/src/gadgets/permutation.rs index 6d8e7d6d..740d73dd 100644 --- a/src/gadgets/permutation.rs +++ b/src/gadgets/permutation.rs @@ -345,8 +345,16 @@ impl SimpleGenerator for PermutationGenera vals }; - let a_values = self.a.iter().map(|chunk| wire_chunk_to_vals(*chunk)).collect(); - let b_values = self.b.iter().map(|chunk| wire_chunk_to_vals(*chunk)).collect(); + let a_values = self + .a + .iter() + .map(|chunk| wire_chunk_to_vals(*chunk)) + .collect(); + let b_values = self + .b + .iter() + .map(|chunk| wire_chunk_to_vals(*chunk)) + .collect(); route( a_values, b_values, From d4aa4d715343be6ca589e9bb2156b49d0d8b67f8 Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Mon, 30 Aug 2021 11:41:57 -0700 Subject: [PATCH 11/31] fixes and new generator --- src/gadgets/permutation.rs | 104 +++++++++++++++++++++++++++++++------ 1 file changed, 89 insertions(+), 15 deletions(-) diff --git a/src/gadgets/permutation.rs b/src/gadgets/permutation.rs index 740d73dd..06e29376 100644 --- a/src/gadgets/permutation.rs +++ b/src/gadgets/permutation.rs @@ -40,25 +40,34 @@ impl, const D: usize> CircuitBuilder { } } - /// Assert that [a, b] is a permutation of [c, d]. + /// Assert that [a1, a2] is a permutation of [b1, b2]. fn assert_permutation_2x2( &mut self, - a: [Target; CHUNK_SIZE], - b: [Target; CHUNK_SIZE], - c: [Target; CHUNK_SIZE], - d: [Target; CHUNK_SIZE], + a1: [Target; CHUNK_SIZE], + a2: [Target; CHUNK_SIZE], + b1: [Target; CHUNK_SIZE], + b2: [Target; CHUNK_SIZE], ) { - let (_, gate_c, gate_d) = self.create_switch(a, b); + let (switch, gate_c, gate_d) = self.create_switch(a1, a2); for e in 0..CHUNK_SIZE { - self.route(c[e], gate_c[e]); - self.route(d[e], gate_d[e]); + self.route(b1[e], gate_c[e]); + self.route(b2[e], gate_d[e]); } + + self.add_generator(TwoByTwoPermutationGenerator:: { + a1, + a2, + b1, + b2, + switch, + _phantom: PhantomData, + }); } fn create_switch( &mut self, - a: [Target; CHUNK_SIZE], - b: [Target; CHUNK_SIZE], + a1: [Target; CHUNK_SIZE], + a2: [Target; CHUNK_SIZE], ) -> (Target, [Target; CHUNK_SIZE], [Target; CHUNK_SIZE]) { if self.current_switch_gates.len() < CHUNK_SIZE { self.current_switch_gates @@ -81,14 +90,14 @@ impl, const D: usize> CircuitBuilder { let mut d = Vec::new(); for e in 0..CHUNK_SIZE { self.route( - a[e], + a1[e], Target::wire( gate_index, SwitchGate::::wire_first_input(next_copy, e), ), ); self.route( - b[e], + a2[e], Target::wire( gate_index, SwitchGate::::wire_second_input(next_copy, e), @@ -321,6 +330,68 @@ fn route( } } +struct TwoByTwoPermutationGenerator { + a1: [Target; CHUNK_SIZE], + a2: [Target; CHUNK_SIZE], + b1: [Target; CHUNK_SIZE], + b2: [Target; CHUNK_SIZE], + switch: Target, + _phantom: PhantomData, +} + +impl SimpleGenerator + for TwoByTwoPermutationGenerator +{ + fn dependencies(&self) -> Vec { + [self.a1, self.a2, self.b1, self.b2] + .to_vec() + .iter() + .map(|arr| arr.to_vec()) + .flatten() + .collect() + } + + fn run_once(&self, witness: &PartialWitness, out_buffer: &mut GeneratedValues) { + let a1_values: Vec<_> = self + .a1 + .to_vec() + .iter() + .map(|x| witness.get_target(*x)) + .collect(); + let a2_values: Vec<_> = self + .a2 + .to_vec() + .iter() + .map(|x| witness.get_target(*x)) + .collect(); + let b1_values: Vec<_> = self + .b1 + .to_vec() + .iter() + .map(|x| witness.get_target(*x)) + .collect(); + let b2_values: Vec<_> = self + .b2 + .to_vec() + .iter() + .map(|x| witness.get_target(*x)) + .collect(); + + let no_switch = a1_values.iter().zip(b1_values.iter()).all(|(a, b)| a == b) + && a2_values.iter().zip(b2_values.iter()).all(|(a, b)| a == b); + let switch = a1_values.iter().zip(b2_values.iter()).all(|(a, b)| a == b) + && a2_values.iter().zip(b1_values.iter()).all(|(a, b)| a == b); + + if no_switch { + out_buffer.set_target(self.switch, F::ZERO); + } else if switch { + out_buffer.set_target(self.switch, F::ONE); + } else { + panic!("No permutation"); + } + } +} + struct PermutationGenerator { a: Vec<[Target; CHUNK_SIZE]>, b: Vec<[Target; CHUNK_SIZE]>, @@ -331,9 +402,12 @@ struct PermutationGenerator { impl SimpleGenerator for PermutationGenerator { fn dependencies(&self) -> Vec { - let mut deps = self.a_switches.clone(); - deps.extend(self.b_switches.clone()); - deps + self.a + .iter() + .map(|arr| arr.to_vec()) + .flatten() + .chain(self.b.iter().map(|arr| arr.to_vec()).flatten()) + .collect() } fn run_once(&self, witness: &PartialWitness, out_buffer: &mut GeneratedValues) { From ab744a7c367601ea4eb47a7170367f449af5cb68 Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Mon, 30 Aug 2021 12:28:39 -0700 Subject: [PATCH 12/31] edits and fixes --- src/gadgets/permutation.rs | 40 +++++++++++++++++++++++++------------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/src/gadgets/permutation.rs b/src/gadgets/permutation.rs index 06e29376..0de5a025 100644 --- a/src/gadgets/permutation.rs +++ b/src/gadgets/permutation.rs @@ -48,10 +48,10 @@ impl, const D: usize> CircuitBuilder { b1: [Target; CHUNK_SIZE], b2: [Target; CHUNK_SIZE], ) { - let (switch, gate_c, gate_d) = self.create_switch(a1, a2); + let (switch, gate_out1, gate_out2) = self.create_switch(a1, a2); for e in 0..CHUNK_SIZE { - self.route(b1[e], gate_c[e]); - self.route(b2[e], gate_d[e]); + self.route(b1[e], gate_out1[e]); + self.route(b2[e], gate_out2[e]); } self.add_generator(TwoByTwoPermutationGenerator:: { @@ -453,7 +453,7 @@ mod tests { use crate::plonk::verifier::verify; #[test] - fn route_2x2() -> Result<()> { + fn test_permutation_2x2() -> Result<()> { type F = CrandallField; type FF = QuarticCrandallField; let config = CircuitConfig::large_config(); @@ -479,24 +479,36 @@ mod tests { verify(proof, &data.verifier_only, &data.common) } - /*fn test_permutation(size: usize) -> Result<()> { + #[test] + fn test_permutation_4x4() -> Result<()> { type F = CrandallField; type FF = QuarticCrandallField; let config = CircuitConfig::large_config(); let pw = PartialWitness::new(config.num_wires); let mut builder = CircuitBuilder::::new(config); - let vec = FF::rand_vec(len); - let v: Vec<_> = vec.iter().map(|x| builder.constant_extension(*x)).collect(); - for i in 0..len { - let it = builder.constant(F::from_canonical_usize(i)); - let elem = builder.constant_extension(vec[i]); - builder.random_access(it, elem, v.clone()); - } + let one = F::ONE; + let two = F::from_canonical_usize(2); + let three = F::from_canonical_usize(3); + let four = F::from_canonical_usize(4); + let five = F::from_canonical_usize(5); + let six = F::from_canonical_usize(6); + let seven = F::from_canonical_usize(7); + let eight = F::from_canonical_usize(8); + + let one_two = [builder.constant(one), builder.constant(two)]; + let three_four = [builder.constant(three), builder.constant(four)]; + let five_six = [builder.constant(five), builder.constant(six)]; + let seven_eight = [builder.constant(seven), builder.constant(eight)]; + + let a = vec![one_two, three_four, five_six, seven_eight]; + let b = vec![seven_eight, one_two, five_six, three_four]; + + builder.assert_permutation(a, b); let data = builder.build(); - let proof = data.prove(pw)?; + let proof = data.prove(pw).unwrap(); verify(proof, &data.verifier_only, &data.common) - }*/ + } } From 0f6e9c5b6832fab9b5113a6f62d02d3176a0608a Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Mon, 30 Aug 2021 12:52:03 -0700 Subject: [PATCH 13/31] progress --- src/gates/switch.rs | 64 +++++++++++++++++++++------------------------ 1 file changed, 30 insertions(+), 34 deletions(-) diff --git a/src/gates/switch.rs b/src/gates/switch.rs index 85a26753..9dbf9ee4 100644 --- a/src/gates/switch.rs +++ b/src/gates/switch.rs @@ -161,12 +161,11 @@ impl, const D: usize, const CHUNK_SIZE: usize> Gate gate_index: usize, _local_constants: &[F], ) -> Vec>> { - /*let gen = SwitchGenerator:: { + (0..self.num_copies).map(|c| Box::new(SwitchGenerator:: { gate_index, gate: self.clone(), - }; - vec![Box::new(gen)]*/ - vec![] + copy: c, + })).collect() } fn num_wires(&self) -> usize { @@ -190,6 +189,7 @@ impl, const D: usize, const CHUNK_SIZE: usize> Gate struct SwitchGenerator, const D: usize, const CHUNK_SIZE: usize> { gate_index: usize, gate: SwitchGate, + copy: usize, } impl, const D: usize, const CHUNK_SIZE: usize> SimpleGenerator @@ -199,21 +199,19 @@ impl, const D: usize, const CHUNK_SIZE: usize> SimpleGenerator< let local_target = |input| Target::wire(self.gate_index, input); let mut deps = Vec::new(); - for c in 0..self.gate.num_copies { - for e in 0..CHUNK_SIZE { - deps.push(local_target( - SwitchGate::::wire_first_input(c, e), - )); - deps.push(local_target( - SwitchGate::::wire_second_input(c, e), - )); - deps.push(local_target( - SwitchGate::::wire_first_output(c, e), - )); - deps.push(local_target( - SwitchGate::::wire_second_output(c, e), - )); - } + for e in 0..CHUNK_SIZE { + deps.push(local_target( + SwitchGate::::wire_first_input(self.copy, e), + )); + deps.push(local_target( + SwitchGate::::wire_second_input(self.copy, e), + )); + deps.push(local_target( + SwitchGate::::wire_first_output(self.copy, e), + )); + deps.push(local_target( + SwitchGate::::wire_second_output(self.copy, e), + )); } deps @@ -227,22 +225,20 @@ impl, const D: usize, const CHUNK_SIZE: usize> SimpleGenerator< let get_local_wire = |input| witness.get_wire(local_wire(input)); - for c in 0..self.gate.num_copies { - let switch_bool_wire = local_wire(SwitchGate::::wire_switch_bool( - self.gate.num_copies, - c, - )); - for e in 0..CHUNK_SIZE { - let first_input = - get_local_wire(SwitchGate::::wire_first_input(c, e)); - let first_output = - get_local_wire(SwitchGate::::wire_first_output(c, e)); + let switch_bool_wire = local_wire(SwitchGate::::wire_switch_bool( + self.gate.num_copies, + self.copy, + )); + for e in 0..CHUNK_SIZE { + let first_input = + get_local_wire(SwitchGate::::wire_first_input(self.copy, e)); + let first_output = + get_local_wire(SwitchGate::::wire_first_output(self.copy, e)); - if first_input == first_output { - out_buffer.set_wire(switch_bool_wire, F::ONE); - } else { - out_buffer.set_wire(switch_bool_wire, F::ZERO); - } + if first_input == first_output { + out_buffer.set_wire(switch_bool_wire, F::ONE); + } else { + out_buffer.set_wire(switch_bool_wire, F::ZERO); } } } From c2439557bfec209b5e932c576c4812c4690beb0c Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Mon, 30 Aug 2021 13:03:46 -0700 Subject: [PATCH 14/31] fix --- src/gates/switch.rs | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/src/gates/switch.rs b/src/gates/switch.rs index 9dbf9ee4..50104b82 100644 --- a/src/gates/switch.rs +++ b/src/gates/switch.rs @@ -161,11 +161,17 @@ impl, const D: usize, const CHUNK_SIZE: usize> Gate gate_index: usize, _local_constants: &[F], ) -> Vec>> { - (0..self.num_copies).map(|c| Box::new(SwitchGenerator:: { - gate_index, - gate: self.clone(), - copy: c, - })).collect() + (0..self.num_copies) + .map(|c| { + let g: Box> = + Box::new(SwitchGenerator:: { + gate_index, + gate: self.clone(), + copy: c, + }); + g + }) + .collect() } fn num_wires(&self) -> usize { @@ -230,10 +236,12 @@ impl, const D: usize, const CHUNK_SIZE: usize> SimpleGenerator< self.copy, )); for e in 0..CHUNK_SIZE { - let first_input = - get_local_wire(SwitchGate::::wire_first_input(self.copy, e)); - let first_output = - get_local_wire(SwitchGate::::wire_first_output(self.copy, e)); + let first_input = get_local_wire(SwitchGate::::wire_first_input( + self.copy, e, + )); + let first_output = get_local_wire(SwitchGate::::wire_first_output( + self.copy, e, + )); if first_input == first_output { out_buffer.set_wire(switch_bool_wire, F::ONE); From 4ea1df82ba5d97e59556810d5ac854ac84cf2a3b Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Mon, 30 Aug 2021 13:14:00 -0700 Subject: [PATCH 15/31] fixes --- src/gadgets/permutation.rs | 3 --- src/gates/switch.rs | 27 +++++++++++++++++---------- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/src/gadgets/permutation.rs b/src/gadgets/permutation.rs index 0de5a025..06c9c711 100644 --- a/src/gadgets/permutation.rs +++ b/src/gadgets/permutation.rs @@ -22,7 +22,6 @@ impl, const D: usize> CircuitBuilder { b.len(), "Permutation must have same number of inputs and outputs" ); - assert_eq!(a[0].len(), b[0].len(), "Chunk sizes must be the same"); match a.len() { // Two empty lists are permutations of one another, trivially. @@ -455,7 +454,6 @@ mod tests { #[test] fn test_permutation_2x2() -> Result<()> { type F = CrandallField; - type FF = QuarticCrandallField; let config = CircuitConfig::large_config(); let pw = PartialWitness::new(config.num_wires); let mut builder = CircuitBuilder::::new(config); @@ -482,7 +480,6 @@ mod tests { #[test] fn test_permutation_4x4() -> Result<()> { type F = CrandallField; - type FF = QuarticCrandallField; let config = CircuitConfig::large_config(); let pw = PartialWitness::new(config.num_wires); let mut builder = CircuitBuilder::::new(config); diff --git a/src/gates/switch.rs b/src/gates/switch.rs index 50104b82..bb9553ad 100644 --- a/src/gates/switch.rs +++ b/src/gates/switch.rs @@ -213,10 +213,7 @@ impl, const D: usize, const CHUNK_SIZE: usize> SimpleGenerator< SwitchGate::::wire_second_input(self.copy, e), )); deps.push(local_target( - SwitchGate::::wire_first_output(self.copy, e), - )); - deps.push(local_target( - SwitchGate::::wire_second_output(self.copy, e), + SwitchGate::::wire_switch_bool(self.copy, e), )); } @@ -231,7 +228,11 @@ impl, const D: usize, const CHUNK_SIZE: usize> SimpleGenerator< let get_local_wire = |input| witness.get_wire(local_wire(input)); - let switch_bool_wire = local_wire(SwitchGate::::wire_switch_bool( + let first_output_wire = local_wire(SwitchGate::::wire_first_output( + self.gate.num_copies, + self.copy, + )); + let second_output_wire = local_wire(SwitchGate::::wire_second_output( self.gate.num_copies, self.copy, )); @@ -239,15 +240,21 @@ impl, const D: usize, const CHUNK_SIZE: usize> SimpleGenerator< let first_input = get_local_wire(SwitchGate::::wire_first_input( self.copy, e, )); - let first_output = get_local_wire(SwitchGate::::wire_first_output( + let second_input = get_local_wire(SwitchGate::::wire_second_input( + self.copy, e, + )); + let switch_bool = get_local_wire(SwitchGate::::wire_switch_bool( self.copy, e, )); - if first_input == first_output { - out_buffer.set_wire(switch_bool_wire, F::ONE); + let (first_output, second_output) = if switch_bool == F::ZERO { + (first_input, second_input) } else { - out_buffer.set_wire(switch_bool_wire, F::ZERO); - } + (second_input, first_input) + }; + + out_buffer.set_wire(first_output_wire, first_output); + out_buffer.set_wire(second_output_wire, second_output); } } } From 485d4862ffa278ff10fcfd92284d1ef8f68df205 Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Tue, 31 Aug 2021 10:14:27 -0700 Subject: [PATCH 16/31] fixes --- src/gadgets/permutation.rs | 1 + src/gates/switch.rs | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/gadgets/permutation.rs b/src/gadgets/permutation.rs index 06c9c711..f21712e6 100644 --- a/src/gadgets/permutation.rs +++ b/src/gadgets/permutation.rs @@ -323,6 +323,7 @@ fn route( scan_index[side], false, ); + scan_index[side] += 1; } } } diff --git a/src/gates/switch.rs b/src/gates/switch.rs index bb9553ad..7f172944 100644 --- a/src/gates/switch.rs +++ b/src/gates/switch.rs @@ -57,6 +57,7 @@ impl, const D: usize, const CHUNK_SIZE: usize> SwitchGate usize { + debug_assert!(copy < num_copies); num_copies * (4 * CHUNK_SIZE) + copy } } @@ -213,7 +214,7 @@ impl, const D: usize, const CHUNK_SIZE: usize> SimpleGenerator< SwitchGate::::wire_second_input(self.copy, e), )); deps.push(local_target( - SwitchGate::::wire_switch_bool(self.copy, e), + SwitchGate::::wire_switch_bool(self.gate.num_copies, self.copy), )); } @@ -228,15 +229,13 @@ impl, const D: usize, const CHUNK_SIZE: usize> SimpleGenerator< let get_local_wire = |input| witness.get_wire(local_wire(input)); - let first_output_wire = local_wire(SwitchGate::::wire_first_output( - self.gate.num_copies, - self.copy, - )); - let second_output_wire = local_wire(SwitchGate::::wire_second_output( - self.gate.num_copies, - self.copy, - )); for e in 0..CHUNK_SIZE { + let first_output_wire = local_wire(SwitchGate::::wire_first_output( + self.copy, e, + )); + let second_output_wire = local_wire(SwitchGate::::wire_second_output( + self.copy, e, + )); let first_input = get_local_wire(SwitchGate::::wire_first_input( self.copy, e, )); @@ -244,7 +243,8 @@ impl, const D: usize, const CHUNK_SIZE: usize> SimpleGenerator< self.copy, e, )); let switch_bool = get_local_wire(SwitchGate::::wire_switch_bool( - self.copy, e, + self.gate.num_copies, + self.copy, )); let (first_output, second_output) = if switch_bool == F::ZERO { From 260d4bd13cef4ab312124a3be005c0df8cda6b53 Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Tue, 31 Aug 2021 10:23:19 -0700 Subject: [PATCH 17/31] removed to_vec calls --- src/gadgets/permutation.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/gadgets/permutation.rs b/src/gadgets/permutation.rs index f21712e6..5a24585f 100644 --- a/src/gadgets/permutation.rs +++ b/src/gadgets/permutation.rs @@ -354,25 +354,21 @@ impl SimpleGenerator fn run_once(&self, witness: &PartialWitness, out_buffer: &mut GeneratedValues) { let a1_values: Vec<_> = self .a1 - .to_vec() .iter() .map(|x| witness.get_target(*x)) .collect(); let a2_values: Vec<_> = self .a2 - .to_vec() .iter() .map(|x| witness.get_target(*x)) .collect(); let b1_values: Vec<_> = self .b1 - .to_vec() .iter() .map(|x| witness.get_target(*x)) .collect(); let b2_values: Vec<_> = self .b2 - .to_vec() .iter() .map(|x| witness.get_target(*x)) .collect(); From 3494839227c9a8e646d8442da8fe4a789b0ea4f2 Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Tue, 31 Aug 2021 10:24:14 -0700 Subject: [PATCH 18/31] removed more to_vec calls (within maps) --- src/gadgets/permutation.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/gadgets/permutation.rs b/src/gadgets/permutation.rs index 5a24585f..f404aff1 100644 --- a/src/gadgets/permutation.rs +++ b/src/gadgets/permutation.rs @@ -344,7 +344,6 @@ impl SimpleGenerator { fn dependencies(&self) -> Vec { [self.a1, self.a2, self.b1, self.b2] - .to_vec() .iter() .map(|arr| arr.to_vec()) .flatten() @@ -399,10 +398,10 @@ struct PermutationGenerator { impl SimpleGenerator for PermutationGenerator { fn dependencies(&self) -> Vec { self.a - .iter() - .map(|arr| arr.to_vec()) + .clone() + .into_iter() .flatten() - .chain(self.b.iter().map(|arr| arr.to_vec()).flatten()) + .chain(self.b.clone().into_iter().flatten()) .collect() } From 3ad036596753df2e727d04d63f5bb6930083c8f5 Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Tue, 31 Aug 2021 14:39:58 -0700 Subject: [PATCH 19/31] fixed infinite loop --- src/gadgets/permutation.rs | 77 +++++++++++++++++++++----------------- src/gates/switch.rs | 6 +-- 2 files changed, 46 insertions(+), 37 deletions(-) diff --git a/src/gadgets/permutation.rs b/src/gadgets/permutation.rs index f404aff1..4e685d2f 100644 --- a/src/gadgets/permutation.rs +++ b/src/gadgets/permutation.rs @@ -204,6 +204,7 @@ fn route( // Bimap: maps indices of values in a to indices of the same values in b let ab_map = bimap_from_lists(a_values, b_values); let switches = [a_switches, b_switches]; + let mut newly_set = [vec![false; n], vec![false; n]]; // Given a side and an index, returns the index in the other side that corresponds to the same value. let ab_map_by_side = |side: usize, index: usize| -> usize { @@ -224,7 +225,7 @@ fn route( // if it still needs to be routed. If so, we add it to partial_routes. let enqueue_other_side = |partial_routes: &mut [BTreeMap], witness: &PartialWitness, - _out_buffer: &mut GeneratedValues, + newly_set: &mut [Vec], side: usize, this_i: usize, subnet: bool| { @@ -238,7 +239,9 @@ fn route( return; } - if witness.contains(switches[other_side][other_switch_i]) { + if witness.contains(switches[other_side][other_switch_i]) + || newly_set[other_side][other_switch_i] + { // The other switch has already been routed. return; } @@ -257,28 +260,50 @@ fn route( // See Figure 8 in the AS-Waksman paper. if even { - enqueue_other_side(&mut partial_routes, witness, out_buffer, 1, n - 2, false); - enqueue_other_side(&mut partial_routes, witness, out_buffer, 1, n - 1, true); + enqueue_other_side( + &mut partial_routes, + witness, + &mut newly_set, + 1, + n - 2, + false, + ); + enqueue_other_side(&mut partial_routes, witness, &mut newly_set, 1, n - 1, true); } else { - enqueue_other_side(&mut partial_routes, witness, out_buffer, 0, n - 1, true); - enqueue_other_side(&mut partial_routes, witness, out_buffer, 1, n - 1, true); + enqueue_other_side(&mut partial_routes, witness, &mut newly_set, 0, n - 1, true); + enqueue_other_side(&mut partial_routes, witness, &mut newly_set, 1, n - 1, true); } - let route_switch = |partial_routes: &mut [BTreeMap], - witness: &PartialWitness, - out_buffer: &mut GeneratedValues, - side: usize, - switch_index: usize, - swap: bool| { + let mut route_switch = |partial_routes: &mut [BTreeMap], + witness: &PartialWitness, + out_buffer: &mut GeneratedValues, + side: usize, + switch_index: usize, + swap: bool| { // First, we actually set the switch configuration. out_buffer.set_target(switches[side][switch_index], F::from_bool(swap)); + newly_set[side][switch_index] = true; // Then, we enqueue the two corresponding wires on the other side of the network, to ensure // that they get routed in the next step. let this_i_1 = switch_index * 2; let this_i_2 = this_i_1 + 1; - enqueue_other_side(partial_routes, witness, out_buffer, side, this_i_1, swap); - enqueue_other_side(partial_routes, witness, out_buffer, side, this_i_2, !swap); + enqueue_other_side( + partial_routes, + witness, + &mut newly_set, + side, + this_i_1, + swap, + ); + enqueue_other_side( + partial_routes, + witness, + &mut newly_set, + side, + this_i_2, + !swap, + ); }; // If {a,b}_only_routes is empty, then we can route any switch next. For efficiency, we will @@ -351,26 +376,10 @@ impl SimpleGenerator } fn run_once(&self, witness: &PartialWitness, out_buffer: &mut GeneratedValues) { - let a1_values: Vec<_> = self - .a1 - .iter() - .map(|x| witness.get_target(*x)) - .collect(); - let a2_values: Vec<_> = self - .a2 - .iter() - .map(|x| witness.get_target(*x)) - .collect(); - let b1_values: Vec<_> = self - .b1 - .iter() - .map(|x| witness.get_target(*x)) - .collect(); - let b2_values: Vec<_> = self - .b2 - .iter() - .map(|x| witness.get_target(*x)) - .collect(); + let a1_values: Vec<_> = self.a1.iter().map(|x| witness.get_target(*x)).collect(); + let a2_values: Vec<_> = self.a2.iter().map(|x| witness.get_target(*x)).collect(); + let b1_values: Vec<_> = self.b1.iter().map(|x| witness.get_target(*x)).collect(); + let b2_values: Vec<_> = self.b2.iter().map(|x| witness.get_target(*x)).collect(); let no_switch = a1_values.iter().zip(b1_values.iter()).all(|(a, b)| a == b) && a2_values.iter().zip(b2_values.iter()).all(|(a, b)| a == b); diff --git a/src/gates/switch.rs b/src/gates/switch.rs index 7f172944..0939fc08 100644 --- a/src/gates/switch.rs +++ b/src/gates/switch.rs @@ -233,9 +233,9 @@ impl, const D: usize, const CHUNK_SIZE: usize> SimpleGenerator< let first_output_wire = local_wire(SwitchGate::::wire_first_output( self.copy, e, )); - let second_output_wire = local_wire(SwitchGate::::wire_second_output( - self.copy, e, - )); + let second_output_wire = local_wire( + SwitchGate::::wire_second_output(self.copy, e), + ); let first_input = get_local_wire(SwitchGate::::wire_first_input( self.copy, e, )); From 10d016a92c2fb30650a559417e5462a89f2c39f6 Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Wed, 1 Sep 2021 16:38:10 -0700 Subject: [PATCH 20/31] chunk size as field --- src/gadgets/permutation.rs | 224 ++++++++++++++--------------------- src/gates/switch.rs | 207 +++++++++++++++----------------- src/plonk/circuit_builder.rs | 29 ++++- 3 files changed, 212 insertions(+), 248 deletions(-) diff --git a/src/gadgets/permutation.rs b/src/gadgets/permutation.rs index 4e685d2f..3016c2a1 100644 --- a/src/gadgets/permutation.rs +++ b/src/gadgets/permutation.rs @@ -12,27 +12,28 @@ use crate::util::bimap::bimap_from_lists; impl, const D: usize> CircuitBuilder { /// Assert that two lists of expressions evaluate to permutations of one another. - pub fn assert_permutation( - &mut self, - a: Vec<[Target; CHUNK_SIZE]>, - b: Vec<[Target; CHUNK_SIZE]>, - ) { + pub fn assert_permutation(&mut self, a: Vec>, b: Vec>) { assert_eq!( a.len(), b.len(), "Permutation must have same number of inputs and outputs" ); + assert_eq!(a[0].len(), b[0].len(), "Chunk size must be the same"); + + let chunk_size = a[0].len(); match a.len() { // Two empty lists are permutations of one another, trivially. 0 => (), // Two singleton lists are permutations of one another as long as their items are equal. 1 => { - for e in 0..CHUNK_SIZE { + for e in 0..chunk_size { self.assert_equal(a[0][e], b[0][e]) } } - 2 => self.assert_permutation_2x2(a[0], a[1], b[0], b[1]), + 2 => { + self.assert_permutation_2x2(a[0].clone(), a[1].clone(), b[0].clone(), b[1].clone()) + } // For larger lists, we recursively use two smaller permutation networks. //_ => self.assert_permutation_recursive(a, b) _ => self.assert_permutation_recursive(a, b), @@ -40,103 +41,98 @@ impl, const D: usize> CircuitBuilder { } /// Assert that [a1, a2] is a permutation of [b1, b2]. - fn assert_permutation_2x2( + fn assert_permutation_2x2( &mut self, - a1: [Target; CHUNK_SIZE], - a2: [Target; CHUNK_SIZE], - b1: [Target; CHUNK_SIZE], - b2: [Target; CHUNK_SIZE], + a1: Vec, + a2: Vec, + b1: Vec, + b2: Vec, ) { + assert!( + a1.len() == a2.len() && a2.len() == b1.len() && b1.len() == b2.len(), + "Chunk size must be the same" + ); + + let chunk_size = a1.len(); + let (switch, gate_out1, gate_out2) = self.create_switch(a1, a2); - for e in 0..CHUNK_SIZE { + for e in 0..chunk_size { self.route(b1[e], gate_out1[e]); self.route(b2[e], gate_out2[e]); } - - self.add_generator(TwoByTwoPermutationGenerator:: { - a1, - a2, - b1, - b2, - switch, - _phantom: PhantomData, - }); } - fn create_switch( + fn create_switch( &mut self, - a1: [Target; CHUNK_SIZE], - a2: [Target; CHUNK_SIZE], - ) -> (Target, [Target; CHUNK_SIZE], [Target; CHUNK_SIZE]) { - if self.current_switch_gates.len() < CHUNK_SIZE { + a1: Vec, + a2: Vec, + ) -> (Target, Vec, Vec) { + assert!(a1.len() == a2.len(), "Chunk size must be the same"); + + let chunk_size = a1.len(); + + if self.current_switch_gates.len() < chunk_size { self.current_switch_gates - .extend(vec![None; CHUNK_SIZE - self.current_switch_gates.len()]); + .extend(vec![None; chunk_size - self.current_switch_gates.len()]); } - let (gate_index, mut next_copy) = match self.current_switch_gates[CHUNK_SIZE - 1] { - None => { - let gate = SwitchGate::::new_from_config(self.config.clone()); - let gate_index = self.add_gate(gate.clone(), vec![]); - (gate_index, 0) - } - Some((idx, next_copy)) => (idx, next_copy), - }; + let (gate, gate_index, mut next_copy) = + match self.current_switch_gates[chunk_size - 1].clone() { + None => { + let gate = SwitchGate::::new_from_config(self.config.clone(), chunk_size); + let gate_index = self.add_gate(gate.clone(), vec![]); + (gate, gate_index, 0) + } + Some((gate, idx, next_copy)) => (gate, idx, next_copy), + }; - let num_copies = - SwitchGate::::max_num_copies(self.config.num_routed_wires); + let num_copies = gate.num_copies; let mut c = Vec::new(); let mut d = Vec::new(); - for e in 0..CHUNK_SIZE { + for e in 0..chunk_size { self.route( a1[e], - Target::wire( - gate_index, - SwitchGate::::wire_first_input(next_copy, e), - ), + Target::wire(gate_index, gate.wire_first_input(next_copy, e)), ); self.route( a2[e], - Target::wire( - gate_index, - SwitchGate::::wire_second_input(next_copy, e), - ), + Target::wire(gate_index, gate.wire_second_input(next_copy, e)), ); c.push(Target::wire( gate_index, - SwitchGate::::wire_first_output(next_copy, e), + gate.wire_first_output(next_copy, e), )); d.push(Target::wire( gate_index, - SwitchGate::::wire_second_output(next_copy, e), + gate.wire_second_output(next_copy, e), )); } - let switch = Target::wire( - gate_index, - SwitchGate::::wire_switch_bool(num_copies, next_copy), - ); - - let c_arr: [Target; CHUNK_SIZE] = c.try_into().unwrap(); - let d_arr: [Target; CHUNK_SIZE] = d.try_into().unwrap(); + let switch = Target::wire(gate_index, gate.wire_switch_bool(next_copy)); next_copy += 1; if next_copy == num_copies { - let new_gate = SwitchGate::::new_from_config(self.config.clone()); + let new_gate = SwitchGate::::new_from_config(self.config.clone(), chunk_size); let new_gate_index = self.add_gate(new_gate.clone(), vec![]); - self.current_switch_gates[CHUNK_SIZE - 1] = Some((new_gate_index, 0)); + self.current_switch_gates[chunk_size - 1] = Some((new_gate, new_gate_index, 0)); } else { - self.current_switch_gates[CHUNK_SIZE - 1] = Some((gate_index, next_copy)); + self.current_switch_gates[chunk_size - 1] = Some((gate, gate_index, next_copy)); } - (switch, c_arr, d_arr) + (switch, c, d) } - fn assert_permutation_recursive( - &mut self, - a: Vec<[Target; CHUNK_SIZE]>, - b: Vec<[Target; CHUNK_SIZE]>, - ) { + fn assert_permutation_recursive(&mut self, a: Vec>, b: Vec>) { + assert_eq!( + a.len(), + b.len(), + "Permutation must have same number of inputs and outputs" + ); + assert_eq!(a[0].len(), b[0].len(), "Chunk size must be the same"); + + let chunk_size = a[0].len(); + let n = a.len(); let even = n % 2 == 0; @@ -156,13 +152,13 @@ impl, const D: usize> CircuitBuilder { let mut a_switches = Vec::new(); let mut b_switches = Vec::new(); for i in 0..a_num_switches { - let (switch, out_1, out_2) = self.create_switch(a[i * 2], a[i * 2 + 1]); + let (switch, out_1, out_2) = self.create_switch(a[i * 2].clone(), a[i * 2 + 1].clone()); a_switches.push(switch); child_1_a.push(out_1); child_2_a.push(out_2); } for i in 0..b_num_switches { - let (switch, out_1, out_2) = self.create_switch(b[i * 2], b[i * 2 + 1]); + let (switch, out_1, out_2) = self.create_switch(b[i * 2].clone(), b[i * 2 + 1].clone()); b_switches.push(switch); child_1_b.push(out_1); child_2_b.push(out_2); @@ -180,7 +176,8 @@ impl, const D: usize> CircuitBuilder { self.assert_permutation(child_1_a, child_1_b); self.assert_permutation(child_2_a, child_2_b); - self.add_generator(PermutationGenerator:: { + self.add_generator(PermutationGenerator:: { + chunk_size, a, b, a_switches, @@ -190,9 +187,9 @@ impl, const D: usize> CircuitBuilder { } } -fn route( - a_values: Vec<[F; CHUNK_SIZE]>, - b_values: Vec<[F; CHUNK_SIZE]>, +fn route( + a_values: Vec>, + b_values: Vec>, a_switches: Vec, b_switches: Vec, witness: &PartialWitness, @@ -354,57 +351,16 @@ fn route( } } } - -struct TwoByTwoPermutationGenerator { - a1: [Target; CHUNK_SIZE], - a2: [Target; CHUNK_SIZE], - b1: [Target; CHUNK_SIZE], - b2: [Target; CHUNK_SIZE], - switch: Target, - _phantom: PhantomData, -} - -impl SimpleGenerator - for TwoByTwoPermutationGenerator -{ - fn dependencies(&self) -> Vec { - [self.a1, self.a2, self.b1, self.b2] - .iter() - .map(|arr| arr.to_vec()) - .flatten() - .collect() - } - - fn run_once(&self, witness: &PartialWitness, out_buffer: &mut GeneratedValues) { - let a1_values: Vec<_> = self.a1.iter().map(|x| witness.get_target(*x)).collect(); - let a2_values: Vec<_> = self.a2.iter().map(|x| witness.get_target(*x)).collect(); - let b1_values: Vec<_> = self.b1.iter().map(|x| witness.get_target(*x)).collect(); - let b2_values: Vec<_> = self.b2.iter().map(|x| witness.get_target(*x)).collect(); - - let no_switch = a1_values.iter().zip(b1_values.iter()).all(|(a, b)| a == b) - && a2_values.iter().zip(b2_values.iter()).all(|(a, b)| a == b); - let switch = a1_values.iter().zip(b2_values.iter()).all(|(a, b)| a == b) - && a2_values.iter().zip(b1_values.iter()).all(|(a, b)| a == b); - - if no_switch { - out_buffer.set_target(self.switch, F::ZERO); - } else if switch { - out_buffer.set_target(self.switch, F::ONE); - } else { - panic!("No permutation"); - } - } -} - -struct PermutationGenerator { - a: Vec<[Target; CHUNK_SIZE]>, - b: Vec<[Target; CHUNK_SIZE]>, +struct PermutationGenerator { + chunk_size: usize, + a: Vec>, + b: Vec>, a_switches: Vec, b_switches: Vec, _phantom: PhantomData, } -impl SimpleGenerator for PermutationGenerator { +impl SimpleGenerator for PermutationGenerator { fn dependencies(&self) -> Vec { self.a .clone() @@ -415,23 +371,15 @@ impl SimpleGenerator for PermutationGenera } fn run_once(&self, witness: &PartialWitness, out_buffer: &mut GeneratedValues) { - let wire_chunk_to_vals = |wire: [Target; CHUNK_SIZE]| { - let mut vals = [F::ZERO; CHUNK_SIZE]; - for e in 0..CHUNK_SIZE { - vals[e] = witness.get_target(wire[e]); - } - vals - }; - let a_values = self .a .iter() - .map(|chunk| wire_chunk_to_vals(*chunk)) + .map(|chunk| chunk.iter().map(|wire| witness.get_target(*wire)).collect()) .collect(); let b_values = self .b .iter() - .map(|chunk| wire_chunk_to_vals(*chunk)) + .map(|chunk| chunk.iter().map(|wire| witness.get_target(*wire)).collect()) .collect(); route( a_values, @@ -450,7 +398,6 @@ mod tests { use super::*; use crate::field::crandall_field::CrandallField; - use crate::field::extension_field::quartic::QuarticCrandallField; use crate::field::field_types::Field; use crate::iop::witness::PartialWitness; use crate::plonk::circuit_data::CircuitConfig; @@ -468,10 +415,10 @@ mod tests { let seven = F::from_canonical_usize(7); let eight = F::from_canonical_usize(8); - let one_two = [builder.constant(one), builder.constant(two)]; - let seven_eight = [builder.constant(seven), builder.constant(eight)]; + let one_two = vec![builder.constant(one), builder.constant(two)]; + let seven_eight = vec![builder.constant(seven), builder.constant(eight)]; - let a = vec![one_two, seven_eight]; + let a = vec![one_two.clone(), seven_eight.clone()]; let b = vec![seven_eight, one_two]; builder.assert_permutation(a, b); @@ -498,12 +445,17 @@ mod tests { let seven = F::from_canonical_usize(7); let eight = F::from_canonical_usize(8); - let one_two = [builder.constant(one), builder.constant(two)]; - let three_four = [builder.constant(three), builder.constant(four)]; - let five_six = [builder.constant(five), builder.constant(six)]; - let seven_eight = [builder.constant(seven), builder.constant(eight)]; + let one_two = vec![builder.constant(one), builder.constant(two)]; + let three_four = vec![builder.constant(three), builder.constant(four)]; + let five_six = vec![builder.constant(five), builder.constant(six)]; + let seven_eight = vec![builder.constant(seven), builder.constant(eight)]; - let a = vec![one_two, three_four, five_six, seven_eight]; + let a = vec![ + one_two.clone(), + three_four.clone(), + five_six.clone(), + seven_eight.clone(), + ]; let b = vec![seven_eight, one_two, five_six, three_four]; builder.assert_permutation(a, b); diff --git a/src/gates/switch.rs b/src/gates/switch.rs index 0939fc08..c854e49a 100644 --- a/src/gates/switch.rs +++ b/src/gates/switch.rs @@ -14,73 +14,73 @@ use crate::plonk::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase}; /// A gate for conditionally swapping input values based on a boolean. #[derive(Clone, Debug)] -pub(crate) struct SwitchGate, const D: usize, const CHUNK_SIZE: usize> { - num_copies: usize, +pub(crate) struct SwitchGate, const D: usize> { + pub(crate) chunk_size: usize, + pub(crate) num_copies: usize, _phantom: PhantomData, } -impl, const D: usize, const CHUNK_SIZE: usize> SwitchGate { - pub fn new(num_copies: usize) -> Self { +impl, const D: usize> SwitchGate { + pub fn new(num_copies: usize, chunk_size: usize) -> Self { Self { + chunk_size, num_copies, _phantom: PhantomData, } } - pub fn new_from_config(config: CircuitConfig) -> Self { - let num_copies = Self::max_num_copies(config.num_routed_wires); - Self::new(num_copies) + pub fn new_from_config(config: CircuitConfig, chunk_size: usize) -> Self { + let num_copies = Self::max_num_copies(config.num_routed_wires, chunk_size); + Self::new(num_copies, chunk_size) } - pub fn max_num_copies(num_routed_wires: usize) -> usize { - num_routed_wires / (4 * CHUNK_SIZE) + pub fn max_num_copies(num_routed_wires: usize, chunk_size: usize) -> usize { + num_routed_wires / (4 * chunk_size) } - pub fn wire_first_input(copy: usize, element: usize) -> usize { - debug_assert!(element < CHUNK_SIZE); - copy * (4 * CHUNK_SIZE) + element + pub fn wire_first_input(&self, copy: usize, element: usize) -> usize { + debug_assert!(element < self.chunk_size); + copy * (4 * self.chunk_size) + element } - pub fn wire_second_input(copy: usize, element: usize) -> usize { - debug_assert!(element < CHUNK_SIZE); - copy * (4 * CHUNK_SIZE) + CHUNK_SIZE + element + pub fn wire_second_input(&self, copy: usize, element: usize) -> usize { + debug_assert!(element < self.chunk_size); + copy * (4 * self.chunk_size) + self.chunk_size + element } - pub fn wire_first_output(copy: usize, element: usize) -> usize { - debug_assert!(element < CHUNK_SIZE); - copy * (4 * CHUNK_SIZE) + 2 * CHUNK_SIZE + element + pub fn wire_first_output(&self, copy: usize, element: usize) -> usize { + debug_assert!(element < self.chunk_size); + copy * (4 * self.chunk_size) + 2 * self.chunk_size + element } - pub fn wire_second_output(copy: usize, element: usize) -> usize { - debug_assert!(element < CHUNK_SIZE); - copy * (4 * CHUNK_SIZE) + 3 * CHUNK_SIZE + element + pub fn wire_second_output(&self, copy: usize, element: usize) -> usize { + debug_assert!(element < self.chunk_size); + copy * (4 * self.chunk_size) + 3 * self.chunk_size + element } - pub fn wire_switch_bool(num_copies: usize, copy: usize) -> usize { - debug_assert!(copy < num_copies); - num_copies * (4 * CHUNK_SIZE) + copy + pub fn wire_switch_bool(&self, copy: usize) -> usize { + debug_assert!(copy < self.num_copies); + self.num_copies * (4 * self.chunk_size) + copy } } -impl, const D: usize, const CHUNK_SIZE: usize> Gate - for SwitchGate -{ +impl, const D: usize> Gate for SwitchGate { fn id(&self) -> String { - format!("{:?}", self, D, CHUNK_SIZE) + format!("{:?}", self, D) } fn eval_unfiltered(&self, vars: EvaluationVars) -> Vec { let mut constraints = Vec::with_capacity(self.num_constraints()); for c in 0..self.num_copies { - let switch_bool = vars.local_wires[Self::wire_switch_bool(self.num_copies, c)]; + let switch_bool = vars.local_wires[self.wire_switch_bool(c)]; let not_switch = F::Extension::ONE - switch_bool; - for e in 0..CHUNK_SIZE { - let first_input = vars.local_wires[Self::wire_first_input(c, e)]; - let second_input = vars.local_wires[Self::wire_second_input(c, e)]; - let first_output = vars.local_wires[Self::wire_first_output(c, e)]; - let second_output = vars.local_wires[Self::wire_second_output(c, e)]; + for e in 0..self.chunk_size { + let first_input = vars.local_wires[self.wire_first_input(c, e)]; + let second_input = vars.local_wires[self.wire_second_input(c, e)]; + let first_output = vars.local_wires[self.wire_first_output(c, e)]; + let second_output = vars.local_wires[self.wire_second_output(c, e)]; constraints.push(switch_bool * (first_input - second_output)); constraints.push(switch_bool * (second_input - first_output)); @@ -96,14 +96,14 @@ impl, const D: usize, const CHUNK_SIZE: usize> Gate let mut constraints = Vec::with_capacity(self.num_constraints()); for c in 0..self.num_copies { - let switch_bool = vars.local_wires[Self::wire_switch_bool(self.num_copies, c)]; + let switch_bool = vars.local_wires[self.wire_switch_bool(c)]; let not_switch = F::ONE - switch_bool; - for e in 0..CHUNK_SIZE { - let first_input = vars.local_wires[Self::wire_first_input(c, e)]; - let second_input = vars.local_wires[Self::wire_second_input(c, e)]; - let first_output = vars.local_wires[Self::wire_first_output(c, e)]; - let second_output = vars.local_wires[Self::wire_second_output(c, e)]; + for e in 0..self.chunk_size { + let first_input = vars.local_wires[self.wire_first_input(c, e)]; + let second_input = vars.local_wires[self.wire_second_input(c, e)]; + let first_output = vars.local_wires[self.wire_first_output(c, e)]; + let second_output = vars.local_wires[self.wire_second_output(c, e)]; constraints.push(switch_bool * (first_input - second_output)); constraints.push(switch_bool * (second_input - first_output)); @@ -124,14 +124,14 @@ impl, const D: usize, const CHUNK_SIZE: usize> Gate let one = builder.one_extension(); for c in 0..self.num_copies { - let switch_bool = vars.local_wires[Self::wire_switch_bool(self.num_copies, c)]; + let switch_bool = vars.local_wires[self.wire_switch_bool(c)]; let not_switch = builder.sub_extension(one, switch_bool); - for e in 0..CHUNK_SIZE { - let first_input = vars.local_wires[Self::wire_first_input(c, e)]; - let second_input = vars.local_wires[Self::wire_second_input(c, e)]; - let first_output = vars.local_wires[Self::wire_first_output(c, e)]; - let second_output = vars.local_wires[Self::wire_second_output(c, e)]; + for e in 0..self.chunk_size { + let first_input = vars.local_wires[self.wire_first_input(c, e)]; + let second_input = vars.local_wires[self.wire_second_input(c, e)]; + let first_output = vars.local_wires[self.wire_first_output(c, e)]; + let second_output = vars.local_wires[self.wire_second_output(c, e)]; let first_switched = builder.sub_extension(first_input, second_output); let first_switched_constraint = builder.mul_extension(switch_bool, first_switched); @@ -164,19 +164,18 @@ impl, const D: usize, const CHUNK_SIZE: usize> Gate ) -> Vec>> { (0..self.num_copies) .map(|c| { - let g: Box> = - Box::new(SwitchGenerator:: { - gate_index, - gate: self.clone(), - copy: c, - }); + let g: Box> = Box::new(SwitchGenerator:: { + gate_index, + gate: self.clone(), + copy: c, + }); g }) .collect() } fn num_wires(&self) -> usize { - Self::wire_switch_bool(self.num_copies, self.num_copies - 1) + 1 + self.wire_switch_bool(self.num_copies - 1) + 1 } fn num_constants(&self) -> usize { @@ -188,34 +187,26 @@ impl, const D: usize, const CHUNK_SIZE: usize> Gate } fn num_constraints(&self) -> usize { - 4 * self.num_copies * CHUNK_SIZE + 4 * self.num_copies * self.chunk_size } } #[derive(Debug)] -struct SwitchGenerator, const D: usize, const CHUNK_SIZE: usize> { +struct SwitchGenerator, const D: usize> { gate_index: usize, - gate: SwitchGate, + gate: SwitchGate, copy: usize, } -impl, const D: usize, const CHUNK_SIZE: usize> SimpleGenerator - for SwitchGenerator -{ +impl, const D: usize> SimpleGenerator for SwitchGenerator { fn dependencies(&self) -> Vec { let local_target = |input| Target::wire(self.gate_index, input); let mut deps = Vec::new(); - for e in 0..CHUNK_SIZE { - deps.push(local_target( - SwitchGate::::wire_first_input(self.copy, e), - )); - deps.push(local_target( - SwitchGate::::wire_second_input(self.copy, e), - )); - deps.push(local_target( - SwitchGate::::wire_switch_bool(self.gate.num_copies, self.copy), - )); + for e in 0..self.gate.chunk_size { + deps.push(local_target(self.gate.wire_first_input(self.copy, e))); + deps.push(local_target(self.gate.wire_second_input(self.copy, e))); + deps.push(local_target(self.gate.wire_switch_bool(self.copy))); } deps @@ -229,23 +220,12 @@ impl, const D: usize, const CHUNK_SIZE: usize> SimpleGenerator< let get_local_wire = |input| witness.get_wire(local_wire(input)); - for e in 0..CHUNK_SIZE { - let first_output_wire = local_wire(SwitchGate::::wire_first_output( - self.copy, e, - )); - let second_output_wire = local_wire( - SwitchGate::::wire_second_output(self.copy, e), - ); - let first_input = get_local_wire(SwitchGate::::wire_first_input( - self.copy, e, - )); - let second_input = get_local_wire(SwitchGate::::wire_second_input( - self.copy, e, - )); - let switch_bool = get_local_wire(SwitchGate::::wire_switch_bool( - self.gate.num_copies, - self.copy, - )); + for e in 0..self.gate.chunk_size { + let first_output_wire = local_wire(self.gate.wire_first_output(self.copy, e)); + let second_output_wire = local_wire(self.gate.wire_second_output(self.copy, e)); + let first_input = get_local_wire(self.gate.wire_first_input(self.copy, e)); + let second_input = get_local_wire(self.gate.wire_second_input(self.copy, e)); + let switch_bool = get_local_wire(self.gate.wire_switch_bool(self.copy)); let (first_output, second_output) = if switch_bool == F::ZERO { (first_input, second_input) @@ -277,40 +257,44 @@ mod tests { #[test] fn wire_indices() { - type SG = SwitchGate; + type SG = SwitchGate; let num_copies = 3; + let chunk_size = 3; let gate = SG { + chunk_size, num_copies, _phantom: PhantomData, }; - assert_eq!(SG::wire_first_input(0, 0), 0); - assert_eq!(SG::wire_first_input(0, 2), 2); - assert_eq!(SG::wire_second_input(0, 0), 3); - assert_eq!(SG::wire_second_input(0, 2), 5); - assert_eq!(SG::wire_first_output(0, 0), 6); - assert_eq!(SG::wire_second_output(0, 2), 11); - assert_eq!(SG::wire_first_input(1, 0), 12); - assert_eq!(SG::wire_second_output(1, 2), 23); - assert_eq!(SG::wire_first_input(2, 0), 24); - assert_eq!(SG::wire_second_output(2, 2), 35); - assert_eq!(SG::wire_switch_bool(num_copies, 0), 36); - assert_eq!(SG::wire_switch_bool(num_copies, 1), 37); - assert_eq!(SG::wire_switch_bool(num_copies, 2), 38); + assert_eq!(gate.wire_first_input(0, 0), 0); + assert_eq!(gate.wire_first_input(0, 2), 2); + assert_eq!(gate.wire_second_input(0, 0), 3); + assert_eq!(gate.wire_second_input(0, 2), 5); + assert_eq!(gate.wire_first_output(0, 0), 6); + assert_eq!(gate.wire_second_output(0, 2), 11); + assert_eq!(gate.wire_first_input(1, 0), 12); + assert_eq!(gate.wire_second_output(1, 2), 23); + assert_eq!(gate.wire_first_input(2, 0), 24); + assert_eq!(gate.wire_second_output(2, 2), 35); + assert_eq!(gate.wire_switch_bool(0), 36); + assert_eq!(gate.wire_switch_bool(1), 37); + assert_eq!(gate.wire_switch_bool(2), 38); } #[test] fn low_degree() { - test_low_degree::(SwitchGate::<_, 4, 3>::new_from_config( + test_low_degree::(SwitchGate::<_, 4>::new_from_config( CircuitConfig::large_config(), + 3, )); } #[test] fn eval_fns() -> Result<()> { - test_eval_fns::(SwitchGate::<_, 4, 3>::new_from_config( + test_eval_fns::(SwitchGate::<_, 4>::new_from_config( CircuitConfig::large_config(), + 3, )) } @@ -319,7 +303,7 @@ mod tests { type F = CrandallField; type FF = QuarticCrandallField; const D: usize = 4; - const CHUNK_SIZE: usize = 4; + const chunk_size: usize = 4; let num_copies = 3; /// Returns the local wires for a switch gate given the inputs and the switch booleans. @@ -336,11 +320,11 @@ mod tests { let switch = switch_bools[c]; switches.push(F::from_bool(switch)); - let mut first_input_chunk = Vec::with_capacity(CHUNK_SIZE); - let mut second_input_chunk = Vec::with_capacity(CHUNK_SIZE); - let mut first_output_chunk = Vec::with_capacity(CHUNK_SIZE); - let mut second_output_chunk = Vec::with_capacity(CHUNK_SIZE); - for e in 0..CHUNK_SIZE { + let mut first_input_chunk = Vec::with_capacity(chunk_size); + let mut second_input_chunk = Vec::with_capacity(chunk_size); + let mut first_output_chunk = Vec::with_capacity(chunk_size); + let mut second_output_chunk = Vec::with_capacity(chunk_size); + for e in 0..chunk_size { let first_input = first_inputs[c][e]; let second_input = second_inputs[c][e]; let first_output = if switch { second_input } else { first_input }; @@ -360,11 +344,12 @@ mod tests { v.iter().map(|&x| x.into()).collect::>() } - let first_inputs: Vec> = (0..num_copies).map(|_| F::rand_vec(CHUNK_SIZE)).collect(); - let second_inputs: Vec> = (0..num_copies).map(|_| F::rand_vec(CHUNK_SIZE)).collect(); + let first_inputs: Vec> = (0..num_copies).map(|_| F::rand_vec(chunk_size)).collect(); + let second_inputs: Vec> = (0..num_copies).map(|_| F::rand_vec(chunk_size)).collect(); let switch_bools = vec![true, false, true]; - let gate = SwitchGate:: { + let gate = SwitchGate:: { + chunk_size, num_copies, _phantom: PhantomData, }; diff --git a/src/plonk/circuit_builder.rs b/src/plonk/circuit_builder.rs index 0daa4943..ed19a89f 100644 --- a/src/plonk/circuit_builder.rs +++ b/src/plonk/circuit_builder.rs @@ -14,6 +14,7 @@ use crate::gates::gate::{Gate, GateInstance, GateRef, PrefixedGate}; use crate::gates::gate_tree::Tree; use crate::gates::noop::NoopGate; use crate::gates::public_input::PublicInputGate; +use crate::gates::switch::SwitchGate; use crate::hash::hash_types::{HashOutTarget, MerkleCapTarget}; use crate::hash::hashing::hash_n_to_hash; use crate::iop::generator::{CopyGenerator, RandomValueGenerator, WitnessGenerator}; @@ -66,7 +67,10 @@ pub struct CircuitBuilder, const D: usize> { /// these constants with gate index `g` and already using `i` arithmetic operations. pub(crate) free_arithmetic: HashMap<(F, F), (usize, usize)>, - pub(crate) current_switch_gates: Vec>, + // `current_switch_gates[chunk_size - 1]` contains None if we have no switch gates with the value + // chunk_size, and contains `(g, i, c)`, if the gate `g`, at index `i`, already contains `c` copies + // of switches + pub(crate) current_switch_gates: Vec, usize, usize)>>, } impl, const D: usize> CircuitBuilder { @@ -509,6 +513,29 @@ impl, const D: usize> CircuitBuilder { } } + /// Fill the remaining unused switch gates with dummy values, so that all + /// `SwitchGenerator` are run. + fn fill_switch_gates(&mut self) { + let zero = self.zero(); + + for chunk_size in 1..=self.current_switch_gates.len() { + if let Some((gate, gate_index, copy)) = + self.current_switch_gates[chunk_size - 1].clone() + { + for element in 0..chunk_size { + let wire_first_input = + Target::wire(gate_index, gate.wire_first_input(copy, element)); + let wire_second_input = + Target::wire(gate_index, gate.wire_second_input(copy, element)); + let wire_switch_bool = Target::wire(gate_index, gate.wire_switch_bool(copy)); + self.route(zero, wire_first_input); + self.route(zero, wire_second_input); + self.route(zero, wire_switch_bool); + } + } + } + } + pub fn print_gate_counts(&self, min_delta: usize) { self.context_log .filter(self.num_gates(), min_delta) From d1fea5cfd361a9422a2ca3a607a75205bb87b874 Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Thu, 2 Sep 2021 11:54:20 -0700 Subject: [PATCH 21/31] witnessgenerator --- Cargo.toml | 1 + src/gates/switch.rs | 72 +++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 68 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 790ff6b9..ed5acab6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,7 @@ edition = "2018" default-run = "bench_recursion" [dependencies] +array_tool = "1.0.3" bimap = "0.4.0" env_logger = "0.9.0" log = "0.4.14" diff --git a/src/gates/switch.rs b/src/gates/switch.rs index c854e49a..b520ef50 100644 --- a/src/gates/switch.rs +++ b/src/gates/switch.rs @@ -1,10 +1,12 @@ use std::marker::PhantomData; +use array_tool::vec::Union; + use crate::field::extension_field::target::ExtensionTarget; use crate::field::extension_field::Extendable; use crate::field::field_types::Field; use crate::gates::gate::Gate; -use crate::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator}; +use crate::iop::generator::{GeneratedValues, WitnessGenerator}; use crate::iop::target::Target; use crate::iop::wire::Wire; use crate::iop::witness::{PartitionWitness, Witness}; @@ -198,8 +200,22 @@ struct SwitchGenerator, const D: usize> { copy: usize, } -impl, const D: usize> SimpleGenerator for SwitchGenerator { - fn dependencies(&self) -> Vec { +impl, const D: usize> SwitchGenerator { + fn in_out_dependencies(&self) -> Vec { + let local_target = |input| Target::wire(self.gate_index, input); + + let mut deps = Vec::new(); + for e in 0..self.gate.chunk_size { + deps.push(local_target(self.gate.wire_first_input(self.copy, e))); + deps.push(local_target(self.gate.wire_second_input(self.copy, e))); + deps.push(local_target(self.gate.wire_first_output(self.copy, e))); + deps.push(local_target(self.gate.wire_second_output(self.copy, e))); + } + + deps + } + + fn in_switch_dependencies(&self) -> Vec { let local_target = |input| Target::wire(self.gate_index, input); let mut deps = Vec::new(); @@ -212,7 +228,32 @@ impl, const D: usize> SimpleGenerator for SwitchGenerator, out_buffer: &mut GeneratedValues) { + fn run_in_out(&self, witness: &PartialWitness, out_buffer: &mut GeneratedValues) { + let local_wire = |input| Wire { + gate: self.gate_index, + input, + }; + + let get_local_wire = |input| witness.get_wire(local_wire(input)); + + for e in 0..self.gate.chunk_size { + let switch_bool_wire = local_wire(self.gate.wire_switch_bool(self.copy)); + let first_input = get_local_wire(self.gate.wire_first_input(self.copy, e)); + let second_input = get_local_wire(self.gate.wire_second_input(self.copy, e)); + let first_output = get_local_wire(self.gate.wire_first_output(self.copy, e)); + let second_output = get_local_wire(self.gate.wire_second_output(self.copy, e)); + + if first_output == first_input && second_output == second_input { + out_buffer.set_wire(switch_bool_wire, F::ZERO); + } else if first_output == second_input && second_output == first_input { + out_buffer.set_wire(switch_bool_wire, F::ONE); + } else { + panic!("No permutation from given inputs to given outputs"); + } + } + } + + fn run_in_switch(&self, witness: &PartialWitness, out_buffer: &mut GeneratedValues) { let local_wire = |input| Wire { gate: self.gate_index, input, @@ -229,8 +270,10 @@ impl, const D: usize> SimpleGenerator for SwitchGenerator, const D: usize> SimpleGenerator for SwitchGenerator, const D: usize> WitnessGenerator for SwitchGenerator { + fn watch_list(&self) -> Vec { + self.in_out_dependencies() + .union(self.in_switch_dependencies()) + } + + fn run(&self, witness: &PartialWitness, out_buffer: &mut GeneratedValues) -> bool { + if witness.contains_all(&self.in_out_dependencies()) { + self.run_in_out(witness, out_buffer); + true + } else if witness.contains_all(&self.in_switch_dependencies()) { + self.run_in_switch(witness, out_buffer); + true + } else { + false + } + } +} + #[cfg(test)] mod tests { use std::marker::PhantomData; From f89f49249a817b69a19c5b447e5d78dc50f682f7 Mon Sep 17 00:00:00 2001 From: Daniel Lubarov Date: Thu, 2 Sep 2021 15:03:03 -0700 Subject: [PATCH 22/31] wip --- src/gadgets/arithmetic_extension.rs | 2 +- src/gadgets/permutation.rs | 38 ++++++++++++++++------------- src/gadgets/range_check.rs | 2 +- src/gadgets/split_base.rs | 2 +- src/gadgets/split_join.rs | 2 +- src/gates/arithmetic.rs | 15 +++++++----- src/gates/base_sum.rs | 2 +- src/gates/constant.rs | 2 +- src/gates/exponentiation.rs | 2 +- src/gates/gmimc.rs | 2 +- src/gates/insertion.rs | 2 +- src/gates/interpolation.rs | 2 +- src/gates/random_access.rs | 2 +- src/gates/reducing.rs | 11 ++++++--- src/gates/switch.rs | 6 ++--- src/iop/generator.rs | 25 ++++++++++++++++--- src/plonk/circuit_builder.rs | 20 ++++++++------- 17 files changed, 83 insertions(+), 54 deletions(-) diff --git a/src/gadgets/arithmetic_extension.rs b/src/gadgets/arithmetic_extension.rs index 9f5d7141..bc9aad6f 100644 --- a/src/gadgets/arithmetic_extension.rs +++ b/src/gadgets/arithmetic_extension.rs @@ -413,7 +413,7 @@ impl, const D: usize> CircuitBuilder { ) -> ExtensionTarget { let inv = self.add_virtual_extension_target(); let one = self.one_extension(); - self.add_generator(QuotientGeneratorExtension { + self.add_simple_generator(QuotientGeneratorExtension { numerator: one, denominator: y, quotient: inv, diff --git a/src/gadgets/permutation.rs b/src/gadgets/permutation.rs index 3016c2a1..0c51057a 100644 --- a/src/gadgets/permutation.rs +++ b/src/gadgets/permutation.rs @@ -6,7 +6,7 @@ use crate::field::{extension_field::Extendable, field_types::Field}; use crate::gates::switch::SwitchGate; use crate::iop::generator::{GeneratedValues, SimpleGenerator}; use crate::iop::target::{BoolTarget, Target}; -use crate::iop::witness::PartialWitness; +use crate::iop::witness::{PartitionWitness, Witness}; use crate::plonk::circuit_builder::CircuitBuilder; use crate::util::bimap::bimap_from_lists; @@ -28,7 +28,7 @@ impl, const D: usize> CircuitBuilder { // Two singleton lists are permutations of one another as long as their items are equal. 1 => { for e in 0..chunk_size { - self.assert_equal(a[0][e], b[0][e]) + self.connect(a[0][e], b[0][e]) } } 2 => { @@ -57,8 +57,8 @@ impl, const D: usize> CircuitBuilder { let (switch, gate_out1, gate_out2) = self.create_switch(a1, a2); for e in 0..chunk_size { - self.route(b1[e], gate_out1[e]); - self.route(b2[e], gate_out2[e]); + self.connect(b1[e], gate_out1[e]); + self.connect(b2[e], gate_out2[e]); } } @@ -91,11 +91,11 @@ impl, const D: usize> CircuitBuilder { let mut c = Vec::new(); let mut d = Vec::new(); for e in 0..chunk_size { - self.route( + self.connect( a1[e], Target::wire(gate_index, gate.wire_first_input(next_copy, e)), ); - self.route( + self.connect( a2[e], Target::wire(gate_index, gate.wire_second_input(next_copy, e)), ); @@ -176,7 +176,7 @@ impl, const D: usize> CircuitBuilder { self.assert_permutation(child_1_a, child_1_b); self.assert_permutation(child_2_a, child_2_b); - self.add_generator(PermutationGenerator:: { + self.add_simple_generator(PermutationGenerator:: { chunk_size, a, b, @@ -192,7 +192,7 @@ fn route( b_values: Vec>, a_switches: Vec, b_switches: Vec, - witness: &PartialWitness, + witness: &PartitionWitness, out_buffer: &mut GeneratedValues, ) { assert_eq!(a_values.len(), b_values.len()); @@ -221,7 +221,7 @@ fn route( // After we route a wire on one side, we find the corresponding wire on the other side and check // if it still needs to be routed. If so, we add it to partial_routes. let enqueue_other_side = |partial_routes: &mut [BTreeMap], - witness: &PartialWitness, + witness: &PartitionWitness, newly_set: &mut [Vec], side: usize, this_i: usize, @@ -272,7 +272,7 @@ fn route( } let mut route_switch = |partial_routes: &mut [BTreeMap], - witness: &PartialWitness, + witness: &PartitionWitness, out_buffer: &mut GeneratedValues, side: usize, switch_index: usize, @@ -351,6 +351,8 @@ fn route( } } } + +#[derive(Debug)] struct PermutationGenerator { chunk_size: usize, a: Vec>, @@ -370,7 +372,7 @@ impl SimpleGenerator for PermutationGenerator { .collect() } - fn run_once(&self, witness: &PartialWitness, out_buffer: &mut GeneratedValues) { + fn run_once(&self, witness: &PartitionWitness, out_buffer: &mut GeneratedValues) { let a_values = self .a .iter() @@ -406,9 +408,10 @@ mod tests { #[test] fn test_permutation_2x2() -> Result<()> { type F = CrandallField; - let config = CircuitConfig::large_config(); - let pw = PartialWitness::new(config.num_wires); - let mut builder = CircuitBuilder::::new(config); + let config = CircuitConfig::large_zk_config(); + + let pw = PartialWitness::new(); + let mut builder = CircuitBuilder::::new(config); let one = F::ONE; let two = F::from_canonical_usize(2); @@ -432,9 +435,10 @@ mod tests { #[test] fn test_permutation_4x4() -> Result<()> { type F = CrandallField; - let config = CircuitConfig::large_config(); - let pw = PartialWitness::new(config.num_wires); - let mut builder = CircuitBuilder::::new(config); + let config = CircuitConfig::large_zk_config(); + + let pw = PartialWitness::new(); + let mut builder = CircuitBuilder::::new(config); let one = F::ONE; let two = F::from_canonical_usize(2); diff --git a/src/gadgets/range_check.rs b/src/gadgets/range_check.rs index 4f3b7d0d..32a3608d 100644 --- a/src/gadgets/range_check.rs +++ b/src/gadgets/range_check.rs @@ -28,7 +28,7 @@ impl, const D: usize> CircuitBuilder { let high_gate = self.add_gate(BaseSumGate::<2>::new(num_bits - n_log), vec![]); let low = Target::wire(low_gate, BaseSumGate::<2>::WIRE_SUM); let high = Target::wire(high_gate, BaseSumGate::<2>::WIRE_SUM); - self.add_generator(LowHighGenerator { + self.add_simple_generator(LowHighGenerator { integer: x, n_log, low, diff --git a/src/gadgets/split_base.rs b/src/gadgets/split_base.rs index 914269d5..02d24833 100644 --- a/src/gadgets/split_base.rs +++ b/src/gadgets/split_base.rs @@ -44,7 +44,7 @@ impl, const D: usize> CircuitBuilder { self.connect(limb.borrow().target, Target::wire(gate_index, wire)); } - self.add_generator(BaseSumGenerator::<2> { + self.add_simple_generator(BaseSumGenerator::<2> { gate_index, limbs: bits.map(|l| *l.borrow()).collect(), }); diff --git a/src/gadgets/split_join.rs b/src/gadgets/split_join.rs index 4b6527ca..143e63e3 100644 --- a/src/gadgets/split_join.rs +++ b/src/gadgets/split_join.rs @@ -47,7 +47,7 @@ impl, const D: usize> CircuitBuilder { } self.connect(acc, integer); - self.add_generator(WireSplitGenerator { + self.add_simple_generator(WireSplitGenerator { integer, gates, num_limbs: bits_per_gate, diff --git a/src/gates/arithmetic.rs b/src/gates/arithmetic.rs index 3c9dcb18..41dee743 100644 --- a/src/gates/arithmetic.rs +++ b/src/gates/arithmetic.rs @@ -110,12 +110,15 @@ impl, const D: usize> Gate for ArithmeticExtensionGate ) -> Vec>> { (0..NUM_ARITHMETIC_OPS) .map(|i| { - let g: Box> = Box::new(ArithmeticExtensionGenerator { - gate_index, - const_0: local_constants[0], - const_1: local_constants[1], - i, - }); + let g: Box> = Box::new( + ArithmeticExtensionGenerator { + gate_index, + const_0: local_constants[0], + const_1: local_constants[1], + i, + } + .adapter(), + ); g }) .collect::>() diff --git a/src/gates/base_sum.rs b/src/gates/base_sum.rs index 341d0fc1..9102bf07 100644 --- a/src/gates/base_sum.rs +++ b/src/gates/base_sum.rs @@ -105,7 +105,7 @@ impl, const D: usize, const B: usize> Gate for BaseSumGat gate_index, num_limbs: self.num_limbs, }; - vec![Box::new(gen)] + vec![Box::new(gen.adapter())] } // 1 for the sum then `num_limbs` for the limbs. diff --git a/src/gates/constant.rs b/src/gates/constant.rs index 894016d6..40493e81 100644 --- a/src/gates/constant.rs +++ b/src/gates/constant.rs @@ -54,7 +54,7 @@ impl, const D: usize> Gate for ConstantGate { gate_index, constant: local_constants[0], }; - vec![Box::new(gen)] + vec![Box::new(gen.adapter())] } fn num_wires(&self) -> usize { diff --git a/src/gates/exponentiation.rs b/src/gates/exponentiation.rs index 1a3a6ea3..2c1f70bc 100644 --- a/src/gates/exponentiation.rs +++ b/src/gates/exponentiation.rs @@ -184,7 +184,7 @@ impl, const D: usize> Gate for ExponentiationGate { gate_index, gate: self.clone(), }; - vec![Box::new(gen)] + vec![Box::new(gen.adapter())] } fn num_wires(&self) -> usize { diff --git a/src/gates/gmimc.rs b/src/gates/gmimc.rs index 682bd1fa..9e645fcc 100644 --- a/src/gates/gmimc.rs +++ b/src/gates/gmimc.rs @@ -217,7 +217,7 @@ impl, const D: usize, const R: usize> Gate for GMiMCGate< gate_index, constants: self.constants.clone(), }; - vec![Box::new(gen)] + vec![Box::new(gen.adapter())] } fn num_wires(&self) -> usize { diff --git a/src/gates/insertion.rs b/src/gates/insertion.rs index 4c1e6fdf..030dfbff 100644 --- a/src/gates/insertion.rs +++ b/src/gates/insertion.rs @@ -220,7 +220,7 @@ impl, const D: usize> Gate for InsertionGate { gate_index, gate: self.clone(), }; - vec![Box::new(gen)] + vec![Box::new(gen.adapter())] } fn num_wires(&self) -> usize { diff --git a/src/gates/interpolation.rs b/src/gates/interpolation.rs index 56924742..53571b71 100644 --- a/src/gates/interpolation.rs +++ b/src/gates/interpolation.rs @@ -189,7 +189,7 @@ impl, const D: usize> Gate for InterpolationGate { gate: self.clone(), _phantom: PhantomData, }; - vec![Box::new(gen)] + vec![Box::new(gen.adapter())] } fn num_wires(&self) -> usize { diff --git a/src/gates/random_access.rs b/src/gates/random_access.rs index 3bba5ad2..1e87fc10 100644 --- a/src/gates/random_access.rs +++ b/src/gates/random_access.rs @@ -167,7 +167,7 @@ impl, const D: usize> Gate for RandomAccessGate { gate_index, gate: self.clone(), }; - vec![Box::new(gen)] + vec![Box::new(gen.adapter())] } fn num_wires(&self) -> usize { diff --git a/src/gates/reducing.rs b/src/gates/reducing.rs index ee3232b2..7545e549 100644 --- a/src/gates/reducing.rs +++ b/src/gates/reducing.rs @@ -136,10 +136,13 @@ impl, const D: usize> Gate for ReducingGate { gate_index: usize, _local_constants: &[F], ) -> Vec>> { - vec![Box::new(ReducingGenerator { - gate_index, - gate: self.clone(), - })] + vec![Box::new( + ReducingGenerator { + gate_index, + gate: self.clone(), + } + .adapter(), + )] } fn num_wires(&self) -> usize { diff --git a/src/gates/switch.rs b/src/gates/switch.rs index b520ef50..954a4997 100644 --- a/src/gates/switch.rs +++ b/src/gates/switch.rs @@ -228,7 +228,7 @@ impl, const D: usize> SwitchGenerator { deps } - fn run_in_out(&self, witness: &PartialWitness, out_buffer: &mut GeneratedValues) { + fn run_in_out(&self, witness: &PartitionWitness, out_buffer: &mut GeneratedValues) { let local_wire = |input| Wire { gate: self.gate_index, input, @@ -253,7 +253,7 @@ impl, const D: usize> SwitchGenerator { } } - fn run_in_switch(&self, witness: &PartialWitness, out_buffer: &mut GeneratedValues) { + fn run_in_switch(&self, witness: &PartitionWitness, out_buffer: &mut GeneratedValues) { let local_wire = |input| Wire { gate: self.gate_index, input, @@ -288,7 +288,7 @@ impl, const D: usize> WitnessGenerator for SwitchGenerator, out_buffer: &mut GeneratedValues) -> bool { + fn run(&self, witness: &PartitionWitness, out_buffer: &mut GeneratedValues) -> bool { if witness.contains_all(&self.in_out_dependencies()) { self.run_in_out(witness, out_buffer); true diff --git a/src/iop/generator.rs b/src/iop/generator.rs index 8174981c..483a7a62 100644 --- a/src/iop/generator.rs +++ b/src/iop/generator.rs @@ -1,4 +1,5 @@ use std::fmt::Debug; +use std::marker::PhantomData; use crate::field::extension_field::target::ExtensionTarget; use crate::field::extension_field::{Extendable, FieldExtension}; @@ -186,16 +187,32 @@ pub trait SimpleGenerator: 'static + Send + Sync + Debug { fn dependencies(&self) -> Vec; fn run_once(&self, witness: &PartitionWitness, out_buffer: &mut GeneratedValues); + + fn adapter(self) -> SimpleGeneratorAdapter + where + Self: Sized, + { + SimpleGeneratorAdapter { + inner: self, + _phantom: PhantomData, + } + } } -impl> WitnessGenerator for SG { +#[derive(Debug)] +pub struct SimpleGeneratorAdapter + ?Sized> { + _phantom: PhantomData, + inner: SG, +} + +impl> WitnessGenerator for SimpleGeneratorAdapter { fn watch_list(&self) -> Vec { - self.dependencies() + self.inner.dependencies() } fn run(&self, witness: &PartitionWitness, out_buffer: &mut GeneratedValues) -> bool { - if witness.contains_all(&self.dependencies()) { - self.run_once(witness, out_buffer); + if witness.contains_all(&self.inner.dependencies()) { + self.inner.run_once(witness, out_buffer); true } else { false diff --git a/src/plonk/circuit_builder.rs b/src/plonk/circuit_builder.rs index ed19a89f..7252cebf 100644 --- a/src/plonk/circuit_builder.rs +++ b/src/plonk/circuit_builder.rs @@ -17,7 +17,9 @@ use crate::gates::public_input::PublicInputGate; use crate::gates::switch::SwitchGate; use crate::hash::hash_types::{HashOutTarget, MerkleCapTarget}; use crate::hash::hashing::hash_n_to_hash; -use crate::iop::generator::{CopyGenerator, RandomValueGenerator, WitnessGenerator}; +use crate::iop::generator::{ + CopyGenerator, RandomValueGenerator, SimpleGenerator, WitnessGenerator, +}; use crate::iop::target::{BoolTarget, Target}; use crate::iop::wire::Wire; use crate::iop::witness::PartitionWitness; @@ -188,7 +190,7 @@ impl, const D: usize> CircuitBuilder { /// Adds a generator which will copy `src` to `dst`. pub fn generate_copy(&mut self, src: Target, dst: Target) { - self.add_generator(CopyGenerator { src, dst }); + self.add_simple_generator(CopyGenerator { src, dst }); } /// Uses Plonk's permutation argument to require that two elements be equal. @@ -215,8 +217,8 @@ impl, const D: usize> CircuitBuilder { self.generators.extend(generators); } - pub fn add_generator>(&mut self, generator: G) { - self.generators.push(Box::new(generator)); + pub fn add_simple_generator>(&mut self, generator: G) { + self.generators.push(Box::new(generator.adapter())); } /// Returns a routable target with a value of 0. @@ -389,7 +391,7 @@ impl, const D: usize> CircuitBuilder { for _ in 0..regular_poly_openings { let gate = self.add_gate(NoopGate, vec![]); for w in 0..num_wires { - self.add_generator(RandomValueGenerator { + self.add_simple_generator(RandomValueGenerator { target: Target::Wire(Wire { gate, input: w }), }); } @@ -403,7 +405,7 @@ impl, const D: usize> CircuitBuilder { let gate_2 = self.add_gate(NoopGate, vec![]); for w in 0..num_routed_wires { - self.add_generator(RandomValueGenerator { + self.add_simple_generator(RandomValueGenerator { target: Target::Wire(Wire { gate: gate_1, input: w, @@ -528,9 +530,9 @@ impl, const D: usize> CircuitBuilder { let wire_second_input = Target::wire(gate_index, gate.wire_second_input(copy, element)); let wire_switch_bool = Target::wire(gate_index, gate.wire_switch_bool(copy)); - self.route(zero, wire_first_input); - self.route(zero, wire_second_input); - self.route(zero, wire_switch_bool); + self.connect(zero, wire_first_input); + self.connect(zero, wire_second_input); + self.connect(zero, wire_switch_bool); } } } From 7acdf976c1f8c00a571d7208d243fc9a67430443 Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Thu, 2 Sep 2021 15:59:17 -0700 Subject: [PATCH 23/31] fixed fill_switch_gates --- src/plonk/circuit_builder.rs | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/src/plonk/circuit_builder.rs b/src/plonk/circuit_builder.rs index 7252cebf..7ee8852f 100644 --- a/src/plonk/circuit_builder.rs +++ b/src/plonk/circuit_builder.rs @@ -521,18 +521,22 @@ impl, const D: usize> CircuitBuilder { let zero = self.zero(); for chunk_size in 1..=self.current_switch_gates.len() { - if let Some((gate, gate_index, copy)) = + if let Some((gate, gate_index, mut copy)) = self.current_switch_gates[chunk_size - 1].clone() { - for element in 0..chunk_size { - let wire_first_input = - Target::wire(gate_index, gate.wire_first_input(copy, element)); - let wire_second_input = - Target::wire(gate_index, gate.wire_second_input(copy, element)); - let wire_switch_bool = Target::wire(gate_index, gate.wire_switch_bool(copy)); - self.connect(zero, wire_first_input); - self.connect(zero, wire_second_input); - self.connect(zero, wire_switch_bool); + while copy < gate.num_copies { + for element in 0..chunk_size { + let wire_first_input = + Target::wire(gate_index, gate.wire_first_input(copy, element)); + let wire_second_input = + Target::wire(gate_index, gate.wire_second_input(copy, element)); + let wire_switch_bool = + Target::wire(gate_index, gate.wire_switch_bool(copy)); + self.connect(zero, wire_first_input); + self.connect(zero, wire_second_input); + self.connect(zero, wire_switch_bool); + } + copy += 1; } } } @@ -550,6 +554,7 @@ impl, const D: usize> CircuitBuilder { let start = Instant::now(); self.fill_arithmetic_gates(); + self.fill_switch_gates(); // Hash the public inputs, and route them to a `PublicInputGate` which will enforce that // those hash wires match the claimed public inputs. From f01d373d1e2d9c0b9c638e3ed0a46384e9473d53 Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Thu, 2 Sep 2021 15:59:51 -0700 Subject: [PATCH 24/31] made switch_bool wires routeable --- src/gates/switch.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/gates/switch.rs b/src/gates/switch.rs index 954a4997..51145b3c 100644 --- a/src/gates/switch.rs +++ b/src/gates/switch.rs @@ -37,32 +37,32 @@ impl, const D: usize> SwitchGate { } pub fn max_num_copies(num_routed_wires: usize, chunk_size: usize) -> usize { - num_routed_wires / (4 * chunk_size) + num_routed_wires / (4 * chunk_size + 1) } pub fn wire_first_input(&self, copy: usize, element: usize) -> usize { debug_assert!(element < self.chunk_size); - copy * (4 * self.chunk_size) + element + copy * (4 * self.chunk_size + 1) + element } pub fn wire_second_input(&self, copy: usize, element: usize) -> usize { debug_assert!(element < self.chunk_size); - copy * (4 * self.chunk_size) + self.chunk_size + element + copy * (4 * self.chunk_size + 1) + self.chunk_size + element } pub fn wire_first_output(&self, copy: usize, element: usize) -> usize { debug_assert!(element < self.chunk_size); - copy * (4 * self.chunk_size) + 2 * self.chunk_size + element + copy * (4 * self.chunk_size + 1) + 2 * self.chunk_size + element } pub fn wire_second_output(&self, copy: usize, element: usize) -> usize { debug_assert!(element < self.chunk_size); - copy * (4 * self.chunk_size) + 3 * self.chunk_size + element + copy * (4 * self.chunk_size + 1) + 3 * self.chunk_size + element } pub fn wire_switch_bool(&self, copy: usize) -> usize { debug_assert!(copy < self.num_copies); - self.num_copies * (4 * self.chunk_size) + copy + copy * (4 * self.chunk_size + 1) + 4 * self.chunk_size } } From 4f7a587bfa03a5e11b3a3b01edacbc2723907d7c Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Fri, 3 Sep 2021 16:43:33 -0700 Subject: [PATCH 25/31] fix for non-2x2 permutation case --- src/gadgets/permutation.rs | 40 +++++++++++++------------------------- 1 file changed, 14 insertions(+), 26 deletions(-) diff --git a/src/gadgets/permutation.rs b/src/gadgets/permutation.rs index 0c51057a..c6bb10fd 100644 --- a/src/gadgets/permutation.rs +++ b/src/gadgets/permutation.rs @@ -55,7 +55,7 @@ impl, const D: usize> CircuitBuilder { let chunk_size = a1.len(); - let (switch, gate_out1, gate_out2) = self.create_switch(a1, a2); + let (_switch, gate_out1, gate_out2) = self.create_switch(a1, a2); for e in 0..chunk_size { self.connect(b1[e], gate_out1[e]); self.connect(b2[e], gate_out2[e]); @@ -177,7 +177,6 @@ impl, const D: usize> CircuitBuilder { self.assert_permutation(child_2_a, child_2_b); self.add_simple_generator(PermutationGenerator:: { - chunk_size, a, b, a_switches, @@ -271,12 +270,13 @@ fn route( enqueue_other_side(&mut partial_routes, witness, &mut newly_set, 1, n - 1, true); } - let mut route_switch = |partial_routes: &mut [BTreeMap], - witness: &PartitionWitness, - out_buffer: &mut GeneratedValues, - side: usize, - switch_index: usize, - swap: bool| { + let route_switch = |partial_routes: &mut [BTreeMap], + witness: &PartitionWitness, + out_buffer: &mut GeneratedValues, + newly_set: &mut [Vec], + side: usize, + switch_index: usize, + swap: bool| { // First, we actually set the switch configuration. out_buffer.set_target(switches[side][switch_index], F::from_bool(swap)); newly_set[side][switch_index] = true; @@ -285,22 +285,8 @@ fn route( // that they get routed in the next step. let this_i_1 = switch_index * 2; let this_i_2 = this_i_1 + 1; - enqueue_other_side( - partial_routes, - witness, - &mut newly_set, - side, - this_i_1, - swap, - ); - enqueue_other_side( - partial_routes, - witness, - &mut newly_set, - side, - this_i_2, - !swap, - ); + enqueue_other_side(partial_routes, witness, newly_set, side, this_i_1, swap); + enqueue_other_side(partial_routes, witness, newly_set, side, this_i_2, !swap); }; // If {a,b}_only_routes is empty, then we can route any switch next. For efficiency, we will @@ -322,6 +308,7 @@ fn route( &mut partial_routes, witness, out_buffer, + &mut newly_set, side, this_switch_i, swap, @@ -331,7 +318,8 @@ fn route( } else { // We can route any switch next. Continue our scan for pending switches. while scan_index[side] < switches[side].len() - && witness.contains(switches[side][scan_index[side]]) + && (witness.contains(switches[side][scan_index[side]]) + || newly_set[side][scan_index[side]]) { scan_index[side] += 1; } @@ -341,6 +329,7 @@ fn route( &mut partial_routes, witness, out_buffer, + &mut newly_set, side, scan_index[side], false, @@ -354,7 +343,6 @@ fn route( #[derive(Debug)] struct PermutationGenerator { - chunk_size: usize, a: Vec>, b: Vec>, a_switches: Vec, From 4c3f3cda39401c3456a2066e2ab8807280d65ba7 Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Fri, 3 Sep 2021 17:03:21 -0700 Subject: [PATCH 26/31] 6x6 test --- src/gadgets/permutation.rs | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/gadgets/permutation.rs b/src/gadgets/permutation.rs index c6bb10fd..fd96f43a 100644 --- a/src/gadgets/permutation.rs +++ b/src/gadgets/permutation.rs @@ -385,6 +385,7 @@ impl SimpleGenerator for PermutationGenerator { #[cfg(test)] mod tests { use anyhow::Result; + use rand::{seq::SliceRandom, thread_rng}; use super::*; use crate::field::crandall_field::CrandallField; @@ -457,4 +458,27 @@ mod tests { verify(proof, &data.verifier_only, &data.common) } + + #[test] + fn test_permutation_6x6() -> Result<()> { + type F = CrandallField; + let config = CircuitConfig::large_config(); + let pw = PartialWitness::new(config.num_wires); + let mut builder = CircuitBuilder::::new(config); + + let lst: Vec = (0..12).map(|n| F::from_canonical_usize(n)).collect(); + let a: Vec> = lst[..] + .windows(2) + .map(|pair| vec![builder.constant(pair[0]), builder.constant(pair[1])]) + .collect(); + let mut b = a.clone(); + b.shuffle(&mut thread_rng()); + + builder.assert_permutation(a, b); + + let data = builder.build(); + let proof = data.prove(pw).unwrap(); + + verify(proof, &data.verifier_only, &data.common) + } } From 1fb7eeb03e8b62c0c85bae2d7b17cc83ef2d148d Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Fri, 3 Sep 2021 17:15:50 -0700 Subject: [PATCH 27/31] variable-sized tests --- src/gadgets/permutation.rs | 112 +++++++++++++++---------------------- src/gates/switch.rs | 18 +++--- 2 files changed, 54 insertions(+), 76 deletions(-) diff --git a/src/gadgets/permutation.rs b/src/gadgets/permutation.rs index fd96f43a..85db5419 100644 --- a/src/gadgets/permutation.rs +++ b/src/gadgets/permutation.rs @@ -394,79 +394,14 @@ mod tests { use crate::plonk::circuit_data::CircuitConfig; use crate::plonk::verifier::verify; - #[test] - fn test_permutation_2x2() -> Result<()> { + fn test_permutation_good(size: usize) -> Result<()> { type F = CrandallField; let config = CircuitConfig::large_zk_config(); let pw = PartialWitness::new(); let mut builder = CircuitBuilder::::new(config); - let one = F::ONE; - let two = F::from_canonical_usize(2); - let seven = F::from_canonical_usize(7); - let eight = F::from_canonical_usize(8); - - let one_two = vec![builder.constant(one), builder.constant(two)]; - let seven_eight = vec![builder.constant(seven), builder.constant(eight)]; - - let a = vec![one_two.clone(), seven_eight.clone()]; - let b = vec![seven_eight, one_two]; - - builder.assert_permutation(a, b); - - let data = builder.build(); - let proof = data.prove(pw).unwrap(); - - verify(proof, &data.verifier_only, &data.common) - } - - #[test] - fn test_permutation_4x4() -> Result<()> { - type F = CrandallField; - let config = CircuitConfig::large_zk_config(); - - let pw = PartialWitness::new(); - let mut builder = CircuitBuilder::::new(config); - - let one = F::ONE; - let two = F::from_canonical_usize(2); - let three = F::from_canonical_usize(3); - let four = F::from_canonical_usize(4); - let five = F::from_canonical_usize(5); - let six = F::from_canonical_usize(6); - let seven = F::from_canonical_usize(7); - let eight = F::from_canonical_usize(8); - - let one_two = vec![builder.constant(one), builder.constant(two)]; - let three_four = vec![builder.constant(three), builder.constant(four)]; - let five_six = vec![builder.constant(five), builder.constant(six)]; - let seven_eight = vec![builder.constant(seven), builder.constant(eight)]; - - let a = vec![ - one_two.clone(), - three_four.clone(), - five_six.clone(), - seven_eight.clone(), - ]; - let b = vec![seven_eight, one_two, five_six, three_four]; - - builder.assert_permutation(a, b); - - let data = builder.build(); - let proof = data.prove(pw).unwrap(); - - verify(proof, &data.verifier_only, &data.common) - } - - #[test] - fn test_permutation_6x6() -> Result<()> { - type F = CrandallField; - let config = CircuitConfig::large_config(); - let pw = PartialWitness::new(config.num_wires); - let mut builder = CircuitBuilder::::new(config); - - let lst: Vec = (0..12).map(|n| F::from_canonical_usize(n)).collect(); + let lst: Vec = (0..size * 2).map(|n| F::from_canonical_usize(n)).collect(); let a: Vec> = lst[..] .windows(2) .map(|pair| vec![builder.constant(pair[0]), builder.constant(pair[1])]) @@ -481,4 +416,47 @@ mod tests { verify(proof, &data.verifier_only, &data.common) } + + fn test_permutation_bad(size: usize) -> Result<()> { + type F = CrandallField; + let config = CircuitConfig::large_zk_config(); + + let pw = PartialWitness::new(); + let mut builder = CircuitBuilder::::new(config); + + let lst1: Vec = (0..size * 2).map(|_| F::rand()).collect(); + let lst2: Vec = (0..size * 2).map(|_| F::rand()).collect(); + let a: Vec> = lst1[..] + .windows(2) + .map(|pair| vec![builder.constant(pair[0]), builder.constant(pair[1])]) + .collect(); + let b: Vec> = lst2[..] + .windows(2) + .map(|pair| vec![builder.constant(pair[0]), builder.constant(pair[1])]) + .collect(); + + builder.assert_permutation(a, b); + + let data = builder.build(); + let proof = data.prove(pw).unwrap(); + + verify(proof, &data.verifier_only, &data.common) + } + + #[test] + fn test_permutations_good() -> Result<()> { + for n in 2..9 { + test_permutation_good(n).unwrap() + } + + Ok(()) + } + + #[test] + #[should_panic] + fn test_permutations_bad() -> () { + for n in 2..9 { + test_permutation_bad(n).unwrap() + } + } } diff --git a/src/gates/switch.rs b/src/gates/switch.rs index 51145b3c..e1657dc4 100644 --- a/src/gates/switch.rs +++ b/src/gates/switch.rs @@ -365,7 +365,7 @@ mod tests { type F = CrandallField; type FF = QuarticCrandallField; const D: usize = 4; - const chunk_size: usize = 4; + const CHUNK_SIZE: usize = 4; let num_copies = 3; /// Returns the local wires for a switch gate given the inputs and the switch booleans. @@ -382,11 +382,11 @@ mod tests { let switch = switch_bools[c]; switches.push(F::from_bool(switch)); - let mut first_input_chunk = Vec::with_capacity(chunk_size); - let mut second_input_chunk = Vec::with_capacity(chunk_size); - let mut first_output_chunk = Vec::with_capacity(chunk_size); - let mut second_output_chunk = Vec::with_capacity(chunk_size); - for e in 0..chunk_size { + let mut first_input_chunk = Vec::with_capacity(CHUNK_SIZE); + let mut second_input_chunk = Vec::with_capacity(CHUNK_SIZE); + let mut first_output_chunk = Vec::with_capacity(CHUNK_SIZE); + let mut second_output_chunk = Vec::with_capacity(CHUNK_SIZE); + for e in 0..CHUNK_SIZE { let first_input = first_inputs[c][e]; let second_input = second_inputs[c][e]; let first_output = if switch { second_input } else { first_input }; @@ -406,12 +406,12 @@ mod tests { v.iter().map(|&x| x.into()).collect::>() } - let first_inputs: Vec> = (0..num_copies).map(|_| F::rand_vec(chunk_size)).collect(); - let second_inputs: Vec> = (0..num_copies).map(|_| F::rand_vec(chunk_size)).collect(); + let first_inputs: Vec> = (0..num_copies).map(|_| F::rand_vec(CHUNK_SIZE)).collect(); + let second_inputs: Vec> = (0..num_copies).map(|_| F::rand_vec(CHUNK_SIZE)).collect(); let switch_bools = vec![true, false, true]; let gate = SwitchGate:: { - chunk_size, + chunk_size: CHUNK_SIZE, num_copies, _phantom: PhantomData, }; From 6f885db67771ee19c44a04d55921441fc0e90dbf Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Sat, 4 Sep 2021 16:44:49 -0700 Subject: [PATCH 28/31] fixes --- src/gadgets/permutation.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/gadgets/permutation.rs b/src/gadgets/permutation.rs index 85db5419..945f58fa 100644 --- a/src/gadgets/permutation.rs +++ b/src/gadgets/permutation.rs @@ -396,6 +396,8 @@ mod tests { fn test_permutation_good(size: usize) -> Result<()> { type F = CrandallField; + const D: usize = 4; + let config = CircuitConfig::large_zk_config(); let pw = PartialWitness::new(); @@ -419,6 +421,8 @@ mod tests { fn test_permutation_bad(size: usize) -> Result<()> { type F = CrandallField; + const D: usize = 4; + let config = CircuitConfig::large_zk_config(); let pw = PartialWitness::new(); From 0e24719908a2675b81f7f37e12570de7814f12c1 Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Sat, 4 Sep 2021 22:31:12 -0700 Subject: [PATCH 29/31] fixes --- src/gates/switch.rs | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/gates/switch.rs b/src/gates/switch.rs index e1657dc4..213279d6 100644 --- a/src/gates/switch.rs +++ b/src/gates/switch.rs @@ -335,12 +335,12 @@ mod tests { assert_eq!(gate.wire_second_input(0, 2), 5); assert_eq!(gate.wire_first_output(0, 0), 6); assert_eq!(gate.wire_second_output(0, 2), 11); - assert_eq!(gate.wire_first_input(1, 0), 12); - assert_eq!(gate.wire_second_output(1, 2), 23); - assert_eq!(gate.wire_first_input(2, 0), 24); - assert_eq!(gate.wire_second_output(2, 2), 35); - assert_eq!(gate.wire_switch_bool(0), 36); - assert_eq!(gate.wire_switch_bool(1), 37); + assert_eq!(gate.wire_switch_bool(0), 12); + assert_eq!(gate.wire_first_input(1, 0), 13); + assert_eq!(gate.wire_second_output(1, 2), 24); + assert_eq!(gate.wire_switch_bool(1), 25); + assert_eq!(gate.wire_first_input(2, 0), 26); + assert_eq!(gate.wire_second_output(2, 2), 37); assert_eq!(gate.wire_switch_bool(2), 38); } @@ -376,11 +376,9 @@ mod tests { ) -> Vec { let num_copies = first_inputs.len(); - let mut switches = Vec::new(); let mut v = Vec::new(); for c in 0..num_copies { let switch = switch_bools[c]; - switches.push(F::from_bool(switch)); let mut first_input_chunk = Vec::with_capacity(CHUNK_SIZE); let mut second_input_chunk = Vec::with_capacity(CHUNK_SIZE); @@ -400,8 +398,9 @@ mod tests { v.append(&mut second_input_chunk); v.append(&mut first_output_chunk); v.append(&mut second_output_chunk); + + v.push(F::from_bool(switch)); } - v.extend(switches); v.iter().map(|&x| x.into()).collect::>() } From 1818e69ce3e20b1be88bbedffde251af40d6a3c9 Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Mon, 6 Sep 2021 08:38:47 -0700 Subject: [PATCH 30/31] addressed comments --- src/gadgets/permutation.rs | 68 +++++++++++++++++++++++--------------- 1 file changed, 41 insertions(+), 27 deletions(-) diff --git a/src/gadgets/permutation.rs b/src/gadgets/permutation.rs index 945f58fa..66d00bcd 100644 --- a/src/gadgets/permutation.rs +++ b/src/gadgets/permutation.rs @@ -1,11 +1,10 @@ use std::collections::BTreeMap; -use std::convert::TryInto; use std::marker::PhantomData; use crate::field::{extension_field::Extendable, field_types::Field}; use crate::gates::switch::SwitchGate; use crate::iop::generator::{GeneratedValues, SimpleGenerator}; -use crate::iop::target::{BoolTarget, Target}; +use crate::iop::target::Target; use crate::iop::witness::{PartitionWitness, Witness}; use crate::plonk::circuit_builder::CircuitBuilder; use crate::util::bimap::bimap_from_lists; @@ -62,12 +61,14 @@ impl, const D: usize> CircuitBuilder { } } + /// Given two input wire chunks, add a new switch to the circuit (by adding one copy to a switch + /// gate). Returns the wire for the switch boolean, and the two output wire chunks. fn create_switch( &mut self, a1: Vec, a2: Vec, ) -> (Target, Vec, Vec) { - assert!(a1.len() == a2.len(), "Chunk size must be the same"); + assert_eq!(a1.len(), a2.len(), "Chunk size must be the same"); let chunk_size = a1.len(); @@ -113,9 +114,7 @@ impl, const D: usize> CircuitBuilder { next_copy += 1; if next_copy == num_copies { - let new_gate = SwitchGate::::new_from_config(self.config.clone(), chunk_size); - let new_gate_index = self.add_gate(new_gate.clone(), vec![]); - self.current_switch_gates[chunk_size - 1] = Some((new_gate, new_gate_index, 0)); + self.current_switch_gates[chunk_size - 1] = None; } else { self.current_switch_gates[chunk_size - 1] = Some((gate, gate_index, next_copy)); } @@ -131,8 +130,6 @@ impl, const D: usize> CircuitBuilder { ); assert_eq!(a[0].len(), b[0].len(), "Chunk size must be the same"); - let chunk_size = a[0].len(); - let n = a.len(); let even = n % 2 == 0; @@ -197,9 +194,15 @@ fn route( assert_eq!(a_values.len(), b_values.len()); let n = a_values.len(); let even = n % 2 == 0; - // Bimap: maps indices of values in a to indices of the same values in b + + // We use a bimap to match indices of values in a to indices of the same values in b. + // This means that given a wire on one side, we can easily find the matching wire on the other side. let ab_map = bimap_from_lists(a_values, b_values); + let switches = [a_switches, b_switches]; + + // We keep track of the new wires we've routed (after routing some wires, we need to check `witness` + // and `newly_set` instead of just `witness`. let mut newly_set = [vec![false; n], vec![false; n]]; // Given a side and an index, returns the index in the other side that corresponds to the same value. @@ -352,12 +355,7 @@ struct PermutationGenerator { impl SimpleGenerator for PermutationGenerator { fn dependencies(&self) -> Vec { - self.a - .clone() - .into_iter() - .flatten() - .chain(self.b.clone().into_iter().flatten()) - .collect() + self.a.iter().chain(&self.b).flatten().cloned().collect() } fn run_once(&self, witness: &PartitionWitness, out_buffer: &mut GeneratedValues) { @@ -405,7 +403,7 @@ mod tests { let lst: Vec = (0..size * 2).map(|n| F::from_canonical_usize(n)).collect(); let a: Vec> = lst[..] - .windows(2) + .chunks(2) .map(|pair| vec![builder.constant(pair[0]), builder.constant(pair[1])]) .collect(); let mut b = a.clone(); @@ -428,29 +426,29 @@ mod tests { let pw = PartialWitness::new(); let mut builder = CircuitBuilder::::new(config); - let lst1: Vec = (0..size * 2).map(|_| F::rand()).collect(); - let lst2: Vec = (0..size * 2).map(|_| F::rand()).collect(); + let lst1: Vec = F::rand_vec(size * 2); + let lst2: Vec = F::rand_vec(size * 2); let a: Vec> = lst1[..] - .windows(2) + .chunks(2) .map(|pair| vec![builder.constant(pair[0]), builder.constant(pair[1])]) .collect(); let b: Vec> = lst2[..] - .windows(2) + .chunks(2) .map(|pair| vec![builder.constant(pair[0]), builder.constant(pair[1])]) .collect(); builder.assert_permutation(a, b); let data = builder.build(); - let proof = data.prove(pw).unwrap(); + data.prove(pw).unwrap(); - verify(proof, &data.verifier_only, &data.common) + Ok(()) } #[test] fn test_permutations_good() -> Result<()> { for n in 2..9 { - test_permutation_good(n).unwrap() + test_permutation_good(n)?; } Ok(()) @@ -458,9 +456,25 @@ mod tests { #[test] #[should_panic] - fn test_permutations_bad() -> () { - for n in 2..9 { - test_permutation_bad(n).unwrap() - } + fn test_permutation_bad_small() { + let size = 2; + + test_permutation_bad(size).unwrap() + } + + #[test] + #[should_panic] + fn test_permutation_bad_medium() { + let size = 6; + + test_permutation_bad(size).unwrap() + } + + #[test] + #[should_panic] + fn test_permutation_bad_large() { + let size = 10; + + test_permutation_bad(size).unwrap() } } From effcc967d078f7684004c2e0694c13f3f6bf9860 Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Mon, 6 Sep 2021 21:39:00 -0700 Subject: [PATCH 31/31] fmt --- src/gadgets/permutation.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/gadgets/permutation.rs b/src/gadgets/permutation.rs index f10e64b2..c47f5f23 100644 --- a/src/gadgets/permutation.rs +++ b/src/gadgets/permutation.rs @@ -1,7 +1,10 @@ use std::collections::BTreeMap; use std::marker::PhantomData; -use crate::field::{extension_field::Extendable, field_types::{Field, PrimeField}}; +use crate::field::{ + extension_field::Extendable, + field_types::{Field, PrimeField}, +}; use crate::gates::switch::SwitchGate; use crate::iop::generator::{GeneratedValues, SimpleGenerator}; use crate::iop::target::Target;