@@ -12,6 +12,7 @@ use powdr_expression::{
1212 visitors:: Children , AlgebraicBinaryOperation , AlgebraicBinaryOperator , AlgebraicUnaryOperation ,
1313} ;
1414use serde:: { Deserialize , Serialize } ;
15+ use std:: collections:: BTreeSet ;
1516use std:: fmt:: Display ;
1617use std:: io:: BufWriter ;
1718use 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 ) ]
320329pub 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
326338impl < 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
346411pub 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}
0 commit comments