Skip to content

Commit 3daad32

Browse files
authored
Introduce column allocator (#3387)
Alternative to #3385 which solves it in apc generation already. The original bug is that when introducing a derived column (right now only `is_valid`), the poly_id is chosen to be the max of the current poly_ids in the machine plus one. But this poly_id may already be pointed to by an original column in the substitutions, so in witgen it looks like an apc column is both computed from an original column, and a derived column. This PR introduces a column allocator which issues new poly_ids that are guaranteed not to be already used. When returning the substitutions, we only keep the substitutions which actually point to an apc column.
1 parent f4bc3cf commit 3daad32

File tree

3 files changed

+90
-25
lines changed

3 files changed

+90
-25
lines changed

autoprecompiles/src/lib.rs

Lines changed: 76 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use powdr_expression::{
1212
visitors::Children, AlgebraicBinaryOperation, AlgebraicBinaryOperator, AlgebraicUnaryOperation,
1313
};
1414
use serde::{Deserialize, Serialize};
15+
use std::collections::BTreeSet;
1516
use std::fmt::Display;
1617
use std::io::BufWriter;
1718
use std::iter::once;
@@ -316,15 +317,26 @@ pub trait InstructionHandler {
316317
fn is_branching(&self, instruction: &Self::Instruction) -> bool;
317318
}
318319

320+
#[derive(Debug, Serialize, Deserialize)]
321+
pub struct Substitution {
322+
/// The index of the original column in the original air
323+
original_poly_index: usize,
324+
/// The `poly_id` of the target column in the APC air
325+
apc_poly_id: u64,
326+
}
327+
319328
#[derive(Debug, Serialize, Deserialize)]
320329
pub struct Apc<T, I> {
330+
/// The basic block this APC is based on
321331
pub block: BasicBlock<I>,
332+
/// The symbolic machine for this APC
322333
pub machine: SymbolicMachine<T>,
323-
pub subs: Vec<Vec<u64>>,
334+
/// For each original air, the substitutions from original columns to APC columns
335+
pub subs: Vec<Vec<Substitution>>,
324336
}
325337

326338
impl<T, I> Apc<T, I> {
327-
pub fn subs(&self) -> &[Vec<u64>] {
339+
pub fn subs(&self) -> &[Vec<Substitution>] {
328340
&self.subs
329341
}
330342

@@ -341,6 +353,59 @@ impl<T, I> Apc<T, I> {
341353
pub fn instructions(&self) -> &[I] {
342354
&self.block.statements
343355
}
356+
357+
/// Create a new APC based on the given basic block, symbolic machine and column allocator
358+
/// The column allocator only issues the subs which are actually used in the machine
359+
fn new(
360+
block: BasicBlock<I>,
361+
machine: SymbolicMachine<T>,
362+
column_allocator: ColumnAllocator,
363+
) -> Self {
364+
// Get all poly_ids in the machine
365+
let all_references = machine
366+
.unique_references()
367+
.map(|r| r.id)
368+
.collect::<BTreeSet<_>>();
369+
// Only keep substitutions from the column allocator if the target poly_id is used in the machine
370+
let subs = column_allocator
371+
.subs
372+
.into_iter()
373+
.map(|subs| {
374+
subs.into_iter()
375+
.enumerate()
376+
.filter_map(|(original_poly_index, apc_poly_id)| {
377+
all_references
378+
.contains(&apc_poly_id)
379+
.then_some(Substitution {
380+
original_poly_index,
381+
apc_poly_id,
382+
})
383+
})
384+
.collect_vec()
385+
})
386+
.collect();
387+
Self {
388+
block,
389+
machine,
390+
subs,
391+
}
392+
}
393+
}
394+
395+
/// Allocates global poly_ids and keeps track of substitutions
396+
struct ColumnAllocator {
397+
/// For each original air, for each original column index, the associated poly_id in the APC air
398+
subs: Vec<Vec<u64>>,
399+
/// The next poly_id to issue
400+
next_poly_id: u64,
401+
}
402+
403+
impl ColumnAllocator {
404+
fn issue_next_poly_id(&mut self) -> u64 {
405+
let id = self.next_poly_id;
406+
self.next_poly_id += 1;
407+
id
408+
}
344409
}
345410

346411
pub fn build<A: Adapter>(
@@ -351,7 +416,7 @@ pub fn build<A: Adapter>(
351416
) -> Result<AdapterApc<A>, crate::constraint_optimizer::Error> {
352417
let start = std::time::Instant::now();
353418

354-
let (machine, subs) = statements_to_symbolic_machine::<A>(
419+
let (machine, column_allocator) = statements_to_symbolic_machine::<A>(
355420
&block,
356421
vm_config.instruction_handler,
357422
&vm_config.bus_map,
@@ -373,7 +438,7 @@ pub fn build<A: Adapter>(
373438
)?;
374439

375440
// add guards to constraints that are not satisfied by zeroes
376-
let machine = add_guards(machine);
441+
let (machine, column_allocator) = add_guards(machine, column_allocator);
377442

378443
metrics::counter!("after_opt_cols", &labels)
379444
.absolute(machine.unique_references().count() as u64);
@@ -384,11 +449,7 @@ pub fn build<A: Adapter>(
384449

385450
let machine = convert_machine_field_type(machine, &A::into_field);
386451

387-
let apc = Apc {
388-
block,
389-
machine,
390-
subs,
391-
};
452+
let apc = Apc::new(block, machine, column_allocator);
392453

393454
if let Some(path) = apc_candidates_dir_path {
394455
let ser_path = path
@@ -447,12 +508,15 @@ fn add_guards_constraint<T: FieldElement>(
447508
}
448509

449510
/// Adds an `is_valid` guard to all constraints and bus interactions, if needed.
450-
fn add_guards<T: FieldElement>(mut machine: SymbolicMachine<T>) -> SymbolicMachine<T> {
511+
fn add_guards<T: FieldElement>(
512+
mut machine: SymbolicMachine<T>,
513+
mut column_allocator: ColumnAllocator,
514+
) -> (SymbolicMachine<T>, ColumnAllocator) {
451515
let pre_degree = machine.degree();
452516

453517
let is_valid_ref = AlgebraicReference {
454518
name: Arc::new("is_valid".to_string()),
455-
id: machine.unique_references().map(|c| c.id).max().unwrap() + 1,
519+
id: column_allocator.issue_next_poly_id(),
456520
};
457521
let is_valid = AlgebraicExpression::Reference(is_valid_ref.clone());
458522

@@ -493,5 +557,5 @@ fn add_guards<T: FieldElement>(mut machine: SymbolicMachine<T>) -> SymbolicMachi
493557
// so it may increase the degree of the machine.
494558
machine.constraints.push(powdr::make_bool(is_valid).into());
495559

496-
machine
560+
(machine, column_allocator)
497561
}

autoprecompiles/src/symbolic_machine_generator.rs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ use crate::{
77
adapter::Adapter,
88
blocks::{BasicBlock, Instruction},
99
expression::AlgebraicExpression,
10-
powdr, BusMap, BusType, InstructionHandler, SymbolicBusInteraction, SymbolicConstraint,
11-
SymbolicMachine,
10+
powdr, BusMap, BusType, ColumnAllocator, InstructionHandler, SymbolicBusInteraction,
11+
SymbolicConstraint, SymbolicMachine,
1212
};
1313

1414
/// Converts the field type of a symbolic machine.
@@ -102,11 +102,11 @@ fn convert_expression<T, U>(
102102
/// that contains, for each instruction in the basic block,
103103
/// a mapping from local column IDs to global column IDs
104104
/// (in the form of a vector).
105-
pub fn statements_to_symbolic_machine<A: Adapter>(
105+
pub(crate) fn statements_to_symbolic_machine<A: Adapter>(
106106
block: &BasicBlock<A::Instruction>,
107107
instruction_handler: &A::InstructionHandler,
108108
bus_map: &BusMap<A::CustomBusTypes>,
109-
) -> (SymbolicMachine<A::PowdrField>, Vec<Vec<u64>>) {
109+
) -> (SymbolicMachine<A::PowdrField>, ColumnAllocator) {
110110
let mut constraints: Vec<SymbolicConstraint<_>> = Vec::new();
111111
let mut bus_interactions: Vec<SymbolicBusInteraction<_>> = Vec::new();
112112
let mut col_subs: Vec<Vec<u64>> = Vec::new();
@@ -184,7 +184,10 @@ pub fn statements_to_symbolic_machine<A: Adapter>(
184184
bus_interactions,
185185
derived_columns: vec![],
186186
},
187-
col_subs,
187+
ColumnAllocator {
188+
subs: col_subs,
189+
next_poly_id: global_idx,
190+
},
188191
)
189192
}
190193

autoprecompiles/src/trace_handler.rs

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -87,15 +87,13 @@ where
8787
.iter()
8888
.map(|subs| {
8989
subs.iter()
90-
.enumerate()
91-
.filter_map(|(dummy_index, poly_id)| {
92-
// Check if this dummy column is present in the final apc row
93-
apc_poly_id_to_index
94-
.get(poly_id)
95-
// If it is, map the dummy index to the apc index
96-
.map(|apc_index| (dummy_index, *apc_index))
90+
.map(|substitution| {
91+
(
92+
substitution.original_poly_index,
93+
apc_poly_id_to_index[&substitution.apc_poly_id],
94+
)
9795
})
98-
.collect()
96+
.collect_vec()
9997
})
10098
.collect();
10199

0 commit comments

Comments
 (0)