use alloc::vec::Vec;
use core::marker::PhantomData;
use p3_binary_field::{BinaryField64, BinaryField128, Ghash128, Poly64};
use p3_challenger::fs::TranscriptField;
use p3_challenger::{FieldChallenger, GrindingChallenger};
use p3_field::{ExtensionField, Field};
use p3_maybe_rayon::prelude::*;
use p3_multilinear_util::point::Point;
use p3_multilinear_util::poly::Poly;
use super::{Basis, SumcheckProver, VariableOrder};
use crate::SumcheckData;
use crate::product_polynomial::ProductPolynomial;
use crate::transcript::{ProverTranscript, SumcheckShape};
#[derive(Debug, Clone)]
pub struct ReprSumcheckProver<F, EF, R: Field> {
inner: SumcheckProver<R, R>,
_transcript: PhantomData<(F, EF)>,
}
impl<F, EF, R> ReprSumcheckProver<F, EF, R>
where
F: Field,
EF: ExtensionField<F>,
R: IntoTranscriptField<EF>,
{
#[tracing::instrument(skip_all)]
pub fn new(prover: SumcheckProver<F, EF>) -> Self {
let SumcheckProver {
poly,
sum,
outstanding,
} = prover;
let (order, evals, weights) = poly.into_scalar_tables();
let evals = Poly::new(R::from_table(evals.into_evals()));
let weights = Poly::new(R::from_table(weights.into_evals()));
Self {
inner: SumcheckProver {
poly: ProductPolynomial::new_unpacked(order, evals, weights),
sum: R::from(sum),
outstanding: outstanding.map(R::from),
},
_transcript: PhantomData,
}
}
#[tracing::instrument(skip_all)]
pub fn from_tables(order: VariableOrder, evals: Poly<EF>, weights: Poly<R>, sum: EF) -> Self {
let evals = Poly::new(R::from_table(evals.into_evals()));
Self::from_repr_tables(order, evals, weights, sum)
}
pub(crate) fn from_repr_tables(
order: VariableOrder,
evals: Poly<R>,
weights: Poly<R>,
sum: EF,
) -> Self {
let poly = ProductPolynomial::new_unpacked(order, evals, weights);
Self {
inner: SumcheckProver::new(poly, R::from(sum)),
_transcript: PhantomData,
}
}
pub fn claimed_sum(&self) -> EF {
self.inner.claimed_sum().into_transcript()
}
pub fn num_variables(&self) -> usize {
self.inner.num_variables()
}
pub fn settle(&mut self) {
self.inner.settle();
}
#[tracing::instrument(skip_all, level = "debug")]
pub fn compute_sumcheck_polynomials<Challenger>(
&mut self,
sumcheck_data: &mut SumcheckData<F, EF>,
challenger: &mut Challenger,
folding_factor: usize,
pow_bits: usize,
) -> Point<EF>
where
F: TranscriptField,
Challenger: FieldChallenger<F> + GrindingChallenger<Witness = F>,
{
let shape = SumcheckShape::new(folding_factor, pow_bits, Basis::Evaluation);
let mut transcript = ProverTranscript::<Challenger, F, EF>::new(challenger, shape);
let challenges = (0..folding_factor)
.map(|_| self.round(sumcheck_data, &mut transcript))
.collect();
transcript.finish();
Point::new(challenges)
}
pub(crate) fn round<Challenger>(
&mut self,
sumcheck_data: &mut SumcheckData<F, EF>,
transcript: &mut ProverTranscript<'_, Challenger, F, EF>,
) -> EF
where
F: TranscriptField,
Challenger: FieldChallenger<F> + GrindingChallenger<Witness = F>,
{
let (c_a, c_inf) = self.inner.measure_round();
let r = sumcheck_data.observe_and_sample(
transcript,
c_a.into_transcript(),
c_inf.into_transcript(),
);
let r_repr = R::from(r);
debug_assert_eq!(r_repr.into_transcript(), r);
self.inner.sum = Basis::Evaluation.reduce_claim(c_a, c_inf, r_repr, self.inner.sum);
self.inner.hold(r_repr);
r
}
pub(crate) fn weights(&mut self) -> Poly<R> {
self.inner.weights()
}
pub(crate) fn evals(&mut self) -> Poly<R> {
self.inner.evals()
}
}
pub trait FromTable<EF: Copy + Send + Sync>: From<EF> + Send {
fn from_table(table: Vec<EF>) -> Vec<Self> {
let image = table.par_iter().map(|&x| Self::from(x)).collect();
drop(table);
image
}
#[inline(always)]
fn try_map_coordinates_into(
_images: &[Self; 128],
_input: &[EF],
_output: &mut [Self],
) -> bool {
false
}
}
pub trait IntoTranscriptField<EF: Copy + Send + Sync>: Field + FromTable<EF> {
fn into_transcript(self) -> EF;
}
impl<EF, R> IntoTranscriptField<EF> for R
where
EF: Copy + Send + Sync + From<R>,
R: Field + FromTable<EF>,
{
fn into_transcript(self) -> EF {
EF::from(self)
}
}
impl<T: Field> FromTable<T> for T {
fn from_table(table: Vec<Self>) -> Vec<Self> {
table
}
}
impl FromTable<BinaryField64> for Poly64 {
fn from_table(table: Vec<BinaryField64>) -> Vec<Self> {
Self::from_tower_vec(table)
}
}
impl FromTable<BinaryField128> for Ghash128 {
fn from_table(table: Vec<BinaryField128>) -> Vec<Self> {
Self::from_tower_vec(table)
}
#[inline]
fn try_map_coordinates_into(
images: &[Self; 128],
input: &[BinaryField128],
output: &mut [Self],
) -> bool {
Self::try_apply_linear_map_into(images, input, output)
}
}
#[cfg(test)]
mod tests {
use alloc::vec::Vec;
use p3_binary_field::{BinaryChallenger, BinaryField128, Ghash128};
use p3_challenger::{CanSample, HashChallenger};
use p3_field::Field;
use p3_keccak::Keccak256Hash;
use p3_multilinear_util::poly::Poly;
use rand::rngs::SmallRng;
use rand::{RngExt, SeedableRng};
use super::{FromTable, ReprSumcheckProver};
use crate::SumcheckData;
use crate::product_polynomial::ProductPolynomial;
use crate::strategy::{SumcheckProver, VariableOrder};
type F = BinaryField128;
type Ch = BinaryChallenger<F, HashChallenger<u8, Keccak256Hash, 32>>;
fn fresh_challenger() -> Ch {
Ch::from_hasher(Vec::new(), Keccak256Hash)
}
fn random_prover(
order: VariableOrder,
num_variables: usize,
seed: u64,
) -> SumcheckProver<F, F> {
let mut rng = SmallRng::seed_from_u64(seed);
let evals: Vec<F> = (0..1 << num_variables).map(|_| rng.random()).collect();
let weights: Vec<F> = (0..1 << num_variables).map(|_| rng.random()).collect();
let poly = match order {
VariableOrder::Prefix => {
ProductPolynomial::new_packed(order, Poly::new(evals), Poly::new(weights))
}
VariableOrder::Suffix => {
ProductPolynomial::new_unpacked(order, Poly::new(evals), Poly::new(weights))
}
};
let sum = poly.dot_product();
SumcheckProver::new(poly, sum)
}
fn assert_repr_rounds_play_the_challenge_field_transcript<R>()
where
R: Field + FromTable<F>,
F: From<R>,
{
let batches = [1, 2, 1, 3, 1, 1];
let pow_bits = 0;
let num_variables: usize = batches.iter().sum();
for order in [VariableOrder::Prefix, VariableOrder::Suffix] {
for split in 0..3 {
let mut reference = random_prover(order, num_variables, 7);
let mut reference_data = SumcheckData::default();
let mut reference_challenger = fresh_challenger();
let mut handoff = random_prover(order, num_variables, 7);
let mut handoff_data = SumcheckData::default();
let mut handoff_challenger = fresh_challenger();
for &rounds in &batches[..split] {
let _ = handoff.compute_sumcheck_polynomials(
&mut handoff_data,
&mut handoff_challenger,
rounds,
pow_bits,
None,
);
}
let head = batches[..split].iter().sum::<usize>();
let mut repr = ReprSumcheckProver::<F, F, R>::new(handoff);
let mut played = 0;
for (batch, &rounds) in batches.iter().enumerate() {
let expected = reference.compute_sumcheck_polynomials(
&mut reference_data,
&mut reference_challenger,
rounds,
pow_bits,
None,
);
played += rounds;
if batch == 3 {
reference.settle();
}
if played <= head {
continue;
}
let got = repr.compute_sumcheck_polynomials(
&mut handoff_data,
&mut handoff_challenger,
rounds,
pow_bits,
);
if batch == 3 {
repr.settle();
}
assert_eq!(got, expected, "{order:?} split {split}");
assert_eq!(repr.claimed_sum(), reference.claimed_sum());
assert_eq!(repr.num_variables(), reference.num_variables());
}
assert_eq!(
handoff_data.polynomial_evaluations,
reference_data.polynomial_evaluations
);
assert_eq!(handoff_data.pow_witnesses, reference_data.pow_witnesses);
assert_eq!(
CanSample::<F>::sample(&mut handoff_challenger),
CanSample::<F>::sample(&mut reference_challenger)
);
reference.settle();
repr.settle();
assert_eq!(repr.claimed_sum(), reference.claimed_sum());
}
}
}
#[test]
fn repr_rounds_play_the_challenge_field_transcript() {
assert_repr_rounds_play_the_challenge_field_transcript::<Ghash128>();
assert_repr_rounds_play_the_challenge_field_transcript::<F>();
}
}