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