halo2_axiom/plonk/
evaluation.rs

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
17/// Return the index in the polynomial of size `isize` after rotation `rot`.
18fn 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/// Value used in a calculation
23#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd)]
24pub enum ValueSource {
25    /// This is a constant value
26    Constant(usize),
27    /// This is an intermediate value
28    Intermediate(usize),
29    /// This is a fixed column
30    Fixed(usize, usize),
31    /// This is an advice (witness) column
32    Advice(usize, usize),
33    /// This is an instance (external) column
34    Instance(usize, usize),
35    /// This is a challenge
36    Challenge(usize),
37    /// beta
38    Beta(),
39    /// gamma
40    Gamma(),
41    /// theta
42    Theta(),
43    /// y
44    Y(),
45    /// Previous value
46    PreviousValue(),
47}
48
49impl Default for ValueSource {
50    fn default() -> Self {
51        ValueSource::Constant(0)
52    }
53}
54
55impl ValueSource {
56    /// Get the value for this source
57    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/// Calculation
95#[derive(Clone, Debug, PartialEq, Eq)]
96pub enum Calculation {
97    /// This is an addition
98    Add(ValueSource, ValueSource),
99    /// This is a subtraction
100    Sub(ValueSource, ValueSource),
101    /// This is a product
102    Mul(ValueSource, ValueSource),
103    /// This is a square
104    Square(ValueSource),
105    /// This is a double
106    Double(ValueSource),
107    /// This is a negation
108    Negate(ValueSource),
109    /// This is Horner's rule: `val = a; val = val * c + b[]`
110    Horner(ValueSource, Vec<ValueSource>, ValueSource),
111    /// This is a simple assignment
112    Store(ValueSource),
113}
114
115impl Calculation {
116    /// Get the resulting value of this calculation
117    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/// Evaluator
169#[derive(Clone, Default, Debug)]
170pub struct Evaluator<C: CurveAffine> {
171    ///  Custom gates evalution
172    pub custom_gates: GraphEvaluator<C>,
173    ///  Lookups evalution
174    pub lookups: Vec<GraphEvaluator<C>>,
175}
176
177/// GraphEvaluator
178#[derive(Clone, Debug)]
179pub struct GraphEvaluator<C: CurveAffine> {
180    /// Constants
181    pub constants: Vec<C::ScalarExt>,
182    /// Rotations
183    pub rotations: Vec<i32>,
184    /// Calculations
185    pub calculations: Vec<CalculationInfo>,
186    /// Number of intermediates
187    pub num_intermediates: usize,
188}
189
190/// EvaluationData
191#[derive(Default, Debug)]
192pub struct EvaluationData<C: CurveAffine> {
193    /// Intermediates
194    pub intermediates: Vec<C::ScalarExt>,
195    /// Rotations
196    pub rotations: Vec<usize>,
197}
198
199/// CaluclationInfo
200#[derive(Clone, Debug)]
201pub struct CalculationInfo {
202    /// Calculation
203    pub calculation: Calculation,
204    /// Target
205    pub target: usize,
206}
207
208impl<C: CurveAffine> Evaluator<C> {
209    /// Creates a new evaluation structure
210    pub fn new(cs: &ConstraintSystem<C::ScalarExt>) -> Self {
211        let mut ev = Evaluator::default();
212
213        // Custom gates
214        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        // Lookups
229        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            // Input coset
245            let compressed_input_coset = evaluate_lc(&lookup.input_expressions);
246            // table coset
247            let compressed_table_coset = evaluate_lc(&lookup.table_expressions);
248            // z(\omega X) (a'(X) + \beta) (s'(X) + \gamma)
249            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    /// Evaluate h poly
266    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        // Calculate the quotient polynomial for each part
290        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                // Calculate the advice and instance cosets
311                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                // Core expression evaluations
343                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                    // Custom gates
353                    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                    // Permutations
386                    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                        // Permutation constraints
418                        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                                // Enforce only for the first set.
428                                // l_0(X) * (1 - z_0(X)) = 0
429                                *value = *value * y
430                                    + ((one - first_set_permutation_product_coset[idx]) * l0[idx]);
431                                // Enforce only for the last set.
432                                // l_last(X) * (z_l(X)^2 - z_l(X)) = 0
433                                *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                                // Except for the first set, enforce.
439                                // l_0(X) * (z_i(X) - z_{i-1}(\omega^(last) X)) = 0
440                                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                                // And for all the sets we enforce:
451                                // (1 - (l_last(X) + l_blind(X))) * (
452                                //   z_i(\omega X) \prod_j (p(X) + \beta s_j(X) + \gamma)
453                                // - z_i(X) \prod_j (p(X) + \delta^j \beta X + \gamma)
454                                // )
455                                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 *= &omega;
493                            }
494                        });
495                    }
496                    #[cfg(feature = "profile")]
497                    end_timer!(timer);
498
499                    #[cfg(feature = "profile")]
500                    let timer = start_timer!(|| "Lookups");
501                    // Lookups
502                    for (n, lookup) in lookups.iter().enumerate() {
503                        // Polynomials required for this lookup.
504                        // Calculated here so these only have to be kept in memory for the short time
505                        // they are actually needed.
506                        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                        // Lookup constraints
520                        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                                // l_0(X) * (1 - z(X)) = 0
548                                *value = *value * y + ((one - product_coset[idx]) * l0[idx]);
549                                // l_last(X) * (z(X)^2 - z(X)) = 0
550                                *value = *value * y
551                                    + ((product_coset[idx] * product_coset[idx]
552                                        - product_coset[idx])
553                                        * l_last[idx]);
554                                // (1 - (l_last(X) + l_blind(X))) * (
555                                //   z(\omega X) (a'(X) + \beta) (s'(X) + \gamma)
556                                //   - z(X) (\theta^{m-1} a_0(X) + ... + a_{m-1}(X) + \beta)
557                                //          (\theta^{m-1} s_0(X) + ... + s_{m-1}(X) + \gamma)
558                                // ) = 0
559                                *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                                // Check that the first values in the permuted input expression and permuted
566                                // fixed expression are the same.
567                                // l_0(X) * (a'(X) - s'(X)) = 0
568                                *value = *value * y + (a_minus_s * l0[idx]);
569                                // Check that each value in the permuted lookup input expression is either
570                                // equal to the value above it, or the value at the same index in the
571                                // permuted table expression.
572                                // (1 - (l_last + l_blind)) * (a′(X) − s′(X))⋅(a′(X) − a′(\omega^{-1} X)) = 0
573                                *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            // Fixed positions to allow easy access
597            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    /// Adds a rotation
611    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    /// Adds a constant
623    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    /// Adds a calculation.
635    /// Currently does the simplest thing possible: just stores the
636    /// resulting value so the result can be reused  when that calculation
637    /// is done multiple times.
638    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    /// Generates an optimized evaluation for the expression
658    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                // Undo subtraction stored as a + (-b) in expressions
698                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    /// Creates a new evaluation structure
761    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        // All rotation index values
785        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        // All calculations, with cached intermediate results
790        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        // Return the result of the last calculation (if any)
808        if let Some(calc) = self.calculations.last() {
809            data.intermediates[calc.target]
810        } else {
811            C::ScalarExt::ZERO
812        }
813    }
814}
815
816/// Simple evaluation of an expression
817pub 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}