diff --git a/halo2_proofs/examples/simple-example-cost-model.rs b/halo2_proofs/examples/simple-example-cost-model.rs new file mode 100644 index 0000000000..76e14c8f67 --- /dev/null +++ b/halo2_proofs/examples/simple-example-cost-model.rs @@ -0,0 +1,243 @@ +use halo2_proofs::{ + arithmetic::FieldExt, + circuit::{Cell, Layouter, SimpleFloorPlanner}, + cost_model_main, + plonk::*, + poly::Rotation, +}; +use pairing::bn256::{Bn256, Fr as Fp}; + +use std::marker::PhantomData; + +#[derive(Clone)] +struct PlonkConfig { + a: Column, + b: Column, + c: Column, + + sa: Column, + sb: Column, + sc: Column, + sm: Column, +} + +trait StandardCs { + fn raw_multiply( + &self, + layouter: &mut impl Layouter, + f: F, + ) -> Result<(Cell, Cell, Cell), Error> + where + F: FnMut() -> Result<(FF, FF, FF), Error>; + fn raw_add( + &self, + layouter: &mut impl Layouter, + f: F, + ) -> Result<(Cell, Cell, Cell), Error> + where + F: FnMut() -> Result<(FF, FF, FF), Error>; + fn copy(&self, layouter: &mut impl Layouter, a: Cell, b: Cell) -> Result<(), Error>; +} + +#[derive(Clone)] +struct MyCircuit { + a: Option, + k: u32, +} + +struct StandardPlonk { + config: PlonkConfig, + _marker: PhantomData, +} + +impl StandardPlonk { + fn new(config: PlonkConfig) -> Self { + StandardPlonk { + config, + _marker: PhantomData, + } + } +} + +impl StandardCs for StandardPlonk { + fn raw_multiply( + &self, + layouter: &mut impl Layouter, + mut f: F, + ) -> Result<(Cell, Cell, Cell), Error> + where + F: FnMut() -> Result<(FF, FF, FF), Error>, + { + layouter.assign_region( + || "mul", + |mut region| { + let mut values = None; + let lhs = region.assign_advice( + || "lhs", + self.config.a, + 0, + || { + values = Some(f()?); + Ok(values.ok_or(Error::Synthesis)?.0) + }, + )?; + let rhs = region.assign_advice( + || "rhs", + self.config.b, + 0, + || Ok(values.ok_or(Error::Synthesis)?.1), + )?; + + let out = region.assign_advice( + || "out", + self.config.c, + 0, + || Ok(values.ok_or(Error::Synthesis)?.2), + )?; + + region.assign_fixed(|| "a", self.config.sa, 0, || Ok(FF::zero()))?; + region.assign_fixed(|| "b", self.config.sb, 0, || Ok(FF::zero()))?; + region.assign_fixed(|| "c", self.config.sc, 0, || Ok(FF::one()))?; + region.assign_fixed(|| "a * b", self.config.sm, 0, || Ok(FF::one()))?; + + Ok((lhs.cell(), rhs.cell(), out.cell())) + }, + ) + } + + fn raw_add( + &self, + layouter: &mut impl Layouter, + mut f: F, + ) -> Result<(Cell, Cell, Cell), Error> + where + F: FnMut() -> Result<(FF, FF, FF), Error>, + { + layouter.assign_region( + || "mul", + |mut region| { + let mut values = None; + let lhs = region.assign_advice( + || "lhs", + self.config.a, + 0, + || { + values = Some(f()?); + Ok(values.ok_or(Error::Synthesis)?.0) + }, + )?; + let rhs = region.assign_advice( + || "rhs", + self.config.b, + 0, + || Ok(values.ok_or(Error::Synthesis)?.1), + )?; + + let out = region.assign_advice( + || "out", + self.config.c, + 0, + || Ok(values.ok_or(Error::Synthesis)?.2), + )?; + + region.assign_fixed(|| "a", self.config.sa, 0, || Ok(FF::one()))?; + region.assign_fixed(|| "b", self.config.sb, 0, || Ok(FF::one()))?; + region.assign_fixed(|| "c", self.config.sc, 0, || Ok(FF::one()))?; + region.assign_fixed(|| "a * b", self.config.sm, 0, || Ok(FF::zero()))?; + + Ok((lhs.cell(), rhs.cell(), out.cell())) + }, + ) + } + + fn copy(&self, layouter: &mut impl Layouter, left: Cell, right: Cell) -> Result<(), Error> { + layouter.assign_region( + || "copy", + |mut region| { + region.constrain_equal(left, right)?; + region.constrain_equal(left, right) + }, + ) + } +} + +impl Circuit for MyCircuit { + type Config = PlonkConfig; + type FloorPlanner = SimpleFloorPlanner; + + fn without_witnesses(&self) -> Self { + Self { a: None, k: self.k } + } + + fn configure(meta: &mut ConstraintSystem) -> PlonkConfig { + let a = meta.advice_column(); + let b = meta.advice_column(); + let c = meta.advice_column(); + + meta.enable_equality(a); + meta.enable_equality(b); + meta.enable_equality(c); + + let sm = meta.fixed_column(); + let sa = meta.fixed_column(); + let sb = meta.fixed_column(); + let sc = meta.fixed_column(); + + meta.create_gate("mini plonk", |meta| { + let a = meta.query_advice(a, Rotation::cur()); + let b = meta.query_advice(b, Rotation::cur()); + let c = meta.query_advice(c, Rotation::cur()); + + let sa = meta.query_fixed(sa, Rotation::cur()); + let sb = meta.query_fixed(sb, Rotation::cur()); + let sc = meta.query_fixed(sc, Rotation::cur()); + let sm = meta.query_fixed(sm, Rotation::cur()); + + vec![a.clone() * sa + b.clone() * sb + a * b * sm + (c * sc * (-F::one()))] + }); + + PlonkConfig { + a, + b, + c, + sa, + sb, + sc, + sm, + // perm, + } + } + + fn synthesize(&self, config: PlonkConfig, mut layouter: impl Layouter) -> Result<(), Error> { + let cs = StandardPlonk::new(config); + + for _ in 0..1 << ((self.k - 1) - 3) { + let mut a_squared = None; + let (a0, _, c0) = cs.raw_multiply(&mut layouter, || { + a_squared = self.a.map(|a| a.square()); + Ok(( + self.a.ok_or(Error::Synthesis)?, + self.a.ok_or(Error::Synthesis)?, + a_squared.ok_or(Error::Synthesis)?, + )) + })?; + let (a1, b1, _) = cs.raw_add(&mut layouter, || { + let fin = a_squared.and_then(|a2| self.a.map(|a| a + a2)); + Ok(( + self.a.ok_or(Error::Synthesis)?, + a_squared.ok_or(Error::Synthesis)?, + fin.ok_or(Error::Synthesis)?, + )) + })?; + cs.copy(&mut layouter, a0, a1)?; + cs.copy(&mut layouter, b1, c0)?; + } + + Ok(()) + } +} + +cost_model_main!(MyCircuit:: { + a: Some(Fp::from(5)), + k: 8 +}); diff --git a/halo2_proofs/src/dev.rs b/halo2_proofs/src/dev.rs index ce8327974a..650d768094 100644 --- a/halo2_proofs/src/dev.rs +++ b/halo2_proofs/src/dev.rs @@ -24,6 +24,9 @@ mod util; pub mod cost; pub use cost::CircuitCost; +pub mod cost_model; +pub use cost_model::*; + mod gates; pub use gates::CircuitGates; diff --git a/halo2_proofs/src/dev/cost_model.rs b/halo2_proofs/src/dev/cost_model.rs new file mode 100644 index 0000000000..872566e4f0 --- /dev/null +++ b/halo2_proofs/src/dev/cost_model.rs @@ -0,0 +1,614 @@ +//! Circuit cost model. +use std::{collections::BTreeMap, fs, io, iter, mem, time::Instant}; + +use crate::{ + arithmetic::{eval_polynomial, kate_division, parallelize, CurveAffine, Engine, Field}, + circuit::{Cell, Layouter, SimpleFloorPlanner}, + multicore, + plonk::*, + poly::{ + batch_invert_assigned, commitment::Params, commitment::ParamsVerifier, EvaluationDomain, + Rotation, + }, + transcript::{Blake2bRead, Blake2bWrite, Challenge255}, +}; +use group::{prime::PrimeCurveAffine, GroupEncoding}; +use pairing::bn256::{Bn256, Fr as Fp, G1Affine}; +use rand_core::OsRng; +use rayon::current_num_threads; + +use super::CircuitCost; + +/// Measures the elapsed time of a closure. +pub fn measure_elapsed_time T>(f: F) -> (f64, T) { + let start = Instant::now(); + let res = f(); + (start.elapsed().as_secs_f64(), res) +} + +/// EstimateResult is to store the output of estimate() +#[derive(Debug)] +pub struct EstimateResult { + prover_time: f64, + mem_usage: f64, +} + +impl EstimateResult { + /// print estimation result. + pub fn print(&self) { + println!("prover time = {} (s)", self.prover_time); + println!("memory usage = {} (KB)", self.mem_usage); + } +} + +impl Calculation { + // The returned argument is the number of additions and multiplications. + fn fake_evaluate(&self) -> (usize, usize) { + match self { + Calculation::Add(_, _) => (1, 0), + Calculation::Sub(_, _) => (1, 0), + Calculation::Mul(_, _) => (0, 1), + Calculation::Negate(_) => (1, 0), + Calculation::LcBeta(_, _) => (1, 1), + Calculation::LcTheta(_, _) => (1, 1), + Calculation::AddGamma(_) => (1, 0), + Calculation::Store(_) => (0, 0), + } + } +} + +struct FakeProverQuery { + rotation: Rotation, +} + +impl Evaluator { + // Returns the number of hadamard addition and product operations. + fn fake_evaluate_h(&self, cs: &ConstraintSystem, l: usize) -> (usize, usize) { + let mut num_mul_lag = 0; + let mut num_add_lag = 0; + // All calculations, with cached intermediate results + for calc in self.calculations.iter() { + let (tmp_num_add_lag, tmp_num_mul_lag) = calc.calculation.fake_evaluate::(); + num_add_lag += tmp_num_add_lag; + num_mul_lag += tmp_num_mul_lag; + } + + // Accumulate value parts + num_add_lag += self.value_parts.len(); + num_mul_lag += self.value_parts.len(); + + for table_result in self.lookup_results.iter() { + let (tmp_num_add_lag, tmp_num_mul_lag) = table_result.fake_evaluate::(); + num_add_lag += tmp_num_add_lag; + num_mul_lag += tmp_num_mul_lag; + } + + // Permutations + let chunk_len = cs.degree() - 2; + let num_perm_slices = (cs.permutation.get_columns().len() + chunk_len - 1) / chunk_len; + + if num_perm_slices > 0 { + // Enforce only for the first set. + // value(X) = value(X) * y + l_0(X) * (1 - z_0(X)) + num_add_lag += 2; + num_mul_lag += 2; + // Enforce only for the last set. + // value(X) = value(X) * y + l_last(X) * (z_l(X)^2 - z_l(X)) + num_add_lag += 2; + num_mul_lag += 3; + // Except for the first set, enforce. + // value(X) = value(X) * y + l_0(X) * (z_i(X) - z_{i-1}(\omega^(last) X)) + num_add_lag += 2 * (num_perm_slices - 1); + num_mul_lag += 2 * (num_perm_slices - 1); + // delta_start * beta_start + num_mul_lag += 1; + + // And for all the sets we enforce: + // (1 - (l_last(X) + l_blind(X))) * ( + // z_i(\omega X) \prod_j (p(X) + \beta s_j(X) + \gamma) + // - z_i(X) \prod_j (p(X) + \delta^j \beta X + \gamma) + // ) + + // Calculate left = z_i(\omega X) \prod_j (p(X) + \beta s_j(X) + \gamma) + let mut tmp_num_add_lag = 0; + let mut tmp_num_mul_lag = 0; + tmp_num_add_lag += 2 * chunk_len; + tmp_num_mul_lag += 2 * chunk_len; + // Calculate right = z_i(X) \prod_j (p(X) + \delta^j \beta X + \gamma), current_delta *= DELTA + tmp_num_add_lag += 2 * chunk_len; + tmp_num_mul_lag += chunk_len; + tmp_num_mul_lag += chunk_len; + // value(X) = value(X) * y + (1 - (l_last(X) + l_blind(X))) * ( + // z_i(\omega X) \prod_j (p(X) + \beta s_j(X) + \gamma) + // - z_i(X) \prod_j (p(X) + \delta^j \beta X + \gamma) + // ). + tmp_num_add_lag += 2; + tmp_num_mul_lag += 2; + + num_add_lag += tmp_num_add_lag * num_perm_slices; + num_mul_lag += tmp_num_mul_lag * num_perm_slices; + + // beta_term *= &extended_omega; + num_mul_lag += 1; + } + + // Lookups + let num_lookups = cs.lookups.len(); + // a_minus_s + num_add_lag += num_lookups; + // value(X) = value(X) * y + l_0(X) * (1 - z(X)) + num_add_lag += 2 * num_lookups; + num_mul_lag += 2 * num_lookups; + // value(X) = value(X) * y + l_last(X) * (z(X)^2 - z(X)) + num_add_lag += 2 * num_lookups; + num_mul_lag += 3 * num_lookups; + // value(X) = value(X) * y + (1 - (l_last(X) + l_blind(X))) * ( + // z(\omega X) (a'(X) + \beta) (s'(X) + \gamma) + // - z(X) (\theta^{m-1} a_0(X) + ... + a_{m-1}(X) + \beta) + // (\theta^{m-1} s_0(X) + ... + s_{m-1}(X) + \gamma) + // ) + num_add_lag += 4 * num_lookups; + num_mul_lag += 5 * num_lookups; + // value(X) = value(X) * y + l_0(X) * (a'(X) - s'(X)) + num_add_lag += 1 * num_lookups; + num_mul_lag += 2 * num_lookups; + // (1 - (l_last + l_blind)) * (a′(X) − s′(X))⋅(a′(X) − a′(\omega^{-1} X)) = 0 + num_add_lag += 2 * num_lookups; + num_mul_lag += 3 * num_lookups; + + num_add_lag *= l; + num_mul_lag *= l; + + (num_add_lag, num_mul_lag) + } +} + +/// estimate is to estimate the prover time, peek memory usage and aggregate circuit size. +pub fn estimate>( + circuit: ConcreteCircuit, + k: usize, +) -> EstimateResult { + // Initialize the polynomial commitment parameters for small k + let small_params: Params = Params::::unsafe_setup::(15_u32); + // To run synthesize and convert simple_selectors to fixed columns. + let vk = keygen_vk(&small_params, &circuit).expect("keygen_vk should not fail"); + let cs = vk.get_cs(); + + let params = { + let s = E::Scalar::random(OsRng); + let rand_c1 = ::generator() * s; + let rand_c2 = ::generator() * s; + let rand_c1: E::G1Affine = rand_c1.into(); + let n = 1 << k; + Params { + k: k as u32, + n: n as u64, + g: (0..n).map(|_| rand_c1).collect(), + g_lagrange: (0..n).map(|_| rand_c1).collect(), + additional_data: Vec::from(rand_c2.to_bytes().as_ref()), + } + }; + + // Initialize the domain + let domain = EvaluationDomain::new(cs.degree() as u32, k as u32); + + let l = 1; // The number of instances + let n = 1 << k as usize; + let rand_ele = E::Scalar::random(&mut OsRng); + let rand_vec: Vec = (0..n).map(|_| rand_ele.clone()).collect(); + let rand_vec2 = rand_vec.clone(); + let rand_values = domain.lagrange_from_vec(rand_vec.clone()); + + // Estimate the time of each operation. + // msm + let (time_msm, _) = measure_elapsed_time(|| params.commit_lagrange(&rand_values)); + // fft + let (time_fft, rand_poly) = measure_elapsed_time(|| domain.lagrange_to_coeff(rand_values)); + // extended fft + let (time_extended_fft, _) = measure_elapsed_time(|| domain.coeff_to_extended(rand_poly)); + // BTree time cost in lookup argument + let (time_btree, _) = measure_elapsed_time(|| { + let mut leftover_table_map: BTreeMap = + rand_vec2 + .iter() + .take(n) + .fold(BTreeMap::new(), |mut acc, coeff| { + *acc.entry(*coeff).or_insert(0) += 1; + acc + }); + for item in &rand_vec2 { + if let Some(count) = leftover_table_map.get_mut(item) { + *count -= 1; + } + } + }); + + // Estimate the number of each operation. + let FuncCount { + num_fft, + num_extended_fft, + num_msm, + num_btree, + num_add, + num_mul, + num_kate_div, + num_add_lag, + num_mul_lag, + mem_usage, + } = dummy_proof(¶ms, &cs, &domain, l); + + println!("num_fft = {}, time_fft = {}", num_fft, time_fft); + println!( + "num_extended_fft = {}, time_extended_fft = {}", + num_extended_fft, time_extended_fft + ); + println!("num_msm = {}, time_msm = {}", num_msm, time_msm); + println!("num_btree = {}, time_btree = {}", num_btree, time_btree); + + let pt_non_linear = (num_fft as f64) * time_fft + + (num_extended_fft as f64) * time_extended_fft + + (num_msm as f64) * time_msm + + (num_btree as f64) * time_btree; + println!("pt_non_linear = {}\n", pt_non_linear); + + let mut rand_ext_vec: Vec = (0..domain.extended_len()) + .map(|_| rand_ele.clone()) + .collect(); + let (time_add_lag, _) = measure_elapsed_time(|| { + parallelize(&mut rand_ext_vec, |rand_ext_vec, _| { + for value in rand_ext_vec.iter_mut() { + *value + rand_ele; + } + }) + }); + println!( + "num_add_lag = {}, time_add_lag = {}", + num_add_lag, time_add_lag + ); + + let (time_mul_lag, _) = measure_elapsed_time(|| { + parallelize(&mut rand_ext_vec, |rand_ext_vec, _| { + for value in rand_ext_vec.iter_mut() { + *value * rand_ele; + } + }) + }); + println!( + "num_mul_lag = {}, time_mul_lag = {}", + num_mul_lag, time_mul_lag + ); + + let mut rand_vec: Vec = (0..n).map(|_| rand_ele.clone()).collect(); + let (time_add, _) = measure_elapsed_time(|| { + parallelize(&mut rand_vec, |rand_vec, _| { + for value in rand_vec.iter_mut() { + *value + rand_ele; + } + }) + }); + println!("num_add = {}, time_add = {}", num_add, time_add); + + let (time_mul, _) = measure_elapsed_time(|| { + parallelize(&mut rand_vec, |rand_vec, _| { + for value in rand_vec.iter_mut() { + *value * rand_ele; + } + }) + }); + println!("num_mul = {}, time_mul = {}", num_mul, time_mul); + + let (time_kate_div, _) = measure_elapsed_time(|| kate_division(&rand_vec, rand_ele)); + println!( + "num_kate_div = {}, time_kate_div = {}", + num_kate_div, time_kate_div + ); + + let pt_linear = (num_add_lag as f64) * time_add_lag + + (num_mul_lag as f64) * time_mul_lag + + (num_add as f64) * time_add + + (num_mul as f64) * time_mul + + (num_kate_div as f64) * time_kate_div; + println!("pt_linear = {}", pt_linear); + + let (pt_random, _) = measure_elapsed_time(|| { + let mut random_poly = domain.empty_coeff(); + for coeff in random_poly.iter_mut() { + *coeff = E::Scalar::random(&mut OsRng); + } + random_poly + }); + println!("pt_random = {}", pt_random); + println!(); + + let prover_time = pt_non_linear + pt_linear + pt_random; + + EstimateResult { + prover_time, + mem_usage: (mem_usage as f64) / 1024.0, // to KB + } +} + +/// Run a circuit proving process. +pub fn simulate_circuit>( + circuit: ConcreteCircuit, + k: usize, +) { + // let public_inputs_size = 0; + + // Initialize the polynomial commitment parameters + let params: Params = Params::::unsafe_setup::(k as u32); + + // Initialize the proving key + let vk = keygen_vk(¶ms, &circuit).expect("keygen_vk should not fail"); + let pk = keygen_pk(¶ms, vk, &circuit).expect("keygen_pk should not fail"); + + // Create a proof + let mut transcript = Blake2bWrite::<_, _, Challenge255<_>>::init(vec![]); + + let (prover_time, _) = measure_elapsed_time(|| { + create_proof(¶ms, &pk, &[circuit], &[&[]], OsRng, &mut transcript) + .expect("proof generation should not fail") + }); + + println!("k = {}, prover_time = {}", k, prover_time); +} + +struct FuncCount { + num_fft: usize, + num_extended_fft: usize, + num_msm: usize, + num_btree: usize, + num_add: usize, + num_mul: usize, + num_kate_div: usize, + num_add_lag: usize, + num_mul_lag: usize, + mem_usage: usize, +} + +fn dummy_proof( + params: &Params, + cs: &ConstraintSystem, + domain: &EvaluationDomain, + l: usize, // The number of input. +) -> FuncCount { + let mut num_fft = 0_usize; + let mut num_extended_fft = 0_usize; + let mut num_msm = 0_usize; + let mut num_btree = 0_usize; + let mut num_add = 0_usize; + let mut num_mul = 0_usize; + let mut num_kate_div = 0_usize; + + // (instance, advice) calculate (poly, coset, commitment) + + // ins_commit, pt += l * n_ins * commit_lagrange_t + num_msm += l * cs.num_instance_columns; + // ins_poly, pt += l * n_ins + lagrange_to_coeff_t + num_fft += l * cs.num_instance_columns; + // ins_coset, pt += l * n_ins + coeff_to_extended_t + num_extended_fft += l * cs.num_instance_columns; + // adv_commit, pt += l * n_adv * commit_lagrange_t + num_msm += l * cs.num_advice_columns; + // adv_poly, pt += l * n_adv * lagrange_to_coeff_t + num_fft += l * cs.num_advice_columns; + // adv_coset, pt += l * n_adv * coeff_to_extended_t + num_extended_fft += l * cs.num_advice_columns; + + // pt += l * n_lookup * commit_permuted + // BTree cost for A' and S'. + let num_lookups = cs.lookups.len(); + num_btree += l * num_lookups; + + // Commit to permutations. + // l * perm_commit_t + // commit_lagrange: z + let num_perm_slices = + (cs.permutation.get_columns().len() + (cs.degree() - 3)) / (cs.degree() - 2); + num_msm += l * num_perm_slices; + // lagrange_to_coeff: z + num_fft += l * num_perm_slices; + // coeff_to_extended: z + num_extended_fft += l * num_perm_slices; + + // pt += lookup_commit_product + // commit_lagrange: z, a', s' + num_msm += l * 3 * num_lookups; + // lagrange_to_coeff: z, a', s' + num_fft += l * 3 * num_lookups; + + // Commit to the vanishing argument's random polynomial for blinding h(x_3) + // vanishing_commit + // commit: random_poly + num_msm += 1; + + // Evaluate the h(X) polynomial + // evaluate_h 3 coeff_to_extended for each lookup argument + num_extended_fft += l * 3 * num_lookups; + + // Construct the vanishing argument's h(X) commitments + // pt += vanishing_construct + // extended_to_coeff: h_poly + num_extended_fft += 1; + // commit: h_poly_i + let num_h_pieces = ((domain.extended_len() as u64 + params.n - 1) / params.n) as usize; + num_msm += num_h_pieces; + + // Evaluate h. + let ev = Evaluator::::new(cs); + let (num_add_lag, num_mul_lag) = ev.fake_evaluate_h(cs, l); + + // Estimate multiopen(gwc). + // commit: h_x, h_x + // The evaluations in multiopen is too small. + // Initialize the query sets. + let queries = (0..l) + .flat_map(|_| { + iter::empty() + .chain( + cs.instance_queries + .iter() + .map(move |&(_, at)| FakeProverQuery { rotation: at }), + ) + .chain( + cs.advice_queries + .iter() + .map(move |&(_, at)| FakeProverQuery { rotation: at }), + ) + .chain((0..num_perm_slices).flat_map(|_| { + iter::empty() + // Open permutation product commitments at x and \omega x + .chain(Some(FakeProverQuery { + rotation: Rotation::cur(), + })) + .chain(Some(FakeProverQuery { + rotation: Rotation::next(), + })) + })) + // Open it at \omega^{last} x for all but the last set. This rotation is only + // sensical for the first row, but we only use this rotation in a constraint + // that is gated on l_0. + .chain((0..num_perm_slices).rev().skip(1).flat_map(|_| { + Some(FakeProverQuery { + rotation: Rotation(-1), + }) + })) + .chain((0..num_lookups).flat_map(|_| { + iter::empty() + // Open lookup product commitments at x + .chain(Some(FakeProverQuery { + rotation: Rotation::cur(), + })) + // Open lookup input commitments at x + .chain(Some(FakeProverQuery { + rotation: Rotation::cur(), + })) + // Open lookup table commitments at x + .chain(Some(FakeProverQuery { + rotation: Rotation::cur(), + })) + // Open lookup input commitments at x_inv + .chain(Some(FakeProverQuery { + rotation: Rotation::prev(), + })) + // Open lookup product commitments at x_next + .chain(Some(FakeProverQuery { + rotation: Rotation::next(), + })) + })) + }) + .chain( + cs.fixed_queries + .iter() + .map(|&(_, at)| FakeProverQuery { rotation: at }), + ) + .chain( + (0..cs.permutation.get_columns().len()).map(|_| FakeProverQuery { + rotation: Rotation::cur(), + }), + ) + // We query the h(X) polynomial at x + .chain( + iter::empty() + .chain(Some(FakeProverQuery { + rotation: Rotation::cur(), + })) + .chain(Some(FakeProverQuery { + rotation: Rotation::cur(), + })), + ); + let mut point_query_map: BTreeMap = BTreeMap::new(); + for query in queries { + if let Some(queries) = point_query_map.get_mut(&query.rotation) { + *queries = *queries + 1_usize; + } else { + point_query_map.insert(query.rotation, 1); + } + } + + for rot in point_query_map.keys() { + let cnt = point_query_map.get(rot).unwrap(); + // poly_batch = poly_batch * *v + poly; + // eval_batch = eval_batch * *v + eval; + num_add += cnt; + num_mul += cnt; + + // poly_batch = &poly_batch - eval_batch; + num_add += 1; + num_kate_div += 1; + num_msm += 1; + } + + // Memory + let mut mem_usage = 0_usize; + // instance / advice / fixed as value poly, and coset: + let n = 1 << params.k as usize; + let ext_n = domain.extended_len(); + mem_usage += l * (cs.num_instance_columns + cs.num_advice_columns) * (ext_n + 2 * n); + mem_usage += cs.num_fixed_columns * (2 * n + ext_n); + // l_0, l_last, l_active_row as coset: + mem_usage += 3 * ext_n; + // lookup compressed_input / compressed_table as value: + // mem_usage += 2 * l * num_lookups * n; + // lookup permuted_input / permuted_table as value: + // mem_usage += 2 * l * num_lookups * n; + // lookup permuted_input / permuted_table as poly: + mem_usage += 2 * l * num_lookups * n; + // lookup Z as poly + mem_usage += l * num_lookups * n; + // permutation sigma as value, poly, and coset: + mem_usage += l * num_perm_slices * (2 * n + ext_n); + // permutation Z as poly,, and coset + mem_usage += l * num_perm_slices * (n + ext_n); + // vanishing random_poly + mem_usage += n; + // evaluate_h lookup values + mem_usage += num_lookups * ext_n; + // evaluate_h single lookup Z / permuted_input / permuted_table as coset + mem_usage += l * 3 * ext_n; + // evaluate_h h_poly as coset + mem_usage += ext_n; + + println!("number of field element: {}", mem_usage); + + mem_usage *= mem::size_of::(); + + FuncCount { + num_fft, + num_extended_fft, + num_msm, + num_btree, + num_add, + num_mul, + num_kate_div, + num_add_lag, + num_mul_lag, + mem_usage, + } +} + +/// Generate a main function to run the cost model for a circuit. +#[macro_export] +macro_rules! cost_model_main { + ($cir:expr) => { + use halo2_proofs::dev::{estimate, simulate_circuit}; + + fn main() { + let mode = std::env::args().nth(1).expect("no running-mode given"); + let k = std::env::args() + .nth(2) + .expect("no circuit size given") + .parse() + .unwrap(); + let circuit = $cir; + if mode.eq(&String::from("simulate")) { + simulate_circuit::(circuit, k); + } else if mode.eq(&String::from("estimate")) { + let res = estimate::(circuit, k); + res.print(); + } else { + panic!("unrecognized format"); + } + } + }; +} diff --git a/halo2_proofs/src/plonk.rs b/halo2_proofs/src/plonk.rs index 1f7cb9301c..35204077e5 100644 --- a/halo2_proofs/src/plonk.rs +++ b/halo2_proofs/src/plonk.rs @@ -30,13 +30,14 @@ mod verifier; pub use assigned::*; pub use circuit::*; pub use error::*; +pub use evaluation::*; pub use keygen::*; pub use prover::*; pub use verifier::*; use std::io; -use self::evaluation::Evaluator; +pub use self::evaluation::Evaluator; /// This is a verifying key which allows for the verification of proofs for a /// particular circuit. @@ -147,6 +148,11 @@ impl ProvingKey { pub fn get_vk(&self) -> &VerifyingKey { &self.vk } + + /// Get the underlying [`Evaluator`]. + pub fn get_ev(&self) -> &Evaluator { + &self.ev + } } impl VerifyingKey { @@ -154,6 +160,11 @@ impl VerifyingKey { pub fn get_domain(&self) -> &EvaluationDomain { &self.domain } + + /// Get the underlying [`ConstraintSystem`]. + pub fn get_cs(&self) -> &ConstraintSystem { + &self.cs + } } #[derive(Clone, Copy, Debug)]