From 95a875e28d213741d9b263ac7c4efe8cb6c8340b Mon Sep 17 00:00:00 2001 From: Daniel Lubarov Date: Thu, 1 Jul 2021 08:12:12 -0700 Subject: [PATCH] Allow virtual targets to be routed (#84) As in plonky1. The semantics of virtual targets in plonky1 were rather weird, but I think it's somewhat better here, since we already separate `generate_copy` and `assert_equal` methods. Users now make more of an explicit choice -- they can use a `VirtualTarget` for the witness generation only using `generate_copy`, or they can involve it in copy constraints. --- src/circuit_builder.rs | 28 ++++++++++++++-------------- src/gadgets/split_join.rs | 6 +++--- src/permutation_argument.rs | 2 +- src/target.rs | 7 +++++-- src/witness.rs | 2 +- 5 files changed, 24 insertions(+), 21 deletions(-) diff --git a/src/circuit_builder.rs b/src/circuit_builder.rs index 7ce4adb3..06c554e3 100644 --- a/src/circuit_builder.rs +++ b/src/circuit_builder.rs @@ -36,7 +36,7 @@ pub struct CircuitBuilder, const D: usize> { /// The next available index for a public input. public_input_index: usize, - /// The next available index for a VirtualAdviceTarget. + /// The next available index for a `VirtualTarget`. virtual_target_index: usize, copy_constraints: Vec<(Target, Target)>, @@ -77,22 +77,18 @@ impl, const D: usize> CircuitBuilder { (0..n).map(|_i| self.add_public_input()).collect() } - /// Adds a new "virtual" advice target. This is not an actual wire in the witness, but just a - /// target that help facilitate witness generation. In particular, a generator can assign a - /// values to a virtual target, which can then be copied to other (virtual or concrete) targets - /// via `generate_copy`. When we generate the final witness (a grid of wire values), these - /// virtual targets will go away. - /// - /// Since virtual targets are not part of the actual permutation argument, they cannot be used - /// with `assert_equal`. - pub fn add_virtual_advice_target(&mut self) -> Target { + /// Adds a new "virtual" target. This is not an actual wire in the witness, but just a target + /// that help facilitate witness generation. In particular, a generator can assign a values to a + /// virtual target, which can then be copied to other (virtual or concrete) targets. When we + /// generate the final witness (a grid of wire values), these virtual targets will go away. + pub fn add_virtual_target(&mut self) -> Target { let index = self.virtual_target_index; self.virtual_target_index += 1; - Target::VirtualAdviceTarget { index } + Target::VirtualTarget { index } } - pub fn add_virtual_advice_targets(&mut self, n: usize) -> Vec { - (0..n).map(|_i| self.add_virtual_advice_target()).collect() + pub fn add_virtual_targets(&mut self, n: usize) -> Vec { + (0..n).map(|_i| self.add_virtual_target()).collect() } pub fn add_gate_no_constants(&mut self, gate_type: GateRef) -> usize { @@ -368,7 +364,11 @@ impl, const D: usize> CircuitBuilder { } for index in 0..self.public_input_index { - target_partitions.add_partition(Target::PublicInput { index }) + target_partitions.add_partition(Target::PublicInput { index }); + } + + for index in 0..self.virtual_target_index { + target_partitions.add_partition(Target::VirtualTarget { index }); } for &(a, b) in &self.copy_constraints { diff --git a/src/gadgets/split_join.rs b/src/gadgets/split_join.rs index 5eb60148..3a2c27f4 100644 --- a/src/gadgets/split_join.rs +++ b/src/gadgets/split_join.rs @@ -9,14 +9,14 @@ use crate::wire::Wire; use crate::witness::PartialWitness; impl, const D: usize> CircuitBuilder { - /// Split the given integer into a list of virtual advice targets, where each one represents a - /// bit of the integer, with little-endian ordering. + /// Split the given integer into a list of virtual targets, where each one represents a bit of + /// the integer, with little-endian ordering. /// /// Note that this only handles witness generation; it does not enforce that the decomposition /// is correct. The output should be treated as a "purported" decomposition which must be /// enforced elsewhere. pub(crate) fn split_le_virtual(&mut self, integer: Target, num_bits: usize) -> Vec { - let bit_targets = self.add_virtual_advice_targets(num_bits); + let bit_targets = self.add_virtual_targets(num_bits); self.add_generator(SplitGenerator { integer, bits: bit_targets.clone(), diff --git a/src/permutation_argument.rs b/src/permutation_argument.rs index 54436ecb..cc202a95 100644 --- a/src/permutation_argument.rs +++ b/src/permutation_argument.rs @@ -57,7 +57,7 @@ impl TargetPartitions { } pub fn to_wire_partitions(&self) -> WirePartitions { - // Here we just drop all CircuitInputs, leaving all GateInputs. + // Here we keep just the Wire targets, filtering out everything else. let mut partitions = Vec::new(); let mut indices = HashMap::new(); diff --git a/src/target.rs b/src/target.rs index 8aec0b5a..e765f7eb 100644 --- a/src/target.rs +++ b/src/target.rs @@ -8,7 +8,10 @@ use crate::wire::Wire; pub enum Target { Wire(Wire), PublicInput { index: usize }, - VirtualAdviceTarget { index: usize }, + /// A target that doesn't have any inherent location in the witness (but it can be copied to + /// another target that does). This is useful for representing intermediate values in witness + /// generation. + VirtualTarget { index: usize }, } impl Target { @@ -20,7 +23,7 @@ impl Target { match self { Target::Wire(wire) => wire.is_routable(config), Target::PublicInput { .. } => true, - Target::VirtualAdviceTarget { .. } => false, + Target::VirtualTarget { .. } => true, } } diff --git a/src/witness.rs b/src/witness.rs index 681049d0..863ee7dd 100644 --- a/src/witness.rs +++ b/src/witness.rs @@ -30,7 +30,7 @@ impl Witness { F: Extendable, { for &(a, b) in copy_constraints { - // TODO: Take care of public inputs once they land. + // TODO: Take care of public inputs once they land, and virtual targets. if let ( Target::Wire(Wire { gate: a_gate,