kimchi/circuits/polynomials/
generic.rs1use alloc::{string::String, vec, vec::Vec};
3
4use 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
55pub const CONSTRAINTS: u32 = 2;
57
58pub const GENERIC_REGISTERS: usize = 3;
60
61pub const GENERIC_COEFFS: usize = GENERIC_REGISTERS + 1 + 1 ;
65
66pub const DOUBLE_GENERIC_COEFFS: usize = GENERIC_COEFFS * 2;
68
69pub const DOUBLE_GENERIC_REGISTERS: usize = GENERIC_REGISTERS * 2;
71
72#[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 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 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#[derive(Clone)]
126pub enum GenericGateSpec<F> {
127 Add {
129 left_coeff: Option<F>,
131 right_coeff: Option<F>,
133 output_coeff: Option<F>,
135 },
136 Mul {
138 output_coeff: Option<F>,
140 mul_coeff: Option<F>,
142 },
143 Const(F),
145 Pub,
147 Plus(F),
149}
150
151impl<F: PrimeField> CircuitGate<F> {
152 pub fn create_generic(wires: GateWires, c: [F; GENERIC_COEFFS * 2]) -> Self {
154 CircuitGate::new(GateType::Generic, wires, c.to_vec())
155 }
156
157 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
246pub mod testing {
258 use super::*;
259 use crate::circuits::wires::Wire;
260 use itertools::iterate;
261
262 impl<F: PrimeField> CircuitGate<F> {
263 pub fn verify_generic(
269 &self,
270 row: usize,
271 witness: &[Vec<F>; COLUMNS],
272 public: &[F],
273 ) -> Result<(), String> {
274 let this: [F; COLUMNS] = array::from_fn(|i| witness[i][row]);
276
277 let zero = F::zero();
279
280 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 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 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 ff += &(&(&witness[register_offset] * &witness[register_offset + 1])
345 * &coefficientsm[coeff_offset + 3]);
346
347 &ff + &coefficientsm[coeff_offset + 4]
349
350 };
352
353 let mut res = generic_gate(0, 0);
354 res += &generic_gate(GENERIC_COEFFS, GENERIC_REGISTERS);
355
356 res += public;
358
359 res = &res
361 * &self
362 .column_evaluations
363 .get()
364 .generic_selector4
365 .interpolate_by_ref();
366 let (_quotient, rest) = res.divide_by_vanishing_poly(self.cs.domain.d1);
371 rest.is_zero()
372 }
373 }
374
375 pub fn create_circuit<F: PrimeField>(start_row: usize, public: usize) -> Vec<CircuitGate<F>> {
381 let mut gates = vec![];
383
384 let mut gates_row = iterate(start_row, |&i| i + 1);
386
387 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 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 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 pub fn fill_in_witness<F: FftField>(
437 start_row: usize,
438 witness: &mut [Vec<F>; COLUMNS],
439 public: &[F],
440 ) {
441 let mut witness_row = iterate(start_row, |&i| i + 1);
443
444 for p in public {
446 let r = witness_row.next().unwrap();
447 witness[0][r] = *p;
448 }
449
450 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 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}