mirror of
https://github.com/logos-storage/plonky2.git
synced 2026-01-04 06:43:07 +00:00
Gate infra
This commit is contained in:
parent
33bd3edd11
commit
9fdff8ea08
@ -7,6 +7,7 @@ edition = "2018"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
unroll = "0.1.5"
|
unroll = "0.1.5"
|
||||||
rayon = "1.5.0"
|
rayon = "1.5.0"
|
||||||
|
num = "0.3"
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
opt-level = 3
|
opt-level = 3
|
||||||
|
|||||||
94
src/circuit_builder.rs
Normal file
94
src/circuit_builder.rs
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
|
use crate::circuit_data::CircuitConfig;
|
||||||
|
use crate::field::field::Field;
|
||||||
|
use crate::gates::gate::{GateInstance, GateRef};
|
||||||
|
use crate::generator::{CopyGenerator, WitnessGenerator2};
|
||||||
|
use crate::target::Target;
|
||||||
|
use crate::gates::constant::ConstantGate2;
|
||||||
|
use crate::wire::Wire;
|
||||||
|
|
||||||
|
pub struct CircuitBuilder2<F: Field> {
|
||||||
|
config: CircuitConfig,
|
||||||
|
gates: HashSet<GateRef<F>>,
|
||||||
|
gate_instances: Vec<GateInstance<F>>,
|
||||||
|
generators: Vec<Box<dyn WitnessGenerator2<F>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F: Field> CircuitBuilder2<F> {
|
||||||
|
pub fn new(config: CircuitConfig) -> Self {
|
||||||
|
CircuitBuilder2 {
|
||||||
|
config,
|
||||||
|
gates: HashSet::new(),
|
||||||
|
gate_instances: Vec::new(),
|
||||||
|
generators: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds a gate to the circuit, and returns its index.
|
||||||
|
pub fn add_gate(&mut self, gate_type: GateRef<F>, constants: Vec<F>) -> usize {
|
||||||
|
// If we haven't seen a gate of this type before, check that it's compatible with our
|
||||||
|
// circuit configuration, then register it.
|
||||||
|
if !self.gates.contains(&gate_type) {
|
||||||
|
self.check_gate_compatibility(&gate_type);
|
||||||
|
self.gates.insert(gate_type.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
let index = self.gate_instances.len();
|
||||||
|
self.gate_instances.push(GateInstance { gate_type, constants });
|
||||||
|
index
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_gate_compatibility(&self, gate: &GateRef<F>) {
|
||||||
|
assert!(gate.0.min_wires(self.config) <= self.config.num_wires);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Shorthand for `generate_copy` and `assert_equal`.
|
||||||
|
/// Both elements must be routable, otherwise this method will panic.
|
||||||
|
pub fn route(&mut self, src: Target, dst: Target) {
|
||||||
|
self.generate_copy(src, dst);
|
||||||
|
self.assert_equal(src, dst);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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 });
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Uses Plonk's permutation argument to require that two elements be equal.
|
||||||
|
/// Both elements must be routable, otherwise this method will panic.
|
||||||
|
pub fn assert_equal(&mut self, x: Target, y: Target) {
|
||||||
|
assert!(x.is_routable(self.config));
|
||||||
|
assert!(y.is_routable(self.config));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_generator<G: WitnessGenerator2<F>>(&mut self, generator: G) {
|
||||||
|
self.generators.push(Box::new(generator));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a routable target with a value of 0.
|
||||||
|
pub fn zero(&mut self) -> Target {
|
||||||
|
self.constant(F::ZERO)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a routable target with a value of 1.
|
||||||
|
pub fn one(&mut self) -> Target {
|
||||||
|
self.constant(F::ONE)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a routable target with a value of 2.
|
||||||
|
pub fn two(&mut self) -> Target {
|
||||||
|
self.constant(F::TWO)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a routable target with a value of `ORDER - 1`.
|
||||||
|
pub fn neg_one(&mut self) -> Target {
|
||||||
|
self.constant(F::NEG_ONE)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a routable target with the given constant value.
|
||||||
|
pub fn constant(&mut self, c: F) -> Target {
|
||||||
|
let gate = self.add_gate(ConstantGate2::get(), vec![c]);
|
||||||
|
Target::Wire(Wire { gate, input: ConstantGate2::WIRE_OUTPUT })
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -5,8 +5,11 @@ use std::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign};
|
|||||||
use std::ptr;
|
use std::ptr;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use num::{BigUint, FromPrimitive, One, Zero};
|
||||||
|
|
||||||
use crate::field::field::Field;
|
use crate::field::field::Field;
|
||||||
use crate::wire::Wire;
|
use crate::wire::Wire;
|
||||||
|
use crate::gates::output_graph::GateOutputLocation;
|
||||||
|
|
||||||
pub(crate) struct EvaluationVars<'a, F: Field> {
|
pub(crate) struct EvaluationVars<'a, F: Field> {
|
||||||
pub(crate) local_constants: &'a [F],
|
pub(crate) local_constants: &'a [F],
|
||||||
@ -58,6 +61,13 @@ impl<F: Field> ConstraintPolynomial<F> {
|
|||||||
Self::from_inner(ConstraintPolynomialInner::NextWireValue(index))
|
Self::from_inner(ConstraintPolynomialInner::NextWireValue(index))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn from_gate_output(gate_output: GateOutputLocation) -> Self {
|
||||||
|
match gate_output {
|
||||||
|
GateOutputLocation::LocalWire(i) => Self::local_wire_value(i),
|
||||||
|
GateOutputLocation::NextWire(i) => Self::next_wire_value(i),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Have these take references?
|
// TODO: Have these take references?
|
||||||
pub fn add(&self, rhs: &Self) -> Self {
|
pub fn add(&self, rhs: &Self) -> Self {
|
||||||
// TODO: Special case for either operand being 0.
|
// TODO: Special case for either operand being 0.
|
||||||
@ -105,7 +115,7 @@ impl<F: Field> ConstraintPolynomial<F> {
|
|||||||
self * self * self
|
self * self * self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn degree(&self) -> usize {
|
pub(crate) fn degree(&self) -> BigUint {
|
||||||
(self.0).0.degree()
|
(self.0).0.degree()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -149,6 +159,15 @@ impl<F: Field> ConstraintPolynomial<F> {
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Replace all occurrences of `from` with `to` in this polynomial graph.
|
||||||
|
pub(crate) fn replace_all(
|
||||||
|
&self,
|
||||||
|
from: Self,
|
||||||
|
to: Self,
|
||||||
|
) -> Self {
|
||||||
|
Self(self.0.replace_all(from.0, to.0))
|
||||||
|
}
|
||||||
|
|
||||||
fn from_inner(inner: ConstraintPolynomialInner<F>) -> Self {
|
fn from_inner(inner: ConstraintPolynomialInner<F>) -> Self {
|
||||||
Self(ConstraintPolynomialRef::new(inner))
|
Self(ConstraintPolynomialRef::new(inner))
|
||||||
}
|
}
|
||||||
@ -413,16 +432,17 @@ impl<F: Field> ConstraintPolynomialInner<F> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn degree(&self) -> usize {
|
fn degree(&self) -> BigUint {
|
||||||
match self {
|
match self {
|
||||||
ConstraintPolynomialInner::Constant(_) => 0,
|
ConstraintPolynomialInner::Constant(_) => BigUint::zero(),
|
||||||
ConstraintPolynomialInner::LocalConstant(_) => 1,
|
ConstraintPolynomialInner::LocalConstant(_) => BigUint::one(),
|
||||||
ConstraintPolynomialInner::NextConstant(_) => 1,
|
ConstraintPolynomialInner::NextConstant(_) => BigUint::one(),
|
||||||
ConstraintPolynomialInner::LocalWireValue(_) => 1,
|
ConstraintPolynomialInner::LocalWireValue(_) => BigUint::one(),
|
||||||
ConstraintPolynomialInner::NextWireValue(_) => 1,
|
ConstraintPolynomialInner::NextWireValue(_) => BigUint::one(),
|
||||||
ConstraintPolynomialInner::Sum { lhs, rhs } => lhs.0.degree().max(rhs.0.degree()),
|
ConstraintPolynomialInner::Sum { lhs, rhs } => lhs.0.degree().max(rhs.0.degree()),
|
||||||
ConstraintPolynomialInner::Product { lhs, rhs } => lhs.0.degree() + rhs.0.degree(),
|
ConstraintPolynomialInner::Product { lhs, rhs } => lhs.0.degree() + rhs.0.degree(),
|
||||||
ConstraintPolynomialInner::Exponentiation { base, exponent } => base.0.degree() * exponent,
|
ConstraintPolynomialInner::Exponentiation { base, exponent } =>
|
||||||
|
base.0.degree() * BigUint::from_usize(*exponent).unwrap(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -431,7 +451,7 @@ impl<F: Field> ConstraintPolynomialInner<F> {
|
|||||||
/// than content. This is useful when we want to use constraint polynomials as `HashMap` keys, but
|
/// than content. This is useful when we want to use constraint polynomials as `HashMap` keys, but
|
||||||
/// we want address-based hashing for performance reasons.
|
/// we want address-based hashing for performance reasons.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct ConstraintPolynomialRef<F: Field>(Rc<ConstraintPolynomialInner<F>>);
|
pub(crate) struct ConstraintPolynomialRef<F: Field>(Rc<ConstraintPolynomialInner<F>>);
|
||||||
|
|
||||||
impl<F: Field> ConstraintPolynomialRef<F> {
|
impl<F: Field> ConstraintPolynomialRef<F> {
|
||||||
fn new(inner: ConstraintPolynomialInner<F>) -> Self {
|
fn new(inner: ConstraintPolynomialInner<F>) -> Self {
|
||||||
@ -451,6 +471,15 @@ impl<F: Field> ConstraintPolynomialRef<F> {
|
|||||||
result
|
result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Replace all occurrences of `from` with `to` in this polynomial graph.
|
||||||
|
fn replace_all(
|
||||||
|
&self,
|
||||||
|
from: ConstraintPolynomialRef<F>,
|
||||||
|
to: ConstraintPolynomialRef<F>,
|
||||||
|
) -> ConstraintPolynomialRef<F> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F: Field> PartialEq for ConstraintPolynomialRef<F> {
|
impl<F: Field> PartialEq for ConstraintPolynomialRef<F> {
|
||||||
|
|||||||
@ -38,6 +38,7 @@ impl Debug for CrandallField {
|
|||||||
impl Field for CrandallField {
|
impl Field for CrandallField {
|
||||||
const ZERO: Self = Self(0);
|
const ZERO: Self = Self(0);
|
||||||
const ONE: Self = Self(1);
|
const ONE: Self = Self(1);
|
||||||
|
const TWO: Self = Self(2);
|
||||||
const NEG_ONE: Self = Self(P - 1);
|
const NEG_ONE: Self = Self(P - 1);
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
|||||||
@ -20,6 +20,7 @@ pub trait Field: 'static
|
|||||||
+ Sync {
|
+ Sync {
|
||||||
const ZERO: Self;
|
const ZERO: Self;
|
||||||
const ONE: Self;
|
const ONE: Self;
|
||||||
|
const TWO: Self;
|
||||||
const NEG_ONE: Self;
|
const NEG_ONE: Self;
|
||||||
|
|
||||||
fn sq(&self) -> Self;
|
fn sq(&self) -> Self;
|
||||||
|
|||||||
31
src/gates/constant.rs
Normal file
31
src/gates/constant.rs
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
use crate::circuit_data::CircuitConfig;
|
||||||
|
use crate::constraint_polynomial::ConstraintPolynomial;
|
||||||
|
use crate::field::field::Field;
|
||||||
|
use crate::gates::deterministic_gate::{DeterministicGate, DeterministicGateAdapter};
|
||||||
|
use crate::gates::gate::GateRef;
|
||||||
|
use crate::gates::output_graph::{GateOutputLocation, OutputGraph};
|
||||||
|
|
||||||
|
/// A gate which takes a single constant parameter and outputs that value.
|
||||||
|
pub struct ConstantGate2;
|
||||||
|
|
||||||
|
impl ConstantGate2 {
|
||||||
|
pub fn get<F: Field>() -> GateRef<F> {
|
||||||
|
GateRef::new(DeterministicGateAdapter::new(ConstantGate2))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const CONST_INPUT: usize = 0;
|
||||||
|
|
||||||
|
pub const WIRE_OUTPUT: usize = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F: Field> DeterministicGate<F> for ConstantGate2 {
|
||||||
|
fn id(&self) -> String {
|
||||||
|
"ConstantGate".into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn outputs(&self, _config: CircuitConfig) -> OutputGraph<F> {
|
||||||
|
let loc = GateOutputLocation::LocalWire(Self::WIRE_OUTPUT);
|
||||||
|
let out = ConstraintPolynomial::local_constant(Self::CONST_INPUT);
|
||||||
|
OutputGraph::single_output(loc, out)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -5,9 +5,10 @@ use crate::constraint_polynomial::{ConstraintPolynomial, EvaluationVars};
|
|||||||
use crate::field::field::Field;
|
use crate::field::field::Field;
|
||||||
use crate::gates::gate::Gate;
|
use crate::gates::gate::Gate;
|
||||||
use crate::generator::{SimpleGenerator, WitnessGenerator2};
|
use crate::generator::{SimpleGenerator, WitnessGenerator2};
|
||||||
use crate::target::Target2;
|
use crate::target::Target;
|
||||||
use crate::wire::Wire;
|
use crate::wire::Wire;
|
||||||
use crate::witness::PartialWitness;
|
use crate::witness::PartialWitness;
|
||||||
|
use crate::gates::output_graph::{OutputGraph, GateOutputLocation};
|
||||||
|
|
||||||
/// A deterministic gate. Each entry in `outputs()` describes how that output is evaluated; this is
|
/// A deterministic gate. Each entry in `outputs()` describes how that output is evaluated; this is
|
||||||
/// used to create both the constraint set and the generator set.
|
/// used to create both the constraint set and the generator set.
|
||||||
@ -18,9 +19,9 @@ pub trait DeterministicGate<F: Field>: 'static {
|
|||||||
/// A unique identifier for this gate.
|
/// A unique identifier for this gate.
|
||||||
fn id(&self) -> String;
|
fn id(&self) -> String;
|
||||||
|
|
||||||
/// A vector of `(i, c)` pairs, where `i` is the index of an output and `c` is the polynomial
|
/// A vector of `(loc, out)` pairs, where `loc` is the location of an output and `out` is a
|
||||||
/// defining how that output is evaluated.
|
/// polynomial defining how that output is evaluated.
|
||||||
fn outputs(&self, config: CircuitConfig) -> Vec<(usize, ConstraintPolynomial<F>)>;
|
fn outputs(&self, config: CircuitConfig) -> OutputGraph<F>;
|
||||||
|
|
||||||
/// Any additional constraints to be enforced, besides the (automatically provided) ones that
|
/// Any additional constraints to be enforced, besides the (automatically provided) ones that
|
||||||
/// constraint output values.
|
/// constraint output values.
|
||||||
@ -60,8 +61,8 @@ impl<F: Field, DG: DeterministicGate<F>> Gate<F> for DeterministicGateAdapter<F,
|
|||||||
fn constraints(&self, config: CircuitConfig) -> Vec<ConstraintPolynomial<F>> {
|
fn constraints(&self, config: CircuitConfig) -> Vec<ConstraintPolynomial<F>> {
|
||||||
// For each output, we add a constraint of the form `out - expression = 0`,
|
// For each output, we add a constraint of the form `out - expression = 0`,
|
||||||
// then we append any additional constraints that the gate defines.
|
// then we append any additional constraints that the gate defines.
|
||||||
self.gate.outputs(config).into_iter()
|
self.gate.outputs(config).outputs.into_iter()
|
||||||
.map(|(i, out)| out - ConstraintPolynomial::local_wire_value(i))
|
.map(|(output_loc, out)| out - ConstraintPolynomial::from_gate_output(output_loc))
|
||||||
.chain(self.gate.additional_constraints(config).into_iter())
|
.chain(self.gate.additional_constraints(config).into_iter())
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
@ -73,12 +74,12 @@ impl<F: Field, DG: DeterministicGate<F>> Gate<F> for DeterministicGateAdapter<F,
|
|||||||
local_constants: Vec<F>,
|
local_constants: Vec<F>,
|
||||||
next_constants: Vec<F>,
|
next_constants: Vec<F>,
|
||||||
) -> Vec<Box<dyn WitnessGenerator2<F>>> {
|
) -> Vec<Box<dyn WitnessGenerator2<F>>> {
|
||||||
self.gate.outputs(config)
|
self.gate.outputs(config).outputs
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(input_index, out)| {
|
.map(|(location, out)| {
|
||||||
let og = OutputGenerator {
|
let og = OutputGenerator {
|
||||||
gate_index,
|
gate_index,
|
||||||
input_index,
|
location,
|
||||||
out,
|
out,
|
||||||
local_constants: local_constants.clone(),
|
local_constants: local_constants.clone(),
|
||||||
next_constants: next_constants.clone(),
|
next_constants: next_constants.clone(),
|
||||||
@ -96,17 +97,17 @@ impl<F: Field, DG: DeterministicGate<F>> Gate<F> for DeterministicGateAdapter<F,
|
|||||||
|
|
||||||
struct OutputGenerator<F: Field> {
|
struct OutputGenerator<F: Field> {
|
||||||
gate_index: usize,
|
gate_index: usize,
|
||||||
input_index: usize,
|
location: GateOutputLocation,
|
||||||
out: ConstraintPolynomial<F>,
|
out: ConstraintPolynomial<F>,
|
||||||
local_constants: Vec<F>,
|
local_constants: Vec<F>,
|
||||||
next_constants: Vec<F>,
|
next_constants: Vec<F>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F: Field> SimpleGenerator<F> for OutputGenerator<F> {
|
impl<F: Field> SimpleGenerator<F> for OutputGenerator<F> {
|
||||||
fn dependencies(&self) -> Vec<Target2> {
|
fn dependencies(&self) -> Vec<Target> {
|
||||||
self.out.dependencies(self.gate_index)
|
self.out.dependencies(self.gate_index)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(Target2::Wire)
|
.map(Target::Wire)
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -125,8 +126,8 @@ impl<F: Field> SimpleGenerator<F> for OutputGenerator<F> {
|
|||||||
// Lookup the values if they exist. If not, we can just insert a zero, knowing
|
// Lookup the values if they exist. If not, we can just insert a zero, knowing
|
||||||
// that it will not be used. (If it was used, it would have been included in our
|
// that it will not be used. (If it was used, it would have been included in our
|
||||||
// dependencies, and this generator would not have run yet.)
|
// dependencies, and this generator would not have run yet.)
|
||||||
let local_value = witness.try_get_target(Target2::Wire(local_wire)).unwrap_or(F::ZERO);
|
let local_value = witness.try_get_target(Target::Wire(local_wire)).unwrap_or(F::ZERO);
|
||||||
let next_value = witness.try_get_target(Target2::Wire(next_wire)).unwrap_or(F::ZERO);
|
let next_value = witness.try_get_target(Target::Wire(next_wire)).unwrap_or(F::ZERO);
|
||||||
|
|
||||||
local_wire_values.push(local_value);
|
local_wire_values.push(local_value);
|
||||||
next_wire_values.push(next_value);
|
next_wire_values.push(next_value);
|
||||||
@ -139,8 +140,14 @@ impl<F: Field> SimpleGenerator<F> for OutputGenerator<F> {
|
|||||||
next_wire_values: &next_wire_values,
|
next_wire_values: &next_wire_values,
|
||||||
};
|
};
|
||||||
|
|
||||||
let result_wire = Wire { gate: self.gate_index, input: self.input_index };
|
let result_wire = match self.location {
|
||||||
|
GateOutputLocation::LocalWire(input) =>
|
||||||
|
Wire { gate: self.gate_index, input },
|
||||||
|
GateOutputLocation::NextWire(input) =>
|
||||||
|
Wire { gate: self.gate_index + 1, input },
|
||||||
|
};
|
||||||
|
|
||||||
let result_value = self.out.evaluate(vars);
|
let result_value = self.out.evaluate(vars);
|
||||||
PartialWitness::singleton(Target2::Wire(result_wire), result_value)
|
PartialWitness::singleton(Target::Wire(result_wire), result_value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,6 +5,7 @@ use crate::circuit_data::CircuitConfig;
|
|||||||
use crate::constraint_polynomial::ConstraintPolynomial;
|
use crate::constraint_polynomial::ConstraintPolynomial;
|
||||||
use crate::field::field::Field;
|
use crate::field::field::Field;
|
||||||
use crate::generator::WitnessGenerator2;
|
use crate::generator::WitnessGenerator2;
|
||||||
|
use num::ToPrimitive;
|
||||||
|
|
||||||
/// A custom gate.
|
/// A custom gate.
|
||||||
// TODO: Remove CircuitConfig params? Could just use fields within each struct.
|
// TODO: Remove CircuitConfig params? Could just use fields within each struct.
|
||||||
@ -44,7 +45,7 @@ pub trait Gate<F: Field>: 'static {
|
|||||||
fn degree(&self, config: CircuitConfig) -> usize {
|
fn degree(&self, config: CircuitConfig) -> usize {
|
||||||
self.constraints(config)
|
self.constraints(config)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|c| c.degree())
|
.map(|c| c.degree().to_usize().expect("degree too large"))
|
||||||
.max()
|
.max()
|
||||||
.unwrap_or(0)
|
.unwrap_or(0)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,11 +4,12 @@ use std::sync::Arc;
|
|||||||
use crate::circuit_data::CircuitConfig;
|
use crate::circuit_data::CircuitConfig;
|
||||||
use crate::constraint_polynomial::ConstraintPolynomial;
|
use crate::constraint_polynomial::ConstraintPolynomial;
|
||||||
use crate::field::field::Field;
|
use crate::field::field::Field;
|
||||||
use crate::gates::deterministic_gate::DeterministicGate;
|
use crate::gates::deterministic_gate::{DeterministicGate, DeterministicGateAdapter};
|
||||||
use crate::gates::gate::{Gate, GateRef};
|
use crate::gates::gate::{Gate, GateRef};
|
||||||
|
use crate::gates::output_graph::{GateOutputLocation, OutputGraph};
|
||||||
use crate::generator::{SimpleGenerator, WitnessGenerator2};
|
use crate::generator::{SimpleGenerator, WitnessGenerator2};
|
||||||
use crate::gmimc::{gmimc_permute_array, gmimc_permute};
|
use crate::gmimc::{gmimc_permute, gmimc_permute_array};
|
||||||
use crate::target::Target2;
|
use crate::target::Target;
|
||||||
use crate::wire::Wire;
|
use crate::wire::Wire;
|
||||||
use crate::witness::PartialWitness;
|
use crate::witness::PartialWitness;
|
||||||
|
|
||||||
@ -21,25 +22,60 @@ pub struct GMiMCGate<F: Field, const W: usize, const R: usize> {
|
|||||||
|
|
||||||
impl<F: Field, const W: usize, const R: usize> GMiMCGate<F, W, R> {
|
impl<F: Field, const W: usize, const R: usize> GMiMCGate<F, W, R> {
|
||||||
pub fn with_constants(constants: Arc<[F; R]>) -> GateRef<F> {
|
pub fn with_constants(constants: Arc<[F; R]>) -> GateRef<F> {
|
||||||
GateRef::new(GMiMCGate::<F, W, R> { constants })
|
let gate = GMiMCGate::<F, W, R> { constants };
|
||||||
|
let adapter = DeterministicGateAdapter::new(gate);
|
||||||
|
GateRef::new(adapter)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_automatic_constants() -> GateRef<F> {
|
pub fn with_automatic_constants() -> GateRef<F> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// If this is set to 1, the first four inputs will be swapped with the next four inputs. This
|
||||||
|
/// is useful for ordering hashes in Merkle proofs. Otherwise, this should be set to 0.
|
||||||
|
// TODO: Assert binary.
|
||||||
|
pub const WIRE_SWITCH: usize = 0;
|
||||||
|
|
||||||
|
/// The wire index for the i'th input to the permutation.
|
||||||
|
pub fn wire_input(i: usize) -> usize {
|
||||||
|
i + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The wire index for the i'th output to the permutation.
|
||||||
|
/// Note that outputs are written to the next gate's wires.
|
||||||
|
pub fn wire_output(i: usize) -> usize {
|
||||||
|
i + 1
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F: Field, const W: usize, const R: usize> Gate<F> for GMiMCGate<F, W, R> {
|
impl<F: Field, const W: usize, const R: usize> DeterministicGate<F> for GMiMCGate<F, W, R> {
|
||||||
fn id(&self) -> String {
|
fn id(&self) -> String {
|
||||||
// TODO: Add W/R
|
// TODO: This won't include generic params?
|
||||||
format!("{:?}", self)
|
format!("{:?}", self)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn constraints(&self, config: CircuitConfig) -> Vec<ConstraintPolynomial<F>> {
|
fn outputs(&self, config: CircuitConfig) -> OutputGraph<F> {
|
||||||
let mut state = (0..W)
|
let original_inputs = (0..W)
|
||||||
.map(|i| ConstraintPolynomial::local_wire_value(i))
|
.map(|i| ConstraintPolynomial::local_wire_value(Self::wire_input(i)))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
// Conditionally switch inputs based on the (boolean) switch wire.
|
||||||
|
let switch = ConstraintPolynomial::local_wire_value(Self::WIRE_SWITCH);
|
||||||
|
let mut state = Vec::new();
|
||||||
|
for i in 0..4 {
|
||||||
|
let a = &original_inputs[i];
|
||||||
|
let b = &original_inputs[i + 4];
|
||||||
|
state.push(a + &switch * (b - a));
|
||||||
|
}
|
||||||
|
for i in 0..4 {
|
||||||
|
let a = &original_inputs[i + 4];
|
||||||
|
let b = &original_inputs[i];
|
||||||
|
state.push(a + &switch * (b - a));
|
||||||
|
}
|
||||||
|
for i in 8..W {
|
||||||
|
state.push(original_inputs[i].clone());
|
||||||
|
}
|
||||||
|
|
||||||
// Value that is implicitly added to each element.
|
// Value that is implicitly added to each element.
|
||||||
// See https://affine.group/2020/02/starkware-challenge
|
// See https://affine.group/2020/02/starkware-challenge
|
||||||
let mut addition_buffer = ConstraintPolynomial::zero();
|
let mut addition_buffer = ConstraintPolynomial::zero();
|
||||||
@ -56,52 +92,19 @@ impl<F: Field, const W: usize, const R: usize> Gate<F> for GMiMCGate<F, W, R> {
|
|||||||
state[i] += &addition_buffer;
|
state[i] += &addition_buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
state
|
let outputs = state.into_iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(i, out)| (GateOutputLocation::NextWire(Self::wire_output(i)), out))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
// A degree of 9 is reasonable for most circuits, and it means that we only need wires for
|
||||||
|
// every other addition buffer state.
|
||||||
|
OutputGraph { outputs }.shrink_degree(9)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generators(
|
fn additional_constraints(&self, _config: CircuitConfig) -> Vec<ConstraintPolynomial<F>> {
|
||||||
&self,
|
let switch = ConstraintPolynomial::local_wire_value(Self::WIRE_SWITCH);
|
||||||
config: CircuitConfig,
|
let switch_bool_constraint = &switch * (&switch - 1);
|
||||||
gate_index: usize,
|
vec![switch_bool_constraint]
|
||||||
local_constants: Vec<F>,
|
|
||||||
next_constants: Vec<F>,
|
|
||||||
) -> Vec<Box<dyn WitnessGenerator2<F>>> {
|
|
||||||
let generator = GMiMCGenerator::<F, W, R> {
|
|
||||||
round_constants: self.constants.clone(),
|
|
||||||
gate_index,
|
|
||||||
};
|
|
||||||
vec![Box::new(generator)]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct GMiMCGenerator<F: Field, const W: usize, const R: usize> {
|
|
||||||
round_constants: Arc<[F; R]>,
|
|
||||||
gate_index: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<F: Field, const W: usize, const R: usize> SimpleGenerator<F> for GMiMCGenerator<F, W, R> {
|
|
||||||
fn dependencies(&self) -> Vec<Target2> {
|
|
||||||
(0..W)
|
|
||||||
.map(|i| Target2::Wire(
|
|
||||||
Wire { gate: self.gate_index, input: i }))
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run_once(&mut self, witness: &PartialWitness<F>) -> PartialWitness<F> {
|
|
||||||
let mut inputs: [F; W] = [F::ZERO; W];
|
|
||||||
for i in 0..W {
|
|
||||||
inputs[i] = witness.get_wire(
|
|
||||||
Wire { gate: self.gate_index, input: i });
|
|
||||||
}
|
|
||||||
|
|
||||||
let outputs = gmimc_permute::<F, W, R>(inputs, self.round_constants.clone());
|
|
||||||
|
|
||||||
let mut result = PartialWitness::new();
|
|
||||||
for i in 0..W {
|
|
||||||
result.set_wire(
|
|
||||||
Wire { gate: self.gate_index + 1, input: i },
|
|
||||||
outputs[i]);
|
|
||||||
}
|
|
||||||
result
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
pub(crate) mod constant;
|
||||||
pub(crate) mod deterministic_gate;
|
pub(crate) mod deterministic_gate;
|
||||||
pub(crate) mod gate;
|
pub(crate) mod gate;
|
||||||
pub(crate) mod gmimc;
|
pub(crate) mod gmimc;
|
||||||
|
pub(crate) mod output_graph;
|
||||||
|
|||||||
66
src/gates/output_graph.rs
Normal file
66
src/gates/output_graph.rs
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
use std::iter;
|
||||||
|
|
||||||
|
use crate::constraint_polynomial::{ConstraintPolynomial, ConstraintPolynomialRef};
|
||||||
|
use crate::field::field::Field;
|
||||||
|
|
||||||
|
/// Represents a set of deterministic gate outputs, expressed as polynomials over witness
|
||||||
|
/// values.
|
||||||
|
pub struct OutputGraph<F: Field> {
|
||||||
|
pub(crate) outputs: Vec<(GateOutputLocation, ConstraintPolynomial<F>)>
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents an output location of a deterministic gate.
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub enum GateOutputLocation {
|
||||||
|
/// A wire belonging to the gate itself.
|
||||||
|
LocalWire(usize),
|
||||||
|
/// A wire belonging to the following gate.
|
||||||
|
NextWire(usize),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F: Field> OutputGraph<F> {
|
||||||
|
/// Creates an output graph with a single output.
|
||||||
|
pub fn single_output(loc: GateOutputLocation, out: ConstraintPolynomial<F>) -> Self {
|
||||||
|
Self { outputs: vec![(loc, out)] }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Compiles an output graph with potentially high-degree polynomials to one with low-degree
|
||||||
|
/// polynomials by introducing extra wires for some intermediate values.
|
||||||
|
///
|
||||||
|
/// Note that this uses a simple greedy algorithm, so the result may not be optimal in terms of wire
|
||||||
|
/// count.
|
||||||
|
pub fn shrink_degree(&self, new_degree: usize) -> Self {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Allocate a new wire for the given target polynomial, and return a new output graph with
|
||||||
|
/// references to the target polynomial replaced with references to that wire.
|
||||||
|
fn allocate_wire(&self, target: ConstraintPolynomial<F>) -> Self {
|
||||||
|
let new_wire_index = self.outputs.iter()
|
||||||
|
.flat_map(|(loc, out)| out.max_wire_input_index())
|
||||||
|
.max()
|
||||||
|
.map_or(0, |i| i + 1);
|
||||||
|
|
||||||
|
let new_wire = ConstraintPolynomial::local_wire_value(new_wire_index);
|
||||||
|
|
||||||
|
let outputs = self.outputs.iter()
|
||||||
|
.map(|(loc, out)| (*loc, out.replace_all(target.clone(), new_wire.clone())))
|
||||||
|
.chain(iter::once((GateOutputLocation::LocalWire(new_wire_index), target.clone())))
|
||||||
|
.collect();
|
||||||
|
Self { outputs }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::constraint_polynomial::ConstraintPolynomial;
|
||||||
|
use crate::gates::output_graph::shrink_degree;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn shrink_exp() {
|
||||||
|
let original = ConstraintPolynomial::local_wire_value(0).exp(10);
|
||||||
|
let shrunk = shrink_degree(original, 3);
|
||||||
|
// `shrunk` should be something similar to (wire0^3)^3 * wire0.
|
||||||
|
assert_eq!(shrunk.max_wire_input_index(), Some(2))
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,12 +1,12 @@
|
|||||||
use crate::field::field::Field;
|
use crate::field::field::Field;
|
||||||
use crate::target::Target2;
|
use crate::target::Target;
|
||||||
use crate::witness::PartialWitness;
|
use crate::witness::PartialWitness;
|
||||||
|
|
||||||
/// A generator participates in the generation of the witness.
|
/// A generator participates in the generation of the witness.
|
||||||
pub trait WitnessGenerator2<F: Field>: 'static {
|
pub trait WitnessGenerator2<F: Field>: 'static {
|
||||||
/// Targets to be "watched" by this generator. Whenever a target in the watch list is populated,
|
/// Targets to be "watched" by this generator. Whenever a target in the watch list is populated,
|
||||||
/// the generator will be queued to run.
|
/// the generator will be queued to run.
|
||||||
fn watch_list(&self) -> Vec<Target2>;
|
fn watch_list(&self) -> Vec<Target>;
|
||||||
|
|
||||||
/// Run this generator, returning a `PartialWitness` containing any new witness elements, and a
|
/// Run this generator, returning a `PartialWitness` containing any new witness elements, and a
|
||||||
/// flag indicating whether the generator is finished. If the flag is true, the generator will
|
/// flag indicating whether the generator is finished. If the flag is true, the generator will
|
||||||
@ -17,13 +17,13 @@ pub trait WitnessGenerator2<F: Field>: 'static {
|
|||||||
|
|
||||||
/// A generator which runs once after a list of dependencies is present in the witness.
|
/// A generator which runs once after a list of dependencies is present in the witness.
|
||||||
pub trait SimpleGenerator<F: Field>: 'static {
|
pub trait SimpleGenerator<F: Field>: 'static {
|
||||||
fn dependencies(&self) -> Vec<Target2>;
|
fn dependencies(&self) -> Vec<Target>;
|
||||||
|
|
||||||
fn run_once(&mut self, witness: &PartialWitness<F>) -> PartialWitness<F>;
|
fn run_once(&mut self, witness: &PartialWitness<F>) -> PartialWitness<F>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F: Field, SG: SimpleGenerator<F>> WitnessGenerator2<F> for SG {
|
impl<F: Field, SG: SimpleGenerator<F>> WitnessGenerator2<F> for SG {
|
||||||
fn watch_list(&self) -> Vec<Target2> {
|
fn watch_list(&self) -> Vec<Target> {
|
||||||
self.dependencies()
|
self.dependencies()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -38,12 +38,12 @@ impl<F: Field, SG: SimpleGenerator<F>> WitnessGenerator2<F> for SG {
|
|||||||
|
|
||||||
/// A generator which copies one wire to another.
|
/// A generator which copies one wire to another.
|
||||||
pub(crate) struct CopyGenerator {
|
pub(crate) struct CopyGenerator {
|
||||||
pub(crate) src: Target2,
|
pub(crate) src: Target,
|
||||||
pub(crate) dst: Target2,
|
pub(crate) dst: Target,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F: Field> SimpleGenerator<F> for CopyGenerator {
|
impl<F: Field> SimpleGenerator<F> for CopyGenerator {
|
||||||
fn dependencies(&self) -> Vec<Target2> {
|
fn dependencies(&self) -> Vec<Target> {
|
||||||
vec![self.src]
|
vec![self.src]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -11,8 +11,8 @@ use field::fft::fft_precompute;
|
|||||||
|
|
||||||
use crate::field::field::Field;
|
use crate::field::field::Field;
|
||||||
use crate::util::log2_ceil;
|
use crate::util::log2_ceil;
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
|
mod circuit_builder;
|
||||||
mod circuit_data;
|
mod circuit_data;
|
||||||
mod constraint_polynomial;
|
mod constraint_polynomial;
|
||||||
mod field;
|
mod field;
|
||||||
@ -22,6 +22,7 @@ mod generator;
|
|||||||
mod gmimc;
|
mod gmimc;
|
||||||
mod proof;
|
mod proof;
|
||||||
mod prover;
|
mod prover;
|
||||||
|
mod recursive_verifier;
|
||||||
mod rescue;
|
mod rescue;
|
||||||
mod target;
|
mod target;
|
||||||
mod util;
|
mod util;
|
||||||
|
|||||||
33
src/proof.rs
33
src/proof.rs
@ -1,12 +1,12 @@
|
|||||||
use crate::field::field::Field;
|
use crate::field::field::Field;
|
||||||
use crate::target::Target2;
|
use crate::target::Target;
|
||||||
|
|
||||||
pub struct Hash<F: Field> {
|
pub struct Hash<F: Field> {
|
||||||
elements: Vec<F>,
|
elements: Vec<F>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct HashTarget {
|
pub struct HashTarget {
|
||||||
elements: Vec<Target2>,
|
elements: Vec<Target>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Proof2<F: Field> {
|
pub struct Proof2<F: Field> {
|
||||||
@ -34,7 +34,24 @@ pub struct ProofTarget2 {
|
|||||||
/// Purported values of each polynomial at each challenge point.
|
/// Purported values of each polynomial at each challenge point.
|
||||||
pub openings: Vec<OpeningSetTarget>,
|
pub openings: Vec<OpeningSetTarget>,
|
||||||
|
|
||||||
// TODO: FRI Merkle proofs.
|
/// A FRI argument for each FRI query.
|
||||||
|
pub fri_proofs: Vec<FriProofTarget>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents a single FRI query, i.e. a path through the reduction tree.
|
||||||
|
pub struct FriProofTarget {
|
||||||
|
/// Merkle proofs for the original purported codewords, i.e. the subject of the LDT.
|
||||||
|
pub initial_merkle_proofs: Vec<MerkleProofTarget>,
|
||||||
|
/// Merkle proofs for the reduced polynomials that were sent in the commit phase.
|
||||||
|
pub intermediate_merkle_proofs: Vec<MerkleProofTarget>,
|
||||||
|
/// The final polynomial in point-value form.
|
||||||
|
pub final_poly: Vec<Target>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct MerkleProofTarget {
|
||||||
|
pub leaf: Vec<Target>,
|
||||||
|
pub siblings: Vec<Target>,
|
||||||
|
// TODO: Also need left/right turn info.
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The purported values of each polynomial at a single point.
|
/// The purported values of each polynomial at a single point.
|
||||||
@ -49,10 +66,10 @@ pub struct OpeningSet<F: Field> {
|
|||||||
|
|
||||||
/// The purported values of each polynomial at a single point.
|
/// The purported values of each polynomial at a single point.
|
||||||
pub struct OpeningSetTarget {
|
pub struct OpeningSetTarget {
|
||||||
pub constants: Vec<Target2>,
|
pub constants: Vec<Target>,
|
||||||
pub plonk_sigmas: Vec<Target2>,
|
pub plonk_sigmas: Vec<Target>,
|
||||||
pub wires: Vec<Target2>,
|
pub wires: Vec<Target>,
|
||||||
// TODO: One or multiple?
|
// TODO: One or multiple?
|
||||||
pub plonk_z: Vec<Target2>,
|
pub plonk_z: Vec<Target>,
|
||||||
pub plonk_t: Vec<Target2>,
|
pub plonk_t: Vec<Target>,
|
||||||
}
|
}
|
||||||
|
|||||||
5
src/recursive_verifier.rs
Normal file
5
src/recursive_verifier.rs
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
use crate::circuit_builder::CircuitBuilder2;
|
||||||
|
use crate::field::field::Field;
|
||||||
|
|
||||||
|
pub fn add_recursive_verifier<F: Field>(builder: &mut CircuitBuilder2<F>) {
|
||||||
|
}
|
||||||
@ -7,22 +7,22 @@ use crate::wire::Wire;
|
|||||||
|
|
||||||
/// A location in the witness.
|
/// A location in the witness.
|
||||||
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
|
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
|
||||||
pub enum Target2 {
|
pub enum Target {
|
||||||
Wire(Wire),
|
Wire(Wire),
|
||||||
PublicInput { index: usize },
|
PublicInput { index: usize },
|
||||||
VirtualAdviceTarget { index: usize },
|
VirtualAdviceTarget { index: usize },
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Target2 {
|
impl Target {
|
||||||
pub fn wire(gate: usize, input: usize) -> Self {
|
pub fn wire(gate: usize, input: usize) -> Self {
|
||||||
Self::Wire(Wire { gate, input })
|
Self::Wire(Wire { gate, input })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_routable(&self, config: CircuitConfig) -> bool {
|
pub fn is_routable(&self, config: CircuitConfig) -> bool {
|
||||||
match self {
|
match self {
|
||||||
Target2::Wire(wire) => wire.is_routable(config),
|
Target::Wire(wire) => wire.is_routable(config),
|
||||||
Target2::PublicInput { .. } => true,
|
Target::PublicInput { .. } => true,
|
||||||
Target2::VirtualAdviceTarget { .. } => false,
|
Target::VirtualAdviceTarget { .. } => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,12 +1,12 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use crate::field::field::Field;
|
use crate::field::field::Field;
|
||||||
use crate::target::Target2;
|
use crate::target::Target;
|
||||||
use crate::wire::Wire;
|
use crate::wire::Wire;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct PartialWitness<F: Field> {
|
pub struct PartialWitness<F: Field> {
|
||||||
target_values: HashMap<Target2, F>,
|
target_values: HashMap<Target, F>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F: Field> PartialWitness<F> {
|
impl<F: Field> PartialWitness<F> {
|
||||||
@ -16,7 +16,7 @@ impl<F: Field> PartialWitness<F> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn singleton(target: Target2, value: F) -> Self {
|
pub fn singleton(target: Target, value: F) -> Self {
|
||||||
let mut witness = PartialWitness::new();
|
let mut witness = PartialWitness::new();
|
||||||
witness.set_target(target, value);
|
witness.set_target(target, value);
|
||||||
witness
|
witness
|
||||||
@ -26,31 +26,31 @@ impl<F: Field> PartialWitness<F> {
|
|||||||
self.target_values.is_empty()
|
self.target_values.is_empty()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_target(&self, target: Target2) -> F {
|
pub fn get_target(&self, target: Target) -> F {
|
||||||
self.target_values[&target]
|
self.target_values[&target]
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn try_get_target(&self, target: Target2) -> Option<F> {
|
pub fn try_get_target(&self, target: Target) -> Option<F> {
|
||||||
self.target_values.get(&target).cloned()
|
self.target_values.get(&target).cloned()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_wire(&self, wire: Wire) -> F {
|
pub fn get_wire(&self, wire: Wire) -> F {
|
||||||
self.get_target(Target2::Wire(wire))
|
self.get_target(Target::Wire(wire))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn contains(&self, target: Target2) -> bool {
|
pub fn contains(&self, target: Target) -> bool {
|
||||||
self.target_values.contains_key(&target)
|
self.target_values.contains_key(&target)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn contains_all(&self, targets: &[Target2]) -> bool {
|
pub fn contains_all(&self, targets: &[Target]) -> bool {
|
||||||
targets.iter().all(|&t| self.contains(t))
|
targets.iter().all(|&t| self.contains(t))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_target(&mut self, target: Target2, value: F) {
|
pub fn set_target(&mut self, target: Target, value: F) {
|
||||||
self.target_values.insert(target, value);
|
self.target_values.insert(target, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_wire(&mut self, wire: Wire, value: F) {
|
pub fn set_wire(&mut self, wire: Wire, value: F) {
|
||||||
self.set_target(Target2::Wire(wire), value)
|
self.set_target(Target::Wire(wire), value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user