kimchi/circuits/polynomials/
generic.rs

1//! This module implements the double generic gate.
2
3//~ The double generic gate contains two generic gates.
4//~
5//~ A generic gate is simply the 2-fan in gate specified in the
6//~ vanilla PLONK protocol that allows us to do operations like:
7//~
8//~ * addition of two registers (into an output register)
9//~ * or multiplication of two registers
10//~ * equality of a register with a constant
11//~
12//~ More generally, the generic gate controls the coefficients $c_i$ in the equation:
13//~
14//~ $$c_0 \cdot l + c_1 \cdot r + c_2 \cdot o + c_3 \cdot (l \times r) + c_4$$
15//~
16//~ The layout of the gate is the following:
17//~
18//~ |  0 |  1 |  2 |  3 |  4 |  5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
19//~ |:--:|:--:|:--:|:--:|:--:|:--:|:-:|:-:|:-:|:-:|:--:|:--:|:--:|:--:|:--:|
20//~ | l1 | r1 | o1 | l2 | r2 | o2 |   |   |   |   |    |    |    |    |    |
21//~
22//~ where l1, r1, and o1 (resp. l2, r2, o2)
23//~ are the left, right, and output registers
24//~ of the first (resp. second) generic gate.
25//~
26//~ The selectors are stored in the coefficient table as:
27//~
28//~ |  0 |  1 |  2 |  3 |  4 |  5 | 6  |  7 |  8 |  9 | 10 | 11 | 12 | 13 | 14 |
29//~ |:--:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|
30//~ | l1 | r1 | o1 | m1 | c1 | l2 | r2 | o2 | m2 | c2 |    |    |    |    |    |
31//~
32//~ with m1 (resp. m2) the mul selector for the first (resp. second) gate,
33//~ and c1 (resp. c2) the constant selector for the first (resp. second) gate.
34//~
35
36use crate::{
37    circuits::{
38        argument::{Argument, ArgumentEnv, ArgumentType},
39        berkeley_columns::BerkeleyChallengeTerm,
40        expr::{constraints::ExprOps, Cache},
41        gate::{CircuitGate, GateType},
42        polynomial::COLUMNS,
43        wires::GateWires,
44    },
45    curve::KimchiCurve,
46    prover_index::ProverIndex,
47};
48use ark_ff::{FftField, PrimeField, Zero};
49use ark_poly::univariate::DensePolynomial;
50use core::{array, marker::PhantomData};
51
52/// Number of constraints produced by the gate.
53pub const CONSTRAINTS: u32 = 2;
54
55/// Number of generic of registers by a single generic gate
56pub const GENERIC_REGISTERS: usize = 3;
57
58/// Number of coefficients used by a single generic gate
59/// Three are used for the registers, one for the multiplication,
60/// and one for the constant.
61pub const GENERIC_COEFFS: usize = GENERIC_REGISTERS + 1 /* mul */ + 1 /* cst */;
62
63/// The double generic gate actually contains two generic gates.
64pub const DOUBLE_GENERIC_COEFFS: usize = GENERIC_COEFFS * 2;
65
66/// Number of generic of registers by a double generic gate.
67pub const DOUBLE_GENERIC_REGISTERS: usize = GENERIC_REGISTERS * 2;
68
69/// Implementation of the `Generic` gate
70#[derive(Default)]
71pub struct Generic<F>(PhantomData<F>);
72
73impl<F> Argument<F> for Generic<F>
74where
75    F: PrimeField,
76{
77    const ARGUMENT_TYPE: ArgumentType = ArgumentType::Gate(GateType::Generic);
78    const CONSTRAINTS: u32 = 2;
79
80    fn constraint_checks<T: ExprOps<F, BerkeleyChallengeTerm>>(
81        env: &ArgumentEnv<F, T>,
82        _cache: &mut Cache,
83    ) -> Vec<T> {
84        // First generic gate
85        let left_coeff1 = env.coeff(0);
86        let right_coeff1 = env.coeff(1);
87        let out_coeff1 = env.coeff(2);
88        let mul_coeff1 = env.coeff(3);
89        let constant1 = env.coeff(4);
90        let left1 = env.witness_curr(0);
91        let right1 = env.witness_curr(1);
92        let out1 = env.witness_curr(2);
93
94        let constraint1 = left_coeff1 * left1.clone()
95            + right_coeff1 * right1.clone()
96            + out_coeff1 * out1
97            + mul_coeff1 * left1 * right1
98            + constant1;
99
100        // Second generic gate
101        let left_coeff2 = env.coeff(5);
102        let right_coeff2 = env.coeff(6);
103        let out_coeff2 = env.coeff(7);
104        let mul_coeff2 = env.coeff(8);
105        let constant2 = env.coeff(9);
106        let left2 = env.witness_curr(3);
107        let right2 = env.witness_curr(4);
108        let out2 = env.witness_curr(5);
109
110        let constraint2 = left_coeff2 * left2.clone()
111            + right_coeff2 * right2.clone()
112            + out_coeff2 * out2
113            + mul_coeff2 * left2 * right2
114            + constant2;
115
116        vec![constraint1, constraint2]
117    }
118}
119
120/// The different type of computation that are possible with a generic gate.
121/// This type is useful to create a generic gate via the [`CircuitGate::create_generic_gadget`] function.
122#[derive(Clone)]
123pub enum GenericGateSpec<F> {
124    /// Add two values.
125    Add {
126        /// Optional coefficient that can be multiplied with the left operand.
127        left_coeff: Option<F>,
128        /// Optional coefficient that can be multiplied with the right operand.
129        right_coeff: Option<F>,
130        /// Optional coefficient that can be multiplied with the output.
131        output_coeff: Option<F>,
132    },
133    /// Multiplication of two values
134    Mul {
135        /// Optional coefficient that can be multiplied with the output.
136        output_coeff: Option<F>,
137        /// Optional coefficient that can be multiplied with the multiplication result.
138        mul_coeff: Option<F>,
139    },
140    /// A constant, the constructor contains the constant itself
141    Const(F),
142    /// A public gate
143    Pub,
144    /// Sum a value to a constant
145    Plus(F),
146}
147
148impl<F: PrimeField> CircuitGate<F> {
149    /// This allows you to create two generic gates that will fit in one row, check [`Self::create_generic_gadget`] for a better to way to create these gates.
150    pub fn create_generic(wires: GateWires, c: [F; GENERIC_COEFFS * 2]) -> Self {
151        CircuitGate::new(GateType::Generic, wires, c.to_vec())
152    }
153
154    /// This allows you to create two generic gates by passing the desired
155    /// `gate1` and `gate2` as two [`GenericGateSpec`].
156    pub fn create_generic_gadget(
157        wires: GateWires,
158        gate1: GenericGateSpec<F>,
159        gate2: Option<GenericGateSpec<F>>,
160    ) -> Self {
161        let mut coeffs = [F::zero(); GENERIC_COEFFS * 2];
162        match gate1 {
163            GenericGateSpec::Add {
164                left_coeff,
165                right_coeff,
166                output_coeff,
167            } => {
168                coeffs[0] = left_coeff.unwrap_or_else(F::one);
169                coeffs[1] = right_coeff.unwrap_or_else(F::one);
170                coeffs[2] = output_coeff.unwrap_or_else(|| -F::one());
171            }
172            GenericGateSpec::Mul {
173                output_coeff,
174                mul_coeff,
175            } => {
176                coeffs[2] = output_coeff.unwrap_or_else(|| -F::one());
177                coeffs[3] = mul_coeff.unwrap_or_else(F::one);
178            }
179            GenericGateSpec::Const(cst) => {
180                coeffs[0] = F::one();
181                coeffs[4] = -cst;
182            }
183            GenericGateSpec::Pub => {
184                coeffs[0] = F::one();
185            }
186            GenericGateSpec::Plus(cst) => {
187                coeffs[0] = F::one();
188                coeffs[1] = F::zero();
189                coeffs[2] = -F::one();
190                coeffs[3] = F::zero();
191                coeffs[4] = cst;
192            }
193        };
194        match gate2 {
195            Some(GenericGateSpec::Add {
196                left_coeff,
197                right_coeff,
198                output_coeff,
199            }) => {
200                coeffs[5] = left_coeff.unwrap_or_else(F::one);
201                coeffs[6] = right_coeff.unwrap_or_else(F::one);
202                coeffs[7] = output_coeff.unwrap_or_else(|| -F::one());
203            }
204            Some(GenericGateSpec::Mul {
205                output_coeff,
206                mul_coeff,
207            }) => {
208                coeffs[7] = output_coeff.unwrap_or_else(|| -F::one());
209                coeffs[8] = mul_coeff.unwrap_or_else(F::one);
210            }
211            Some(GenericGateSpec::Const(cst)) => {
212                coeffs[5] = F::one();
213                coeffs[9] = -cst;
214            }
215            Some(GenericGateSpec::Pub) => {
216                unimplemented!();
217            }
218            Some(GenericGateSpec::Plus(cst)) => {
219                coeffs[5] = F::one();
220                coeffs[6] = F::zero();
221                coeffs[7] = -F::one();
222                coeffs[8] = F::zero();
223                coeffs[9] = cst;
224            }
225            None => (),
226        };
227        Self::create_generic(wires, coeffs)
228    }
229
230    pub fn extend_generic(
231        gates: &mut Vec<Self>,
232        curr_row: &mut usize,
233        wires: GateWires,
234        gate1: GenericGateSpec<F>,
235        gate2: Option<GenericGateSpec<F>>,
236    ) {
237        let gate = Self::create_generic_gadget(wires, gate1, gate2);
238        *curr_row += 1;
239        gates.extend_from_slice(&[gate]);
240    }
241}
242
243// -------------------------------------------------
244
245//~ The constraints:
246//~
247//~ * $w_0 \cdot c_0 + w_1 \cdot c_1 + w_2 \cdot c_2 + w_0 \cdot w_1 \cdot c_3 + c_4$
248//~ * $w_3 \cdot c_5 + w_4 \cdot c_6 + w_5 \cdot c_7 + w_3 w_4 c_8 + c_9$
249//~
250//~ where the $c_i$ are the `coefficients`.
251
252// -------------------------------------------------
253
254pub mod testing {
255    use super::*;
256    use crate::circuits::wires::Wire;
257    use itertools::iterate;
258
259    impl<F: PrimeField> CircuitGate<F> {
260        /// verifies that the generic gate constraints are solved by the witness
261        ///
262        /// # Errors
263        ///
264        /// Will give error if `self.typ` is not `GateType::Generic`.
265        pub fn verify_generic(
266            &self,
267            row: usize,
268            witness: &[Vec<F>; COLUMNS],
269            public: &[F],
270        ) -> Result<(), String> {
271            // assignments
272            let this: [F; COLUMNS] = array::from_fn(|i| witness[i][row]);
273
274            // constants
275            let zero = F::zero();
276
277            // check if it's the correct gate
278            ensure_eq!(self.typ, GateType::Generic, "generic: incorrect gate");
279
280            let check_single = |coeffs_offset, register_offset| {
281                let get = |offset| {
282                    self.coeffs
283                        .get(offset)
284                        .copied()
285                        .unwrap_or_else(|| F::zero())
286                };
287                let l_coeff = get(coeffs_offset);
288                let r_coeff = get(coeffs_offset + 1);
289                let o_coeff = get(coeffs_offset + 2);
290                let m_coeff = get(coeffs_offset + 3);
291                let c_coeff = get(coeffs_offset + 4);
292
293                let sum = l_coeff * this[register_offset]
294                    + r_coeff * this[register_offset + 1]
295                    + o_coeff * this[register_offset + 2];
296                let mul = m_coeff * this[register_offset] * this[register_offset + 1];
297                let public = if coeffs_offset == 0 {
298                    public.get(row).copied().unwrap_or_else(F::zero)
299                } else {
300                    F::zero()
301                };
302                ensure_eq!(
303                    zero,
304                    sum + mul + c_coeff - public,
305                    "generic: incorrect gate"
306                );
307                Ok(())
308            };
309
310            check_single(0, 0)?;
311            check_single(GENERIC_COEFFS, GENERIC_REGISTERS)
312        }
313    }
314
315    impl<const FULL_ROUNDS: usize, F, G, Srs> ProverIndex<FULL_ROUNDS, G, Srs>
316    where
317        F: PrimeField,
318        G: KimchiCurve<FULL_ROUNDS, ScalarField = F>,
319        Srs: poly_commitment::SRS<G>,
320    {
321        /// Function to verify the generic polynomials with a witness.
322        pub fn verify_generic(
323            &mut self,
324            witness: &[DensePolynomial<F>; COLUMNS],
325            public: &DensePolynomial<F>,
326        ) -> bool {
327            let coefficientsm: [_; COLUMNS] = array::from_fn(|i| {
328                self.column_evaluations.get().coefficients8[i]
329                    .clone()
330                    .interpolate()
331            });
332
333            let generic_gate = |coeff_offset, register_offset| {
334                // addition (of left, right, output wires)
335                let mut ff = &coefficientsm[coeff_offset] * &witness[register_offset];
336                ff += &(&coefficientsm[coeff_offset + 1] * &witness[register_offset + 1]);
337                ff += &(&coefficientsm[coeff_offset + 2] * &witness[register_offset + 2]);
338
339                // multiplication
340                ff += &(&(&witness[register_offset] * &witness[register_offset + 1])
341                    * &coefficientsm[coeff_offset + 3]);
342
343                // constant
344                &ff + &coefficientsm[coeff_offset + 4]
345
346                // note: skip alpha power, as we're testing for completeness
347            };
348
349            let mut res = generic_gate(0, 0);
350            res += &generic_gate(GENERIC_COEFFS, GENERIC_REGISTERS);
351
352            // public inputs
353            res += public;
354
355            // selector poly
356            res = &res
357                * &self
358                    .column_evaluations
359                    .get()
360                    .generic_selector4
361                    .interpolate_by_ref();
362            // Interpolation above is inefficient, as is the rest of the function,
363            // would be better just to check the equation on all the rows.
364
365            // verify that it is divisible by Z_H
366            let (_quotient, rest) = res.divide_by_vanishing_poly(self.cs.domain.d1);
367            rest.is_zero()
368        }
369    }
370
371    /// Create a generic circuit
372    ///
373    /// # Panics
374    ///
375    /// Will panic if `gates_row` is None.
376    pub fn create_circuit<F: PrimeField>(start_row: usize, public: usize) -> Vec<CircuitGate<F>> {
377        // create constraint system with a single generic gate
378        let mut gates = vec![];
379
380        // create generic gates
381        let mut gates_row = iterate(start_row, |&i| i + 1);
382
383        // public input
384        for _ in 0..public {
385            let r = gates_row.next().unwrap();
386            gates.push(CircuitGate::create_generic_gadget(
387                Wire::for_row(r),
388                GenericGateSpec::Pub,
389                None,
390            ));
391        }
392
393        // add and mul
394        for _ in 0..10 {
395            let r = gates_row.next().unwrap();
396            let g1 = GenericGateSpec::Add {
397                left_coeff: None,
398                right_coeff: Some(3u32.into()),
399                output_coeff: None,
400            };
401            let g2 = GenericGateSpec::Mul {
402                output_coeff: None,
403                mul_coeff: Some(2u32.into()),
404            };
405            gates.push(CircuitGate::create_generic_gadget(
406                Wire::for_row(r),
407                g1,
408                Some(g2),
409            ));
410        }
411
412        // two consts
413        for _ in 0..10 {
414            let r = gates_row.next().unwrap();
415            let g1 = GenericGateSpec::Const(3u32.into());
416            let g2 = GenericGateSpec::Const(5u32.into());
417            gates.push(CircuitGate::create_generic_gadget(
418                Wire::for_row(r),
419                g1,
420                Some(g2),
421            ));
422        }
423
424        gates
425    }
426
427    /// Fill in a witness created via [`create_circuit`]
428    ///
429    /// # Panics
430    ///
431    /// Will panic if `witness_row` is None.
432    pub fn fill_in_witness<F: FftField>(
433        start_row: usize,
434        witness: &mut [Vec<F>; COLUMNS],
435        public: &[F],
436    ) {
437        // fill witness
438        let mut witness_row = iterate(start_row, |&i| i + 1);
439
440        // public
441        for p in public {
442            let r = witness_row.next().unwrap();
443            witness[0][r] = *p;
444        }
445
446        // add and mul
447        for _ in 0..10 {
448            let r = witness_row.next().unwrap();
449
450            witness[0][r] = 11u32.into();
451            witness[1][r] = 23u32.into();
452            witness[2][r] = (11u32 + 23u32 * 3u32).into();
453
454            witness[3][r] = 11u32.into();
455            witness[4][r] = 23u32.into();
456            witness[5][r] = (11u32 * 23 * 2).into();
457        }
458
459        // const
460        for _ in 0..10 {
461            let r = witness_row.next().unwrap();
462
463            witness[0][r] = 3u32.into();
464
465            witness[3][r] = 5u32.into();
466        }
467    }
468}