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};
51
52pub const CONSTRAINTS: u32 = 2;
54
55pub const GENERIC_REGISTERS: usize = 3;
57
58pub const GENERIC_COEFFS: usize = GENERIC_REGISTERS + 1 + 1 ;
62
63pub const DOUBLE_GENERIC_COEFFS: usize = GENERIC_COEFFS * 2;
65
66pub const DOUBLE_GENERIC_REGISTERS: usize = GENERIC_REGISTERS * 2;
68
69#[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 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 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#[derive(Clone)]
123pub enum GenericGateSpec<F> {
124 Add {
126 left_coeff: Option<F>,
128 right_coeff: Option<F>,
130 output_coeff: Option<F>,
132 },
133 Mul {
135 output_coeff: Option<F>,
137 mul_coeff: Option<F>,
139 },
140 Const(F),
142 Pub,
144 Plus(F),
146}
147
148impl<F: PrimeField> CircuitGate<F> {
149 pub fn create_generic(wires: GateWires, c: [F; GENERIC_COEFFS * 2]) -> Self {
151 CircuitGate::new(GateType::Generic, wires, c.to_vec())
152 }
153
154 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
243pub mod testing {
255 use super::*;
256 use crate::circuits::wires::Wire;
257 use itertools::iterate;
258
259 impl<F: PrimeField> CircuitGate<F> {
260 pub fn verify_generic(
266 &self,
267 row: usize,
268 witness: &[Vec<F>; COLUMNS],
269 public: &[F],
270 ) -> Result<(), String> {
271 let this: [F; COLUMNS] = array::from_fn(|i| witness[i][row]);
273
274 let zero = F::zero();
276
277 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 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 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 ff += &(&(&witness[register_offset] * &witness[register_offset + 1])
341 * &coefficientsm[coeff_offset + 3]);
342
343 &ff + &coefficientsm[coeff_offset + 4]
345
346 };
348
349 let mut res = generic_gate(0, 0);
350 res += &generic_gate(GENERIC_COEFFS, GENERIC_REGISTERS);
351
352 res += public;
354
355 res = &res
357 * &self
358 .column_evaluations
359 .get()
360 .generic_selector4
361 .interpolate_by_ref();
362 let (_quotient, rest) = res.divide_by_vanishing_poly(self.cs.domain.d1);
367 rest.is_zero()
368 }
369 }
370
371 pub fn create_circuit<F: PrimeField>(start_row: usize, public: usize) -> Vec<CircuitGate<F>> {
377 let mut gates = vec![];
379
380 let mut gates_row = iterate(start_row, |&i| i + 1);
382
383 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 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 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 pub fn fill_in_witness<F: FftField>(
433 start_row: usize,
434 witness: &mut [Vec<F>; COLUMNS],
435 public: &[F],
436 ) {
437 let mut witness_row = iterate(start_row, |&i| i + 1);
439
440 for p in public {
442 let r = witness_row.next().unwrap();
443 witness[0][r] = *p;
444 }
445
446 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 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}