diff --git a/msm/src/serialization/mod.rs b/msm/src/serialization/mod.rs index 16451e06ac..6746f7a9ab 100644 --- a/msm/src/serialization/mod.rs +++ b/msm/src/serialization/mod.rs @@ -1,7 +1,6 @@ +use crate::{mvlookup::LookupTableID, MVLookup}; use ark_ff::Field; -use crate::mvlookup::LookupTableID; - /// The number of intermediate limbs of 4 bits required for the circuit pub const N_INTERMEDIATE_LIMBS: usize = 20; @@ -10,6 +9,7 @@ pub mod constraints; pub mod interpreter; pub mod witness; +#[derive(Clone, Copy, Debug)] pub enum LookupTable { RangeCheck15, RangeCheck4, @@ -24,15 +24,19 @@ impl LookupTableID for LookupTable { } } +pub type Lookup = MVLookup; + #[cfg(test)] mod tests { use kimchi::circuits::domains::EvaluationDomains; use poly_commitment::pairing_proof::PairingSRS; use rand::Rng as _; + use super::{Lookup, LookupTable}; + use crate::{ columns::Column, - lookups::LookupTableIDs, + mvlookup::MVLookupWitness, precomputed_srs::get_bn254_srs, proof::ProofInputs, prover::prove, @@ -44,6 +48,40 @@ mod tests { BaseSponge, Fp, OpeningProof, ScalarSponge, BN254, N_LIMBS, }; + use ark_ff::{FftField, Field, PrimeField}; + + impl LookupTable { + fn into_lookup_vector( + self, + domain: EvaluationDomains, + ) -> Vec> { + assert!(domain.d1.size >= (1 << 15)); + match self { + Self::RangeCheck15 => (0..(1 << 15)) + .map(|i| Lookup { + table_id: LookupTable::RangeCheck15, + numerator: -F::one(), + value: vec![F::from(i as u64)], + }) + .collect::>>(), + Self::RangeCheck4 => (0..(1 << 15)) + .map(|i| { + if i < (1 << 4) { + F::from(i as u64) + } else { + F::zero() + } + }) + .map(|x| Lookup { + table_id: LookupTable::RangeCheck4, + numerator: -F::one(), + value: vec![x], + }) + .collect::>>(), + } + } + } + #[test] fn test_completeness() { let mut rng = o1_utils::tests::make_test_rng(); @@ -77,7 +115,12 @@ mod tests { } let mut constraints = vec![]; - for limbs in field_elements { + + let mut rangecheck15: [Vec>; N_LIMBS] = std::array::from_fn(|_| vec![]); + let mut rangecheck4: [Vec>; N_INTERMEDIATE_LIMBS] = + std::array::from_fn(|_| vec![]); + + for (i, limbs) in field_elements.into_iter().enumerate() { let mut constraint_env = constraints::Env::::create(); // Witness deserialize_field_element(&mut witness_env, limbs); @@ -102,11 +145,83 @@ mod tests { constraints.push(cst.clone()) } } + + for (j, lookup) in witness_env.rangecheck4_lookups.iter().enumerate() { + rangecheck4[j].push(lookup.clone()) + } + + for (j, lookup) in witness_env.rangecheck15_lookups.iter().enumerate() { + rangecheck15[j].push(lookup.clone()) + } + + witness_env.add_rangecheck4_table_value(i); + + witness_env.reset() } + let rangecheck15_m = witness_env.get_rangecheck15_normalized_multipliticies(domain); + let rangecheck15_t = LookupTable::RangeCheck15 + .into_lookup_vector(domain) + .into_iter() + .enumerate() + .map( + |( + i, + Lookup { + table_id, + numerator, + value, + }, + )| { + Lookup { + table_id, + numerator: numerator * rangecheck15_m[i], + value, + } + }, + ); + + let rangecheck4_m = witness_env.get_rangecheck4_normalized_multipliticies(domain); + let rangecheck4_t = LookupTable::RangeCheck4 + .into_lookup_vector(domain) + .into_iter() + .enumerate() + .map( + |( + i, + Lookup { + table_id, + numerator, + value, + }, + )| { + Lookup { + table_id, + numerator: numerator * rangecheck4_m[i], + value, + } + }, + ); + + let lookup_witness_rangecheck4: MVLookupWitness = { + MVLookupWitness { + f: rangecheck4.to_vec(), + t: rangecheck4_t.collect(), + m: rangecheck4_m, + } + }; + + let lookup_witness_rangecheck15: MVLookupWitness = { + MVLookupWitness { + f: rangecheck15.to_vec(), + t: rangecheck15_t.collect(), + m: rangecheck15_m, + } + }; + let proof_inputs = ProofInputs { evaluations: *witness, - mvlookups: vec![], + mvlookups: vec![lookup_witness_rangecheck15, lookup_witness_rangecheck4], public_input_size: 0, }; @@ -118,7 +233,7 @@ mod tests { Column, _, SERIALIZATION_N_COLUMNS, - LookupTableIDs, + LookupTable, >(domain, &srs, &constraints, proof_inputs, &mut rng) .unwrap(); diff --git a/msm/src/serialization/witness.rs b/msm/src/serialization/witness.rs index 563d3cff7b..5b16a64c29 100644 --- a/msm/src/serialization/witness.rs +++ b/msm/src/serialization/witness.rs @@ -4,7 +4,10 @@ use o1_utils::FieldHelpers; use crate::columns::Column; use crate::serialization::interpreter::InterpreterEnv; +use crate::serialization::{Lookup, LookupTable}; use crate::N_LIMBS; +use kimchi::circuits::domains::EvaluationDomains; +use std::iter; use super::N_INTERMEDIATE_LIMBS; @@ -17,9 +20,26 @@ pub struct Env { /// field Kimchi gate pub intermediate_limbs: [Fp; N_INTERMEDIATE_LIMBS], + /// Keep track of the RangeCheck4 lookup multiplicities // Boxing to avoid stack overflow pub lookup_multiplicities_rangecheck4: Box<[Fp; 1 << 4]>, + + /// Keep track of the RangeCheck4 table multiplicities. + /// The value `0` is used as a (repeated) dummy value. + // Boxing to avoid stack overflow + pub lookup_t_multiplicities_rangecheck4: Box<[Fp; 1 << 4]>, + + /// Keep track of the RangeCheck15 lookup multiplicities + /// No t multiplicities as we do suppose we have a domain of + /// size `1 << 15` + // Boxing to avoid stack overflow pub lookup_multiplicities_rangecheck15: Box<[Fp; 1 << 15]>, + + /// Keep track of the rangecheck 4 lookups for each row. + pub rangecheck4_lookups: Vec>, + + /// Keep track of the rangecheck 15 lookups for each row. + pub rangecheck15_lookups: Vec>, } impl InterpreterEnv for Env { @@ -48,21 +68,29 @@ impl InterpreterEnv for Env { } fn range_check15(&mut self, value: &Self::Variable) { - // FIXME: this is not the full intended implementation let value_biguint = value.to_biguint(); assert!(value_biguint < BigUint::from(2u128.pow(15))); // Adding multiplicities let value_usize: usize = value_biguint.clone().try_into().unwrap(); self.lookup_multiplicities_rangecheck15[value_usize] += Fp::one(); + self.rangecheck15_lookups.push(Lookup { + table_id: LookupTable::RangeCheck15, + numerator: Fp::one(), + value: vec![*value], + }) } fn range_check4(&mut self, value: &Self::Variable) { - // FIXME: this is not the full intended implementation let value_biguint = value.to_biguint(); assert!(value_biguint < BigUint::from(2u128.pow(4))); // Adding multiplicities let value_usize: usize = value_biguint.clone().try_into().unwrap(); self.lookup_multiplicities_rangecheck4[value_usize] += Fp::one(); + self.rangecheck4_lookups.push(Lookup { + table_id: LookupTable::RangeCheck4, + numerator: Fp::one(), + value: vec![*value], + }) } fn copy(&mut self, x: &Self::Variable, position: Self::Position) -> Self::Variable { @@ -112,6 +140,43 @@ impl Env { } } } + + pub fn add_rangecheck4_table_value(&mut self, i: usize) { + if i < (1 << 4) { + self.lookup_t_multiplicities_rangecheck4[i] += Fp::one(); + } else { + self.lookup_t_multiplicities_rangecheck4[0] += Fp::one(); + } + } + + pub fn reset(&mut self) { + self.rangecheck15_lookups = vec![]; + self.rangecheck4_lookups = vec![]; + } + + pub fn get_rangecheck4_normalized_multipliticies( + &self, + domain: EvaluationDomains, + ) -> Vec { + let mut m = vec![Fp::zero(); 1 << 4]; + self.lookup_multiplicities_rangecheck4 + .into_iter() + .zip(self.lookup_t_multiplicities_rangecheck4.iter()) + .enumerate() + .for_each(|(i, (m_f, m_t))| m[i] = m_f / m_t); + let repeated_dummy_value: Vec = iter::repeat(m[0]) + .take((domain.d1.size - (1 << 4)) as usize) + .collect(); + m.extend(repeated_dummy_value); + m + } + + pub fn get_rangecheck15_normalized_multipliticies( + &self, + _domain: EvaluationDomains, + ) -> Vec { + self.lookup_multiplicities_rangecheck15.to_vec() + } } impl Env { @@ -120,8 +185,13 @@ impl Env { current_kimchi_limbs: [Fp::zero(); 3], msm_limbs: [Fp::zero(); N_LIMBS], intermediate_limbs: [Fp::zero(); N_INTERMEDIATE_LIMBS], + lookup_multiplicities_rangecheck4: Box::new([Fp::zero(); 1 << 4]), + lookup_t_multiplicities_rangecheck4: Box::new([Fp::zero(); 1 << 4]), + lookup_multiplicities_rangecheck15: Box::new([Fp::zero(); 1 << 15]), + rangecheck4_lookups: vec![], + rangecheck15_lookups: vec![], } } }