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