1use super::super::{
2 circuit::Expression, ChallengeBeta, ChallengeGamma, ChallengeTheta, ChallengeX, Error,
3 ProvingKey,
4};
5use super::Argument;
6use crate::multicore::{self, IntoParallelIterator};
7#[cfg(feature = "multicore")]
8use crate::multicore::{
9 IndexedParallelIterator, IntoParallelRefIterator, IntoParallelRefMutIterator, ParallelIterator,
10 ParallelSliceMut,
11};
12use crate::plonk::evaluation::evaluate;
13use crate::{
14 arithmetic::{eval_polynomial, parallelize, CurveAffine},
15 poly::{
16 commitment::{Blind, Params},
17 Coeff, EvaluationDomain, LagrangeCoeff, Polynomial, ProverQuery, Rotation,
18 },
19 transcript::{EncodedChallenge, TranscriptWrite},
20};
21#[cfg(feature = "profile")]
22use ark_std::{end_timer, start_timer};
23use ff::WithSmallOrderMulGroup;
24use group::{
25 ff::{BatchInvert, Field},
26 Curve,
27};
28use rand_core::RngCore;
29
30use std::collections::HashMap;
31use std::hash::Hash;
32use std::{
33 collections::BTreeMap,
34 iter,
35 ops::{Mul, MulAssign},
36};
37
38#[derive(Debug)]
39pub(in crate::plonk) struct Permuted<C: CurveAffine> {
40 compressed_input_expression: Polynomial<C::Scalar, LagrangeCoeff>,
41 permuted_input_expression: Polynomial<C::Scalar, LagrangeCoeff>,
42 permuted_input_poly: Polynomial<C::Scalar, Coeff>,
43 permuted_input_blind: Blind<C::Scalar>,
44 compressed_table_expression: Polynomial<C::Scalar, LagrangeCoeff>,
45 permuted_table_expression: Polynomial<C::Scalar, LagrangeCoeff>,
46 permuted_table_poly: Polynomial<C::Scalar, Coeff>,
47 permuted_table_blind: Blind<C::Scalar>,
48}
49
50#[derive(Debug)]
51pub(in crate::plonk) struct Committed<C: CurveAffine> {
52 pub(in crate::plonk) permuted_input_poly: Polynomial<C::Scalar, Coeff>,
53 permuted_input_blind: Blind<C::Scalar>,
54 pub(in crate::plonk) permuted_table_poly: Polynomial<C::Scalar, Coeff>,
55 permuted_table_blind: Blind<C::Scalar>,
56 pub(in crate::plonk) product_poly: Polynomial<C::Scalar, Coeff>,
57 product_blind: Blind<C::Scalar>,
58}
59
60pub(in crate::plonk) struct Evaluated<C: CurveAffine> {
61 constructed: Committed<C>,
62}
63
64impl<F: WithSmallOrderMulGroup<3>> Argument<F> {
65 #[allow(clippy::too_many_arguments)]
75 pub(in crate::plonk) fn commit_permuted<
76 'a,
77 'params: 'a,
78 C,
79 P: Params<'params, C>,
80 E: EncodedChallenge<C>,
81 R: RngCore,
82 T: TranscriptWrite<C, E>,
83 >(
84 &self,
85 pk: &ProvingKey<C>,
86 params: &P,
87 domain: &EvaluationDomain<C::Scalar>,
88 theta: ChallengeTheta<C>,
89 advice_values: &'a [Polynomial<C::Scalar, LagrangeCoeff>],
90 fixed_values: &'a [Polynomial<C::Scalar, LagrangeCoeff>],
91 instance_values: &'a [Polynomial<C::Scalar, LagrangeCoeff>],
92 challenges: &'a [C::Scalar],
93 mut rng: R,
94 transcript: &mut T,
95 ) -> Result<Permuted<C>, Error>
96 where
97 F: Hash,
98 C: CurveAffine<ScalarExt = F>,
99 C::Curve: Mul<F, Output = C::Curve> + MulAssign<F>,
100 {
101 let compress_expressions = |expressions: &[Expression<C::Scalar>]| {
103 let compressed_expression = expressions
104 .iter()
105 .map(|expression| {
106 pk.vk.domain.lagrange_from_vec(evaluate(
107 expression,
108 params.n() as usize,
109 1,
110 fixed_values,
111 advice_values,
112 instance_values,
113 challenges,
114 ))
115 })
116 .fold(domain.empty_lagrange(), |acc, expression| {
117 acc * *theta + &expression
118 });
119 compressed_expression
120 };
121
122 let compressed_input_expression = compress_expressions(&self.input_expressions);
124
125 let compressed_table_expression = compress_expressions(&self.table_expressions);
127
128 let (permuted_input_expression, permuted_table_expression) = permute_expression_pair(
130 pk,
131 params,
132 domain,
133 &mut rng,
134 &compressed_input_expression,
135 &compressed_table_expression,
136 )?;
137
138 let mut commit_values = |values: &Polynomial<C::Scalar, LagrangeCoeff>| {
140 let poly = pk.vk.domain.lagrange_to_coeff(values.clone());
141 let blind = Blind(C::Scalar::random(&mut rng));
142 let commitment = params.commit_lagrange(values, blind).to_affine();
143 (poly, blind, commitment)
144 };
145
146 let (permuted_input_poly, permuted_input_blind, permuted_input_commitment) =
148 commit_values(&permuted_input_expression);
149
150 let (permuted_table_poly, permuted_table_blind, permuted_table_commitment) =
152 commit_values(&permuted_table_expression);
153
154 transcript.write_point(permuted_input_commitment)?;
156
157 transcript.write_point(permuted_table_commitment)?;
159
160 Ok(Permuted {
161 compressed_input_expression,
162 permuted_input_expression,
163 permuted_input_poly,
164 permuted_input_blind,
165 compressed_table_expression,
166 permuted_table_expression,
167 permuted_table_poly,
168 permuted_table_blind,
169 })
170 }
171}
172
173impl<C: CurveAffine> Permuted<C> {
174 pub(in crate::plonk) fn commit_product<
180 'params,
181 P: Params<'params, C>,
182 E: EncodedChallenge<C>,
183 R: RngCore,
184 T: TranscriptWrite<C, E>,
185 >(
186 self,
187 pk: &ProvingKey<C>,
188 params: &P,
189 beta: ChallengeBeta<C>,
190 gamma: ChallengeGamma<C>,
191 mut rng: R,
192 transcript: &mut T,
193 ) -> Result<Committed<C>, Error> {
194 let blinding_factors = pk.vk.cs.blinding_factors();
195 let mut lookup_product = vec![C::Scalar::ZERO; params.n() as usize];
207 parallelize(&mut lookup_product, |lookup_product, start| {
209 for ((lookup_product, permuted_input_value), permuted_table_value) in lookup_product
210 .iter_mut()
211 .zip(self.permuted_input_expression[start..].iter())
212 .zip(self.permuted_table_expression[start..].iter())
213 {
214 *lookup_product = (*beta + permuted_input_value) * &(*gamma + permuted_table_value);
215 }
216 });
217
218 lookup_product.iter_mut().batch_invert();
221
222 parallelize(&mut lookup_product, |product, start| {
226 for (i, product) in product.iter_mut().enumerate() {
227 let i = i + start;
228
229 *product *= &(self.compressed_input_expression[i] + &*beta);
230 *product *= &(self.compressed_table_expression[i] + &*gamma);
231 }
232 });
233
234 let z = iter::once(C::Scalar::ONE)
250 .chain(lookup_product)
251 .scan(C::Scalar::ONE, |state, cur| {
252 *state *= &cur;
253 Some(*state)
254 })
255 .take(params.n() as usize - blinding_factors)
258 .chain((0..blinding_factors).map(|_| C::Scalar::random(&mut rng)))
260 .collect::<Vec<_>>();
261 assert_eq!(z.len(), params.n() as usize);
262 let z = pk.vk.domain.lagrange_from_vec(z);
263
264 #[cfg(feature = "sanity-checks")]
265 {
268 let u = (params.n() as usize) - (blinding_factors + 1);
270
271 assert_eq!(z[0], C::Scalar::ONE);
273
274 for i in 0..u {
277 let mut left = z[i + 1];
278 let permuted_input_value = &self.permuted_input_expression[i];
279
280 let permuted_table_value = &self.permuted_table_expression[i];
281
282 left *= &(*beta + permuted_input_value);
283 left *= &(*gamma + permuted_table_value);
284
285 let mut right = z[i];
286 let mut input_term = self.compressed_input_expression[i];
287 let mut table_term = self.compressed_table_expression[i];
288
289 input_term += &(*beta);
290 table_term += &(*gamma);
291 right *= &(input_term * &table_term);
292
293 assert_eq!(left, right);
294 }
295
296 assert_eq!(z[u], C::Scalar::ONE);
300 }
301
302 let product_blind = Blind(C::Scalar::random(rng));
303 let product_commitment = params.commit_lagrange(&z, product_blind).to_affine();
304 let z = pk.vk.domain.lagrange_to_coeff(z);
305
306 transcript.write_point(product_commitment)?;
308
309 Ok(Committed::<C> {
310 permuted_input_poly: self.permuted_input_poly,
311 permuted_input_blind: self.permuted_input_blind,
312 permuted_table_poly: self.permuted_table_poly,
313 permuted_table_blind: self.permuted_table_blind,
314 product_poly: z,
315 product_blind,
316 })
317 }
318}
319
320impl<C: CurveAffine> Committed<C> {
321 pub(in crate::plonk) fn evaluate<E: EncodedChallenge<C>, T: TranscriptWrite<C, E>>(
322 self,
323 pk: &ProvingKey<C>,
324 x: ChallengeX<C>,
325 transcript: &mut T,
326 ) -> Result<Evaluated<C>, Error> {
327 let domain = &pk.vk.domain;
328 let x_inv = domain.rotate_omega(*x, Rotation::prev());
329 let x_next = domain.rotate_omega(*x, Rotation::next());
330
331 let product_eval = eval_polynomial(&self.product_poly, *x);
332 let product_next_eval = eval_polynomial(&self.product_poly, x_next);
333 let permuted_input_eval = eval_polynomial(&self.permuted_input_poly, *x);
334 let permuted_input_inv_eval = eval_polynomial(&self.permuted_input_poly, x_inv);
335 let permuted_table_eval = eval_polynomial(&self.permuted_table_poly, *x);
336
337 for eval in iter::empty()
339 .chain(Some(product_eval))
340 .chain(Some(product_next_eval))
341 .chain(Some(permuted_input_eval))
342 .chain(Some(permuted_input_inv_eval))
343 .chain(Some(permuted_table_eval))
344 {
345 transcript.write_scalar(eval)?;
346 }
347
348 Ok(Evaluated { constructed: self })
349 }
350}
351
352impl<C: CurveAffine> Evaluated<C> {
353 pub(in crate::plonk) fn open<'a>(
354 &'a self,
355 pk: &'a ProvingKey<C>,
356 x: ChallengeX<C>,
357 ) -> impl Iterator<Item = ProverQuery<'a, C>> + Clone {
358 let x_inv = pk.vk.domain.rotate_omega(*x, Rotation::prev());
359 let x_next = pk.vk.domain.rotate_omega(*x, Rotation::next());
360
361 iter::empty()
362 .chain(Some(ProverQuery {
364 point: *x,
365 poly: &self.constructed.product_poly,
366 blind: self.constructed.product_blind,
367 }))
368 .chain(Some(ProverQuery {
370 point: *x,
371 poly: &self.constructed.permuted_input_poly,
372 blind: self.constructed.permuted_input_blind,
373 }))
374 .chain(Some(ProverQuery {
376 point: *x,
377 poly: &self.constructed.permuted_table_poly,
378 blind: self.constructed.permuted_table_blind,
379 }))
380 .chain(Some(ProverQuery {
382 point: x_inv,
383 poly: &self.constructed.permuted_input_poly,
384 blind: self.constructed.permuted_input_blind,
385 }))
386 .chain(Some(ProverQuery {
388 point: x_next,
389 poly: &self.constructed.product_poly,
390 blind: self.constructed.product_blind,
391 }))
392 }
393}
394
395type ExpressionPair<F> = (Polynomial<F, LagrangeCoeff>, Polynomial<F, LagrangeCoeff>);
396
397fn permute_expression_pair<'params, C: CurveAffine, P: Params<'params, C>, R: RngCore>(
404 pk: &ProvingKey<C>,
405 params: &P,
406 domain: &EvaluationDomain<C::Scalar>,
407 mut rng: R,
408 input_expression: &Polynomial<C::Scalar, LagrangeCoeff>,
409 table_expression: &Polynomial<C::Scalar, LagrangeCoeff>,
410) -> Result<ExpressionPair<C::Scalar>, Error>
411where
412 C::Scalar: Hash,
413{
414 let num_threads = multicore::current_num_threads();
415 let usable_rows = params.n() as usize - (pk.vk.cs.blinding_factors() + 1);
428
429 let input_expression = &input_expression[0..usable_rows];
430
431 #[cfg(feature = "profile")]
433 let input_time = start_timer!(|| "permute_par input hashmap (cpu par)");
434 let capacity = usable_rows / num_threads + 1;
436
437 #[cfg(feature = "multicore")]
438 let input_uniques: HashMap<C::Scalar, usize> = input_expression
439 .par_iter()
440 .fold(
441 || HashMap::with_capacity(capacity),
442 |mut acc, coeff| {
443 *acc.entry(*coeff).or_insert(0) += 1;
444 acc
445 },
446 )
447 .reduce_with(|mut m1, m2| {
448 m2.into_iter().for_each(|(k, v)| {
449 *m1.entry(k).or_insert(0) += v;
450 });
451 m1
452 })
453 .unwrap();
454 #[cfg(not(feature = "multicore"))]
455 let input_uniques: HashMap<C::Scalar, usize> =
456 input_expression
457 .iter()
458 .fold(HashMap::with_capacity(capacity), |mut acc, coeff| {
459 *acc.entry(*coeff).or_insert(0) += 1;
460 acc
461 });
462 #[cfg(feature = "profile")]
463 end_timer!(input_time);
464
465 #[cfg(feature = "profile")]
466 let timer = start_timer!(|| "permute_par input unique ranges (cpu par)");
467
468 #[cfg(feature = "multicore")]
469 let input_unique_ranges = input_uniques
470 .par_iter()
471 .fold(
472 || Vec::with_capacity(capacity),
473 |mut input_ranges, (&coeff, &count)| {
474 if input_ranges.is_empty() {
475 input_ranges.push((coeff, 0..count));
476 } else {
477 let prev_end = input_ranges.last().unwrap().1.end;
478 input_ranges.push((coeff, prev_end..prev_end + count));
479 }
480 input_ranges
481 },
482 )
483 .reduce_with(|r1, mut r2| {
484 let r1_end = r1.last().unwrap().1.end;
485 r2.par_iter_mut().for_each(|r2| {
486 r2.1.start += r1_end;
487 r2.1.end += r1_end;
488 });
489 [r1, r2].concat()
490 })
491 .unwrap();
492 #[cfg(not(feature = "multicore"))]
493 let input_unique_ranges = input_uniques.iter().fold(
494 Vec::with_capacity(capacity),
495 |mut input_ranges, (&coeff, &count)| {
496 if input_ranges.is_empty() {
497 input_ranges.push((coeff, 0..count));
498 } else {
499 let prev_end = input_ranges.last().unwrap().1.end;
500 input_ranges.push((coeff, prev_end..prev_end + count));
501 }
502 input_ranges
503 },
504 );
505 #[cfg(feature = "profile")]
506 end_timer!(timer);
507
508 #[cfg(feature = "profile")]
509 let to_vec_time = start_timer!(|| "to_vec");
510 let mut sorted_table_coeffs = table_expression[0..usable_rows].to_vec();
511 #[cfg(feature = "profile")]
512 end_timer!(to_vec_time);
513 #[cfg(feature = "profile")]
514 let sort_table_time = start_timer!(|| "permute_par sort table");
515 #[cfg(feature = "multicore")]
516 sorted_table_coeffs.par_sort();
517 #[cfg(not(feature = "multicore"))]
518 sorted_table_coeffs.sort();
519 #[cfg(feature = "profile")]
520 end_timer!(sort_table_time);
521
522 #[cfg(feature = "profile")]
523 let timer = start_timer!(|| "leftover table coeffs (cpu par)");
524
525 let leftover_table_coeffs: Vec<C::Scalar> = sorted_table_coeffs
526 .as_slice()
527 .into_par_iter()
528 .enumerate()
529 .filter_map(|(i, coeff)| {
530 ((i != 0 && coeff == &sorted_table_coeffs[i - 1]) || !input_uniques.contains_key(coeff))
531 .then_some(*coeff)
532 })
533 .collect();
534 #[cfg(feature = "profile")]
535 end_timer!(timer);
536
537 let (mut permuted_input_expression, mut permuted_table_coeffs): (Vec<_>, Vec<_>) =
538 input_unique_ranges
539 .into_par_iter()
540 .enumerate()
541 .flat_map(|(i, (coeff, range))| {
542 let leftover_range_start = range.start - i;
544 let leftover_range_end = range.end - i - 1;
545 [(coeff, coeff)].into_par_iter().chain(
546 leftover_table_coeffs[leftover_range_start..leftover_range_end]
547 .into_par_iter()
548 .map(move |leftover_table_coeff| (coeff, *leftover_table_coeff)),
549 )
550 })
551 .unzip();
552 permuted_input_expression.resize_with(params.n() as usize, || C::Scalar::random(&mut rng));
553 permuted_table_coeffs.resize_with(params.n() as usize, || C::Scalar::random(&mut rng));
554
555 Ok((
556 domain.lagrange_from_vec(permuted_input_expression),
557 domain.lagrange_from_vec(permuted_table_coeffs),
558 ))
559}
560
561#[allow(dead_code)]
568fn permute_expression_pair_seq<'params, C: CurveAffine, P: Params<'params, C>, R: RngCore>(
569 pk: &ProvingKey<C>,
570 params: &P,
571 domain: &EvaluationDomain<C::Scalar>,
572 mut rng: R,
573 input_expression: &Polynomial<C::Scalar, LagrangeCoeff>,
574 table_expression: &Polynomial<C::Scalar, LagrangeCoeff>,
575) -> Result<ExpressionPair<C::Scalar>, Error> {
576 let blinding_factors = pk.vk.cs.blinding_factors();
577 let usable_rows = params.n() as usize - (blinding_factors + 1);
578
579 let mut permuted_input_expression: Vec<C::Scalar> = input_expression.to_vec();
580 permuted_input_expression.truncate(usable_rows);
581
582 #[cfg(feature = "multicore")]
584 permuted_input_expression.par_sort();
585 #[cfg(not(feature = "multicore"))]
586 permuted_input_expression.sort();
587
588 let mut leftover_table_map: BTreeMap<C::Scalar, u32> = table_expression
590 .iter()
591 .take(usable_rows)
592 .fold(BTreeMap::new(), |mut acc, coeff| {
593 *acc.entry(*coeff).or_insert(0) += 1;
594 acc
595 });
596 let mut permuted_table_coeffs = vec![C::Scalar::ZERO; usable_rows];
597
598 let mut repeated_input_rows = permuted_input_expression
599 .iter()
600 .zip(permuted_table_coeffs.iter_mut())
601 .enumerate()
602 .filter_map(|(row, (input_value, table_value))| {
603 if row == 0 || *input_value != permuted_input_expression[row - 1] {
605 *table_value = *input_value;
606 if let Some(count) = leftover_table_map.get_mut(input_value) {
608 assert!(*count > 0);
609 *count -= 1;
610 None
611 } else {
612 panic!("{:?}", Error::ConstraintSystemFailure);
614 }
616 } else {
618 Some(row)
619 }
620 })
621 .collect::<Vec<_>>();
622
623 for (coeff, count) in leftover_table_map.iter() {
625 for _ in 0..*count {
626 permuted_table_coeffs[repeated_input_rows.pop().unwrap()] = *coeff;
627 }
628 }
629 assert!(repeated_input_rows.is_empty());
630
631 permuted_input_expression
632 .extend((0..(blinding_factors + 1)).map(|_| C::Scalar::random(&mut rng)));
633 permuted_table_coeffs.extend((0..(blinding_factors + 1)).map(|_| C::Scalar::random(&mut rng)));
634 assert_eq!(permuted_input_expression.len(), params.n() as usize);
635 assert_eq!(permuted_table_coeffs.len(), params.n() as usize);
636
637 #[cfg(feature = "sanity-checks")]
638 {
639 let mut last = None;
640 for (a, b) in permuted_input_expression
641 .iter()
642 .zip(permuted_table_coeffs.iter())
643 .take(usable_rows)
644 {
645 if *a != *b {
646 assert_eq!(*a, last.unwrap());
647 }
648 last = Some(*a);
649 }
650 }
651
652 Ok((
653 domain.lagrange_from_vec(permuted_input_expression),
654 domain.lagrange_from_vec(permuted_table_coeffs),
655 ))
656}