sp1_core_machine/operations/
is_zero.rs

1//! An operation to check if the input is 0.
2//!
3//! This is guaranteed to return 1 if and only if the input is 0.
4//!
5//! The idea is that 1 - input * inverse is exactly the boolean value indicating whether the input
6//! is 0.
7use p3_air::AirBuilder;
8use p3_field::{AbstractField, Field};
9use sp1_derive::AlignedBorrow;
10
11use sp1_stark::air::SP1AirBuilder;
12
13/// A set of columns needed to compute whether the given word is 0.
14#[derive(AlignedBorrow, Default, Debug, Clone, Copy)]
15#[repr(C)]
16pub struct IsZeroOperation<T> {
17    /// The inverse of the input.
18    pub inverse: T,
19
20    /// Result indicating whether the input is 0. This equals `inverse * input == 0`.
21    pub result: T,
22}
23
24impl<F: Field> IsZeroOperation<F> {
25    pub fn populate(&mut self, a: u32) -> u32 {
26        self.populate_from_field_element(F::from_canonical_u32(a))
27    }
28
29    pub fn populate_from_field_element(&mut self, a: F) -> u32 {
30        if a == F::zero() {
31            self.inverse = F::zero();
32            self.result = F::one();
33        } else {
34            self.inverse = a.inverse();
35            self.result = F::zero();
36        }
37        let prod = self.inverse * a;
38        debug_assert!(prod == F::one() || prod == F::zero());
39        (a == F::zero()) as u32
40    }
41
42    pub fn eval<AB: SP1AirBuilder>(
43        builder: &mut AB,
44        a: AB::Expr,
45        cols: IsZeroOperation<AB::Var>,
46        is_real: AB::Expr,
47    ) {
48        let one: AB::Expr = AB::F::one().into();
49
50        // 1. Input == 0 => is_zero = 1 regardless of the inverse.
51        // 2. Input != 0
52        //   2.1. inverse is correctly set => is_zero = 0.
53        //   2.2. inverse is incorrect
54        //     2.2.1 inverse is nonzero => is_zero isn't bool, it fails.
55        //     2.2.2 inverse is 0 => is_zero is 1. But then we would assert that a = 0. And that
56        //                           assert fails.
57
58        // If the input is 0, then any product involving it is 0. If it is nonzero and its inverse
59        // is correctly set, then the product is 1.
60        let is_zero = one.clone() - cols.inverse * a.clone();
61        builder.when(is_real.clone()).assert_eq(is_zero, cols.result);
62        builder.when(is_real.clone()).assert_bool(cols.result);
63
64        // If the result is 1, then the input is 0.
65        builder.when(is_real.clone()).when(cols.result).assert_zero(a.clone());
66    }
67}