halo2_base/gates/flex_gate/mod.rs
1use crate::{
2 halo2_proofs::{
3 plonk::{
4 Advice, Assigned, Column, ConstraintSystem, FirstPhase, Fixed, SecondPhase, Selector,
5 ThirdPhase,
6 },
7 poly::Rotation,
8 },
9 utils::ScalarField,
10 AssignedValue, Context,
11 QuantumCell::{self, Constant, Existing, Witness, WitnessFraction},
12};
13use itertools::Itertools;
14use serde::{Deserialize, Serialize};
15use std::{
16 iter::{self},
17 marker::PhantomData,
18};
19
20pub mod threads;
21
22/// Vector of thread advice column break points
23pub type ThreadBreakPoints = Vec<usize>;
24/// Vector of vectors tracking the thread break points across different halo2 phases
25pub type MultiPhaseThreadBreakPoints = Vec<ThreadBreakPoints>;
26
27/// The maximum number of phases in halo2.
28pub(super) const MAX_PHASE: usize = 3;
29
30/// # Vertical Gate Strategy:
31/// `q_0 * (a + b * c - d) = 0`
32/// where
33/// * `a = value[0], b = value[1], c = value[2], d = value[3]`
34/// * `q = q_enable[0]`
35/// * `q` is either 0 or 1 so this is just a simple selector
36///
37/// We chose `a + b * c` instead of `a * b + c` to allow "chaining" of gates, i.e., the output of one gate because `a` in the next gate.
38///
39/// A configuration for a basic gate chip describing the selector, and advice column values.
40#[derive(Clone, Debug)]
41pub struct BasicGateConfig<F: ScalarField> {
42 /// [Selector] column that stores selector values that are used to activate gates in the advice column.
43 // `q_enable` will have either length 1 or 2, depending on the strategy
44 pub q_enable: Selector,
45 /// [Column] that stores the advice values of the gate.
46 pub value: Column<Advice>,
47 /// Marker for the field type.
48 _marker: PhantomData<F>,
49}
50
51impl<F: ScalarField> BasicGateConfig<F> {
52 /// Constructor
53 pub fn new(q_enable: Selector, value: Column<Advice>) -> Self {
54 Self { q_enable, value, _marker: PhantomData }
55 }
56
57 /// Instantiates a new [BasicGateConfig].
58 ///
59 /// Assumes `phase` is in the range [0, MAX_PHASE).
60 /// * `meta`: [ConstraintSystem] used for the gate
61 /// * `phase`: The phase to add the gate to
62 pub fn configure(meta: &mut ConstraintSystem<F>, phase: u8) -> Self {
63 let value = match phase {
64 0 => meta.advice_column_in(FirstPhase),
65 1 => meta.advice_column_in(SecondPhase),
66 2 => meta.advice_column_in(ThirdPhase),
67 _ => panic!("Currently BasicGate only supports {MAX_PHASE} phases"),
68 };
69 meta.enable_equality(value);
70
71 let q_enable = meta.selector();
72
73 let config = Self { q_enable, value, _marker: PhantomData };
74 config.create_gate(meta);
75 config
76 }
77
78 /// Wrapper for [ConstraintSystem].create_gate(name, meta) creates a gate form [q * (a + b * c - out)].
79 /// * `meta`: [ConstraintSystem] used for the gate
80 fn create_gate(&self, meta: &mut ConstraintSystem<F>) {
81 meta.create_gate("1 column a + b * c = out", |meta| {
82 let q = meta.query_selector(self.q_enable);
83
84 let a = meta.query_advice(self.value, Rotation::cur());
85 let b = meta.query_advice(self.value, Rotation::next());
86 let c = meta.query_advice(self.value, Rotation(2));
87 let out = meta.query_advice(self.value, Rotation(3));
88
89 vec![q * (a + b * c - out)]
90 })
91 }
92}
93
94/// A Config struct defining the parameters for [FlexGateConfig]
95#[derive(Clone, Default, Debug, Serialize, Deserialize)]
96pub struct FlexGateConfigParams {
97 /// Specifies the number of rows in the circuit to be 2<sup>k</sup>
98 pub k: usize,
99 /// The number of advice columns per phase
100 pub num_advice_per_phase: Vec<usize>,
101 /// The number of fixed columns
102 pub num_fixed: usize,
103}
104
105/// Defines a configuration for a flex gate chip describing the selector, and advice column values for the chip.
106#[derive(Clone, Debug)]
107pub struct FlexGateConfig<F: ScalarField> {
108 /// A [Vec] of [BasicGateConfig] that define gates for each halo2 phase.
109 pub basic_gates: Vec<Vec<BasicGateConfig<F>>>,
110 /// A [Vec] of [Fixed] [Column]s for allocating constant values.
111 pub constants: Vec<Column<Fixed>>,
112 /// Max number of usable rows in the circuit.
113 pub max_rows: usize,
114}
115
116impl<F: ScalarField> FlexGateConfig<F> {
117 /// Generates a new [FlexGateConfig]
118 ///
119 /// * `meta`: [ConstraintSystem] of the circuit
120 /// * `params`: see [FlexGateConfigParams]
121 pub fn configure(meta: &mut ConstraintSystem<F>, params: FlexGateConfigParams) -> Self {
122 // create fixed (constant) columns and enable equality constraints
123 let mut constants = Vec::with_capacity(params.num_fixed);
124 for _i in 0..params.num_fixed {
125 let c = meta.fixed_column();
126 meta.enable_equality(c);
127 // meta.enable_constant(c);
128 constants.push(c);
129 }
130
131 let mut basic_gates = vec![];
132 for (phase, &num_columns) in params.num_advice_per_phase.iter().enumerate() {
133 let config =
134 (0..num_columns).map(|_| BasicGateConfig::configure(meta, phase as u8)).collect();
135 basic_gates.push(config);
136 }
137 log::info!("Poisoned rows after FlexGateConfig::configure {}", meta.minimum_rows());
138 Self {
139 basic_gates,
140 constants,
141 // Warning: this needs to be updated if you create more advice columns after this `FlexGateConfig` is created
142 max_rows: (1 << params.k) - meta.minimum_rows(),
143 }
144 }
145}
146
147/// Trait that defines basic arithmetic operations for a gate.
148pub trait GateInstructions<F: ScalarField> {
149 /// Returns a slice of the [ScalarField] field elements 2^i for i in 0..F::NUM_BITS.
150 fn pow_of_two(&self) -> &[F];
151
152 /// Constrains and returns `a + b * 1 = out`.
153 ///
154 /// Defines a vertical gate of form | a | b | 1 | a + b | where (a + b) = out.
155 /// * `ctx`: [Context] to add the constraints to
156 /// * `a`: [QuantumCell] value
157 /// * `b`: [QuantumCell] value to add to 'a`
158 fn add(
159 &self,
160 ctx: &mut Context<F>,
161 a: impl Into<QuantumCell<F>>,
162 b: impl Into<QuantumCell<F>>,
163 ) -> AssignedValue<F> {
164 let a = a.into();
165 let b = b.into();
166 let out_val = *a.value() + b.value();
167 ctx.assign_region_last([a, b, Constant(F::ONE), Witness(out_val)], [0])
168 }
169
170 /// Constrains and returns `out = a + 1`.
171 ///
172 /// * `ctx`: [Context] to add the constraints to
173 /// * `a`: [QuantumCell] value
174 fn inc(&self, ctx: &mut Context<F>, a: impl Into<QuantumCell<F>>) -> AssignedValue<F> {
175 self.add(ctx, a, Constant(F::ONE))
176 }
177
178 /// Constrains and returns `a + b * (-1) = out`.
179 ///
180 /// Defines a vertical gate of form | a - b | b | 1 | a |, where (a - b) = out.
181 /// * `ctx`: [Context] to add the constraints to
182 /// * `a`: [QuantumCell] value
183 /// * `b`: [QuantumCell] value to subtract from 'a'
184 fn sub(
185 &self,
186 ctx: &mut Context<F>,
187 a: impl Into<QuantumCell<F>>,
188 b: impl Into<QuantumCell<F>>,
189 ) -> AssignedValue<F> {
190 let a = a.into();
191 let b = b.into();
192 let out_val = *a.value() - b.value();
193 // slightly better to not have to compute -F::ONE since F::ONE is cached
194 ctx.assign_region([Witness(out_val), b, Constant(F::ONE), a], [0]);
195 ctx.get(-4)
196 }
197
198 /// Constrains and returns `out = a - 1`.
199 ///
200 /// * `ctx`: [Context] to add the constraints to
201 /// * `a`: [QuantumCell] value
202 fn dec(&self, ctx: &mut Context<F>, a: impl Into<QuantumCell<F>>) -> AssignedValue<F> {
203 self.sub(ctx, a, Constant(F::ONE))
204 }
205
206 /// Constrains and returns `a - b * c = out`.
207 ///
208 /// Defines a vertical gate of form | a - b * c | b | c | a |, where (a - b * c) = out.
209 /// * `ctx`: [Context] to add the constraints to
210 /// * `a`: [QuantumCell] value to subtract 'b * c' from
211 /// * `b`: [QuantumCell] value
212 /// * `c`: [QuantumCell] value
213 fn sub_mul(
214 &self,
215 ctx: &mut Context<F>,
216 a: impl Into<QuantumCell<F>>,
217 b: impl Into<QuantumCell<F>>,
218 c: impl Into<QuantumCell<F>>,
219 ) -> AssignedValue<F> {
220 let a = a.into();
221 let b = b.into();
222 let c = c.into();
223 let out_val = *a.value() - *b.value() * c.value();
224 ctx.assign_region_last([Witness(out_val), b, c, a], [0]);
225 ctx.get(-4)
226 }
227
228 /// Constrains and returns `a * (-1) = out`.
229 ///
230 /// Defines a vertical gate of form | a | -a | 1 | 0 |, where (-a) = out.
231 /// * `ctx`: the [Context] to add the constraints to
232 /// * `a`: [QuantumCell] value to negate
233 fn neg(&self, ctx: &mut Context<F>, a: impl Into<QuantumCell<F>>) -> AssignedValue<F> {
234 let a = a.into();
235 let out_val = -*a.value();
236 ctx.assign_region([a, Witness(out_val), Constant(F::ONE), Constant(F::ZERO)], [0]);
237 ctx.get(-3)
238 }
239
240 /// Constrains and returns `0 + a * b = out`.
241 ///
242 /// Defines a vertical gate of form | 0 | a | b | a * b |, where (a * b) = out.
243 /// * `ctx`: [Context] to add the constraints to
244 /// * `a`: [QuantumCell] value
245 /// * `b`: [QuantumCell] value to multiply 'a' by
246 fn mul(
247 &self,
248 ctx: &mut Context<F>,
249 a: impl Into<QuantumCell<F>>,
250 b: impl Into<QuantumCell<F>>,
251 ) -> AssignedValue<F> {
252 let a = a.into();
253 let b = b.into();
254 let out_val = *a.value() * b.value();
255 ctx.assign_region_last([Constant(F::ZERO), a, b, Witness(out_val)], [0])
256 }
257
258 /// Constrains and returns `a * b + c = out`.
259 ///
260 /// Defines a vertical gate of form | c | a | b | a * b + c |, where (a * b + c) = out.
261 /// * `ctx`: [Context] to add the constraints to
262 /// * `a`: [QuantumCell] value
263 /// * `b`: [QuantumCell] value to multiply 'a' by
264 /// * `c`: [QuantumCell] value to add to 'a * b'
265 fn mul_add(
266 &self,
267 ctx: &mut Context<F>,
268 a: impl Into<QuantumCell<F>>,
269 b: impl Into<QuantumCell<F>>,
270 c: impl Into<QuantumCell<F>>,
271 ) -> AssignedValue<F> {
272 let a = a.into();
273 let b = b.into();
274 let c = c.into();
275 let out_val = *a.value() * b.value() + c.value();
276 ctx.assign_region_last([c, a, b, Witness(out_val)], [0])
277 }
278
279 /// Constrains and returns `(1 - a) * b = b - a * b`.
280 ///
281 /// Defines a vertical gate of form | (1 - a) * b | a | b | b |, where (1 - a) * b = out.
282 /// * `ctx`: [Context] to add the constraints to
283 /// * `a`: [QuantumCell] value
284 /// * `b`: [QuantumCell] value to multiply 'a' by
285 fn mul_not(
286 &self,
287 ctx: &mut Context<F>,
288 a: impl Into<QuantumCell<F>>,
289 b: impl Into<QuantumCell<F>>,
290 ) -> AssignedValue<F> {
291 let a = a.into();
292 let b = b.into();
293 let out_val = (F::ONE - a.value()) * b.value();
294 ctx.assign_region_smart([Witness(out_val), a, b, b], [0], [(2, 3)], []);
295 ctx.get(-4)
296 }
297
298 /// Constrains that x is boolean (e.g. 0 or 1).
299 ///
300 /// Defines a vertical gate of form | 0 | x | x | x |.
301 /// * `ctx`: [Context] to add the constraints to
302 /// * `x`: [QuantumCell] value to constrain
303 fn assert_bit(&self, ctx: &mut Context<F>, x: AssignedValue<F>) {
304 ctx.assign_region([Constant(F::ZERO), Existing(x), Existing(x), Existing(x)], [0]);
305 }
306
307 /// Constrains and returns a / b = out.
308 ///
309 /// Defines a vertical gate of form | 0 | a / b | b | a |, where a / b = out.
310 ///
311 /// Assumes `b != 0`.
312 /// * `ctx`: [Context] to add the constraints to
313 /// * `a`: [QuantumCell] value
314 /// * `b`: [QuantumCell] value to divide 'a' by
315 fn div_unsafe(
316 &self,
317 ctx: &mut Context<F>,
318 a: impl Into<QuantumCell<F>>,
319 b: impl Into<QuantumCell<F>>,
320 ) -> AssignedValue<F> {
321 let a = a.into();
322 let b = b.into();
323 // TODO: if really necessary, make `c` of type `Assigned<F>`
324 // this would require the API using `Assigned<F>` instead of `F` everywhere, so leave as last resort
325 let c = b.value().invert().unwrap() * a.value();
326 ctx.assign_region([Constant(F::ZERO), Witness(c), b, a], [0]);
327 ctx.get(-3)
328 }
329
330 /// Constrains that `a` is equal to `constant` value.
331 /// * `ctx`: [Context] to add the constraints to
332 /// * `a`: [QuantumCell] value
333 /// * `constant`: constant value to constrain `a` to be equal to
334 fn assert_is_const(&self, ctx: &mut Context<F>, a: &AssignedValue<F>, constant: &F) {
335 if !ctx.witness_gen_only {
336 ctx.copy_manager.lock().unwrap().constant_equalities.push((*constant, a.cell.unwrap()));
337 }
338 }
339
340 /// Constrains and returns the inner product of `<a, b>`.
341 ///
342 /// Assumes 'a' and 'b' are the same length.
343 /// * `ctx`: [Context] to add the constraints to
344 /// * `a`: Iterator of [QuantumCell] values
345 /// * `b`: Iterator of [QuantumCell] values to take inner product of `a` by
346 fn inner_product<QA>(
347 &self,
348 ctx: &mut Context<F>,
349 a: impl IntoIterator<Item = QA>,
350 b: impl IntoIterator<Item = QuantumCell<F>>,
351 ) -> AssignedValue<F>
352 where
353 QA: Into<QuantumCell<F>>;
354
355 /// Returns the inner product of `<a, b>` and the last element of `a` after it has been assigned.
356 ///
357 /// **NOT** encouraged for general usage.
358 /// This is a low-level function, where you want to avoid first assigning `a` and then copying the last element into the
359 /// correct cell for this computation.
360 ///
361 /// Assumes 'a' and 'b' are the same length.
362 /// * `ctx`: [Context] of the circuit
363 /// * `a`: Iterator of [QuantumCell]s
364 /// * `b`: Iterator of [QuantumCell]s to take inner product of `a` by
365 fn inner_product_left_last<QA>(
366 &self,
367 ctx: &mut Context<F>,
368 a: impl IntoIterator<Item = QA>,
369 b: impl IntoIterator<Item = QuantumCell<F>>,
370 ) -> (AssignedValue<F>, AssignedValue<F>)
371 where
372 QA: Into<QuantumCell<F>>;
373
374 /// Returns `(<a,b>, a_assigned)`. See `inner_product` for more details.
375 ///
376 /// **NOT** encouraged for general usage.
377 /// This is a low-level function, useful for when you want to simultaneously compute an inner product while assigning
378 /// private witnesses for the first time. This avoids first assigning `a` and then copying into the correct cells
379 /// for this computation. We do not return the assignments of `a` in `inner_product` as an optimization to avoid
380 /// the memory allocation of having to collect the vectors.
381 ///
382 /// Assumes 'a' and 'b' are the same length.
383 fn inner_product_left<QA>(
384 &self,
385 ctx: &mut Context<F>,
386 a: impl IntoIterator<Item = QA>,
387 b: impl IntoIterator<Item = QuantumCell<F>>,
388 ) -> (AssignedValue<F>, Vec<AssignedValue<F>>)
389 where
390 QA: Into<QuantumCell<F>>;
391
392 /// Calculates and constrains the inner product.
393 ///
394 /// Returns the assignment trace where `output[i]` has the running sum `sum_{j=0..=i} a[j] * b[j]`.
395 ///
396 /// Assumes 'a' and 'b' are the same length.
397 /// * `ctx`: [Context] to add the constraints to
398 /// * `a`: Iterator of [QuantumCell] values
399 /// * `b`: Iterator of [QuantumCell] values to calculate the partial sums of the inner product of `a` by.
400 fn inner_product_with_sums<'thread, QA>(
401 &self,
402 ctx: &'thread mut Context<F>,
403 a: impl IntoIterator<Item = QA>,
404 b: impl IntoIterator<Item = QuantumCell<F>>,
405 ) -> Box<dyn Iterator<Item = AssignedValue<F>> + 'thread>
406 where
407 QA: Into<QuantumCell<F>>;
408
409 /// Constrains and returns the sum of [QuantumCell]'s in iterator `a`.
410 /// * `ctx`: [Context] to add the constraints to
411 /// * `a`: Iterator of [QuantumCell] values to sum
412 fn sum<Q>(&self, ctx: &mut Context<F>, a: impl IntoIterator<Item = Q>) -> AssignedValue<F>
413 where
414 Q: Into<QuantumCell<F>>,
415 {
416 let mut a = a.into_iter().peekable();
417 let start = a.next();
418 if start.is_none() {
419 return ctx.load_zero();
420 }
421 let start = start.unwrap().into();
422 if a.peek().is_none() {
423 return ctx.assign_region_last([start], []);
424 }
425 let (len, hi) = a.size_hint();
426 assert_eq!(Some(len), hi);
427
428 let mut sum = *start.value();
429 let cells = iter::once(start).chain(a.flat_map(|a| {
430 let a = a.into();
431 sum += a.value();
432 [a, Constant(F::ONE), Witness(sum)]
433 }));
434 ctx.assign_region_last(cells, (0..len).map(|i| 3 * i as isize))
435 }
436
437 /// Calculates and constrains the sum of the elements of `a`.
438 ///
439 /// Returns the assignment trace where `output[i]` has the running sum `sum_{j=0..=i} a[j]`.
440 /// * `ctx`: [Context] to add the constraints to
441 /// * `a`: Iterator of [QuantumCell] values to sum
442 fn partial_sums<'thread, Q>(
443 &self,
444 ctx: &'thread mut Context<F>,
445 a: impl IntoIterator<Item = Q>,
446 ) -> Box<dyn Iterator<Item = AssignedValue<F>> + 'thread>
447 where
448 Q: Into<QuantumCell<F>>,
449 {
450 let mut a = a.into_iter().peekable();
451 let start = a.next();
452 if start.is_none() {
453 return Box::new(iter::once(ctx.load_zero()));
454 }
455 let start = start.unwrap().into();
456 if a.peek().is_none() {
457 return Box::new(iter::once(ctx.assign_region_last([start], [])));
458 }
459 let (len, hi) = a.size_hint();
460 assert_eq!(Some(len), hi);
461
462 let mut sum = *start.value();
463 let cells = iter::once(start).chain(a.flat_map(|a| {
464 let a = a.into();
465 sum += a.value();
466 [a, Constant(F::ONE), Witness(sum)]
467 }));
468 ctx.assign_region(cells, (0..len).map(|i| 3 * i as isize));
469 Box::new((0..=len).rev().map(|i| ctx.get(-1 - 3 * (i as isize))))
470 }
471
472 /// Calculates and constrains the accumulated product of 'a' and 'b' i.e. `x_i = b_1 * (a_1...a_{i - 1})
473 /// + b_2 * (a_2...a_{i - 1})
474 /// + ...
475 /// + b_i`
476 ///
477 /// Returns the assignment trace where `output[i]` is the running accumulated product x_i.
478 ///
479 /// Assumes 'a' and 'b' are the same length.
480 /// * `ctx`: [Context] to add the constraints to
481 /// * `a`: Iterator of [QuantumCell] values
482 /// * `b`: Iterator of [QuantumCell] values to take the accumulated product of `a` by
483 fn accumulated_product<QA, QB>(
484 &self,
485 ctx: &mut Context<F>,
486 a: impl IntoIterator<Item = QA>,
487 b: impl IntoIterator<Item = QB>,
488 ) -> Vec<AssignedValue<F>>
489 where
490 QA: Into<QuantumCell<F>>,
491 QB: Into<QuantumCell<F>>,
492 {
493 let mut b = b.into_iter();
494 let mut a = a.into_iter();
495 let b_first = b.next();
496 if let Some(b_first) = b_first {
497 let b_first = ctx.assign_region_last([b_first], []);
498 std::iter::successors(Some(b_first), |x| {
499 a.next().zip(b.next()).map(|(a, b)| self.mul_add(ctx, Existing(*x), a, b))
500 })
501 .collect()
502 } else {
503 vec![]
504 }
505 }
506
507 /// Constrains and returns the sum of products of `coeff * (a * b)` defined in `values` plus a variable `var` e.g.
508 /// `x = var + values[0].0 * (values[0].1 * values[0].2) + values[1].0 * (values[1].1 * values[1].2) + ... + values[n].0 * (values[n].1 * values[n].2)`.
509 /// * `ctx`: [Context] to add the constraints to.
510 /// * `values`: Iterator of tuples `(coeff, a, b)` where `coeff` is a field element, `a` and `b` are [QuantumCell]'s.
511 /// * `var`: [QuantumCell] that represents the value of a variable added to the sum.
512 fn sum_products_with_coeff_and_var(
513 &self,
514 ctx: &mut Context<F>,
515 values: impl IntoIterator<Item = (F, QuantumCell<F>, QuantumCell<F>)>,
516 var: QuantumCell<F>,
517 ) -> AssignedValue<F>;
518
519 /// Constrains and returns `a || b`, assuming `a` and `b` are boolean.
520 ///
521 /// Defines a vertical gate of form `| 1 - b | 1 | b | 1 | b | a | 1 - b | out |`, where `out = a + b - a * b`.
522 /// * `ctx`: [Context] to add the constraints to.
523 /// * `a`: [QuantumCell] that contains a boolean value.
524 /// * `b`: [QuantumCell] that contains a boolean value.
525 fn or(
526 &self,
527 ctx: &mut Context<F>,
528 a: impl Into<QuantumCell<F>>,
529 b: impl Into<QuantumCell<F>>,
530 ) -> AssignedValue<F> {
531 let a = a.into();
532 let b = b.into();
533 let not_b_val = F::ONE - b.value();
534 let out_val = *a.value() + b.value() - *a.value() * b.value();
535 let cells = [
536 Witness(not_b_val),
537 Constant(F::ONE),
538 b,
539 Constant(F::ONE),
540 b,
541 a,
542 Witness(not_b_val),
543 Witness(out_val),
544 ];
545 ctx.assign_region_smart(cells, [0, 4], [(0, 6), (2, 4)], []);
546 ctx.last().unwrap()
547 }
548
549 /// Constrains and returns `a & b`, assumeing `a` and `b` are boolean.
550 ///
551 /// Defines a vertical gate of form | 0 | a | b | out |, where out = a * b.
552 /// * `ctx`: [Context] to add the constraints to.
553 /// * `a`: [QuantumCell] that contains a boolean value.
554 /// * `b`: [QuantumCell] that contains a boolean value.
555 fn and(
556 &self,
557 ctx: &mut Context<F>,
558 a: impl Into<QuantumCell<F>>,
559 b: impl Into<QuantumCell<F>>,
560 ) -> AssignedValue<F> {
561 self.mul(ctx, a, b)
562 }
563
564 /// Constrains and returns `!a` assumeing `a` is boolean.
565 ///
566 /// Defines a vertical gate of form | 1 - a | a | 1 | 1 |, where 1 - a = out.
567 /// * `ctx`: [Context] to add the constraints to.
568 /// * `a`: [QuantumCell] that contains a boolean value.
569 fn not(&self, ctx: &mut Context<F>, a: impl Into<QuantumCell<F>>) -> AssignedValue<F> {
570 self.sub(ctx, Constant(F::ONE), a)
571 }
572
573 /// Constrains and returns `sel ? a : b` assuming `sel` is boolean.
574 ///
575 /// Defines a vertical gate of form `| 1 - sel | sel | 1 | a | 1 - sel | sel | 1 | b | out |`, where out = sel * a + (1 - sel) * b.
576 /// * `ctx`: [Context] to add the constraints to.
577 /// * `a`: [QuantumCell] that contains a boolean value.
578 /// * `b`: [QuantumCell] that contains a boolean value.
579 /// * `sel`: [QuantumCell] that contains a boolean value.
580 fn select(
581 &self,
582 ctx: &mut Context<F>,
583 a: impl Into<QuantumCell<F>>,
584 b: impl Into<QuantumCell<F>>,
585 sel: impl Into<QuantumCell<F>>,
586 ) -> AssignedValue<F>;
587
588 /// Constains and returns `a || (b && c)`, assuming `a`, `b` and `c` are boolean.
589 ///
590 /// Defines a vertical gate of form `| 1 - b c | b | c | 1 | a - 1 | 1 - b c | out | a - 1 | 1 | 1 | a |`, where out = a + b * c - a * b * c.
591 /// * `ctx`: [Context] to add the constraints to.
592 /// * `a`: [QuantumCell] that contains a boolean value.
593 /// * `b`: [QuantumCell] that contains a boolean value.
594 /// * `c`: [QuantumCell] that contains a boolean value.
595 fn or_and(
596 &self,
597 ctx: &mut Context<F>,
598 a: impl Into<QuantumCell<F>>,
599 b: impl Into<QuantumCell<F>>,
600 c: impl Into<QuantumCell<F>>,
601 ) -> AssignedValue<F>;
602
603 /// Constrains and returns an indicator vector from a slice of boolean values, where `output[idx] = 1` iff idx = (the number represented by `bits` in binary little endian), otherwise `output[idx] = 0`.
604 /// * `ctx`: [Context] to add the constraints to
605 /// * `bits`: slice of [QuantumCell]'s that contains boolean values
606 ///
607 /// # Assumptions
608 /// * `bits` is non-empty
609 fn bits_to_indicator(
610 &self,
611 ctx: &mut Context<F>,
612 bits: &[AssignedValue<F>],
613 ) -> Vec<AssignedValue<F>> {
614 let k = bits.len();
615 assert!(k > 0, "bits_to_indicator: bits must be non-empty");
616
617 // (inv_last_bit, last_bit) = (1, 0) if bits[k - 1] = 0
618 let (inv_last_bit, last_bit) = {
619 ctx.assign_region(
620 [
621 Witness(F::ONE - bits[k - 1].value()),
622 Existing(bits[k - 1]),
623 Constant(F::ONE),
624 Constant(F::ONE),
625 ],
626 [0],
627 );
628 (ctx.get(-4), ctx.get(-3))
629 };
630 let mut indicator = Vec::with_capacity(2 * (1 << k) - 2);
631 let mut offset = 0;
632 indicator.push(inv_last_bit);
633 indicator.push(last_bit);
634 for (idx, bit) in bits.iter().rev().enumerate().skip(1) {
635 for old_idx in 0..(1 << idx) {
636 // inv_prod_val = (1 - bit) * indicator[offset + old_idx]
637 let inv_prod_val = (F::ONE - bit.value()) * indicator[offset + old_idx].value();
638 ctx.assign_region(
639 [
640 Witness(inv_prod_val),
641 Existing(indicator[offset + old_idx]),
642 Existing(*bit),
643 Existing(indicator[offset + old_idx]),
644 ],
645 [0],
646 );
647 indicator.push(ctx.get(-4));
648
649 // prod = bit * indicator[offset + old_idx]
650 let prod = self.mul(ctx, Existing(indicator[offset + old_idx]), Existing(*bit));
651 indicator.push(prod);
652 }
653 offset += 1 << idx;
654 }
655 indicator.split_off((1 << k) - 2)
656 }
657
658 /// Constrains and returns a [Vec] `indicator` of length `len`, where `indicator[i] == 1 if i == idx otherwise 0`, if `idx >= len` then `indicator` is all zeros.
659 ///
660 /// Assumes `len` is greater than 0.
661 /// * `ctx`: [Context] to add the constraints to
662 /// * `idx`: [QuantumCell] index of the indicator vector to be set to 1
663 /// * `len`: length of the `indicator` vector
664 fn idx_to_indicator(
665 &self,
666 ctx: &mut Context<F>,
667 idx: impl Into<QuantumCell<F>>,
668 len: usize,
669 ) -> Vec<AssignedValue<F>> {
670 let mut idx = idx.into();
671 (0..len)
672 .map(|i| {
673 // need to use assigned idx after i > 0 so equality constraint holds
674 if i == 0 {
675 // unroll `is_zero` to make sure if `idx == Witness(_)` it is replaced by `Existing(_)` in later iterations
676 let x = idx.value();
677 let (is_zero, inv) = if x.is_zero_vartime() {
678 (F::ONE, Assigned::Trivial(F::ONE))
679 } else {
680 (F::ZERO, Assigned::Rational(F::ONE, *x))
681 };
682 let cells = [
683 Witness(is_zero),
684 idx,
685 WitnessFraction(inv),
686 Constant(F::ONE),
687 Constant(F::ZERO),
688 idx,
689 Witness(is_zero),
690 Constant(F::ZERO),
691 ];
692 ctx.assign_region_smart(cells, [0, 4], [(0, 6), (1, 5)], []); // note the two `idx` need to be constrained equal: (1, 5)
693 idx = Existing(ctx.get(-3)); // replacing `idx` with Existing cell so future loop iterations constrain equality of all `idx`s
694 ctx.get(-2)
695 } else {
696 self.is_equal(ctx, idx, Constant(F::from(i as u64)))
697 }
698 })
699 .collect()
700 }
701
702 /// Constrains the inner product of `a` and `indicator` and returns `a[idx]` (e.g. the value of `a` at `idx`).
703 ///
704 /// Assumes that `a` and `indicator` are non-empty iterators of the same length, the values of `indicator` are boolean,
705 /// and that `indicator` has at most one `1` bit.
706 /// * `ctx`: [Context] to add the constraints to
707 /// * `a`: Iterator of [QuantumCell]'s that contains field elements
708 /// * `indicator`: Iterator of [AssignedValue]'s where `indicator[i] == 1` if `i == idx`, otherwise `0`
709 fn select_by_indicator<Q>(
710 &self,
711 ctx: &mut Context<F>,
712 a: impl IntoIterator<Item = Q>,
713 indicator: impl IntoIterator<Item = AssignedValue<F>>,
714 ) -> AssignedValue<F>
715 where
716 Q: Into<QuantumCell<F>>,
717 {
718 let mut sum = F::ZERO;
719 let a = a.into_iter();
720 let (len, hi) = a.size_hint();
721 assert_eq!(Some(len), hi);
722
723 let cells =
724 std::iter::once(Constant(F::ZERO)).chain(a.zip(indicator).flat_map(|(a, ind)| {
725 let a = a.into();
726 sum = if ind.value().is_zero_vartime() { sum } else { *a.value() };
727 [a, Existing(ind), Witness(sum)]
728 }));
729 ctx.assign_region_last(cells, (0..len).map(|i| 3 * i as isize))
730 }
731
732 /// Constrains and returns `cells[idx]` if `idx < cells.len()`, otherwise return 0.
733 ///
734 /// Assumes that `cells` and `idx` are non-empty iterators of the same length.
735 /// * `ctx`: [Context] to add the constraints to
736 /// * `cells`: Iterator of [QuantumCell]s to select from
737 /// * `idx`: [QuantumCell] with value `idx` where `idx` is the index of the cell to be selected
738 fn select_from_idx<Q>(
739 &self,
740 ctx: &mut Context<F>,
741 cells: impl IntoIterator<Item = Q>,
742 idx: impl Into<QuantumCell<F>>,
743 ) -> AssignedValue<F>
744 where
745 Q: Into<QuantumCell<F>>,
746 {
747 let cells = cells.into_iter();
748 let (len, hi) = cells.size_hint();
749 assert_eq!(Some(len), hi);
750
751 let ind = self.idx_to_indicator(ctx, idx, len);
752 self.select_by_indicator(ctx, cells, ind)
753 }
754
755 /// `array2d` is an array of fixed length arrays.
756 /// Assumes:
757 /// * `array2d.len() == indicator.len()`
758 /// * `array2d[i].len() == array2d[j].len()` for all `i,j`.
759 /// * the values of `indicator` are boolean and that `indicator` has at most one `1` bit.
760 /// * the lengths of `array2d` and `indicator` are the same.
761 ///
762 /// Returns the "dot product" of `array2d` with `indicator` as a fixed length (1d) array of length `array2d[0].len()`.
763 fn select_array_by_indicator<AR, AV>(
764 &self,
765 ctx: &mut Context<F>,
766 array2d: &[AR],
767 indicator: &[AssignedValue<F>],
768 ) -> Vec<AssignedValue<F>>
769 where
770 AR: AsRef<[AV]>,
771 AV: AsRef<AssignedValue<F>>,
772 {
773 (0..array2d[0].as_ref().len())
774 .map(|j| {
775 self.select_by_indicator(
776 ctx,
777 array2d.iter().map(|array_i| *array_i.as_ref()[j].as_ref()),
778 indicator.iter().copied(),
779 )
780 })
781 .collect()
782 }
783
784 /// Constrains that a cell is equal to 0 and returns `1` if `a = 0`, otherwise `0`.
785 ///
786 /// Defines a vertical gate of form `| out | a | inv | 1 | 0 | a | out | 0 |`, where out = 1 if a = 0, otherwise out = 0.
787 /// * `ctx`: [Context] to add the constraints to
788 /// * `a`: [QuantumCell] value to be constrained
789 fn is_zero(&self, ctx: &mut Context<F>, a: AssignedValue<F>) -> AssignedValue<F> {
790 let x = a.value();
791 let (is_zero, inv) = if x.is_zero_vartime() {
792 (F::ONE, Assigned::Trivial(F::ONE))
793 } else {
794 (F::ZERO, Assigned::Rational(F::ONE, *x))
795 };
796
797 let cells = [
798 Witness(is_zero),
799 Existing(a),
800 WitnessFraction(inv),
801 Constant(F::ONE),
802 Constant(F::ZERO),
803 Existing(a),
804 Witness(is_zero),
805 Constant(F::ZERO),
806 ];
807 ctx.assign_region_smart(cells, [0, 4], [(0, 6)], []);
808 ctx.get(-2)
809 }
810
811 /// Constrains that the value of two cells are equal: b - a = 0, returns `1` if `a = b`, otherwise `0`.
812 /// * `ctx`: [Context] to add the constraints to
813 /// * `a`: [QuantumCell] value
814 /// * `b`: [QuantumCell] value to compare to `a`
815 fn is_equal(
816 &self,
817 ctx: &mut Context<F>,
818 a: impl Into<QuantumCell<F>>,
819 b: impl Into<QuantumCell<F>>,
820 ) -> AssignedValue<F> {
821 let diff = self.sub(ctx, a, b);
822 self.is_zero(ctx, diff)
823 }
824
825 /// Constrains and returns little-endian bit vector representation of `a`.
826 ///
827 /// Assumes `range_bits >= bit_length(a)`.
828 /// * `a`: [QuantumCell] of the value to convert
829 /// * `range_bits`: range of bits needed to represent `a`
830 fn num_to_bits(
831 &self,
832 ctx: &mut Context<F>,
833 a: AssignedValue<F>,
834 range_bits: usize,
835 ) -> Vec<AssignedValue<F>>;
836
837 /// Constrains and computes `a`<sup>`exp`</sup> where both `a, exp` are witnesses. The exponent is computed in the native field `F`.
838 ///
839 /// Constrains that `exp` has at most `max_bits` bits.
840 fn pow_var(
841 &self,
842 ctx: &mut Context<F>,
843 a: AssignedValue<F>,
844 exp: AssignedValue<F>,
845 max_bits: usize,
846 ) -> AssignedValue<F>;
847
848 /// Performs and constrains Lagrange interpolation on `coords` and evaluates the resulting polynomial at `x`.
849 ///
850 /// Given pairs `coords[i] = (x_i, y_i)`, let `f` be the unique degree `len(coords) - 1` polynomial such that `f(x_i) = y_i` for all `i`.
851 ///
852 /// Returns:
853 /// (f(x), Prod_i(x - x_i))
854 /// * `ctx`: [Context] to add the constraints to
855 /// * `coords`: immutable reference to a slice of tuples of [AssignedValue]s representing the points to interpolate over such that `coords[i] = (x_i, y_i)`
856 /// * `x`: x-coordinate of the point to evaluate `f` at
857 ///
858 /// # Assumptions
859 /// * `coords` is non-empty
860 fn lagrange_and_eval(
861 &self,
862 ctx: &mut Context<F>,
863 coords: &[(AssignedValue<F>, AssignedValue<F>)],
864 x: AssignedValue<F>,
865 ) -> (AssignedValue<F>, AssignedValue<F>) {
866 assert!(!coords.is_empty(), "coords should not be empty");
867 let mut z = self.sub(ctx, Existing(x), Existing(coords[0].0));
868 for coord in coords.iter().skip(1) {
869 let sub = self.sub(ctx, Existing(x), Existing(coord.0));
870 z = self.mul(ctx, Existing(z), Existing(sub));
871 }
872 let mut eval = None;
873 for i in 0..coords.len() {
874 // compute (x - x_i) * Prod_{j != i} (x_i - x_j)
875 let mut denom = self.sub(ctx, Existing(x), Existing(coords[i].0));
876 for j in 0..coords.len() {
877 if i == j {
878 continue;
879 }
880 let sub = self.sub(ctx, coords[i].0, coords[j].0);
881 denom = self.mul(ctx, denom, sub);
882 }
883 // TODO: batch inversion
884 let is_zero = self.is_zero(ctx, denom);
885 self.assert_is_const(ctx, &is_zero, &F::ZERO);
886
887 // y_i / denom
888 let quot = self.div_unsafe(ctx, coords[i].1, denom);
889 eval = if let Some(eval) = eval {
890 let eval = self.add(ctx, eval, quot);
891 Some(eval)
892 } else {
893 Some(quot)
894 };
895 }
896 let out = self.mul(ctx, eval.unwrap(), z);
897 (out, z)
898 }
899}
900
901/// A chip that implements the [GateInstructions] trait supporting basic arithmetic operations.
902#[derive(Clone, Debug)]
903pub struct GateChip<F: ScalarField> {
904 /// The field elements 2^i for i in 0..F::NUM_BITS.
905 pub pow_of_two: Vec<F>,
906 /// To avoid Montgomery conversion in `F::from` for common small numbers, we keep a cache of field elements.
907 pub field_element_cache: Vec<F>,
908}
909
910impl<F: ScalarField> Default for GateChip<F> {
911 fn default() -> Self {
912 Self::new()
913 }
914}
915
916impl<F: ScalarField> GateChip<F> {
917 /// Returns a new [GateChip] with some precomputed values. This can be called out of circuit and has no extra dependencies.
918 pub fn new() -> Self {
919 let mut pow_of_two = Vec::with_capacity(F::NUM_BITS as usize);
920 let two = F::from(2);
921 pow_of_two.push(F::ONE);
922 pow_of_two.push(two);
923 for _ in 2..F::NUM_BITS {
924 pow_of_two.push(two * pow_of_two.last().unwrap());
925 }
926 let field_element_cache = (0..1024).map(|i| F::from(i)).collect();
927
928 Self { pow_of_two, field_element_cache }
929 }
930
931 /// Calculates and constrains the inner product of `<a, b>`.
932 /// If the first element of `b` is `Constant(F::ONE)`, then an optimization is performed to save 3 cells.
933 ///
934 /// Returns `true` if `b` start with `Constant(F::ONE)`, and `false` otherwise.
935 ///
936 /// Assumes `a` and `b` are the same length.
937 /// * `ctx`: [Context] of the circuit
938 /// * `a`: Iterator of [QuantumCell] values
939 /// * `b`: Iterator of [QuantumCell] values to take inner product of `a` by
940 fn inner_product_simple<QA>(
941 &self,
942 ctx: &mut Context<F>,
943 a: impl IntoIterator<Item = QA>,
944 b: impl IntoIterator<Item = QuantumCell<F>>,
945 ) -> bool
946 where
947 QA: Into<QuantumCell<F>>,
948 {
949 let mut sum;
950 let mut a = a.into_iter();
951 let mut b = b.into_iter().peekable();
952
953 let b_starts_with_one = matches!(b.peek(), Some(Constant(c)) if c == &F::ONE);
954 let cells = if b_starts_with_one {
955 b.next();
956 let start_a = a.next().unwrap().into();
957 sum = *start_a.value();
958 iter::once(start_a)
959 } else {
960 sum = F::ZERO;
961 iter::once(Constant(F::ZERO))
962 }
963 .chain(a.zip(b).flat_map(|(a, b)| {
964 let a = a.into();
965 sum += *a.value() * b.value();
966 [a, b, Witness(sum)]
967 }));
968
969 if ctx.witness_gen_only() {
970 ctx.assign_region(cells, vec![]);
971 } else {
972 let cells = cells.collect::<Vec<_>>();
973 let lo = cells.len();
974 let len = lo / 3;
975 ctx.assign_region(cells, (0..len).map(|i| 3 * i as isize));
976 };
977 b_starts_with_one
978 }
979}
980
981impl<F: ScalarField> GateInstructions<F> for GateChip<F> {
982 /// Returns a slice of the [ScalarField] elements 2<sup>i</sup> for i in 0..F::NUM_BITS.
983 fn pow_of_two(&self) -> &[F] {
984 &self.pow_of_two
985 }
986
987 /// Constrains and returns the inner product of `<a, b>`.
988 /// If the first element of `b` is `Constant(F::ONE)`, then an optimization is performed to save 3 cells.
989 ///
990 /// Assumes 'a' and 'b' are the same length.
991 /// * `ctx`: [Context] to add the constraints to
992 /// * `a`: Iterator of [QuantumCell] values
993 /// * `b`: Iterator of [QuantumCell] values to take inner product of `a` by
994 fn inner_product<QA>(
995 &self,
996 ctx: &mut Context<F>,
997 a: impl IntoIterator<Item = QA>,
998 b: impl IntoIterator<Item = QuantumCell<F>>,
999 ) -> AssignedValue<F>
1000 where
1001 QA: Into<QuantumCell<F>>,
1002 {
1003 self.inner_product_simple(ctx, a, b);
1004 ctx.last().unwrap()
1005 }
1006
1007 /// Returns the inner product of `<a, b>` and the last element of `a` after it has been assigned.
1008 ///
1009 /// **NOT** encouraged for general usage.
1010 /// This is a low-level function, where you want to avoid first assigning `a` and then copying the last element into the
1011 /// correct cell for this computation.
1012 ///
1013 /// Assumes 'a' and 'b' are the same length.
1014 /// * `ctx`: [Context] of the circuit
1015 /// * `a`: Iterator of [QuantumCell]s
1016 /// * `b`: Iterator of [QuantumCell]s to take inner product of `a` by
1017 fn inner_product_left_last<QA>(
1018 &self,
1019 ctx: &mut Context<F>,
1020 a: impl IntoIterator<Item = QA>,
1021 b: impl IntoIterator<Item = QuantumCell<F>>,
1022 ) -> (AssignedValue<F>, AssignedValue<F>)
1023 where
1024 QA: Into<QuantumCell<F>>,
1025 {
1026 let a = a.into_iter();
1027 let (len, hi) = a.size_hint();
1028 assert_eq!(Some(len), hi);
1029 let row_offset = ctx.advice.len();
1030 let b_starts_with_one = self.inner_product_simple(ctx, a, b);
1031 let a_last = if b_starts_with_one {
1032 if len == 1 {
1033 ctx.get(row_offset as isize)
1034 } else {
1035 ctx.get((row_offset + 1 + 3 * (len - 2)) as isize)
1036 }
1037 } else {
1038 ctx.get((row_offset + 1 + 3 * (len - 1)) as isize)
1039 };
1040 (ctx.last().unwrap(), a_last)
1041 }
1042
1043 /// Returns `(<a,b>, a_assigned)`. See `inner_product` for more details.
1044 ///
1045 /// **NOT** encouraged for general usage.
1046 /// This is a low-level function, useful for when you want to simultaneously compute an inner product while assigning
1047 /// private witnesses for the first time. This avoids first assigning `a` and then copying into the correct cells
1048 /// for this computation. We do not return the assignments of `a` in `inner_product` as an optimization to avoid
1049 /// the memory allocation of having to collect the vectors.
1050 ///
1051 /// We do not return `b_assigned` because if `b` starts with `Constant(F::ONE)`, the first element of `b` is not assigned.
1052 ///
1053 /// Assumes 'a' and 'b' are the same length.
1054 fn inner_product_left<QA>(
1055 &self,
1056 ctx: &mut Context<F>,
1057 a: impl IntoIterator<Item = QA>,
1058 b: impl IntoIterator<Item = QuantumCell<F>>,
1059 ) -> (AssignedValue<F>, Vec<AssignedValue<F>>)
1060 where
1061 QA: Into<QuantumCell<F>>,
1062 {
1063 let a = a.into_iter().collect_vec();
1064 let len = a.len();
1065 let row_offset = ctx.advice.len();
1066 let b_starts_with_one = self.inner_product_simple(ctx, a, b);
1067 let a_assigned = (0..len)
1068 .map(|i| {
1069 if b_starts_with_one {
1070 if i == 0 {
1071 ctx.get(row_offset as isize)
1072 } else {
1073 ctx.get((row_offset + 1 + 3 * (i - 1)) as isize)
1074 }
1075 } else {
1076 ctx.get((row_offset + 1 + 3 * i) as isize)
1077 }
1078 })
1079 .collect_vec();
1080 (ctx.last().unwrap(), a_assigned)
1081 }
1082
1083 /// Calculates and constrains the inner product.
1084 ///
1085 /// Returns the assignment trace where `output[i]` has the running sum `sum_{j=0..=i} a[j] * b[j]`.
1086 ///
1087 /// Assumes 'a' and 'b' are the same length.
1088 /// * `ctx`: [Context] to add the constraints to
1089 /// * `a`: Iterator of [QuantumCell] values
1090 /// * `b`: Iterator of [QuantumCell] values to calculate the partial sums of the inner product of `a` by
1091 fn inner_product_with_sums<'thread, QA>(
1092 &self,
1093 ctx: &'thread mut Context<F>,
1094 a: impl IntoIterator<Item = QA>,
1095 b: impl IntoIterator<Item = QuantumCell<F>>,
1096 ) -> Box<dyn Iterator<Item = AssignedValue<F>> + 'thread>
1097 where
1098 QA: Into<QuantumCell<F>>,
1099 {
1100 let row_offset = ctx.advice.len();
1101 let b_starts_with_one = self.inner_product_simple(ctx, a, b);
1102 if b_starts_with_one {
1103 Box::new((row_offset..ctx.advice.len()).step_by(3).map(|i| ctx.get(i as isize)))
1104 } else {
1105 // in this case the first assignment is 0 so we skip it
1106 Box::new((row_offset..ctx.advice.len()).step_by(3).skip(1).map(|i| ctx.get(i as isize)))
1107 }
1108 }
1109
1110 /// Constrains and returns the sum of products of `coeff * (a * b)` defined in `values` plus a variable `var` e.g.
1111 /// `x = var + values[0].0 * (values[0].1 * values[0].2) + values[1].0 * (values[1].1 * values[1].2) + ... + values[n].0 * (values[n].1 * values[n].2)`.
1112 /// * `ctx`: [Context] to add the constraints to
1113 /// * `values`: Iterator of tuples `(coeff, a, b)` where `coeff` is a field element, `a` and `b` are [QuantumCell]'s
1114 /// * `var`: [QuantumCell] that represents the value of a variable added to the sum
1115 fn sum_products_with_coeff_and_var(
1116 &self,
1117 ctx: &mut Context<F>,
1118 values: impl IntoIterator<Item = (F, QuantumCell<F>, QuantumCell<F>)>,
1119 var: QuantumCell<F>,
1120 ) -> AssignedValue<F> {
1121 // Create an iterator starting with `var` and
1122 let (a, b): (Vec<_>, Vec<_>) = std::iter::once((var, Constant(F::ONE)))
1123 .chain(values.into_iter().filter_map(|(c, va, vb)| {
1124 if c == F::ONE {
1125 Some((va, vb))
1126 } else if c != F::ZERO {
1127 let prod = self.mul(ctx, va, vb);
1128 Some((QuantumCell::Existing(prod), Constant(c)))
1129 } else {
1130 None
1131 }
1132 }))
1133 .unzip();
1134 self.inner_product(ctx, a, b)
1135 }
1136
1137 /// Constrains and returns `sel ? a : b` assuming `sel` is boolean.
1138 ///
1139 /// Defines a vertical gate of form `| 1 - sel | sel | 1 | a | 1 - sel | sel | 1 | b | out |`, where out = sel * a + (1 - sel) * b.
1140 /// * `ctx`: [Context] to add the constraints to
1141 /// * `a`: [QuantumCell] that contains a boolean value
1142 /// * `b`: [QuantumCell] that contains a boolean value
1143 /// * `sel`: [QuantumCell] that contains a boolean value
1144 fn select(
1145 &self,
1146 ctx: &mut Context<F>,
1147 a: impl Into<QuantumCell<F>>,
1148 b: impl Into<QuantumCell<F>>,
1149 sel: impl Into<QuantumCell<F>>,
1150 ) -> AssignedValue<F> {
1151 let a = a.into();
1152 let b = b.into();
1153 let sel = sel.into();
1154 let diff_val = *a.value() - b.value();
1155 let out_val = diff_val * sel.value() + b.value();
1156 // | a - b | 1 | b | a |
1157 // | b | sel | a - b | out |
1158 let cells = [
1159 Witness(diff_val),
1160 Constant(F::ONE),
1161 b,
1162 a,
1163 b,
1164 sel,
1165 Witness(diff_val),
1166 Witness(out_val),
1167 ];
1168 ctx.assign_region_smart(cells, [0, 4], [(0, 6), (2, 4)], []);
1169 ctx.last().unwrap()
1170 }
1171
1172 /// Constains and returns `a || (b && c)`, assuming `a`, `b` and `c` are boolean.
1173 ///
1174 /// Defines a vertical gate of form `| 1 - b c | b | c | 1 | a - 1 | 1 - b c | out | a - 1 | 1 | 1 | a |`, where out = a + b * c - a * b * c.
1175 /// * `ctx`: [Context] to add the constraints to
1176 /// * `a`: [QuantumCell] that contains a boolean value
1177 /// * `b`: [QuantumCell] that contains a boolean value
1178 /// * `c`: [QuantumCell] that contains a boolean value
1179 fn or_and(
1180 &self,
1181 ctx: &mut Context<F>,
1182 a: impl Into<QuantumCell<F>>,
1183 b: impl Into<QuantumCell<F>>,
1184 c: impl Into<QuantumCell<F>>,
1185 ) -> AssignedValue<F> {
1186 let a = a.into();
1187 let b = b.into();
1188 let c = c.into();
1189 let bc_val = *b.value() * c.value();
1190 let not_bc_val = F::ONE - bc_val;
1191 let not_a_val = *a.value() - F::ONE;
1192 let out_val = bc_val + a.value() - bc_val * a.value();
1193 let cells = [
1194 Witness(not_bc_val),
1195 b,
1196 c,
1197 Constant(F::ONE),
1198 Witness(not_a_val),
1199 Witness(not_bc_val),
1200 Witness(out_val),
1201 Witness(not_a_val),
1202 Constant(F::ONE),
1203 Constant(F::ONE),
1204 a,
1205 ];
1206 ctx.assign_region_smart(cells, [0, 3, 7], [(4, 7), (0, 5)], []);
1207 ctx.get(-5)
1208 }
1209
1210 /// Constrains and returns little-endian bit vector representation of `a`.
1211 ///
1212 /// Assumes `range_bits >= number of bits in a`.
1213 /// * `a`: [QuantumCell] of the value to convert
1214 /// * `range_bits`: range of bits needed to represent `a`. Assumes `range_bits > 0`.
1215 fn num_to_bits(
1216 &self,
1217 ctx: &mut Context<F>,
1218 a: AssignedValue<F>,
1219 range_bits: usize,
1220 ) -> Vec<AssignedValue<F>> {
1221 let bits = a.value().to_u64_limbs(range_bits, 1).into_iter().map(|x| Witness(F::from(x)));
1222
1223 let mut bit_cells = Vec::with_capacity(range_bits);
1224 let row_offset = ctx.advice.len();
1225 let acc = self.inner_product(
1226 ctx,
1227 bits,
1228 self.pow_of_two[..range_bits].iter().map(|c| Constant(*c)),
1229 );
1230 ctx.constrain_equal(&a, &acc);
1231 debug_assert!(range_bits > 0);
1232 bit_cells.push(ctx.get(row_offset as isize));
1233 for i in 1..range_bits {
1234 bit_cells.push(ctx.get((row_offset + 1 + 3 * (i - 1)) as isize));
1235 }
1236
1237 for bit_cell in &bit_cells {
1238 self.assert_bit(ctx, *bit_cell);
1239 }
1240 bit_cells
1241 }
1242
1243 /// Constrains and computes `a^exp` where both `a, exp` are witnesses. The exponent is computed in the native field `F`.
1244 ///
1245 /// Constrains that `exp` has at most `max_bits` bits.
1246 fn pow_var(
1247 &self,
1248 ctx: &mut Context<F>,
1249 a: AssignedValue<F>,
1250 exp: AssignedValue<F>,
1251 max_bits: usize,
1252 ) -> AssignedValue<F> {
1253 let exp_bits = self.num_to_bits(ctx, exp, max_bits);
1254 // standard square-and-mul approach
1255 let mut acc = ctx.load_constant(F::ONE);
1256 for (i, bit) in exp_bits.into_iter().rev().enumerate() {
1257 if i > 0 {
1258 // square
1259 acc = self.mul(ctx, acc, acc);
1260 }
1261 let mul = self.mul(ctx, acc, a);
1262 acc = self.select(ctx, mul, acc, bit);
1263 }
1264 acc
1265 }
1266}