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                coeffs[5] = F::one();
218                unimplemented!();
219            }
220            Some(GenericGateSpec::Plus(cst)) => {
221                coeffs[5] = F::one();
222                coeffs[6] = F::zero();
223                coeffs[7] = -F::one();
224                coeffs[8] = F::zero();
225                coeffs[9] = cst;
226            }
227            None => (),
228        };
229        Self::create_generic(wires, coeffs)
230    }
231
232    pub fn extend_generic(
233        gates: &mut Vec<Self>,
234        curr_row: &mut usize,
235        wires: GateWires,
236        gate1: GenericGateSpec<F>,
237        gate2: Option<GenericGateSpec<F>>,
238    ) {
239        let gate = Self::create_generic_gadget(wires, gate1, gate2);
240        *curr_row += 1;
241        gates.extend_from_slice(&[gate]);
242    }
243}
244
245// -------------------------------------------------
246
247//~ The constraints:
248//~
249//~ * $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$
250//~ * $w_3 \cdot c_5 + w_4 \cdot c_6 + w_5 \cdot c_7 + w_3 w_4 c_8 + c_9$
251//~
252//~ where the $c_i$ are the `coefficients`.
253
254// -------------------------------------------------
255
256pub mod testing {
257    use super::*;
258    use crate::circuits::wires::Wire;
259    use itertools::iterate;
260
261    impl<F: PrimeField> CircuitGate<F> {
262        /// verifies that the generic gate constraints are solved by the witness
263        ///
264        /// # Errors
265        ///
266        /// Will give error if `self.typ` is not `GateType::Generic`.
267        pub fn verify_generic(
268            &self,
269            row: usize,
270            witness: &[Vec<F>; COLUMNS],
271            public: &[F],
272        ) -> Result<(), String> {
273            // assignments
274            let this: [F; COLUMNS] = array::from_fn(|i| witness[i][row]);
275
276            // constants
277            let zero = F::zero();
278
279            // check if it's the correct gate
280            ensure_eq!(self.typ, GateType::Generic, "generic: incorrect gate");
281
282            let check_single = |coeffs_offset, register_offset| {
283                let get = |offset| {
284                    self.coeffs
285                        .get(offset)
286                        .copied()
287                        .unwrap_or_else(|| F::zero())
288                };
289                let l_coeff = get(coeffs_offset);
290                let r_coeff = get(coeffs_offset + 1);
291                let o_coeff = get(coeffs_offset + 2);
292                let m_coeff = get(coeffs_offset + 3);
293                let c_coeff = get(coeffs_offset + 4);
294
295                let sum = l_coeff * this[register_offset]
296                    + r_coeff * this[register_offset + 1]
297                    + o_coeff * this[register_offset + 2];
298                let mul = m_coeff * this[register_offset] * this[register_offset + 1];
299                let public = if coeffs_offset == 0 {
300                    public.get(row).copied().unwrap_or_else(F::zero)
301                } else {
302                    F::zero()
303                };
304                ensure_eq!(
305                    zero,
306                    sum + mul + c_coeff - public,
307                    "generic: incorrect gate"
308                );
309                Ok(())
310            };
311
312            check_single(0, 0)?;
313            check_single(GENERIC_COEFFS, GENERIC_REGISTERS)
314        }
315    }
316
317    impl<F: PrimeField, G: KimchiCurve<ScalarField = F>, OpeningProof: OpenProof<G>>
318        ProverIndex<G, OpeningProof>
319    {
320        /// Function to verify the generic polynomials with a witness.
321        pub fn verify_generic(
322            &mut self,
323            witness: &[DensePolynomial<F>; COLUMNS],
324            public: &DensePolynomial<F>,
325        ) -> bool {
326            let coefficientsm: [_; COLUMNS] = array::from_fn(|i| {
327                self.column_evaluations.get().coefficients8[i]
328                    .clone()
329                    .interpolate()
330            });
331
332            let generic_gate = |coeff_offset, register_offset| {
333                // addition (of left, right, output wires)
334                let mut ff = &coefficientsm[coeff_offset] * &witness[register_offset];
335                ff += &(&coefficientsm[coeff_offset + 1] * &witness[register_offset + 1]);
336                ff += &(&coefficientsm[coeff_offset + 2] * &witness[register_offset + 2]);
337
338                // multiplication
339                ff += &(&(&witness[register_offset] * &witness[register_offset + 1])
340                    * &coefficientsm[coeff_offset + 3]);
341
342                // constant
343                &ff + &coefficientsm[coeff_offset + 4]
344
345                // note: skip alpha power, as we're testing for completeness
346            };
347
348            let mut res = generic_gate(0, 0);
349            res += &generic_gate(GENERIC_COEFFS, GENERIC_REGISTERS);
350
351            // public inputs
352            res += public;
353
354            // selector poly
355            res = &res
356                * &self
357                    .column_evaluations
358                    .get()
359                    .generic_selector4
360                    .interpolate_by_ref();
361            // Interpolation above is inefficient, as is the rest of the function,
362            // would be better just to check the equation on all the rows.
363
364            // verify that it is divisible by Z_H
365            match res.divide_by_vanishing_poly(self.cs.domain.d1) {
366                Some((_quotient, rest)) => rest.is_zero(),
367                None => false,
368            }
369        }
370    }
371
372    /// Create a generic circuit
373    ///
374    /// # Panics
375    ///
376    /// Will panic if `gates_row` is None.
377    pub fn create_circuit<F: PrimeField>(start_row: usize, public: usize) -> Vec<CircuitGate<F>> {
378        // create constraint system with a single generic gate
379        let mut gates = vec![];
380
381        // create generic gates
382        let mut gates_row = iterate(start_row, |&i| i + 1);
383
384        // public input
385        for _ in 0..public {
386            let r = gates_row.next().unwrap();
387            gates.push(CircuitGate::create_generic_gadget(
388                Wire::for_row(r),
389                GenericGateSpec::Pub,
390                None,
391            ));
392        }
393
394        // add and mul
395        for _ in 0..10 {
396            let r = gates_row.next().unwrap();
397            let g1 = GenericGateSpec::Add {
398                left_coeff: None,
399                right_coeff: Some(3u32.into()),
400                output_coeff: None,
401            };
402            let g2 = GenericGateSpec::Mul {
403                output_coeff: None,
404                mul_coeff: Some(2u32.into()),
405            };
406            gates.push(CircuitGate::create_generic_gadget(
407                Wire::for_row(r),
408                g1,
409                Some(g2),
410            ));
411        }
412
413        // two consts
414        for _ in 0..10 {
415            let r = gates_row.next().unwrap();
416            let g1 = GenericGateSpec::Const(3u32.into());
417            let g2 = GenericGateSpec::Const(5u32.into());
418            gates.push(CircuitGate::create_generic_gadget(
419                Wire::for_row(r),
420                g1,
421                Some(g2),
422            ));
423        }
424
425        gates
426    }
427
428    /// Fill in a witness created via [`create_circuit`]
429    ///
430    /// # Panics
431    ///
432    /// Will panic if `witness_row` is None.
433    pub fn fill_in_witness<F: FftField>(
434        start_row: usize,
435        witness: &mut [Vec<F>; COLUMNS],
436        public: &[F],
437    ) {
438        // fill witness
439        let mut witness_row = iterate(start_row, |&i| i + 1);
440
441        // public
442        for p in public {
443            let r = witness_row.next().unwrap();
444            witness[0][r] = *p;
445        }
446
447        // add and mul
448        for _ in 0..10 {
449            let r = witness_row.next().unwrap();
450
451            witness[0][r] = 11u32.into();
452            witness[1][r] = 23u32.into();
453            witness[2][r] = (11u32 + 23u32 * 3u32).into();
454
455            witness[3][r] = 11u32.into();
456            witness[4][r] = 23u32.into();
457            witness[5][r] = (11u32 * 23 * 2).into();
458        }
459
460        // const
461        for _ in 0..10 {
462            let r = witness_row.next().unwrap();
463
464            witness[0][r] = 3u32.into();
465
466            witness[3][r] = 5u32.into();
467        }
468    }
469}