Skip to main content

p3_sumcheck/
commit.rs

1//! Base-field commitment used by the sumcheck opening protocol.
2
3use p3_challenger::CanObserve;
4use p3_commit::Mmcs;
5use p3_dft::TwoAdicSubgroupDft;
6use p3_field::TwoAdicField;
7use p3_matrix::Matrix;
8use p3_matrix::dense::{DenseMatrix, RowMajorMatrix, RowMajorMatrixView, RowMajorMatrixViewMut};
9use p3_multilinear_util::poly::Poly;
10use tracing::info_span;
11
12use crate::strategy::VariableOrder;
13
14/// Encodes and commits the initial base-field polynomial.
15///
16/// This is the first WHIR commitment. It lays out the polynomial according to
17/// the residual variable order, applies the Reed-Solomon expansion with `dft`,
18/// commits the resulting codeword matrix with `mmcs`, and observes the Merkle
19/// root in the transcript.
20///
21/// Prefix order transposes the local folding block before padding so the first
22/// folded variables become columns. Suffix order keeps the folding block as the
23/// row width and only zero-pads the row count.
24pub fn commit_base<F, Dft, MT, Challenger>(
25    order: VariableOrder,
26    dft: &Dft,
27    mmcs: &MT,
28    challenger: &mut Challenger,
29    poly: &Poly<F>,
30    folding: usize,
31    starting_log_inv_rate: usize,
32) -> (MT::Commitment, MT::ProverData<DenseMatrix<F>>)
33where
34    F: TwoAdicField,
35    Dft: TwoAdicSubgroupDft<F>,
36    MT: Mmcs<F>,
37    Challenger: CanObserve<MT::Commitment>,
38{
39    let num_variables = poly.num_variables();
40    let height = 1 << (num_variables + starting_log_inv_rate - folding);
41
42    let encoded = match order {
43        VariableOrder::Prefix => {
44            let padded = info_span!("transpose & pad").in_scope(|| {
45                let width = 1 << folding;
46
47                // Allocate the zero-padded codeword buffer once.
48                // Trailing rows stay zero and become the Reed-Solomon expansion.
49                let mut values = F::zero_vec(height * width);
50
51                // Transpose the folding blocks straight into the leading rows.
52                // This reuses the cache-blocked transpose with no extra allocation.
53                let folded =
54                    RowMajorMatrixView::new(poly.as_slice(), 1 << (num_variables - folding));
55                folded.transpose_into(&mut RowMajorMatrixViewMut::new(
56                    &mut values[..1 << num_variables],
57                    width,
58                ));
59
60                RowMajorMatrix::new(values, width)
61            });
62            info_span!("dft", height = padded.height(), width = padded.width())
63                .in_scope(|| dft.dft_batch(padded).to_row_major_matrix())
64        }
65        VariableOrder::Suffix => {
66            let padded = info_span!("pad").in_scope(|| {
67                let width = 1 << folding;
68                let src = poly.as_slice();
69
70                // Folding blocks are already contiguous, so copy them into the
71                // leading rows of one zero-padded buffer; no realloc on pad.
72                let mut values = F::zero_vec(height * width);
73                values[..src.len()].copy_from_slice(src);
74                RowMajorMatrix::new(values, width)
75            });
76            info_span!("dft", height = padded.height(), width = padded.width())
77                .in_scope(|| dft.dft_batch(padded).to_row_major_matrix())
78        }
79    };
80
81    let (root, prover_data) = info_span!("commit_matrix").in_scope(|| mmcs.commit_matrix(encoded));
82    challenger.observe(root.clone());
83    (root, prover_data)
84}