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 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
245pub mod testing {
257 use super::*;
258 use crate::circuits::wires::Wire;
259 use itertools::iterate;
260
261 impl<F: PrimeField> CircuitGate<F> {
262 pub fn verify_generic(
268 &self,
269 row: usize,
270 witness: &[Vec<F>; COLUMNS],
271 public: &[F],
272 ) -> Result<(), String> {
273 let this: [F; COLUMNS] = array::from_fn(|i| witness[i][row]);
275
276 let zero = F::zero();
278
279 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 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 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 ff += &(&(&witness[register_offset] * &witness[register_offset + 1])
340 * &coefficientsm[coeff_offset + 3]);
341
342 &ff + &coefficientsm[coeff_offset + 4]
344
345 };
347
348 let mut res = generic_gate(0, 0);
349 res += &generic_gate(GENERIC_COEFFS, GENERIC_REGISTERS);
350
351 res += public;
353
354 res = &res
356 * &self
357 .column_evaluations
358 .get()
359 .generic_selector4
360 .interpolate_by_ref();
361 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 pub fn create_circuit<F: PrimeField>(start_row: usize, public: usize) -> Vec<CircuitGate<F>> {
378 let mut gates = vec![];
380
381 let mut gates_row = iterate(start_row, |&i| i + 1);
383
384 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 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 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 pub fn fill_in_witness<F: FftField>(
434 start_row: usize,
435 witness: &mut [Vec<F>; COLUMNS],
436 public: &[F],
437 ) {
438 let mut witness_row = iterate(start_row, |&i| i + 1);
440
441 for p in public {
443 let r = witness_row.next().unwrap();
444 witness[0][r] = *p;
445 }
446
447 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 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}