Skip to content

Commit

Permalink
Add the optimization goal framework (#334)
Browse files Browse the repository at this point in the history
* finalize method

* setter/getter, and wrapprs in ConstraintSystemRef

* style

* incorporate comments

* constraints as default optimization goal

* remove reduce_constraint_weight

* None still inlines

* export OptimizationGoal; add assertion checks

* retouch the comments

Co-authored-by: weikeng <[email protected]>
  • Loading branch information
npwardberkeley and weikengchen authored Jan 4, 2021
1 parent 54a0b1a commit 8d9055d
Show file tree
Hide file tree
Showing 4 changed files with 94 additions and 42 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
## Pending

### Breaking changes
- #334 Outlining linear combinations is now specified via the optimization goal interface.

### Features

Expand Down
107 changes: 78 additions & 29 deletions relations/src/r1cs/constraint_system.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ pub struct ConstraintSystem<F: Field> {
/// The number of linear combinations
pub num_linear_combinations: usize,

/// The parameter we aim to minimize in this constraint system (either the
/// number of constraints or their total weight).
pub optimization_goal: OptimizationGoal,

/// Assignments to the public input variables. This is empty if `self.mode
/// == SynthesisMode::Setup`.
pub instance_assignment: Vec<F>,
Expand Down Expand Up @@ -91,6 +95,18 @@ pub enum SynthesisMode {
},
}

/// Defines the parameter to optimize for a `ConstraintSystem`.
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
pub enum OptimizationGoal {
/// Make no attempt to optimize.
None,
/// Minimize the number of constraints.
Constraints,
/// Minimize the total weight of the constraints (the number of nonzero
/// entries across all constraints).
Weight,
}

impl<F: Field> ConstraintSystem<F> {
#[inline]
fn make_row(&self, l: &LinearCombination<F>) -> Vec<(F, usize)> {
Expand All @@ -109,7 +125,7 @@ impl<F: Field> ConstraintSystem<F> {
.collect()
}

/// Construct an ampty `ConstraintSystem`.
/// Construct an empty `ConstraintSystem`.
pub fn new() -> Self {
Self {
num_instance_variables: 1,
Expand All @@ -131,6 +147,8 @@ impl<F: Field> ConstraintSystem<F> {
mode: SynthesisMode::Prove {
construct_matrices: true,
},

optimization_goal: OptimizationGoal::Constraints,
}
}

Expand All @@ -149,6 +167,24 @@ impl<F: Field> ConstraintSystem<F> {
self.mode == SynthesisMode::Setup
}

/// Check whether this constraint system aims to optimize weight,
/// number of constraints, or neither.
pub fn optimization_goal(&self) -> OptimizationGoal {
self.optimization_goal
}

/// Specify whether this constraint system should aim to optimize weight,
/// number of constraints, or neither.
pub fn set_optimization_goal(&mut self, goal: OptimizationGoal) {
// `set_optimization_goal` should only be executed before any constraint or value is created.
assert_eq!(self.num_instance_variables, 1);
assert_eq!(self.num_witness_variables, 0);
assert_eq!(self.num_constraints, 0);
assert_eq!(self.num_linear_combinations, 0);

self.optimization_goal = goal;
}

/// Check whether or not `self` will construct matrices.
pub fn should_construct_matrices(&self) -> bool {
match self.mode {
Expand Down Expand Up @@ -238,7 +274,8 @@ impl<F: Field> ConstraintSystem<F> {
Ok(())
}

/// Count the number of times each LC is used within other LCs in the constraint system
/// Count the number of times each LC is used within other LCs in the
/// constraint system
fn lc_num_times_used(&self, count_sinks: bool) -> Vec<usize> {
let mut num_times_used = vec![0; self.lc_map.len()];

Expand Down Expand Up @@ -269,7 +306,6 @@ impl<F: Field> ConstraintSystem<F> {
/// The transformer function returns the number of new witness variables needed
/// and a vector of new witness assignments (if not in the setup mode).
/// (usize, Option<Vec<F>>)
///
pub fn transform_lc_map(
&mut self,
transformer: &mut dyn FnMut(
Expand Down Expand Up @@ -309,14 +345,14 @@ impl<F: Field> ConstraintSystem<F> {
// Delete linear combinations that are no longer used.
//
// Deletion is safe for both outlining and inlining:
// * Inlining: the LC is substituted directly into all use
// sites, and so once it is fully inlined, it is redundant.
// * Inlining: the LC is substituted directly into all use sites, and so once it
// is fully inlined, it is redundant.
//
// * Outlining: the LC is associated with a new variable `w`,
// and a new constraint of the form `lc_data * 1 = w`, where
// `lc_data` is the actual data in the linear combination.
// Furthermore, we replace its entry in `new_lc_map` with `(1, w)`.
// Once `w` is fully inlined, then we can delete the entry from `new_lc_map`
// * Outlining: the LC is associated with a new variable `w`, and a new
// constraint of the form `lc_data * 1 = w`, where `lc_data` is the actual
// data in the linear combination. Furthermore, we replace its entry in
// `new_lc_map` with `(1, w)`. Once `w` is fully inlined, then we can delete
// the entry from `new_lc_map`
//
num_times_used[lc_index.0] -= 1;
if num_times_used[lc_index.0] == 0 {
Expand Down Expand Up @@ -404,8 +440,9 @@ impl<F: Field> ConstraintSystem<F> {
// If true, the LC is replaced with 1 * this witness variable.
// Otherwise, the LC is inlined.
//
// Each iteration first updates the LC according to outlinings in prior iterations,
// and then sees if it should be outlined, and if so adds the outlining to the map.
// Each iteration first updates the LC according to outlinings in prior
// iterations, and then sees if it should be outlined, and if so adds
// the outlining to the map.
//
self.transform_lc_map(&mut |cs, num_times_used, inlined_lc| {
let mut should_dedicate_a_witness_variable = false;
Expand Down Expand Up @@ -473,16 +510,14 @@ impl<F: Field> ConstraintSystem<F> {
}
}

/// Reduce the constraint weight.
///
/// At this moment, it is a wrapper to `outline_lcs`.
/// More weight reductions may be added later.
///
/// Useful for SNARKs like [\[Marlin\]](https://eprint.iacr.org/2019/1047) or
/// [\[Fractal\]](https://eprint.iacr.org/2019/1076), where addition gates
/// are not cheap.
pub fn reduce_constraint_weight(&mut self) {
self.outline_lcs();
/// Finalize the constraint system (either by outlining or inlining,
/// if an optimization goal is set).
pub fn finalize(&mut self) {
match self.optimization_goal {
OptimizationGoal::None => self.inline_all_lcs(),
OptimizationGoal::Constraints => self.inline_all_lcs(),
OptimizationGoal::Weight => self.outline_lcs(),
};
}

/// This step must be called after constraint generation has completed, and
Expand Down Expand Up @@ -782,6 +817,23 @@ impl<F: Field> ConstraintSystemRef<F> {
.map_or(0, |cs| cs.borrow().num_witness_variables)
}

/// Check whether this constraint system aims to optimize weight,
/// number of constraints, or neither.
#[inline]
pub fn optimization_goal(&self) -> OptimizationGoal {
self.inner().map_or(OptimizationGoal::Constraints, |cs| {
cs.borrow().optimization_goal()
})
}

/// Specify whether this constraint system should aim to optimize weight,
/// number of constraints, or neither.
#[inline]
pub fn set_optimization_goal(&self, goal: OptimizationGoal) {
self.inner()
.map_or((), |cs| cs.borrow_mut().set_optimization_goal(goal))
}

/// Check whether or not `self` will construct matrices.
#[inline]
pub fn should_construct_matrices(&self) -> bool {
Expand Down Expand Up @@ -864,14 +916,11 @@ impl<F: Field> ConstraintSystemRef<F> {
}
}

/// Reduce the constraint weight.
///
/// Useful for SNARKs like [\[Marlin\]](https://eprint.iacr.org/2019/1047) or
/// [\[Fractal\]](https://eprint.iacr.org/2019/1076), where addition gates
/// are not cheap.
pub fn reduce_constraint_weight(&self) {
/// Finalize the constraint system (either by outlining or inlining,
/// if an optimization goal is set).
pub fn finalize(&self) {
if let Some(cs) = self.inner() {
cs.borrow_mut().reduce_constraint_weight()
cs.borrow_mut().finalize()
}
}

Expand Down
2 changes: 1 addition & 1 deletion relations/src/r1cs/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ pub use tracing::info_span;
pub use ark_ff::{Field, ToConstraintField};
pub use constraint_system::{
ConstraintMatrices, ConstraintSynthesizer, ConstraintSystem, ConstraintSystemRef, Namespace,
SynthesisMode,
OptimizationGoal, SynthesisMode,
};
pub use error::SynthesisError;

Expand Down
26 changes: 14 additions & 12 deletions snark/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ pub trait SNARK<F: PrimeField> {
/// Errors encountered during setup, proving, or verification.
type Error: 'static + ark_std::error::Error;

/// Takes in a description of a computation (specified in R1CS constraints), and
/// samples proving and verification keys for that circuit.
/// Takes in a description of a computation (specified in R1CS constraints),
/// and samples proving and verification keys for that circuit.
fn circuit_specific_setup<C: ConstraintSynthesizer<F>, R: RngCore + CryptoRng>(
circuit: C,
rng: &mut R,
Expand All @@ -51,8 +51,8 @@ pub trait SNARK<F: PrimeField> {
) -> Result<Self::Proof, Self::Error>;

/// Checks that `proof` is a valid proof of the satisfaction of circuit
/// encoded in `circuit_vk`, with respect to the public input `public_input`.
/// as R1CS constraints).
/// encoded in `circuit_vk`, with respect to the public input `public_input`,
/// specified as R1CS constraints.
fn verify(
circuit_vk: &Self::VerifyingKey,
public_input: &[F],
Expand All @@ -68,8 +68,8 @@ pub trait SNARK<F: PrimeField> {
) -> Result<Self::ProcessedVerifyingKey, Self::Error>;

/// Checks that `proof` is a valid proof of the satisfaction of circuit
/// encoded in `circuit_pvk`, with respect to the public input `public_input`.
/// as R1CS constraints).
/// encoded in `circuit_pvk`, with respect to the public input `public_input`,
/// specified as R1CS constraints.
fn verify_with_processed_vk(
circuit_pvk: &Self::ProcessedVerifyingKey,
public_input: &[F],
Expand Down Expand Up @@ -102,21 +102,23 @@ pub enum UniversalSetupIndexError<Bound, E> {
/// A SNARK with universal setup. That is, a SNARK where the trusted setup is
/// circuit-independent.
pub trait UniversalSetupSNARK<F: PrimeField>: SNARK<F> {
/// Specifies how to bound the size of public parameters required to generate
/// the index proving and verification keys for a given circuit.
/// Specifies how to bound the size of public parameters required to
/// generate the index proving and verification keys for a given
/// circuit.
type ComputationBound: Clone + Default + Debug;
/// Specifies the type of universal public parameters.
type PublicParameters: Clone + Debug;

/// Specifies how to bound the size of public parameters required to generate
/// the index proving and verification keys for a given circuit.
/// Specifies how to bound the size of public parameters required to
/// generate the index proving and verification keys for a given
/// circuit.
fn universal_setup<R: RngCore + CryptoRng>(
compute_bound: &Self::ComputationBound,
rng: &mut R,
) -> Result<Self::PublicParameters, Self::Error>;

/// Indexes the public parameters according to the circuit `circuit`, and outputs
/// circuit-specific proving and verification keys.
/// Indexes the public parameters according to the circuit `circuit`, and
/// outputs circuit-specific proving and verification keys.
fn index<C: ConstraintSynthesizer<F>, R: RngCore + CryptoRng>(
pp: &Self::PublicParameters,
circuit: C,
Expand Down

0 comments on commit 8d9055d

Please sign in to comment.