kimchi/circuits/polynomials/and.rs
1//! This module includes the AND gadget implementation and the witness creation code.
2//! Note that this module does not need any new gate type for the AND operation.
3use alloc::{vec, vec::Vec};
4use core::array;
5
6use super::{
7 generic::GenericGateSpec,
8 xor::{init_xor, num_xors},
9};
10use crate::circuits::{
11 gate::{CircuitGate, Connect},
12 lookup::{
13 self,
14 tables::{GateLookupTable, LookupTable},
15 },
16 polynomial::COLUMNS,
17 wires::Wire,
18};
19use ark_ff::PrimeField;
20use num_bigint::BigUint;
21use o1_utils::{BigUintFieldHelpers, BigUintHelpers, BitwiseOps, FieldHelpers, Two};
22
23//~ We implement the AND gadget making use of the XOR gadget and the Generic gate. A new gate type is not needed, but we could potentially
24//~ add an `And16` gate type reusing the same ideas of `Xor16` so as to save one final generic gate, at the cost of one additional AND
25//~ lookup table that would have the same size as that of the Xor.
26//~ For now, we are willing to pay this small overhead and produce AND gadget as follows:
27//~
28//~ We observe that we can express bitwise addition as follows:
29//~ $$A + B = (A \oplus B) + 2 \cdot (A \wedge B)$$
30//~
31//~ where $\oplus$ is the bitwise XOR operation, $\wedge$ is the bitwise AND operation, and $+$ is the addition operation.
32//~ In other words, the value of the addition is nothing but the XOR of its operands, plus the carry bit if both operands are 1.
33//~ Thus, we can rewrite the above equation to obtain a definition of the AND operation as follows:
34//~ $$A \wedge B = \frac{A + B - (A \oplus B)}{2}$$
35//~ Let us define the following operations for better readability:
36//~
37//~ ```text
38//~ a + b = sum
39//~ a x b = xor
40//~ a ^ b = and
41//~ ```
42//~
43//~ Then, we can rewrite the above equation as follows:
44//~ $$ 2 \cdot and = sum - xor $$
45//~ which can be expressed as a double generic gate.
46//~
47//~ Then, our AND gadget for $n$ bytes looks as follows:
48//~
49//~ * $n/8$ Xor16 gates
50//~ * 1 (single) Generic gate to check that the final row of the XOR chain is all zeros.
51//~ * 1 (double) Generic gate to check sum $a + b = sum$ and the conjunction equation $2\cdot and = sum - xor$.
52//~
53//~ Finally, we connect the wires in the following positions (apart from the ones already connected for the XOR gates):
54//~
55//~ * Column 2 of the first Xor16 row (the output of the XOR operation) is connected to the right input of the second generic operation of the last row.
56//~ * Column 2 of the first generic operation of the last row is connected to the left input of the second generic operation of the last row.
57//~ Meaning,
58//~
59//~ * the `xor` in `a x b = xor` is connected to the `xor` in `2 \cdot and = sum - xor`
60//~ * the `sum` in `a + b = sum` is connected to the `sum` in `2 \cdot and = sum - xor`
61
62impl<F: PrimeField> CircuitGate<F> {
63 /// Extends an AND gadget for `bytes` length.
64 /// The full operation being performed is the following:
65 /// `a AND b = 1/2 * (a + b - (a XOR b))`
66 /// Includes:
67 /// - num_xors Xor16 gates to perform `xor = a XOR b`
68 /// - 1 Generic gate to constrain the final row to be zero with itself
69 /// - 1 double Generic gate to perform the AND operation as `a + b = sum` and `2 * and = sum - xor`
70 ///
71 /// Input:
72 /// - gates : vector of circuit gates comprising the full circuit
73 /// - bytes : number of bytes of the AND operation
74 ///
75 /// Output:
76 /// - next_row : next row after this gate
77 ///
78 /// Warning:
79 /// - if there's any public input for the and, don't forget to wire it
80 pub fn extend_and(gates: &mut Vec<Self>, bytes: usize) -> usize {
81 assert!(bytes > 0, "Bytes must be a positive number");
82 let xor_row = gates.len();
83 let and_row = Self::extend_xor_gadget(gates, bytes * 8);
84 let (_, mut and_gates) = Self::create_and(and_row, bytes);
85 // extend the whole circuit with the AND gadget
86 gates.append(&mut and_gates);
87
88 // connect the XOR inputs to the inputs of the first generic gate
89 gates.connect_cell_pair((xor_row, 0), (and_row, 0));
90 gates.connect_cell_pair((xor_row, 1), (and_row, 1));
91 // connect the sum output of the first generic gate to the left input of the second generic gate
92 gates.connect_cell_pair((and_row, 2), (and_row, 3));
93 // connect the XOR output to the right input of the second generic gate
94 gates.connect_cell_pair((xor_row, 2), (and_row, 4));
95
96 gates.len()
97 }
98
99 // Creates an AND gadget for `bytes` length.
100 // The full operation being performed is the following:
101 // `a AND b = 1/2 * (a + b - (a XOR b))`
102 // Includes:
103 // - 1 double Generic gate to perform the AND operation as `a + b = sum` and `2 * and = sum - xor`
104 // Input:
105 // - new_row : row where the AND generic gate starts
106 // - bytes : number of bytes of the AND operation
107 // Outputs tuple (next_row, circuit_gates) where
108 // - next_row : next row after this gate
109 // - gates : vector of circuit gates comprising the AND double generic gate
110 // Warning:
111 // - don't forget to connect the wiring from the and
112 fn create_and(new_row: usize, bytes: usize) -> (usize, Vec<Self>) {
113 assert!(bytes > 0, "Bytes must be a positive number");
114
115 // a + b = sum
116 let sum = GenericGateSpec::Add {
117 left_coeff: None,
118 right_coeff: None,
119 output_coeff: None,
120 };
121 // 2 * and = sum - xor
122 let and = GenericGateSpec::Add {
123 left_coeff: None,
124 right_coeff: Some(-F::one()),
125 output_coeff: Some(-F::two()),
126 };
127 let gates = vec![(Self::create_generic_gadget(Wire::for_row(new_row), sum, Some(and)))];
128
129 (new_row + gates.len(), gates)
130 }
131}
132
133/// Get the AND lookup table
134pub fn lookup_table<F: PrimeField>() -> LookupTable<F> {
135 lookup::tables::get_table::<F>(GateLookupTable::Xor)
136}
137
138/// Create a And for inputs as field elements starting at row 0
139/// Input: first input, second input, and desired byte length
140/// Panics if the input is too large for the chosen number of bytes
141pub fn create_and_witness<F: PrimeField>(input1: F, input2: F, bytes: usize) -> [Vec<F>; COLUMNS] {
142 let input1_big = input1.to_biguint();
143 let input2_big = input2.to_biguint();
144 if bytes * 8 < input1_big.bitlen() || bytes * 8 < input2_big.bitlen() {
145 panic!("Bytes must be greater or equal than the inputs length");
146 }
147
148 // Compute BigUint output of AND, XOR
149 let big_and = BigUint::bitwise_and(&input1_big, &input2_big, bytes);
150 let big_xor = BigUint::bitwise_xor(&input1_big, &input2_big);
151 // Transform BigUint values to field elements
152 let xor = big_xor.to_field().unwrap();
153 let and = big_and.to_field().unwrap();
154 let sum = input1 + input2;
155
156 let and_row = num_xors(bytes * 8) + 1;
157 let mut and_witness: [Vec<F>; COLUMNS] = array::from_fn(|_| vec![F::zero(); and_row + 1]);
158
159 init_xor(&mut and_witness, 0, bytes * 8, (input1, input2, xor));
160 // Fill in double generic witness
161 and_witness[0][and_row] = input1;
162 and_witness[1][and_row] = input2;
163 and_witness[2][and_row] = sum;
164 and_witness[3][and_row] = sum;
165 and_witness[4][and_row] = xor;
166 and_witness[5][and_row] = and;
167
168 and_witness
169}
170
171/// Extends an AND witness to the whole witness
172/// Input: first input, second input, and desired byte length
173/// Panics if the input is too large for the chosen number of bytes
174pub fn extend_and_witness<F: PrimeField>(
175 witness: &mut [Vec<F>; COLUMNS],
176 input1: F,
177 input2: F,
178 bytes: usize,
179) {
180 let and_witness = create_and_witness(input1, input2, bytes);
181 for col in 0..COLUMNS {
182 witness[col].extend(and_witness[col].iter());
183 }
184}