Skip to content

Commit bba060d

Browse files
georgwieseSchaeff
andauthored
MockBackend: Implement later-stage witness generation (#2168)
Ticks the third item off #2152 This should make the mock prover feature-complete! --------- Co-authored-by: Thibaut Schaeffer <[email protected]>
1 parent 72632ab commit bba060d

7 files changed

+90
-31
lines changed

backend/src/mock/connection_constraint_checker.rs

+6-1
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@ impl<F: FieldElement> Connection<F> {
155155
pub struct ConnectionConstraintChecker<'a, F: FieldElement> {
156156
pub connections: &'a [Connection<F>],
157157
pub machines: BTreeMap<String, Machine<'a, F>>,
158+
pub challenges: &'a BTreeMap<u64, F>,
158159
}
159160

160161
impl<'a, F: FieldElement> ConnectionConstraintChecker<'a, F> {
@@ -247,7 +248,11 @@ impl<'a, F: FieldElement> ConnectionConstraintChecker<'a, F> {
247248
(0..machine.size)
248249
.into_par_iter()
249250
.filter_map(|row| {
250-
let variables = Variables { machine, row };
251+
let variables = Variables {
252+
machine,
253+
row,
254+
challenges: self.challenges,
255+
};
251256
let mut evaluator =
252257
ExpressionEvaluator::new(&variables, &machine.intermediate_definitions);
253258
let result = evaluator.evaluate(&selected_expressions.selector).unwrap();

backend/src/mock/evaluator.rs

+8-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
use powdr_ast::analyzed::PolynomialType;
1+
use std::collections::BTreeMap;
2+
3+
use powdr_ast::analyzed::{Challenge, PolynomialType};
24
use powdr_executor::witgen::{AffineResult, AlgebraicVariable, SymbolicVariables};
35
use powdr_number::FieldElement;
46

@@ -7,6 +9,7 @@ use super::machine::Machine;
79
pub struct Variables<'a, F> {
810
pub machine: &'a Machine<'a, F>,
911
pub row: usize,
12+
pub challenges: &'a BTreeMap<u64, F>,
1013
}
1114

1215
impl<'a, F: FieldElement> Variables<'a, F> {
@@ -31,4 +34,8 @@ impl<'a, F: FieldElement> SymbolicVariables<F> for &Variables<'a, F> {
3134
fn value<'b>(&self, var: AlgebraicVariable<'b>) -> AffineResult<AlgebraicVariable<'b>, F> {
3235
Ok(self.constant_value(var).into())
3336
}
37+
38+
fn challenge<'b>(&self, challenge: &'b Challenge) -> AffineResult<AlgebraicVariable<'b>, F> {
39+
Ok(self.challenges[&challenge.id].into())
40+
}
3441
}

backend/src/mock/machine.rs

+10-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use std::collections::BTreeMap;
33
use itertools::Itertools;
44
use powdr_ast::analyzed::{AlgebraicExpression, AlgebraicReferenceThin, Analyzed, PolyID};
55
use powdr_backend_utils::{machine_fixed_columns, machine_witness_columns};
6-
use powdr_executor::constant_evaluator::VariablySizedColumn;
6+
use powdr_executor::{constant_evaluator::VariablySizedColumn, witgen::WitgenCallback};
77
use powdr_number::{DegreeType, FieldElement};
88

99
/// A collection of columns with self-contained constraints.
@@ -22,8 +22,10 @@ impl<'a, F: FieldElement> Machine<'a, F> {
2222
witness: &'a [(String, Vec<F>)],
2323
fixed: &'a [(String, VariablySizedColumn<F>)],
2424
pil: &'a Analyzed<F>,
25+
witgen_callback: &WitgenCallback<F>,
26+
challenges: &BTreeMap<u64, F>,
2527
) -> Option<Self> {
26-
let witness = machine_witness_columns(witness, pil, &machine_name);
28+
let mut witness = machine_witness_columns(witness, pil, &machine_name);
2729
let size = witness
2830
.iter()
2931
.map(|(_, v)| v.len())
@@ -36,6 +38,12 @@ impl<'a, F: FieldElement> Machine<'a, F> {
3638
return None;
3739
}
3840

41+
for stage in 1..pil.stage_count() {
42+
log::debug!("Generating stage-{stage} witness for machine {machine_name}");
43+
witness =
44+
witgen_callback.next_stage_witness(pil, &witness, challenges.clone(), stage as u8);
45+
}
46+
3947
let fixed = machine_fixed_columns(fixed, pil);
4048
let fixed = fixed.get(&(size as DegreeType)).unwrap();
4149

backend/src/mock/mod.rs

+49-15
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,19 @@
1-
use std::{collections::BTreeMap, io, marker::PhantomData, path::PathBuf, sync::Arc};
1+
use std::{
2+
collections::BTreeMap,
3+
hash::{DefaultHasher, Hash, Hasher},
4+
io,
5+
marker::PhantomData,
6+
path::PathBuf,
7+
sync::Arc,
8+
};
29

310
use connection_constraint_checker::{Connection, ConnectionConstraintChecker};
411
use machine::Machine;
512
use polynomial_constraint_checker::PolynomialConstraintChecker;
6-
use powdr_ast::analyzed::Analyzed;
13+
use powdr_ast::{
14+
analyzed::{AlgebraicExpression, Analyzed},
15+
parsed::visitor::AllChildren,
16+
};
717
use powdr_executor::{constant_evaluator::VariablySizedColumn, witgen::WitgenCallback};
818
use powdr_number::{DegreeType, FieldElement};
919

@@ -70,37 +80,61 @@ impl<F: FieldElement> Backend<F> for MockBackend<F> {
7080
&self,
7181
witness: &[(String, Vec<F>)],
7282
prev_proof: Option<Proof>,
73-
_witgen_callback: WitgenCallback<F>,
83+
witgen_callback: WitgenCallback<F>,
7484
) -> Result<Proof, Error> {
7585
if prev_proof.is_some() {
7686
unimplemented!();
7787
}
7888

89+
let challenges = self
90+
.machine_to_pil
91+
.values()
92+
.flat_map(|pil| pil.identities.iter())
93+
.flat_map(|identity| identity.all_children())
94+
.filter_map(|expr| match expr {
95+
AlgebraicExpression::Challenge(challenge) => {
96+
// Use the hash of the ID as the challenge.
97+
// This way, if the same challenge is used by different machines, they will
98+
// have the same value.
99+
let mut hasher = DefaultHasher::new();
100+
challenge.id.hash(&mut hasher);
101+
Some((challenge.id, F::from(hasher.finish())))
102+
}
103+
_ => None,
104+
})
105+
.collect::<BTreeMap<_, _>>();
106+
79107
let machines = self
80108
.machine_to_pil
109+
// TODO: We should probably iterate in parallel, because Machine::try_new might generate
110+
// later-stage witnesses, which is expensive.
111+
// However, for now, doing it sequentially simplifies debugging.
81112
.iter()
82-
.filter_map(|(machine, pil)| {
83-
Machine::try_new(machine.clone(), witness, &self.fixed, pil)
113+
.filter_map(|(machine_name, pil)| {
114+
Machine::try_new(
115+
machine_name.clone(),
116+
witness,
117+
&self.fixed,
118+
pil,
119+
&witgen_callback,
120+
&challenges,
121+
)
84122
})
85123
.map(|machine| (machine.machine_name.clone(), machine))
86124
.collect::<BTreeMap<_, _>>();
87125

88-
let mut is_ok = true;
89-
for (_, machine) in machines.iter() {
90-
let result = PolynomialConstraintChecker::new(machine).check();
91-
is_ok &= !result.has_errors();
92-
}
93-
94-
is_ok &= ConnectionConstraintChecker {
126+
let is_ok = machines.values().all(|machine| {
127+
!PolynomialConstraintChecker::new(machine, &challenges)
128+
.check()
129+
.has_errors()
130+
}) && ConnectionConstraintChecker {
95131
connections: &self.connections,
96132
machines,
133+
challenges: &challenges,
97134
}
98135
.check()
99136
.is_ok();
100137

101-
// TODO:
102-
// - Check later-stage witness
103-
104138
match is_ok {
105139
true => Ok(Vec::new()),
106140
false => Err(Error::BackendError("Constraint check failed".to_string())),

backend/src/mock/polynomial_constraint_checker.rs

+7-2
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,15 @@ use super::machine::Machine;
1414

1515
pub struct PolynomialConstraintChecker<'a, F> {
1616
machine: &'a Machine<'a, F>,
17+
challenges: &'a BTreeMap<u64, F>,
1718
}
1819

1920
impl<'a, F: FieldElement> PolynomialConstraintChecker<'a, F> {
20-
pub fn new(machine: &'a Machine<'a, F>) -> Self {
21-
Self { machine }
21+
pub fn new(machine: &'a Machine<'a, F>, challenges: &'a BTreeMap<u64, F>) -> Self {
22+
Self {
23+
machine,
24+
challenges,
25+
}
2226
}
2327

2428
pub fn check(&self) -> MachineResult<'a, F> {
@@ -54,6 +58,7 @@ impl<'a, F: FieldElement> PolynomialConstraintChecker<'a, F> {
5458
let variables = Variables {
5559
machine: self.machine,
5660
row,
61+
challenges: self.challenges,
5762
};
5863
let mut evaluator =
5964
ExpressionEvaluator::new(&variables, &self.machine.intermediate_definitions);

pipeline/tests/asm.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ fn block_machine_exact_number_of_rows_asm() {
3939
fn challenges_asm() {
4040
let f = "asm/challenges.asm";
4141
let pipeline: Pipeline<GoldilocksField> = make_simple_prepared_pipeline(f);
42-
// TODO Mock prover doesn't support this test yet.
42+
test_mock_backend(pipeline.clone());
4343
test_plonky3_pipeline(pipeline);
4444
}
4545

@@ -69,7 +69,7 @@ fn secondary_block_machine_add2() {
6969
fn second_phase_hint() {
7070
let f = "asm/second_phase_hint.asm";
7171
let pipeline: Pipeline<GoldilocksField> = make_simple_prepared_pipeline(f);
72-
// TODO Mock prover doesn't support this test yet.
72+
test_mock_backend(pipeline.clone());
7373
test_plonky3_pipeline(pipeline);
7474
}
7575

@@ -179,15 +179,15 @@ fn block_to_block_empty_submachine() {
179179
fn block_to_block_with_bus_monolithic() {
180180
let f = "asm/block_to_block_with_bus.asm";
181181
let pipeline: Pipeline<GoldilocksField> = make_simple_prepared_pipeline(f);
182-
// TODO Mock prover doesn't support this test yet.
182+
test_mock_backend(pipeline.clone());
183183
test_plonky3_pipeline(pipeline);
184184
}
185185

186186
#[test]
187187
fn block_to_block_with_bus_different_sizes() {
188188
let f = "asm/block_to_block_with_bus_different_sizes.asm";
189189
let pipeline: Pipeline<GoldilocksField> = make_simple_prepared_pipeline(f);
190-
// TODO Mock prover doesn't support this test yet.
190+
test_mock_backend(pipeline.clone());
191191
test_plonky3_pipeline(pipeline);
192192
}
193193

@@ -205,7 +205,7 @@ fn block_to_block_with_bus_composite() {
205205
use powdr_pipeline::test_util::test_halo2_with_backend_variant;
206206
let f = "asm/block_to_block_with_bus.asm";
207207
let pipeline = make_simple_prepared_pipeline(f);
208-
// TODO Mock prover doesn't support this test yet.
208+
test_mock_backend(pipeline.clone());
209209
test_halo2_with_backend_variant(pipeline, BackendVariant::Composite);
210210
}
211211

pipeline/tests/powdr_std.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -181,40 +181,40 @@ fn memory_small_test() {
181181
fn permutation_via_challenges() {
182182
let f = "std/permutation_via_challenges.asm";
183183
let pipeline: Pipeline<GoldilocksField> = make_simple_prepared_pipeline(f);
184+
test_mock_backend(pipeline.clone());
184185
test_plonky3_pipeline(pipeline);
185-
// TODO Mock prover doesn't support this test yet.
186186
}
187187

188188
#[test]
189189
fn lookup_via_challenges() {
190190
let f = "std/lookup_via_challenges.asm";
191191
let pipeline: Pipeline<GoldilocksField> = make_simple_prepared_pipeline(f);
192+
test_mock_backend(pipeline.clone());
192193
test_plonky3_pipeline(pipeline);
193-
// TODO Mock prover doesn't support this test yet.
194194
}
195195

196196
#[test]
197197
fn lookup_via_challenges_range_constraint() {
198198
let f = "std/lookup_via_challenges_range_constraint.asm";
199199
let pipeline: Pipeline<GoldilocksField> = make_simple_prepared_pipeline(f);
200+
test_mock_backend(pipeline.clone());
200201
test_plonky3_pipeline(pipeline);
201-
// TODO Mock prover doesn't support this test yet.
202202
}
203203

204204
#[test]
205205
fn bus_lookup() {
206206
let f = "std/bus_lookup.asm";
207207
let pipeline: Pipeline<GoldilocksField> = make_simple_prepared_pipeline(f);
208+
test_mock_backend(pipeline.clone());
208209
test_plonky3_pipeline(pipeline);
209-
// TODO Mock prover doesn't support this test yet.
210210
}
211211

212212
#[test]
213213
fn bus_permutation() {
214214
let f = "std/bus_permutation.asm";
215215
let pipeline: Pipeline<GoldilocksField> = make_simple_prepared_pipeline(f);
216+
test_mock_backend(pipeline.clone());
216217
test_plonky3_pipeline(pipeline);
217-
// TODO Mock prover doesn't support this test yet.
218218
}
219219

220220
#[test]

0 commit comments

Comments
 (0)