kimchi/circuits/polynomials/
generic.rs1use 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
53pub const CONSTRAINTS: u32 = 2;
55
56pub const GENERIC_REGISTERS: usize = 3;
58
59pub const GENERIC_COEFFS: usize = GENERIC_REGISTERS + 1 + 1 ;
63
64pub const DOUBLE_GENERIC_COEFFS: usize = GENERIC_COEFFS * 2;
66
67pub const DOUBLE_GENERIC_REGISTERS: usize = GENERIC_REGISTERS * 2;
69
70#[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 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 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#[derive(Clone)]
124pub enum GenericGateSpec<F> {
125 Add {
127 left_coeff: Option<F>,
129 right_coeff: Option<F>,
131 output_coeff: Option<F>,
133 },
134 Mul {
136 output_coeff: Option<F>,
138 mul_coeff: Option<F>,
140 },
141 Const(F),
143 Pub,
145 Plus(F),
147}
148
149impl<F: PrimeField> CircuitGate<F> {
150 pub fn create_generic(wires: GateWires, c: [F; GENERIC_COEFFS * 2]) -> Self {
152 CircuitGate::new(GateType::Generic, wires, c.to_vec())
153 }
154
155 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
244pub mod testing {
256 use super::*;
257 use crate::circuits::wires::Wire;
258 use itertools::iterate;
259
260 impl<F: PrimeField> CircuitGate<F> {
261 pub fn verify_generic(
267 &self,
268 row: usize,
269 witness: &[Vec<F>; COLUMNS],
270 public: &[F],
271 ) -> Result<(), String> {
272 let this: [F; COLUMNS] = array::from_fn(|i| witness[i][row]);
274
275 let zero = F::zero();
277
278 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 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 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 ff += &(&(&witness[register_offset] * &witness[register_offset + 1])
339 * &coefficientsm[coeff_offset + 3]);
340
341 &ff + &coefficientsm[coeff_offset + 4]
343
344 };
346
347 let mut res = generic_gate(0, 0);
348 res += &generic_gate(GENERIC_COEFFS, GENERIC_REGISTERS);
349
350 res += public;
352
353 res = &res
355 * &self
356 .column_evaluations
357 .get()
358 .generic_selector4
359 .interpolate_by_ref();
360 let (_quotient, rest) = res.divide_by_vanishing_poly(self.cs.domain.d1);
365 rest.is_zero()
366 }
367 }
368
369 pub fn create_circuit<F: PrimeField>(start_row: usize, public: usize) -> Vec<CircuitGate<F>> {
375 let mut gates = vec![];
377
378 let mut gates_row = iterate(start_row, |&i| i + 1);
380
381 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 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 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 pub fn fill_in_witness<F: FftField>(
431 start_row: usize,
432 witness: &mut [Vec<F>; COLUMNS],
433 public: &[F],
434 ) {
435 let mut witness_row = iterate(start_row, |&i| i + 1);
437
438 for p in public {
440 let r = witness_row.next().unwrap();
441 witness[0][r] = *p;
442 }
443
444 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 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}