Skip to main content

kimchi/circuits/polynomials/
generic.rs

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