mirror of
https://github.com/logos-storage/plonky2.git
synced 2026-01-04 06:43:07 +00:00
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.
This commit is contained in:
parent
574a3d4847
commit
95a875e28d
@ -36,7 +36,7 @@ pub struct CircuitBuilder<F: Extendable<D>, 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<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
(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<Target> {
|
||||
(0..n).map(|_i| self.add_virtual_advice_target()).collect()
|
||||
pub fn add_virtual_targets(&mut self, n: usize) -> Vec<Target> {
|
||||
(0..n).map(|_i| self.add_virtual_target()).collect()
|
||||
}
|
||||
|
||||
pub fn add_gate_no_constants(&mut self, gate_type: GateRef<F, D>) -> usize {
|
||||
@ -368,7 +364,11 @@ impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
}
|
||||
|
||||
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 {
|
||||
|
||||
@ -9,14 +9,14 @@ use crate::wire::Wire;
|
||||
use crate::witness::PartialWitness;
|
||||
|
||||
impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
/// 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<Target> {
|
||||
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(),
|
||||
|
||||
@ -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();
|
||||
|
||||
|
||||
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -30,7 +30,7 @@ impl<F: Field> Witness<F> {
|
||||
F: Extendable<D>,
|
||||
{
|
||||
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,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user