progress on permutation

This commit is contained in:
Nicholas Ward 2021-08-23 12:13:31 -07:00
parent 032e2feeb4
commit b0a855a9c3
5 changed files with 163 additions and 38 deletions

View File

@ -107,7 +107,7 @@ impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
exponent_bits: impl IntoIterator<Item = impl Borrow<BoolTarget>>,
) -> 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<BoolTarget> =
exponent_bits.into_iter().map(|b| *b.borrow()).collect();

View File

@ -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;

115
src/gadgets/permutation.rs Normal file
View File

@ -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<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
/// Assert that two lists of expressions evaluate to permutations of one another.
pub fn assert_permutation<const CHUNK_SIZE: usize>(
&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<const CHUNK_SIZE: usize>(
&mut self,
a: [Target; CHUNK_SIZE],
b: [Target; CHUNK_SIZE],
c: [Target; CHUNK_SIZE],
d: [Target; CHUNK_SIZE],
) {
let gate = SwitchGate::<F, D, CHUNK_SIZE>::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<const CHUNK_SIZE: usize>(
&mut self,
a: Vec<[Target; CHUNK_SIZE]>,
b: Vec<[Target; CHUNK_SIZE]>,
) {
}
}
struct PermutationGenerator<const CHUNK_SIZE: usize> {
gate_index: usize,
}
impl<F: Field, const CHUNK_SIZE: usize> SimpleGenerator<F> for PermutationGenerator<CHUNK_SIZE> {
fn dependencies(&self) -> Vec<Target> {
todo!()
}
fn run_once(&self, witness: &PartialWitness<F>, out_buffer: &mut GeneratedValues<F>) {
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::<F, 4>::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)
}
}

View File

@ -20,14 +20,18 @@ pub(crate) struct ExponentiationGate<F: Extendable<D>, const D: usize> {
}
impl<F: Extendable<D>, const D: usize> ExponentiationGate<F, D> {
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::<CrandallField, _, 4>(ExponentiationGate::new(config));
test_low_degree::<CrandallField, _, 4>(ExponentiationGate::new_from_config(config));
}
#[test]
fn eval_fns() -> Result<()> {
test_eval_fns::<CrandallField, _, 4>(ExponentiationGate::new(CircuitConfig::large_config()))
test_eval_fns::<CrandallField, _, 4>(ExponentiationGate::new_from_config(
CircuitConfig::large_config(),
))
}
#[test]

View File

@ -20,45 +20,49 @@ pub(crate) struct SwitchGate<F: Extendable<D>, const D: usize, const CHUNK_SIZE:
}
impl<F: Extendable<D>, const D: usize, const CHUNK_SIZE: usize> SwitchGate<F, D, CHUNK_SIZE> {
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<F: Extendable<D>, 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<F: Extendable<D>, 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]