1#![allow(clippy::too_many_arguments)]
2
3use crate::multicore;
4use crate::plonk::{lookup, permutation, Any, ProvingKey};
5use crate::poly::Basis;
6use crate::{
7 arithmetic::{parallelize, CurveAffine},
8 poly::{Coeff, ExtendedLagrangeCoeff, LagrangeCoeff, Polynomial, Rotation},
9};
10#[cfg(feature = "profile")]
11use ark_std::{end_timer, start_timer};
12use ff::{Field, PrimeField, WithSmallOrderMulGroup};
13use multicore::{IntoParallelIterator, ParallelIterator};
14
15use super::{ConstraintSystem, Expression};
16
17fn get_rotation_idx(idx: usize, rot: i32, rot_scale: i32, isize: i32) -> usize {
19 (((idx as i32) + (rot * rot_scale)).rem_euclid(isize)) as usize
20}
21
22#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd)]
24pub enum ValueSource {
25 Constant(usize),
27 Intermediate(usize),
29 Fixed(usize, usize),
31 Advice(usize, usize),
33 Instance(usize, usize),
35 Challenge(usize),
37 Beta(),
39 Gamma(),
41 Theta(),
43 Y(),
45 PreviousValue(),
47}
48
49impl Default for ValueSource {
50 fn default() -> Self {
51 ValueSource::Constant(0)
52 }
53}
54
55impl ValueSource {
56 pub fn get<F: Field, B: Basis>(
58 &self,
59 rotations: &[usize],
60 constants: &[F],
61 intermediates: &[F],
62 fixed_values: &[Polynomial<F, B>],
63 advice_values: &[Polynomial<F, B>],
64 instance_values: &[Polynomial<F, B>],
65 challenges: &[F],
66 beta: &F,
67 gamma: &F,
68 theta: &F,
69 y: &F,
70 previous_value: &F,
71 ) -> F {
72 match self {
73 ValueSource::Constant(idx) => constants[*idx],
74 ValueSource::Intermediate(idx) => intermediates[*idx],
75 ValueSource::Fixed(column_index, rotation) => {
76 fixed_values[*column_index][rotations[*rotation]]
77 }
78 ValueSource::Advice(column_index, rotation) => {
79 advice_values[*column_index][rotations[*rotation]]
80 }
81 ValueSource::Instance(column_index, rotation) => {
82 instance_values[*column_index][rotations[*rotation]]
83 }
84 ValueSource::Challenge(index) => challenges[*index],
85 ValueSource::Beta() => *beta,
86 ValueSource::Gamma() => *gamma,
87 ValueSource::Theta() => *theta,
88 ValueSource::Y() => *y,
89 ValueSource::PreviousValue() => *previous_value,
90 }
91 }
92}
93
94#[derive(Clone, Debug, PartialEq, Eq)]
96pub enum Calculation {
97 Add(ValueSource, ValueSource),
99 Sub(ValueSource, ValueSource),
101 Mul(ValueSource, ValueSource),
103 Square(ValueSource),
105 Double(ValueSource),
107 Negate(ValueSource),
109 Horner(ValueSource, Vec<ValueSource>, ValueSource),
111 Store(ValueSource),
113}
114
115impl Calculation {
116 pub fn evaluate<F: Field, B: Basis>(
118 &self,
119 rotations: &[usize],
120 constants: &[F],
121 intermediates: &[F],
122 fixed_values: &[Polynomial<F, B>],
123 advice_values: &[Polynomial<F, B>],
124 instance_values: &[Polynomial<F, B>],
125 challenges: &[F],
126 beta: &F,
127 gamma: &F,
128 theta: &F,
129 y: &F,
130 previous_value: &F,
131 ) -> F {
132 let get_value = |value: &ValueSource| {
133 value.get(
134 rotations,
135 constants,
136 intermediates,
137 fixed_values,
138 advice_values,
139 instance_values,
140 challenges,
141 beta,
142 gamma,
143 theta,
144 y,
145 previous_value,
146 )
147 };
148 match self {
149 Calculation::Add(a, b) => get_value(a) + get_value(b),
150 Calculation::Sub(a, b) => get_value(a) - get_value(b),
151 Calculation::Mul(a, b) => get_value(a) * get_value(b),
152 Calculation::Square(v) => get_value(v).square(),
153 Calculation::Double(v) => get_value(v).double(),
154 Calculation::Negate(v) => -get_value(v),
155 Calculation::Horner(start_value, parts, factor) => {
156 let factor = get_value(factor);
157 let mut value = get_value(start_value);
158 for part in parts.iter() {
159 value = value * factor + get_value(part);
160 }
161 value
162 }
163 Calculation::Store(v) => get_value(v),
164 }
165 }
166}
167
168#[derive(Clone, Default, Debug)]
170pub struct Evaluator<C: CurveAffine> {
171 pub custom_gates: GraphEvaluator<C>,
173 pub lookups: Vec<GraphEvaluator<C>>,
175}
176
177#[derive(Clone, Debug)]
179pub struct GraphEvaluator<C: CurveAffine> {
180 pub constants: Vec<C::ScalarExt>,
182 pub rotations: Vec<i32>,
184 pub calculations: Vec<CalculationInfo>,
186 pub num_intermediates: usize,
188}
189
190#[derive(Default, Debug)]
192pub struct EvaluationData<C: CurveAffine> {
193 pub intermediates: Vec<C::ScalarExt>,
195 pub rotations: Vec<usize>,
197}
198
199#[derive(Clone, Debug)]
201pub struct CalculationInfo {
202 pub calculation: Calculation,
204 pub target: usize,
206}
207
208impl<C: CurveAffine> Evaluator<C> {
209 pub fn new(cs: &ConstraintSystem<C::ScalarExt>) -> Self {
211 let mut ev = Evaluator::default();
212
213 let mut parts = Vec::new();
215 for gate in cs.gates.iter() {
216 parts.extend(
217 gate.polynomials()
218 .iter()
219 .map(|poly| ev.custom_gates.add_expression(poly)),
220 );
221 }
222 ev.custom_gates.add_calculation(Calculation::Horner(
223 ValueSource::PreviousValue(),
224 parts,
225 ValueSource::Y(),
226 ));
227
228 for lookup in cs.lookups.iter() {
230 let mut graph = GraphEvaluator::default();
231
232 let mut evaluate_lc = |expressions: &Vec<Expression<_>>| {
233 let parts = expressions
234 .iter()
235 .map(|expr| graph.add_expression(expr))
236 .collect();
237 graph.add_calculation(Calculation::Horner(
238 ValueSource::Constant(0),
239 parts,
240 ValueSource::Theta(),
241 ))
242 };
243
244 let compressed_input_coset = evaluate_lc(&lookup.input_expressions);
246 let compressed_table_coset = evaluate_lc(&lookup.table_expressions);
248 let right_gamma = graph.add_calculation(Calculation::Add(
250 compressed_table_coset,
251 ValueSource::Gamma(),
252 ));
253 let lc = graph.add_calculation(Calculation::Add(
254 compressed_input_coset,
255 ValueSource::Beta(),
256 ));
257 graph.add_calculation(Calculation::Mul(lc, right_gamma));
258
259 ev.lookups.push(graph);
260 }
261
262 ev
263 }
264
265 pub(in crate::plonk) fn evaluate_h(
267 &self,
268 pk: &ProvingKey<C>,
269 advice_polys: &[&[Polynomial<C::ScalarExt, Coeff>]],
270 instance_polys: &[&[Polynomial<C::ScalarExt, Coeff>]],
271 challenges: &[C::ScalarExt],
272 y: C::ScalarExt,
273 beta: C::ScalarExt,
274 gamma: C::ScalarExt,
275 theta: C::ScalarExt,
276 lookups: &[Vec<lookup::prover::Committed<C>>],
277 permutations: &[permutation::prover::Committed<C>],
278 ) -> Polynomial<C::ScalarExt, ExtendedLagrangeCoeff> {
279 let domain = &pk.vk.domain;
280 let size = 1 << domain.k() as usize;
281 let rot_scale = 1;
282 let extended_omega = domain.get_extended_omega();
283 let omega = domain.get_omega();
284 let isize = size as i32;
285 let one = C::ScalarExt::ONE;
286 let p = &pk.vk.cs.permutation;
287 let num_parts = domain.extended_len() >> domain.k();
288
289 let mut current_extended_omega = one;
291 let value_parts: Vec<Polynomial<C::ScalarExt, LagrangeCoeff>> = (0..num_parts)
292 .map(|_| {
293 #[cfg(feature = "profile")]
294 let fixed_timer = start_timer!(|| "Fixed coeff_to_extended_part");
295 let fixed: Vec<Polynomial<C::ScalarExt, LagrangeCoeff>> = (&pk.fixed_polys)
296 .into_par_iter()
297 .map(|p| domain.coeff_to_extended_part(p.clone(), current_extended_omega))
298 .collect();
299 let fixed = &fixed[..];
300 let l0 = domain.coeff_to_extended_part(pk.l0.clone(), current_extended_omega);
301 let l_last =
302 domain.coeff_to_extended_part(pk.l_last.clone(), current_extended_omega);
303 let l_active_row =
304 domain.coeff_to_extended_part(pk.l_active_row.clone(), current_extended_omega);
305 #[cfg(feature = "profile")]
306 end_timer!(fixed_timer);
307
308 #[cfg(feature = "profile")]
309 let advice_timer = start_timer!(|| "Advice coeff_to_extended_part");
310 let advice: Vec<Vec<Polynomial<C::Scalar, LagrangeCoeff>>> = advice_polys
312 .into_par_iter()
313 .map(|advice_polys| {
314 advice_polys
315 .iter()
316 .map(|poly| {
317 domain.coeff_to_extended_part(poly.clone(), current_extended_omega)
318 })
319 .collect()
320 })
321 .collect();
322 #[cfg(feature = "profile")]
323 end_timer!(advice_timer);
324 #[cfg(feature = "profile")]
325 let instance_timer = start_timer!(|| "Instance coeff_to_extended_part");
326 let instance: Vec<Vec<Polynomial<C::Scalar, LagrangeCoeff>>> = instance_polys
327 .into_par_iter()
328 .map(|instance_polys| {
329 instance_polys
330 .iter()
331 .map(|poly| {
332 domain.coeff_to_extended_part(poly.clone(), current_extended_omega)
333 })
334 .collect()
335 })
336 .collect();
337 #[cfg(feature = "profile")]
338 end_timer!(instance_timer);
339
340 let mut values = domain.empty_lagrange();
341
342 let num_threads = multicore::current_num_threads();
344 for (((advice, instance), lookups), permutation) in advice
345 .iter()
346 .zip(instance.iter())
347 .zip(lookups.iter())
348 .zip(permutations.iter())
349 {
350 #[cfg(feature = "profile")]
351 let timer = start_timer!(|| "Custom gates");
352 multicore::scope(|scope| {
354 let chunk_size = (size + num_threads - 1) / num_threads;
355 for (thread_idx, values) in values.chunks_mut(chunk_size).enumerate() {
356 let start = thread_idx * chunk_size;
357 scope.spawn(move |_| {
358 let mut eval_data = self.custom_gates.instance();
359 for (i, value) in values.iter_mut().enumerate() {
360 let idx = start + i;
361 *value = self.custom_gates.evaluate(
362 &mut eval_data,
363 fixed,
364 advice,
365 instance,
366 challenges,
367 &beta,
368 &gamma,
369 &theta,
370 &y,
371 value,
372 idx,
373 rot_scale,
374 isize,
375 );
376 }
377 });
378 }
379 });
380 #[cfg(feature = "profile")]
381 end_timer!(timer);
382
383 #[cfg(feature = "profile")]
384 let timer = start_timer!(|| "Permutations");
385 let sets = &permutation.sets;
387 if !sets.is_empty() {
388 let blinding_factors = pk.vk.cs.blinding_factors();
389 let last_rotation = Rotation(-((blinding_factors + 1) as i32));
390 let chunk_len = pk.vk.cs.degree() - 2;
391 let delta_start = beta * &C::Scalar::ZETA;
392
393 let permutation_product_cosets: Vec<
394 Polynomial<C::ScalarExt, LagrangeCoeff>,
395 > = sets
396 .into_par_iter()
397 .map(|set| {
398 domain.coeff_to_extended_part(
399 set.permutation_product_poly.clone(),
400 current_extended_omega,
401 )
402 })
403 .collect();
404 let permutation_cosets: Vec<Polynomial<C::ScalarExt, LagrangeCoeff>> =
405 (&pk.permutation.polys)
406 .into_par_iter()
407 .map(|p| {
408 domain.coeff_to_extended_part(p.clone(), current_extended_omega)
409 })
410 .collect();
411
412 let first_set_permutation_product_coset =
413 permutation_product_cosets.first().unwrap();
414 let last_set_permutation_product_coset =
415 permutation_product_cosets.last().unwrap();
416
417 parallelize(&mut values, |values, start| {
419 let mut beta_term =
420 current_extended_omega * omega.pow_vartime([start as u64]);
421 for (i, value) in values.iter_mut().enumerate() {
422 let idx = start + i;
423 let r_next = get_rotation_idx(idx, 1, rot_scale, isize);
424 let r_last =
425 get_rotation_idx(idx, last_rotation.0, rot_scale, isize);
426
427 *value = *value * y
430 + ((one - first_set_permutation_product_coset[idx]) * l0[idx]);
431 *value = *value * y
434 + ((last_set_permutation_product_coset[idx]
435 * last_set_permutation_product_coset[idx]
436 - last_set_permutation_product_coset[idx])
437 * l_last[idx]);
438 for (set_idx, permutation_product_coset) in
441 permutation_product_cosets.iter().enumerate()
442 {
443 if set_idx != 0 {
444 *value = *value * y
445 + ((permutation_product_coset[idx]
446 - permutation_product_cosets[set_idx - 1][r_last])
447 * l0[idx]);
448 }
449 }
450 let mut current_delta = delta_start * beta_term;
456 for (
457 (columns, permutation_product_coset),
458 permutation_coset_chunk,
459 ) in p
460 .columns
461 .chunks(chunk_len)
462 .zip(permutation_product_cosets.iter())
463 .zip(permutation_cosets.chunks(chunk_len))
464 {
465 let mut left = permutation_product_coset[r_next];
466 for (values, permutation) in columns
467 .iter()
468 .map(|&column| match column.column_type() {
469 Any::Advice(_) => &advice[column.index()],
470 Any::Fixed => &fixed[column.index()],
471 Any::Instance => &instance[column.index()],
472 })
473 .zip(permutation_coset_chunk.iter())
474 {
475 left *= values[idx] + beta * permutation[idx] + gamma;
476 }
477
478 let mut right = permutation_product_coset[idx];
479 for values in
480 columns.iter().map(|&column| match column.column_type() {
481 Any::Advice(_) => &advice[column.index()],
482 Any::Fixed => &fixed[column.index()],
483 Any::Instance => &instance[column.index()],
484 })
485 {
486 right *= values[idx] + current_delta + gamma;
487 current_delta *= &C::Scalar::DELTA;
488 }
489
490 *value = *value * y + ((left - right) * l_active_row[idx]);
491 }
492 beta_term *= ω
493 }
494 });
495 }
496 #[cfg(feature = "profile")]
497 end_timer!(timer);
498
499 #[cfg(feature = "profile")]
500 let timer = start_timer!(|| "Lookups");
501 for (n, lookup) in lookups.iter().enumerate() {
503 let product_coset = pk.vk.domain.coeff_to_extended_part(
507 lookup.product_poly.clone(),
508 current_extended_omega,
509 );
510 let permuted_input_coset = pk.vk.domain.coeff_to_extended_part(
511 lookup.permuted_input_poly.clone(),
512 current_extended_omega,
513 );
514 let permuted_table_coset = pk.vk.domain.coeff_to_extended_part(
515 lookup.permuted_table_poly.clone(),
516 current_extended_omega,
517 );
518
519 parallelize(&mut values, |values, start| {
521 let lookup_evaluator = &self.lookups[n];
522 let mut eval_data = lookup_evaluator.instance();
523 for (i, value) in values.iter_mut().enumerate() {
524 let idx = start + i;
525
526 let table_value = lookup_evaluator.evaluate(
527 &mut eval_data,
528 fixed,
529 advice,
530 instance,
531 challenges,
532 &beta,
533 &gamma,
534 &theta,
535 &y,
536 &C::ScalarExt::ZERO,
537 idx,
538 rot_scale,
539 isize,
540 );
541
542 let r_next = get_rotation_idx(idx, 1, rot_scale, isize);
543 let r_prev = get_rotation_idx(idx, -1, rot_scale, isize);
544
545 let a_minus_s =
546 permuted_input_coset[idx] - permuted_table_coset[idx];
547 *value = *value * y + ((one - product_coset[idx]) * l0[idx]);
549 *value = *value * y
551 + ((product_coset[idx] * product_coset[idx]
552 - product_coset[idx])
553 * l_last[idx]);
554 *value = *value * y
560 + ((product_coset[r_next]
561 * (permuted_input_coset[idx] + beta)
562 * (permuted_table_coset[idx] + gamma)
563 - product_coset[idx] * table_value)
564 * l_active_row[idx]);
565 *value = *value * y + (a_minus_s * l0[idx]);
569 *value = *value * y
574 + (a_minus_s
575 * (permuted_input_coset[idx]
576 - permuted_input_coset[r_prev])
577 * l_active_row[idx]);
578 }
579 });
580 }
581 #[cfg(feature = "profile")]
582 end_timer!(timer);
583 }
584 current_extended_omega *= extended_omega;
585 values
586 })
587 .collect();
588
589 domain.extended_from_lagrange_vec(value_parts)
590 }
591}
592
593impl<C: CurveAffine> Default for GraphEvaluator<C> {
594 fn default() -> Self {
595 Self {
596 constants: vec![
598 C::ScalarExt::ZERO,
599 C::ScalarExt::ONE,
600 C::ScalarExt::from(2u64),
601 ],
602 rotations: Vec::new(),
603 calculations: Vec::new(),
604 num_intermediates: 0,
605 }
606 }
607}
608
609impl<C: CurveAffine> GraphEvaluator<C> {
610 fn add_rotation(&mut self, rotation: &Rotation) -> usize {
612 let position = self.rotations.iter().position(|&c| c == rotation.0);
613 match position {
614 Some(pos) => pos,
615 None => {
616 self.rotations.push(rotation.0);
617 self.rotations.len() - 1
618 }
619 }
620 }
621
622 fn add_constant(&mut self, constant: &C::ScalarExt) -> ValueSource {
624 let position = self.constants.iter().position(|&c| c == *constant);
625 ValueSource::Constant(match position {
626 Some(pos) => pos,
627 None => {
628 self.constants.push(*constant);
629 self.constants.len() - 1
630 }
631 })
632 }
633
634 fn add_calculation(&mut self, calculation: Calculation) -> ValueSource {
639 let existing_calculation = self
640 .calculations
641 .iter()
642 .find(|c| c.calculation == calculation);
643 match existing_calculation {
644 Some(existing_calculation) => ValueSource::Intermediate(existing_calculation.target),
645 None => {
646 let target = self.num_intermediates;
647 self.calculations.push(CalculationInfo {
648 calculation,
649 target,
650 });
651 self.num_intermediates += 1;
652 ValueSource::Intermediate(target)
653 }
654 }
655 }
656
657 fn add_expression(&mut self, expr: &Expression<C::ScalarExt>) -> ValueSource {
659 match expr {
660 Expression::Constant(scalar) => self.add_constant(scalar),
661 Expression::Selector(_selector) => unreachable!(),
662 Expression::Fixed(query) => {
663 let rot_idx = self.add_rotation(&query.rotation);
664 self.add_calculation(Calculation::Store(ValueSource::Fixed(
665 query.column_index,
666 rot_idx,
667 )))
668 }
669 Expression::Advice(query) => {
670 let rot_idx = self.add_rotation(&query.rotation);
671 self.add_calculation(Calculation::Store(ValueSource::Advice(
672 query.column_index,
673 rot_idx,
674 )))
675 }
676 Expression::Instance(query) => {
677 let rot_idx = self.add_rotation(&query.rotation);
678 self.add_calculation(Calculation::Store(ValueSource::Instance(
679 query.column_index,
680 rot_idx,
681 )))
682 }
683 Expression::Challenge(challenge) => self.add_calculation(Calculation::Store(
684 ValueSource::Challenge(challenge.index()),
685 )),
686 Expression::Negated(a) => match **a {
687 Expression::Constant(scalar) => self.add_constant(&-scalar),
688 _ => {
689 let result_a = self.add_expression(a);
690 match result_a {
691 ValueSource::Constant(0) => result_a,
692 _ => self.add_calculation(Calculation::Negate(result_a)),
693 }
694 }
695 },
696 Expression::Sum(a, b) => {
697 match &**b {
699 Expression::Negated(b_int) => {
700 let result_a = self.add_expression(a);
701 let result_b = self.add_expression(b_int);
702 if result_a == ValueSource::Constant(0) {
703 self.add_calculation(Calculation::Negate(result_b))
704 } else if result_b == ValueSource::Constant(0) {
705 result_a
706 } else {
707 self.add_calculation(Calculation::Sub(result_a, result_b))
708 }
709 }
710 _ => {
711 let result_a = self.add_expression(a);
712 let result_b = self.add_expression(b);
713 if result_a == ValueSource::Constant(0) {
714 result_b
715 } else if result_b == ValueSource::Constant(0) {
716 result_a
717 } else if result_a <= result_b {
718 self.add_calculation(Calculation::Add(result_a, result_b))
719 } else {
720 self.add_calculation(Calculation::Add(result_b, result_a))
721 }
722 }
723 }
724 }
725 Expression::Product(a, b) => {
726 let result_a = self.add_expression(a);
727 let result_b = self.add_expression(b);
728 if result_a == ValueSource::Constant(0) || result_b == ValueSource::Constant(0) {
729 ValueSource::Constant(0)
730 } else if result_a == ValueSource::Constant(1) {
731 result_b
732 } else if result_b == ValueSource::Constant(1) {
733 result_a
734 } else if result_a == ValueSource::Constant(2) {
735 self.add_calculation(Calculation::Double(result_b))
736 } else if result_b == ValueSource::Constant(2) {
737 self.add_calculation(Calculation::Double(result_a))
738 } else if result_a == result_b {
739 self.add_calculation(Calculation::Square(result_a))
740 } else if result_a <= result_b {
741 self.add_calculation(Calculation::Mul(result_a, result_b))
742 } else {
743 self.add_calculation(Calculation::Mul(result_b, result_a))
744 }
745 }
746 Expression::Scaled(a, f) => {
747 if *f == C::ScalarExt::ZERO {
748 ValueSource::Constant(0)
749 } else if *f == C::ScalarExt::ONE {
750 self.add_expression(a)
751 } else {
752 let cst = self.add_constant(f);
753 let result_a = self.add_expression(a);
754 self.add_calculation(Calculation::Mul(result_a, cst))
755 }
756 }
757 }
758 }
759
760 pub fn instance(&self) -> EvaluationData<C> {
762 EvaluationData {
763 intermediates: vec![C::ScalarExt::ZERO; self.num_intermediates],
764 rotations: vec![0usize; self.rotations.len()],
765 }
766 }
767
768 pub fn evaluate<B: Basis>(
769 &self,
770 data: &mut EvaluationData<C>,
771 fixed: &[Polynomial<C::ScalarExt, B>],
772 advice: &[Polynomial<C::ScalarExt, B>],
773 instance: &[Polynomial<C::ScalarExt, B>],
774 challenges: &[C::ScalarExt],
775 beta: &C::ScalarExt,
776 gamma: &C::ScalarExt,
777 theta: &C::ScalarExt,
778 y: &C::ScalarExt,
779 previous_value: &C::ScalarExt,
780 idx: usize,
781 rot_scale: i32,
782 isize: i32,
783 ) -> C::ScalarExt {
784 for (rot_idx, rot) in self.rotations.iter().enumerate() {
786 data.rotations[rot_idx] = get_rotation_idx(idx, *rot, rot_scale, isize);
787 }
788
789 for calc in self.calculations.iter() {
791 data.intermediates[calc.target] = calc.calculation.evaluate(
792 &data.rotations,
793 &self.constants,
794 &data.intermediates,
795 fixed,
796 advice,
797 instance,
798 challenges,
799 beta,
800 gamma,
801 theta,
802 y,
803 previous_value,
804 );
805 }
806
807 if let Some(calc) = self.calculations.last() {
809 data.intermediates[calc.target]
810 } else {
811 C::ScalarExt::ZERO
812 }
813 }
814}
815
816pub fn evaluate<F: Field, B: Basis>(
818 expression: &Expression<F>,
819 size: usize,
820 rot_scale: i32,
821 fixed: &[Polynomial<F, B>],
822 advice: &[Polynomial<F, B>],
823 instance: &[Polynomial<F, B>],
824 challenges: &[F],
825) -> Vec<F> {
826 let mut values = vec![F::ZERO; size];
827 let isize = size as i32;
828 parallelize(&mut values, |values, start| {
829 for (i, value) in values.iter_mut().enumerate() {
830 let idx = start + i;
831 *value = expression.evaluate(
832 &|scalar| scalar,
833 &|_| panic!("virtual selectors are removed during optimization"),
834 &|query| {
835 fixed[query.column_index]
836 [get_rotation_idx(idx, query.rotation.0, rot_scale, isize)]
837 },
838 &|query| {
839 advice[query.column_index]
840 [get_rotation_idx(idx, query.rotation.0, rot_scale, isize)]
841 },
842 &|query| {
843 instance[query.column_index]
844 [get_rotation_idx(idx, query.rotation.0, rot_scale, isize)]
845 },
846 &|challenge| challenges[challenge.index()],
847 &|a| -a,
848 &|a, b| a + &b,
849 &|a, b| a * b,
850 &|a, scalar| a * scalar,
851 );
852 }
853 });
854 values
855}