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