Skip to content

Commit

Permalink
Extend stwo to Support Machines with Varying Degrees (#2280)
Browse files Browse the repository at this point in the history
### Summary
This PR introduces support for proving machines with differing degrees
in the `stwo` prover. It enables handling proving columns of varying
sizes.

---

### Details
1. **Support for Proving Columns of Different Sizes**:
- Machines with different column sizes require their own distinct
components.
- All components are stored in a single vector, from which a slice is
created and passed into the `prove` function to handle the proving
process.
- Constant columns are indexed globally across all components to ensure
consistency and correctness.

2. **Verification**:
- The verification function requires the sizes of all columns (including
constant columns and witness columns) as input.

---

### Changes
- Updated the logic for managing machine-specific components and
assembling them for the proving process.
- Implemented indexing of constant columns across components.

---

### Testing
- Added tests to validate proving functionality with machines of
differing degrees for stwo prover.


---

---------

Co-authored-by: Thibaut Schaeffer <[email protected]>
  • Loading branch information
ShuangWu121 and Schaeff authored Jan 13, 2025
1 parent 65a2fdd commit 71eaf88
Show file tree
Hide file tree
Showing 5 changed files with 238 additions and 135 deletions.
22 changes: 14 additions & 8 deletions backend/src/stwo/circuit_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ use core::unreachable;
use powdr_ast::parsed::visitor::AllChildren;
use powdr_executor_utils::expression_evaluator::{ExpressionEvaluator, TerminalAccess};
use std::collections::HashSet;
use std::sync::Arc;

extern crate alloc;
use alloc::collections::btree_map::BTreeMap;
Expand Down Expand Up @@ -53,14 +52,18 @@ where
}

pub struct PowdrEval<T> {
analyzed: Arc<Analyzed<T>>,
log_degree: u32,
analyzed: Analyzed<T>,
// the pre-processed are indexed in the whole proof, instead of in each component.
// this offset represents the index of the first pre-processed column in this component
preprocess_col_offset: usize,
witness_columns: BTreeMap<PolyID, usize>,
constant_shifted: BTreeMap<PolyID, usize>,
constant_columns: BTreeMap<PolyID, usize>,
}

impl<T: FieldElement> PowdrEval<T> {
pub fn new(analyzed: Arc<Analyzed<T>>) -> Self {
pub fn new(analyzed: Analyzed<T>, preprocess_col_offset: usize, log_degree: u32) -> Self {
let witness_columns: BTreeMap<PolyID, usize> = analyzed
.definitions_in_source_order(PolynomialType::Committed)
.flat_map(|(symbol, _)| symbol.array_elements())
Expand All @@ -86,7 +89,9 @@ impl<T: FieldElement> PowdrEval<T> {
.collect();

Self {
log_degree,
analyzed,
preprocess_col_offset,
witness_columns,
constant_shifted,
constant_columns,
Expand Down Expand Up @@ -126,10 +131,10 @@ impl<F: Clone> TerminalAccess<F> for &Data<'_, F> {

impl<T: FieldElement> FrameworkEval for PowdrEval<T> {
fn log_size(&self) -> u32 {
self.analyzed.degree().ilog2()
self.log_degree
}
fn max_constraint_log_degree_bound(&self) -> u32 {
self.analyzed.degree().ilog2() + 1
self.log_degree + 1
}
fn evaluate<E: EvalAtRow>(&self, mut eval: E) -> E {
assert!(
Expand All @@ -155,8 +160,9 @@ impl<T: FieldElement> FrameworkEval for PowdrEval<T> {
.map(|(i, poly_id)| {
(
*poly_id,
// PreprocessedColumn::Plonk(i) is unused argument in get_preprocessed_column
eval.get_preprocessed_column(PreprocessedColumn::Plonk(i)),
eval.get_preprocessed_column(PreprocessedColumn::Plonk(
i + self.preprocess_col_offset,
)),
)
})
.collect();
Expand All @@ -169,7 +175,7 @@ impl<T: FieldElement> FrameworkEval for PowdrEval<T> {
(
*poly_id,
eval.get_preprocessed_column(PreprocessedColumn::Plonk(
i + constant_eval.len(),
i + constant_eval.len() + self.preprocess_col_offset,
)),
)
})
Expand Down
4 changes: 0 additions & 4 deletions backend/src/stwo/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,6 @@ impl<F: FieldElement> BackendFactory<F> for RestrictedFactory {
return Err(Error::BackendError("Proving key unused".to_string()));
}

if pil.degrees().len() > 1 {
return Err(Error::NoVariableDegreeAvailable);
}

let mut stwo: Box<StwoProver<F, SimdBackend, Blake2sMerkleChannel, Blake2sChannel>> =
Box::new(StwoProver::new(pil, fixed)?);

Expand Down
17 changes: 15 additions & 2 deletions backend/src/stwo/proof.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
use serde::Deserialize;
use serde::Serialize;
use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
use stwo_prover::core::backend::Backend;
use stwo_prover::core::backend::Column;
use stwo_prover::core::backend::ColumnOps;
use stwo_prover::core::channel::MerkleChannel;
use stwo_prover::core::fields::m31::BaseField;
use stwo_prover::core::fields::m31::M31;
use stwo_prover::core::poly::circle::{CanonicCoset, CircleEvaluation};
use stwo_prover::core::poly::BitReversedOrder;
use stwo_prover::core::prover::StarkProof;
use stwo_prover::core::ColumnVec;

/// For each possible size, the commitment and prover data
Expand Down Expand Up @@ -124,3 +126,14 @@ impl<B: Backend> From<StarkProvingKey<B>> for SerializableStarkProvingKey {
Self { preprocessed }
}
}

#[derive(Serialize, Deserialize)]
pub struct Proof<MC: MerkleChannel>
where
MC::H: DeserializeOwned + Serialize,
{
pub stark_proof: StarkProof<MC::H>,
pub constant_col_log_sizes: Vec<u32>,
pub witness_col_log_sizes: Vec<u32>,
pub machine_log_sizes: Vec<u32>,
}
Loading

0 comments on commit 71eaf88

Please sign in to comment.