halo2_axiom/plonk/
lookup.rs

1use super::circuit::Expression;
2use ff::Field;
3use std::fmt::{self, Debug};
4
5pub(crate) mod prover;
6pub(crate) mod verifier;
7
8#[derive(Clone)]
9pub struct Argument<F: Field> {
10    pub(crate) name: String,
11    pub(crate) input_expressions: Vec<Expression<F>>,
12    pub(crate) table_expressions: Vec<Expression<F>>,
13}
14
15impl<F: Field> Debug for Argument<F> {
16    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
17        f.debug_struct("Argument")
18            .field("input_expressions", &self.input_expressions)
19            .field("table_expressions", &self.table_expressions)
20            .finish()
21    }
22}
23
24impl<F: Field> Argument<F> {
25    /// Constructs a new lookup argument.
26    ///
27    /// `table_map` is a sequence of `(input, table)` tuples.
28    pub fn new<S: AsRef<str>>(name: S, table_map: Vec<(Expression<F>, Expression<F>)>) -> Self {
29        let (input_expressions, table_expressions) = table_map.into_iter().unzip();
30        Argument {
31            name: name.as_ref().to_string(),
32            input_expressions,
33            table_expressions,
34        }
35    }
36
37    pub(crate) fn required_degree(&self) -> usize {
38        assert_eq!(self.input_expressions.len(), self.table_expressions.len());
39
40        // The first value in the permutation poly should be one.
41        // degree 2:
42        // l_0(X) * (1 - z(X)) = 0
43        //
44        // The "last" value in the permutation poly should be a boolean, for
45        // completeness and soundness.
46        // degree 3:
47        // l_last(X) * (z(X)^2 - z(X)) = 0
48        //
49        // Enable the permutation argument for only the rows involved.
50        // degree (2 + input_degree + table_degree) or 4, whichever is larger:
51        // (1 - (l_last(X) + l_blind(X))) * (
52        //   z(\omega X) (a'(X) + \beta) (s'(X) + \gamma)
53        //   - z(X) (\theta^{m-1} a_0(X) + ... + a_{m-1}(X) + \beta) (\theta^{m-1} s_0(X) + ... + s_{m-1}(X) + \gamma)
54        // ) = 0
55        //
56        // The first two values of a' and s' should be the same.
57        // degree 2:
58        // l_0(X) * (a'(X) - s'(X)) = 0
59        //
60        // Either the two values are the same, or the previous
61        // value of a' is the same as the current value.
62        // degree 3:
63        // (1 - (l_last(X) + l_blind(X))) * (a′(X) − s′(X))⋅(a′(X) − a′(\omega^{-1} X)) = 0
64        let mut input_degree = 1;
65        for expr in self.input_expressions.iter() {
66            input_degree = std::cmp::max(input_degree, expr.degree());
67        }
68        let mut table_degree = 1;
69        for expr in self.table_expressions.iter() {
70            table_degree = std::cmp::max(table_degree, expr.degree());
71        }
72
73        // In practice because input_degree and table_degree are initialized to
74        // one, the latter half of this max() invocation is at least 4 always,
75        // rendering this call pointless except to be explicit in case we change
76        // the initialization of input_degree/table_degree in the future.
77        std::cmp::max(
78            // (1 - (l_last + l_blind)) z(\omega X) (a'(X) + \beta) (s'(X) + \gamma)
79            4,
80            // (1 - (l_last + l_blind)) z(X) (\theta^{m-1} a_0(X) + ... + a_{m-1}(X) + \beta) (\theta^{m-1} s_0(X) + ... + s_{m-1}(X) + \gamma)
81            2 + input_degree + table_degree,
82        )
83    }
84
85    /// Returns input of this argument
86    pub fn input_expressions(&self) -> &Vec<Expression<F>> {
87        &self.input_expressions
88    }
89
90    /// Returns table of this argument
91    pub fn table_expressions(&self) -> &Vec<Expression<F>> {
92        &self.table_expressions
93    }
94
95    /// Returns name of this argument
96    pub fn name(&self) -> &str {
97        &self.name
98    }
99}