mirror of https://github.com/zcash/halo2.git
Compare commits
6 Commits
5252810196
...
d223584f86
Author | SHA1 | Date |
---|---|---|
str4d | d223584f86 | |
Daira-Emma Hopwood | 7df93fd855 | |
adria0 | daaa638966 | |
Daira-Emma Hopwood | 81729eca91 | |
Daira-Emma Hopwood | 4a8e640afd | |
Jack Grigg | 3836b6e327 |
|
@ -12,7 +12,7 @@ jobs:
|
|||
- uses: actions/checkout@v3
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: nightly
|
||||
toolchain: '1.76.0'
|
||||
override: true
|
||||
|
||||
# - name: Setup mdBook
|
||||
|
@ -26,7 +26,7 @@ jobs:
|
|||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: install
|
||||
args: mdbook --git https://github.com/HollowMan6/mdBook.git --rev 62e01b34c23b957579c04ee1b24b57814ed8a4d5
|
||||
args: mdbook --git https://github.com/HollowMan6/mdBook.git --rev 5830c9555a4dc051675d17f1fcb04dd0920543e8
|
||||
|
||||
- name: Install mdbook-katex and mdbook-pdf
|
||||
uses: actions-rs/cargo@v1
|
||||
|
@ -40,6 +40,11 @@ jobs:
|
|||
- name: Build halo2 book
|
||||
run: mdbook build book/
|
||||
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: nightly-2023-10-05
|
||||
override: true
|
||||
|
||||
- name: Build latest rustdocs
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
# halo2 [![Crates.io](https://img.shields.io/crates/v/halo2.svg)](https://crates.io/crates/halo2) #
|
||||
# halo2
|
||||
|
||||
## [Documentation](https://docs.rs/halo2)
|
||||
## Usage
|
||||
|
||||
This repository contains the [halo2_proofs](halo2_proofs/README.md) and
|
||||
[halo2_gadgets](halo2_gadgets/README.md) crates, which should be used directly.
|
||||
|
||||
## Minimum Supported Rust Version
|
||||
|
||||
|
|
|
@ -14,8 +14,6 @@ title = "The halo2 Book"
|
|||
macros = "macros.txt"
|
||||
renderers = ["html"]
|
||||
|
||||
[output.katex]
|
||||
|
||||
[output.html]
|
||||
|
||||
[output.html.print]
|
||||
|
|
|
@ -54,6 +54,7 @@ blake2b_simd = "1"
|
|||
maybe-rayon = {version = "0.1.0", default-features = false}
|
||||
|
||||
# Developer tooling dependencies
|
||||
criterion = { version = "0.3", optional = true }
|
||||
plotters = { version = "0.3.0", default-features = false, optional = true }
|
||||
tabbycat = { version = "0.1", features = ["attributes"], optional = true }
|
||||
|
||||
|
@ -78,6 +79,7 @@ getrandom = { version = "0.2", features = ["js"] }
|
|||
[features]
|
||||
default = ["batch", "multicore"]
|
||||
multicore = ["maybe-rayon/threads"]
|
||||
dev-cost = ["criterion"]
|
||||
dev-graph = ["plotters", "tabbycat"]
|
||||
test-dev-graph = [
|
||||
"dev-graph",
|
||||
|
|
|
@ -3,13 +3,16 @@
|
|||
use std::{
|
||||
cmp,
|
||||
collections::{HashMap, HashSet},
|
||||
iter,
|
||||
iter::{self, Sum},
|
||||
marker::PhantomData,
|
||||
ops::{Add, Mul},
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
use ff::{Field, PrimeField};
|
||||
use group::prime::PrimeGroup;
|
||||
use group::{prime::PrimeGroup, Group};
|
||||
use pasta_curves::arithmetic::{CurveAffine, CurveExt};
|
||||
use rand_core::OsRng;
|
||||
|
||||
use crate::{
|
||||
circuit::{layouter::RegionColumn, Value},
|
||||
|
@ -20,6 +23,8 @@ use crate::{
|
|||
poly::Rotation,
|
||||
};
|
||||
|
||||
mod time;
|
||||
|
||||
/// Measures a circuit to determine its costs, and explain what contributes to them.
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug)]
|
||||
|
@ -28,8 +33,12 @@ pub struct CircuitCost<G: PrimeGroup, ConcreteCircuit: Circuit<G::Scalar>> {
|
|||
k: u32,
|
||||
/// Maximum degree of the circuit.
|
||||
max_deg: usize,
|
||||
/// Number of instance columns.
|
||||
instance_columns: usize,
|
||||
/// Number of advice columns.
|
||||
advice_columns: usize,
|
||||
/// Costs of the gate expressions.
|
||||
gate_expressions: Vec<time::ExpressionCost>,
|
||||
/// Number of direct queries for each column type.
|
||||
instance_queries: usize,
|
||||
advice_queries: usize,
|
||||
|
@ -38,8 +47,8 @@ pub struct CircuitCost<G: PrimeGroup, ConcreteCircuit: Circuit<G::Scalar>> {
|
|||
lookups: usize,
|
||||
/// Number of columns in the global permutation.
|
||||
permutation_cols: usize,
|
||||
/// Number of distinct sets of points in the multiopening argument.
|
||||
point_sets: usize,
|
||||
/// Size of each distinct sets of points in the multiopening argument.
|
||||
point_sets: Vec<usize>,
|
||||
/// Maximum rows used over all columns
|
||||
max_rows: usize,
|
||||
/// Maximum rows used over all advice columns
|
||||
|
@ -274,6 +283,27 @@ impl<G: PrimeGroup, ConcreteCircuit: Circuit<G::Scalar>> CircuitCost<G, Concrete
|
|||
|
||||
assert!((1 << k) >= cs.minimum_rows());
|
||||
|
||||
// Measure the gate expressions.
|
||||
let gate_expressions = cs
|
||||
.gates
|
||||
.iter()
|
||||
.flat_map(move |gate| {
|
||||
gate.polynomials().iter().map(move |poly| {
|
||||
poly.evaluate(
|
||||
&|_| (0, 0, 0).into(),
|
||||
&|_| panic!("virtual selectors are removed during optimization"),
|
||||
&|_, _, _| (0, 0, 0).into(),
|
||||
&|_, _, _| (0, 0, 0).into(),
|
||||
&|_, _, _| (0, 0, 0).into(),
|
||||
&|a| a,
|
||||
&|a, b| a + b + (1, 0, 0).into(),
|
||||
&|a, b| a + b + (0, 1, 0).into(),
|
||||
&|a, _| a + (0, 0, 1).into(),
|
||||
)
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
|
||||
// Figure out how many point sets we have due to queried cells.
|
||||
let mut column_queries: HashMap<Column<Any>, HashSet<i32>> = HashMap::new();
|
||||
for (c, r) in iter::empty()
|
||||
|
@ -318,13 +348,15 @@ impl<G: PrimeGroup, ConcreteCircuit: Circuit<G::Scalar>> CircuitCost<G, Concrete
|
|||
CircuitCost {
|
||||
k,
|
||||
max_deg,
|
||||
instance_columns: cs.num_instance_columns,
|
||||
advice_columns: cs.num_advice_columns,
|
||||
gate_expressions,
|
||||
instance_queries: cs.instance_queries.len(),
|
||||
advice_queries: cs.advice_queries.len(),
|
||||
fixed_queries: cs.fixed_queries.len(),
|
||||
lookups: cs.lookups.len(),
|
||||
permutation_cols,
|
||||
point_sets: point_sets.len(),
|
||||
point_sets: point_sets.into_iter().map(|set| set.len()).collect(),
|
||||
_marker: PhantomData::default(),
|
||||
max_rows: layout.total_rows,
|
||||
max_advice_rows: layout.total_advice_rows,
|
||||
|
@ -402,7 +434,7 @@ impl<G: PrimeGroup, ConcreteCircuit: Circuit<G::Scalar>> CircuitCost<G, Concrete
|
|||
// Multiopening argument:
|
||||
// - f_commitment
|
||||
// - 1 eval per set of points in multiopen argument
|
||||
multiopen: ProofContribution::new(1, self.point_sets),
|
||||
multiopen: ProofContribution::new(1, self.point_sets.len()),
|
||||
|
||||
// Polycommit:
|
||||
// - s_poly commitment
|
||||
|
@ -417,7 +449,7 @@ impl<G: PrimeGroup, ConcreteCircuit: Circuit<G::Scalar>> CircuitCost<G, Concrete
|
|||
}
|
||||
|
||||
/// (commitments, evaluations)
|
||||
#[derive(Debug)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
struct ProofContribution {
|
||||
commitments: usize,
|
||||
evaluations: usize,
|
||||
|
@ -459,7 +491,7 @@ impl Mul<usize> for ProofContribution {
|
|||
}
|
||||
|
||||
/// The marginal size of a Halo 2 proof, broken down into its contributing factors.
|
||||
#[derive(Debug)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct MarginalProofSize<G: PrimeGroup> {
|
||||
instance: ProofContribution,
|
||||
advice: ProofContribution,
|
||||
|
@ -481,7 +513,7 @@ impl<G: PrimeGroup> From<MarginalProofSize<G>> for usize {
|
|||
}
|
||||
|
||||
/// The size of a Halo 2 proof, broken down into its contributing factors.
|
||||
#[derive(Debug)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct ProofSize<G: PrimeGroup> {
|
||||
instance: ProofContribution,
|
||||
advice: ProofContribution,
|
||||
|
|
|
@ -0,0 +1,387 @@
|
|||
use std::{
|
||||
iter::{self, Sum},
|
||||
marker::PhantomData,
|
||||
ops::{Add, Mul},
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
use group::{ff::Field, prime::PrimeGroup, Group};
|
||||
use pasta_curves::arithmetic::{CurveAffine, CurveExt};
|
||||
|
||||
use super::{CircuitCost, MarginalProofSize, ProofContribution, ProofSize};
|
||||
use crate::{
|
||||
plonk::Circuit,
|
||||
transcript::{Blake2bRead, Transcript, TranscriptRead},
|
||||
};
|
||||
|
||||
impl<C: CurveExt, ConcreteCircuit: Circuit<<C as Group>::Scalar>> CircuitCost<C, ConcreteCircuit> {
|
||||
/// Returns the marginal verifying cost per instance of this circuit.
|
||||
pub fn marginal_verifying(&self) -> MarginalVerifyingCost<C::AffineExt> {
|
||||
let chunks = self.permutation_chunks();
|
||||
|
||||
MarginalVerifyingCost {
|
||||
// Transcript:
|
||||
// - the marginal proof size
|
||||
proof: self.marginal_proof_size(),
|
||||
|
||||
// Cells:
|
||||
// - 1 polynomial commitment per instance column per instance
|
||||
// - 1 commitment per instance column per instance
|
||||
instance_columns: self.instance_columns,
|
||||
|
||||
// Gates:
|
||||
gates: TimeContribution::new(0, self.gate_expressions.iter().copied()),
|
||||
|
||||
// Lookup arguments:
|
||||
// - 7 additions per lookup argument per instance
|
||||
// - 6 multiplications per lookup argument per instance
|
||||
lookups: TimeContribution::new(0, [(1, 1), (1, 2), (1, 1), (4, 2)].into_iter())
|
||||
* self.lookups,
|
||||
|
||||
// Global permutation argument:
|
||||
// - 5 * chunks + 3 additions and multiplications per instance
|
||||
equality: TimeContribution::new(
|
||||
0,
|
||||
[(1, 1)]
|
||||
.into_iter()
|
||||
.chain((0..chunks - 1).map(|_| (1, 1)))
|
||||
.chain((0..chunks).map(|_| (4 * chunks + 3, 4 * chunks + 3))),
|
||||
),
|
||||
|
||||
_marker: PhantomData::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the verifying cost for the given number of instances of this circuit.
|
||||
pub fn verifying(&self, instances: usize) -> VerifyingCost<C::AffineExt> {
|
||||
let marginal = self.marginal_verifying();
|
||||
|
||||
VerifyingCost {
|
||||
// Transcript:
|
||||
// - the proof
|
||||
// - marginal cost per instance
|
||||
proof: self.proof_size(instances),
|
||||
instance_columns: marginal.instance_columns * instances,
|
||||
|
||||
// - Verifying key
|
||||
// - 5 challenges
|
||||
|
||||
// Gates:
|
||||
// - marginal cost per instance
|
||||
gates: marginal.gates * instances,
|
||||
|
||||
// Lookup arguments:
|
||||
// - marginal cost per instance
|
||||
lookups: marginal.lookups * instances,
|
||||
|
||||
// Global permutation argument:
|
||||
// - marginal cost per instance
|
||||
// - TODO: global cost
|
||||
equality: marginal.equality * instances,
|
||||
|
||||
// Vanishing argument:
|
||||
// - expressions + 1 commitments
|
||||
// - 1 random_poly eval
|
||||
vanishing: TimeContribution::new(0, [(1, 1)].into_iter()),
|
||||
|
||||
// Multiopening argument:
|
||||
// - TODO: point set evals
|
||||
// - TODO: Lagrange interpolation per point set
|
||||
// - TODO: inversions
|
||||
// - 2 additions and mults per point in multiopen argument
|
||||
// - 2 additions per set of points in multiopen argument
|
||||
// - 1 multiplication per set of points in multiopen argument
|
||||
multiopen: self
|
||||
.point_sets
|
||||
.iter()
|
||||
.map(|points| TimeContribution::new(0, (2 * points + 2, 2 * points + 1).into()))
|
||||
.sum(),
|
||||
|
||||
// Polycommit:
|
||||
// - s_poly commitment
|
||||
// - inner product argument (2 * k round commitments)
|
||||
// - a
|
||||
// - xi
|
||||
polycomm: ProofContribution::new(1 + 2 * self.k, 2),
|
||||
|
||||
_marker: PhantomData::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The marginal time cost of verifying a specific Halo 2 proof, broken down into its
|
||||
/// contributing factors.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct MarginalVerifyingCost<C: CurveAffine> {
|
||||
proof: MarginalProofSize<C::Curve>,
|
||||
instance_columns: usize,
|
||||
gates: TimeContribution,
|
||||
lookups: TimeContribution,
|
||||
equality: TimeContribution,
|
||||
_marker: PhantomData<C>,
|
||||
}
|
||||
|
||||
impl<C: CurveAffine> MarginalVerifyingCost<C> {
|
||||
fn transcript_inputs(&self) -> ProofContribution {
|
||||
self.proof.instance + self.proof.advice + self.proof.lookups + self.proof.equality
|
||||
}
|
||||
|
||||
fn expressions(&self) -> ExpressionCost {
|
||||
iter::empty()
|
||||
.chain(self.gates.expressions.iter())
|
||||
.chain(self.lookups.expressions.iter())
|
||||
.chain(self.equality.expressions.iter())
|
||||
.sum()
|
||||
}
|
||||
|
||||
/// Estimates the concrete time cost for verifying this proof.
|
||||
#[cfg(feature = "dev-cost")]
|
||||
pub fn estimate(&self) -> TimeCost {
|
||||
TimeCost::estimate::<C>(
|
||||
self.instance_columns,
|
||||
self.transcript_inputs(),
|
||||
self.expressions(),
|
||||
)
|
||||
}
|
||||
|
||||
/// Evaluates the concrete time cost for verifying this proof.
|
||||
pub fn evaluate(&self, single_field_add: Duration, single_field_mul: Duration) -> TimeCost {
|
||||
TimeCost::evaluate::<C>(
|
||||
self.instance_columns,
|
||||
self.transcript_inputs(),
|
||||
self.expressions(),
|
||||
single_field_add,
|
||||
single_field_mul,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// The time cost of verifying a specific Halo 2 proof, broken down into its contributing
|
||||
/// factors.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct VerifyingCost<C: CurveAffine> {
|
||||
proof: ProofSize<C::Curve>,
|
||||
instance_columns: usize,
|
||||
gates: TimeContribution,
|
||||
lookups: TimeContribution,
|
||||
equality: TimeContribution,
|
||||
vanishing: TimeContribution,
|
||||
multiopen: TimeContribution,
|
||||
polycomm: TimeContribution,
|
||||
_marker: PhantomData<C>,
|
||||
}
|
||||
|
||||
impl<C: CurveAffine> VerifyingCost<C> {
|
||||
fn transcript_inputs(&self) -> ProofContribution {
|
||||
// TODO
|
||||
self.proof.instance + self.proof.advice + self.proof.lookups + self.proof.equality
|
||||
}
|
||||
|
||||
fn expressions(&self) -> ExpressionCost {
|
||||
iter::empty()
|
||||
.chain(self.gates.expressions.iter())
|
||||
.chain(self.lookups.expressions.iter())
|
||||
.chain(self.equality.expressions.iter())
|
||||
.chain(self.vanishing.expressions.iter())
|
||||
.chain(self.multiopen.expressions.iter())
|
||||
.chain(self.polycomm.expressions.iter())
|
||||
.sum()
|
||||
}
|
||||
|
||||
/// Estimates the concrete time cost for verifying this proof.
|
||||
#[cfg(feature = "dev-cost")]
|
||||
pub fn estimate(&self) -> TimeCost {
|
||||
TimeCost::estimate::<C>(
|
||||
self.instance_columns,
|
||||
self.transcript_inputs(),
|
||||
self.expressions(),
|
||||
)
|
||||
}
|
||||
|
||||
/// Evaluates the concrete time cost for verifying this proof.
|
||||
pub fn evaluate(&self, single_field_add: Duration, single_field_mul: Duration) -> TimeCost {
|
||||
TimeCost::evaluate::<C>(
|
||||
self.instance_columns,
|
||||
self.transcript_inputs(),
|
||||
self.expressions(),
|
||||
single_field_add,
|
||||
single_field_mul,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// The estimated time cost of proving or verifying a specific Halo 2 circuit.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct TimeCost {
|
||||
transcript: Duration,
|
||||
expressions: Duration,
|
||||
}
|
||||
|
||||
impl TimeCost {
|
||||
#[cfg(feature = "dev-cost")]
|
||||
fn estimate<C: CurveAffine>(
|
||||
instance_columns: usize,
|
||||
transcript_inputs: ProofContribution,
|
||||
expressions: ExpressionCost,
|
||||
) -> Self {
|
||||
use rand_core::OsRng;
|
||||
|
||||
let pairs = [0; 100].map(|_| (C::Scalar::random(OsRng), C::Scalar::random(OsRng)));
|
||||
|
||||
let runner = |f: fn(C::Scalar, C::Scalar) -> C::Scalar| {
|
||||
let start = Instant::now();
|
||||
for _ in 0..100 {
|
||||
for (a, b) in pairs.into_iter() {
|
||||
let _ = criterion::black_box(f(a, b));
|
||||
}
|
||||
}
|
||||
Instant::now().duration_since(start) / (100 * pairs.len() as u32)
|
||||
};
|
||||
|
||||
let single_field_add = runner(|a, b| a + b);
|
||||
let single_field_mul = runner(|a, b| a * b);
|
||||
|
||||
Self::evaluate::<C>(
|
||||
instance_columns,
|
||||
transcript_inputs,
|
||||
expressions,
|
||||
single_field_add,
|
||||
single_field_mul,
|
||||
)
|
||||
}
|
||||
|
||||
fn evaluate<C: CurveAffine>(
|
||||
instance_columns: usize,
|
||||
transcript_inputs: ProofContribution,
|
||||
expressions: ExpressionCost,
|
||||
single_field_add: Duration,
|
||||
single_field_mul: Duration,
|
||||
) -> Self {
|
||||
let dummy_point = C::generator();
|
||||
|
||||
// Transcript cost
|
||||
let transcript = {
|
||||
let mut transcript = Blake2bRead::init(std::io::repeat(1));
|
||||
let start = Instant::now();
|
||||
for _ in 0..instance_columns {
|
||||
let _ = transcript.common_point(dummy_point).unwrap();
|
||||
}
|
||||
for _ in 0..transcript_inputs.commitments {
|
||||
let _ = transcript.read_point().unwrap();
|
||||
}
|
||||
for _ in 0..transcript_inputs.evaluations {
|
||||
let _ = transcript.read_scalar().unwrap();
|
||||
}
|
||||
Instant::now().duration_since(start)
|
||||
};
|
||||
|
||||
// Expressions cost
|
||||
let expressions = single_field_add * expressions.add as u32
|
||||
+ single_field_mul * (expressions.mul + expressions.scale) as u32;
|
||||
|
||||
Self {
|
||||
transcript,
|
||||
expressions,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the total estimated time cost.
|
||||
pub fn total(&self) -> Duration {
|
||||
self.transcript + self.expressions
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub(super) struct ExpressionCost {
|
||||
add: usize,
|
||||
mul: usize,
|
||||
scale: usize,
|
||||
}
|
||||
|
||||
/// Use when multiplications and scalings have the same cost.
|
||||
impl From<(usize, usize)> for ExpressionCost {
|
||||
fn from((add, mul): (usize, usize)) -> Self {
|
||||
Self { add, mul, scale: 0 }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<(usize, usize, usize)> for ExpressionCost {
|
||||
fn from((add, mul, scale): (usize, usize, usize)) -> Self {
|
||||
Self { add, mul, scale }
|
||||
}
|
||||
}
|
||||
|
||||
impl Add for ExpressionCost {
|
||||
type Output = Self;
|
||||
|
||||
fn add(self, rhs: Self) -> Self::Output {
|
||||
Self {
|
||||
add: self.add + rhs.add,
|
||||
mul: self.mul + rhs.mul,
|
||||
scale: self.scale + rhs.scale,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Sum<&'a ExpressionCost> for ExpressionCost {
|
||||
fn sum<I: Iterator<Item = &'a ExpressionCost>>(iter: I) -> Self {
|
||||
iter.fold((0, 0, 0).into(), |acc, expr| acc + *expr)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct TimeContribution {
|
||||
polynomial_commitments: usize,
|
||||
expressions: Vec<ExpressionCost>,
|
||||
}
|
||||
|
||||
impl TimeContribution {
|
||||
fn new<E>(polynomial_commitments: usize, expressions: impl Iterator<Item = E>) -> Self
|
||||
where
|
||||
ExpressionCost: From<E>,
|
||||
{
|
||||
Self {
|
||||
polynomial_commitments,
|
||||
expressions: expressions.map(ExpressionCost::from).collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Add for TimeContribution {
|
||||
type Output = Self;
|
||||
|
||||
fn add(self, mut rhs: Self) -> Self::Output {
|
||||
let mut expressions = self.expressions;
|
||||
expressions.append(&mut rhs.expressions);
|
||||
Self {
|
||||
polynomial_commitments: self.polynomial_commitments + rhs.polynomial_commitments,
|
||||
expressions,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Mul<usize> for TimeContribution {
|
||||
type Output = Self;
|
||||
|
||||
fn mul(self, instances: usize) -> Self::Output {
|
||||
Self {
|
||||
polynomial_commitments: self.polynomial_commitments * instances,
|
||||
expressions: iter::repeat(self.expressions.into_iter())
|
||||
.take(instances)
|
||||
.flatten()
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Sum for TimeContribution {
|
||||
fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
|
||||
iter.fold(
|
||||
TimeContribution {
|
||||
polynomial_commitments: 0,
|
||||
expressions: vec![],
|
||||
},
|
||||
|acc, expr| acc + expr,
|
||||
)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue