arnaucube
arnaucube/hyperplonkPublic Self-hosted repository
hyperplonk/poly-iop/src/prod_check/util.rs
poly-iop/src/prod_check/util.rsRaw
  1//! This module implements useful functions for the product check protocol.
  2
  3use crate::{
  4    errors::PolyIOPErrors, structs::IOPProof, utils::get_index, zero_check::ZeroCheck, PolyIOP,
  5};
  6use arithmetic::VirtualPolynomial;
  7use ark_ff::PrimeField;
  8use ark_poly::DenseMultilinearExtension;
  9use ark_std::{end_timer, start_timer};
 10use std::rc::Rc;
 11use transcript::IOPTranscript;
 12
 13/// Compute the product polynomial `prod(x)` where
 14///
 15///  - `prod(0,x) := prod(0, x1, …, xn)` is the MLE over the
 16/// evaluations of `f(x)/g(x)` on the boolean hypercube {0,1}^n
 17///
 18/// - `prod(1,x)` is a MLE over the evaluations of `prod(x, 0) * prod(x, 1)`
 19/// on the boolean hypercube {0,1}^n
 20///
 21/// The caller needs to check num_vars matches in f and g
 22/// Cost: linear in N.
 23pub(super) fn compute_product_poly<F: PrimeField>(
 24    fx: &Rc<DenseMultilinearExtension<F>>,
 25    gx: &Rc<DenseMultilinearExtension<F>>,
 26) -> Result<Rc<DenseMultilinearExtension<F>>, PolyIOPErrors> {
 27    let start = start_timer!(|| "compute evaluations of prod polynomial");
 28    let num_vars = fx.num_vars;
 29
 30    // ===================================
 31    // prod(0, x)
 32    // ===================================
 33    let prod_0x_eval = compute_prod_0(fx, gx)?;
 34
 35    // ===================================
 36    // prod(1, x)
 37    // ===================================
 38    //
 39    // `prod(1, x)` can be computed via recursing the following formula for 2^n-1
 40    // times
 41    //
 42    // `prod(1, x_1, ..., x_n) :=
 43    //      prod(x_1, x_2, ..., x_n, 0) * prod(x_1, x_2, ..., x_n, 1)`
 44    //
 45    // At any given step, the right hand side of the equation
 46    // is available via either eval_0x or the current view of eval_1x
 47    let mut prod_1x_eval = vec![];
 48    for x in 0..(1 << num_vars) - 1 {
 49        // sign will decide if the evaluation should be looked up from eval_0x or
 50        // eval_1x; x_zero_index is the index for the evaluation (x_2, ..., x_n,
 51        // 0); x_one_index is the index for the evaluation (x_2, ..., x_n, 1);
 52        let (x_zero_index, x_one_index, sign) = get_index(x, num_vars);
 53        if !sign {
 54            prod_1x_eval.push(prod_0x_eval[x_zero_index] * prod_0x_eval[x_one_index]);
 55        } else {
 56            // sanity check: if we are trying to look up from the eval_1x table,
 57            // then the target index must already exist
 58            if x_zero_index >= prod_1x_eval.len() || x_one_index >= prod_1x_eval.len() {
 59                return Err(PolyIOPErrors::ShouldNotArrive);
 60            }
 61            prod_1x_eval.push(prod_1x_eval[x_zero_index] * prod_1x_eval[x_one_index]);
 62        }
 63    }
 64
 65    // prod(1, 1, ..., 1) := 0
 66    prod_1x_eval.push(F::zero());
 67
 68    // ===================================
 69    // prod(x)
 70    // ===================================
 71    // prod(x)'s evaluation is indeed `e := [eval_0x[..], eval_1x[..]].concat()`
 72    let eval = [prod_0x_eval.as_slice(), prod_1x_eval.as_slice()].concat();
 73
 74    let prod_x = Rc::new(DenseMultilinearExtension::from_evaluations_vec(
 75        num_vars + 1,
 76        eval,
 77    ));
 78
 79    end_timer!(start);
 80    Ok(prod_x)
 81}
 82
 83/// generate the zerocheck proof for the virtual polynomial
 84///    prod(1, x) - prod(x, 0) * prod(x, 1) + alpha * (f(x) - prod(0, x) * g(x))
 85///
 86/// Returns proof and Q(x) for testing purpose.
 87///
 88/// Cost: O(N)
 89pub(super) fn prove_zero_check<F: PrimeField>(
 90    fx: &Rc<DenseMultilinearExtension<F>>,
 91    gx: &Rc<DenseMultilinearExtension<F>>,
 92    prod_x: &Rc<DenseMultilinearExtension<F>>,
 93    alpha: &F,
 94    transcript: &mut IOPTranscript<F>,
 95) -> Result<(IOPProof<F>, VirtualPolynomial<F>), PolyIOPErrors> {
 96    let start = start_timer!(|| "zerocheck in product check");
 97
 98    let prod_partial_evals = build_prod_partial_eval(prod_x)?;
 99    let prod_0x = prod_partial_evals[0].clone();
100    let prod_1x = prod_partial_evals[1].clone();
101    let prod_x0 = prod_partial_evals[2].clone();
102    let prod_x1 = prod_partial_evals[3].clone();
103
104    // compute g(x) * prod(0, x) * alpha
105    let mut q_x = VirtualPolynomial::new_from_mle(gx, F::one());
106    q_x.mul_by_mle(prod_0x, *alpha)?;
107
108    //   g(x) * prod(0, x) * alpha
109    // - f(x) * alpha
110    q_x.add_mle_list([fx.clone()], -*alpha)?;
111
112    // Q(x) := prod(1,x) - prod(x, 0) * prod(x, 1)
113    //       + alpha * (
114    //             g(x) * prod(0, x)
115    //           - f(x))
116    q_x.add_mle_list([prod_x0, prod_x1], -F::one())?;
117    q_x.add_mle_list([prod_1x], F::one())?;
118
119    let iop_proof = <PolyIOP<F> as ZeroCheck<F>>::prove(&q_x, transcript)?;
120
121    end_timer!(start);
122    Ok((iop_proof, q_x))
123}
124
125/// Helper function of the IOP.
126///
127/// Input:
128/// - prod(x)
129///
130/// Output: the following 4 polynomials
131/// - prod(0, x)
132/// - prod(1, x)
133/// - prod(x, 0)
134/// - prod(x, 1)
135fn build_prod_partial_eval<F: PrimeField>(
136    prod_x: &Rc<DenseMultilinearExtension<F>>,
137) -> Result<[Rc<DenseMultilinearExtension<F>>; 4], PolyIOPErrors> {
138    let start = start_timer!(|| "build partial prod polynomial");
139
140    let prod_x_eval = &prod_x.evaluations;
141    let num_vars = prod_x.num_vars - 1;
142
143    // prod(0, x)
144    let prod_0_x = Rc::new(DenseMultilinearExtension::from_evaluations_slice(
145        num_vars,
146        &prod_x_eval[0..1 << num_vars],
147    ));
148    // prod(1, x)
149    let prod_1_x = Rc::new(DenseMultilinearExtension::from_evaluations_slice(
150        num_vars,
151        &prod_x_eval[1 << num_vars..1 << (num_vars + 1)],
152    ));
153
154    // ===================================
155    // prod(x, 0) and prod(x, 1)
156    // ===================================
157    //
158    // now we compute eval_x0 and eval_x1
159    // eval_0x will be the odd coefficients of eval
160    // and eval_1x will be the even coefficients of eval
161    let mut eval_x0 = vec![];
162    let mut eval_x1 = vec![];
163    for (x, &prod_x) in prod_x_eval.iter().enumerate() {
164        if x & 1 == 0 {
165            eval_x0.push(prod_x);
166        } else {
167            eval_x1.push(prod_x);
168        }
169    }
170    let prod_x_0 = Rc::new(DenseMultilinearExtension::from_evaluations_vec(
171        num_vars, eval_x0,
172    ));
173    let prod_x_1 = Rc::new(DenseMultilinearExtension::from_evaluations_vec(
174        num_vars, eval_x1,
175    ));
176
177    end_timer!(start);
178
179    Ok([prod_0_x, prod_1_x, prod_x_0, prod_x_1])
180}
181
182/// Returns the evaluations of
183/// - `prod(0,x) := prod(0, x1, …, xn)` which is the MLE over the
184/// evaluations of f(x)/g(x) on the boolean hypercube {0,1}^n:
185///
186/// The caller needs to check num_vars matches in f/g
187/// Cost: linear in N.
188fn compute_prod_0<F: PrimeField>(
189    fx: &DenseMultilinearExtension<F>,
190    gx: &DenseMultilinearExtension<F>,
191) -> Result<Vec<F>, PolyIOPErrors> {
192    let start = start_timer!(|| "compute prod(0,x)");
193
194    let mut prod_0x_evals = vec![];
195    for (&fi, &gi) in fx.iter().zip(gx.iter()) {
196        prod_0x_evals.push(fi / gi);
197    }
198
199    end_timer!(start);
200    Ok(prod_0x_evals)
201}