Skip to main content

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}