1use std::{
4 collections::{HashMap, HashSet},
5 iter,
6 marker::PhantomData,
7 ops::{Add, Mul},
8};
9
10use ff::{Field, PrimeField};
11use group::prime::PrimeGroup;
12
13use crate::{
14 plonk::{
15 Advice, Any, Assigned, Assignment, Circuit, Column, ConstraintSystem, Error, Fixed,
16 FloorPlanner, Instance, Selector,
17 },
18 poly::Rotation,
19};
20
21#[derive(Debug)]
23pub struct CircuitCost<G: PrimeGroup, ConcreteCircuit: Circuit<G::Scalar>> {
24 k: usize,
26 max_deg: usize,
28 advice_columns: usize,
30 instance_queries: usize,
32 advice_queries: usize,
33 fixed_queries: usize,
34 lookups: usize,
36 permutation_cols: usize,
38 point_sets: usize,
40
41 _marker: PhantomData<(G, ConcreteCircuit)>,
42}
43
44struct Assembly {
45 selectors: Vec<Vec<bool>>,
46}
47
48impl<F: Field> Assignment<F> for Assembly {
49 fn enter_region<NR, N>(&mut self, _: N)
50 where
51 NR: Into<String>,
52 N: FnOnce() -> NR,
53 {
54 }
56
57 fn exit_region(&mut self) {
58 }
60
61 fn enable_selector<A, AR>(&mut self, _: A, selector: &Selector, row: usize) -> Result<(), Error>
62 where
63 A: FnOnce() -> AR,
64 AR: Into<String>,
65 {
66 self.selectors[selector.0][row] = true;
67
68 Ok(())
69 }
70
71 fn query_instance(&self, _: Column<Instance>, _: usize) -> Result<Option<F>, Error> {
72 Ok(None)
73 }
74
75 fn assign_advice<V, VR, A, AR>(
76 &mut self,
77 _: A,
78 _: Column<Advice>,
79 _: usize,
80 _: V,
81 ) -> Result<(), Error>
82 where
83 V: FnOnce() -> Result<VR, Error>,
84 VR: Into<Assigned<F>>,
85 A: FnOnce() -> AR,
86 AR: Into<String>,
87 {
88 Ok(())
89 }
90
91 fn assign_fixed<V, VR, A, AR>(
92 &mut self,
93 _: A,
94 _: Column<Fixed>,
95 _: usize,
96 _: V,
97 ) -> Result<(), Error>
98 where
99 V: FnOnce() -> Result<VR, Error>,
100 VR: Into<Assigned<F>>,
101 A: FnOnce() -> AR,
102 AR: Into<String>,
103 {
104 Ok(())
105 }
106
107 fn copy(&mut self, _: Column<Any>, _: usize, _: Column<Any>, _: usize) -> Result<(), Error> {
108 Ok(())
109 }
110
111 fn fill_from_row(
112 &mut self,
113 _: Column<Fixed>,
114 _: usize,
115 _: Option<Assigned<F>>,
116 ) -> Result<(), Error> {
117 Ok(())
118 }
119
120 fn push_namespace<NR, N>(&mut self, _: N)
121 where
122 NR: Into<String>,
123 N: FnOnce() -> NR,
124 {
125 }
127
128 fn pop_namespace(&mut self, _: Option<String>) {
129 }
131}
132
133impl<G: PrimeGroup, ConcreteCircuit: Circuit<G::Scalar>> CircuitCost<G, ConcreteCircuit> {
134 pub fn measure(k: usize, circuit: &ConcreteCircuit) -> Self {
138 let mut cs = ConstraintSystem::default();
140 let config = ConcreteCircuit::configure(&mut cs);
141 let mut assembly = Assembly {
142 selectors: vec![vec![false; 1 << k]; cs.num_selectors],
143 };
144 ConcreteCircuit::FloorPlanner::synthesize(
145 &mut assembly,
146 circuit,
147 config,
148 cs.constants.clone(),
149 )
150 .unwrap();
151 let (cs, _) = cs.compress_selectors(assembly.selectors);
152
153 assert!((1 << k) >= cs.minimum_rows());
154
155 let mut column_queries: HashMap<Column<Any>, HashSet<i32>> = HashMap::new();
157 for (c, r) in iter::empty()
158 .chain(
159 cs.advice_queries
160 .iter()
161 .map(|(c, r)| (Column::<Any>::from(*c), *r)),
162 )
163 .chain(cs.instance_queries.iter().map(|(c, r)| ((*c).into(), *r)))
164 .chain(cs.fixed_queries.iter().map(|(c, r)| ((*c).into(), *r)))
165 .chain(
166 cs.permutation
167 .get_columns()
168 .into_iter()
169 .map(|c| (c, Rotation::cur())),
170 )
171 {
172 column_queries.entry(c).or_default().insert(r.0);
173 }
174 let mut point_sets: HashSet<Vec<i32>> = HashSet::new();
175 for (_, r) in column_queries {
176 let mut query_set: Vec<_> = r.into_iter().collect();
178 query_set.sort_unstable();
179 point_sets.insert(query_set);
180 }
181
182 point_sets.insert(vec![0, 1]); point_sets.insert(vec![-1, 0]); point_sets.insert(vec![0]); point_sets.insert(vec![0, 1]); let max_deg = cs.degree();
190 let permutation_cols = cs.permutation.get_columns().len();
191 if permutation_cols > max_deg - 2 {
192 point_sets.insert(vec![-((cs.blinding_factors() + 1) as i32), 0, 1]);
194 }
195
196 CircuitCost {
197 k,
198 max_deg,
199 advice_columns: cs.num_advice_columns,
200 instance_queries: cs.instance_queries.len(),
201 advice_queries: cs.advice_queries.len(),
202 fixed_queries: cs.fixed_queries.len(),
203 lookups: cs.lookups.len(),
204 permutation_cols,
205 point_sets: point_sets.len(),
206 _marker: PhantomData::default(),
207 }
208 }
209
210 fn permutation_chunks(&self) -> usize {
211 let chunk_size = self.max_deg - 2;
212 (self.permutation_cols + chunk_size - 1) / chunk_size
213 }
214
215 pub fn marginal_proof_size(&self) -> MarginalProofSize<G> {
217 let chunks = self.permutation_chunks();
218
219 MarginalProofSize {
220 instance: ProofContribution::new(0, self.instance_queries),
225 advice: ProofContribution::new(self.advice_columns, self.advice_queries),
226
227 lookups: ProofContribution::new(3 * self.lookups, 5 * self.lookups),
231
232 equality: ProofContribution::new(chunks, 3 * chunks - 1),
236
237 _marker: PhantomData::default(),
238 }
239 }
240
241 pub fn proof_size(&self, instances: usize) -> ProofSize<G> {
243 let marginal = self.marginal_proof_size();
244
245 ProofSize {
246 instance: marginal.instance * instances,
250 advice: marginal.advice * instances,
251 fixed: ProofContribution::new(0, self.fixed_queries),
252
253 lookups: marginal.lookups * instances,
256
257 equality: marginal.equality * instances
261 + ProofContribution::new(0, self.permutation_cols),
262
263 vanishing: ProofContribution::new(self.max_deg, 1),
267
268 multiopen: ProofContribution::new(1, self.point_sets),
272
273 polycomm: ProofContribution::new(1 + 2 * self.k, 2),
279
280 _marker: PhantomData::default(),
281 }
282 }
283}
284
285#[derive(Debug)]
287struct ProofContribution {
288 commitments: usize,
289 evaluations: usize,
290}
291
292impl ProofContribution {
293 fn new(commitments: usize, evaluations: usize) -> Self {
294 ProofContribution {
295 commitments,
296 evaluations,
297 }
298 }
299
300 fn len(&self, point: usize, scalar: usize) -> usize {
301 self.commitments * point + self.evaluations * scalar
302 }
303}
304
305impl Add for ProofContribution {
306 type Output = Self;
307
308 fn add(self, rhs: Self) -> Self::Output {
309 Self {
310 commitments: self.commitments + rhs.commitments,
311 evaluations: self.evaluations + rhs.evaluations,
312 }
313 }
314}
315
316impl Mul<usize> for ProofContribution {
317 type Output = Self;
318
319 fn mul(self, instances: usize) -> Self::Output {
320 Self {
321 commitments: self.commitments * instances,
322 evaluations: self.evaluations * instances,
323 }
324 }
325}
326
327#[derive(Debug)]
329pub struct MarginalProofSize<G: PrimeGroup> {
330 instance: ProofContribution,
331 advice: ProofContribution,
332 lookups: ProofContribution,
333 equality: ProofContribution,
334 _marker: PhantomData<G>,
335}
336
337impl<G: PrimeGroup> From<MarginalProofSize<G>> for usize {
338 fn from(proof: MarginalProofSize<G>) -> Self {
339 let point = G::Repr::default().as_ref().len();
340 let scalar = <G::Scalar as PrimeField>::Repr::default().as_ref().len();
341
342 proof.instance.len(point, scalar)
343 + proof.advice.len(point, scalar)
344 + proof.lookups.len(point, scalar)
345 + proof.equality.len(point, scalar)
346 }
347}
348
349#[derive(Debug)]
351pub struct ProofSize<G: PrimeGroup> {
352 instance: ProofContribution,
353 advice: ProofContribution,
354 fixed: ProofContribution,
355 lookups: ProofContribution,
356 equality: ProofContribution,
357 vanishing: ProofContribution,
358 multiopen: ProofContribution,
359 polycomm: ProofContribution,
360 _marker: PhantomData<G>,
361}
362
363impl<G: PrimeGroup> From<ProofSize<G>> for usize {
364 fn from(proof: ProofSize<G>) -> Self {
365 let point = G::Repr::default().as_ref().len();
366 let scalar = <G::Scalar as PrimeField>::Repr::default().as_ref().len();
367
368 proof.instance.len(point, scalar)
369 + proof.advice.len(point, scalar)
370 + proof.fixed.len(point, scalar)
371 + proof.lookups.len(point, scalar)
372 + proof.equality.len(point, scalar)
373 + proof.vanishing.len(point, scalar)
374 + proof.multiopen.len(point, scalar)
375 + proof.polycomm.len(point, scalar)
376 }
377}