From 82d7aea3797a44f20bb97c5fb1a19a3cd82a0c9f Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 13 Nov 2025 15:14:59 +0000 Subject: [PATCH 01/60] Conversion. --- autoprecompiles/Cargo.toml | 1 + autoprecompiles/src/lib.rs | 1 + autoprecompiles/src/optimizer.rs | 9 +++++++-- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/autoprecompiles/Cargo.toml b/autoprecompiles/Cargo.toml index 89820f9b1e..fa7e8f6725 100644 --- a/autoprecompiles/Cargo.toml +++ b/autoprecompiles/Cargo.toml @@ -23,6 +23,7 @@ rayon = "1.10.0" strum = { version = "0.27.0", features = ["derive"] } metrics = "0.23.0" +crepe = "0.1.8" [package.metadata.cargo-udeps.ignore] development = ["env_logger"] diff --git a/autoprecompiles/src/lib.rs b/autoprecompiles/src/lib.rs index e4e5a3b6d3..28e4af3313 100644 --- a/autoprecompiles/src/lib.rs +++ b/autoprecompiles/src/lib.rs @@ -40,6 +40,7 @@ mod stats_logger; pub mod symbolic_machine_generator; pub use pgo::{PgoConfig, PgoType}; pub use powdr_constraint_solver::inliner::DegreeBound; +pub mod rule_based_optimizer; pub mod trace_handler; #[derive(Clone)] diff --git a/autoprecompiles/src/optimizer.rs b/autoprecompiles/src/optimizer.rs index 1606736b8c..d71485ca50 100644 --- a/autoprecompiles/src/optimizer.rs +++ b/autoprecompiles/src/optimizer.rs @@ -7,7 +7,7 @@ use powdr_constraint_solver::constraint_system::{ AlgebraicConstraint, ComputationMethod, DerivedVariable, }; use powdr_constraint_solver::inliner::{self, inline_everything_below_degree_bound}; -use powdr_constraint_solver::solver::new_solver; +use powdr_constraint_solver::solver::{self, new_solver}; use powdr_constraint_solver::{ constraint_system::{BusInteraction, ConstraintSystem}, grouped_expression::GroupedExpression, @@ -16,6 +16,7 @@ use powdr_number::FieldElement; use crate::constraint_optimizer::trivial_simplifications; use crate::range_constraint_optimizer::optimize_range_constraints; +use crate::rule_based_optimizer::rule_based_optimization; use crate::{ adapter::Adapter, constraint_optimizer::optimize_constraints, @@ -39,7 +40,11 @@ pub fn optimize( stats_logger.log("exec bus optimization", &machine); } - let mut constraint_system = symbolic_machine_to_constraint_system(machine); + let constraint_system = symbolic_machine_to_constraint_system(machine); + + let mut constraint_system = rule_based_optimization(constraint_system); + stats_logger.log("rule-based optimization", &constraint_system); + let mut solver = new_solver(constraint_system.clone(), bus_interaction_handler.clone()); loop { let stats = stats_logger::Stats::from(&constraint_system); From 7cd68ef5c5c7a7a8dbfb7d03df90f48ae388e182 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 13 Nov 2025 20:14:01 +0000 Subject: [PATCH 02/60] Simple solve constraint. --- autoprecompiles/src/rule_based_optimizer.rs | 107 ++++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 autoprecompiles/src/rule_based_optimizer.rs diff --git a/autoprecompiles/src/rule_based_optimizer.rs b/autoprecompiles/src/rule_based_optimizer.rs new file mode 100644 index 0000000000..d874dd242b --- /dev/null +++ b/autoprecompiles/src/rule_based_optimizer.rs @@ -0,0 +1,107 @@ +use itertools::Itertools; +use powdr_constraint_solver::{ + constraint_system::ConstraintSystem, + grouped_expression::{GroupedExpression, GroupedExpressionComponent}, + indexed_constraint_system::apply_substitutions, +}; +use powdr_number::{BabyBearField, FieldElement, LargeInt}; + +use crepe::crepe; + +type F = BabyBearField; + +crepe! { + @input + struct Constraint<'a>(&'a GroupedExpression); + + struct Var<'a>(&'a str); + + struct IsAffine<'a>(&'a GroupedExpression); + + // TODO it should not be required that it is a constraint, but + // we cannot get expression from anywhere else. + IsAffine(e) <- Constraint(e), (e.is_affine()); + + struct ExprHasLinearComponent<'a>(&'a GroupedExpression, F, Var<'a>); + ExprHasLinearComponent(e, coeff, var) <- IsAffine(e), for (coeff, var) in linear_components(e); + + struct LinearComponentCount<'a>(&'a GroupedExpression, usize); + LinearComponentCount(e, count) <- IsAffine(e), for count in std::iter::once(e.linear_components().count()); + + struct AffineExpression<'a>(&'a GroupedExpression, F, Var<'a>, F); + + AffineExpression(e, coeff, var, offset) <- + IsAffine(e), + LinearComponentCount(e, 1), + ExprHasLinearComponent(e, coeff, var), + for offset in std::iter::once(*e.constant_offset()); + + @output + struct Assignment<'a>(Var<'a>, F); + Assignment(var, value) <- + Constraint(e), + AffineExpression(e, coeff, var, offset), + for value in std::iter::once(-offset / coeff); +} + +fn linear_components<'a>(expr: &'a GroupedExpression) -> Vec<(F, Var<'a>)> { + expr.linear_components() + .into_iter() + .map(|(v, c)| (*c, Var(v.as_str()))) + .collect() +} + +pub fn rule_based_optimization< + T: FieldElement, + V: std::hash::Hash + Eq + Ord + Clone + std::fmt::Display, +>( + system: ConstraintSystem, +) -> ConstraintSystem { + if T::modulus().to_arbitrary_integer() != BabyBearField::modulus().to_arbitrary_integer() { + return system; + } + let mut rt = Crepe::new(); + + let transformed_expressions = system + .algebraic_constraints + .iter() + .map(|c| transform_grouped_expression(&c.expression)) + .collect_vec(); + + rt.extend(transformed_expressions.iter().map(|e| Constraint(e))); + + let (assignments,) = rt.run(); + for Assignment(var, value) in assignments { + println!("Inferred assignment: {} = {}", var.0, value); + } + // apply_substitutions( + // system, + // assignments + // .into_iter() + // .map(|Assignment(var, value)| (var.0.to_string(), value)), + // ) + system +} + +fn transform_grouped_expression< + T: FieldElement, + V: std::hash::Hash + Eq + Ord + Clone + std::fmt::Display, +>( + expr: &GroupedExpression, +) -> GroupedExpression { + expr.clone() + .into_summands() + .map(|s| match s { + GroupedExpressionComponent::Quadratic(l, r) => { + transform_grouped_expression(&l) * transform_grouped_expression(&r) + } + GroupedExpressionComponent::Linear(v, c) => { + GroupedExpression::from_unknown_variable(v.to_string()) + * BabyBearField::from(c.to_arbitrary_integer()) + } + GroupedExpressionComponent::Constant(c) => { + GroupedExpression::from_number(c.to_arbitrary_integer().into()) + } + }) + .sum() +} From 828c68758c4d2d6caf8a3ce160cbabe9fc894e3e Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 13 Nov 2025 21:05:53 +0000 Subject: [PATCH 03/60] Simplify variable type. --- autoprecompiles/src/rule_based_optimizer.rs | 107 +++++++++++++------- 1 file changed, 71 insertions(+), 36 deletions(-) diff --git a/autoprecompiles/src/rule_based_optimizer.rs b/autoprecompiles/src/rule_based_optimizer.rs index d874dd242b..ae0cf8c300 100644 --- a/autoprecompiles/src/rule_based_optimizer.rs +++ b/autoprecompiles/src/rule_based_optimizer.rs @@ -1,3 +1,5 @@ +use std::{collections::HashMap, fmt::Display, hash::Hash}; + use itertools::Itertools; use powdr_constraint_solver::{ constraint_system::ConstraintSystem, @@ -10,26 +12,25 @@ use crepe::crepe; type F = BabyBearField; +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +struct Var(u32); + crepe! { @input - struct Constraint<'a>(&'a GroupedExpression); - - struct Var<'a>(&'a str); - - struct IsAffine<'a>(&'a GroupedExpression); + struct Constraint<'a>(&'a GroupedExpression); + struct IsAffine<'a>(&'a GroupedExpression); // TODO it should not be required that it is a constraint, but // we cannot get expression from anywhere else. IsAffine(e) <- Constraint(e), (e.is_affine()); - struct ExprHasLinearComponent<'a>(&'a GroupedExpression, F, Var<'a>); + struct ExprHasLinearComponent<'a>(&'a GroupedExpression, F, Var); ExprHasLinearComponent(e, coeff, var) <- IsAffine(e), for (coeff, var) in linear_components(e); - struct LinearComponentCount<'a>(&'a GroupedExpression, usize); + struct LinearComponentCount<'a>(&'a GroupedExpression, usize); LinearComponentCount(e, count) <- IsAffine(e), for count in std::iter::once(e.linear_components().count()); - struct AffineExpression<'a>(&'a GroupedExpression, F, Var<'a>, F); - + struct AffineExpression<'a>(&'a GroupedExpression, F, Var, F); AffineExpression(e, coeff, var, offset) <- IsAffine(e), LinearComponentCount(e, 1), @@ -37,24 +38,18 @@ crepe! { for offset in std::iter::once(*e.constant_offset()); @output - struct Assignment<'a>(Var<'a>, F); + struct Assignment(Var, F); Assignment(var, value) <- Constraint(e), AffineExpression(e, coeff, var, offset), for value in std::iter::once(-offset / coeff); } -fn linear_components<'a>(expr: &'a GroupedExpression) -> Vec<(F, Var<'a>)> { - expr.linear_components() - .into_iter() - .map(|(v, c)| (*c, Var(v.as_str()))) - .collect() +fn linear_components<'a>(expr: &'a GroupedExpression) -> Vec<(F, Var)> { + expr.linear_components().map(|(v, c)| (*c, *v)).collect() } -pub fn rule_based_optimization< - T: FieldElement, - V: std::hash::Hash + Eq + Ord + Clone + std::fmt::Display, ->( +pub fn rule_based_optimization( system: ConstraintSystem, ) -> ConstraintSystem { if T::modulus().to_arbitrary_integer() != BabyBearField::modulus().to_arbitrary_integer() { @@ -62,41 +57,81 @@ pub fn rule_based_optimization< } let mut rt = Crepe::new(); + let mut var_mapper = Default::default(); let transformed_expressions = system .algebraic_constraints .iter() - .map(|c| transform_grouped_expression(&c.expression)) + .map(|c| transform_grouped_expression(&c.expression, &mut var_mapper)) .collect_vec(); rt.extend(transformed_expressions.iter().map(|e| Constraint(e))); let (assignments,) = rt.run(); - for Assignment(var, value) in assignments { - println!("Inferred assignment: {} = {}", var.0, value); + for Assignment(var, value) in &assignments { + println!( + "Inferred assignment: {} = {}", + var_mapper.backward(*var).unwrap(), + value + ); + } + apply_substitutions( + system, + assignments.into_iter().map(|Assignment(var, value)| { + ( + var_mapper.backward(var).unwrap().clone(), + GroupedExpression::from_number(T::from(value.to_arbitrary_integer())), + ) + }), + ) +} + +struct VarMapper { + forward: HashMap, + backward: HashMap, + next_id: u32, +} + +impl Default for VarMapper { + fn default() -> Self { + Self { + forward: HashMap::new(), + backward: HashMap::new(), + next_id: 0, + } + } +} + +impl VarMapper { + fn forward(&mut self, v: V) -> Var { + if let Some(var) = self.forward.get(&v) { + *var + } else { + let var = Var(self.next_id); + self.forward.insert(v.clone(), var); + self.backward.insert(var, v); + self.next_id += 1; + var + } + } + + fn backward(&self, var: Var) -> Option<&V> { + self.backward.get(&var) } - // apply_substitutions( - // system, - // assignments - // .into_iter() - // .map(|Assignment(var, value)| (var.0.to_string(), value)), - // ) - system } -fn transform_grouped_expression< - T: FieldElement, - V: std::hash::Hash + Eq + Ord + Clone + std::fmt::Display, ->( +fn transform_grouped_expression( expr: &GroupedExpression, -) -> GroupedExpression { + var_mapper: &mut VarMapper, +) -> GroupedExpression { expr.clone() .into_summands() .map(|s| match s { GroupedExpressionComponent::Quadratic(l, r) => { - transform_grouped_expression(&l) * transform_grouped_expression(&r) + transform_grouped_expression(&l, var_mapper) + * transform_grouped_expression(&r, var_mapper) } GroupedExpressionComponent::Linear(v, c) => { - GroupedExpression::from_unknown_variable(v.to_string()) + GroupedExpression::from_unknown_variable(var_mapper.forward(v.clone())) * BabyBearField::from(c.to_arbitrary_integer()) } GroupedExpressionComponent::Constant(c) => { From a432b7c7e98b3fbebfda08da78d06b1242327750 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 13 Nov 2025 21:31:12 +0000 Subject: [PATCH 04/60] Use indexed system. --- autoprecompiles/src/constraint_optimizer.rs | 4 ++ autoprecompiles/src/optimizer.rs | 8 +-- autoprecompiles/src/rule_based_optimizer.rs | 72 +++++++++++++-------- 3 files changed, 52 insertions(+), 32 deletions(-) diff --git a/autoprecompiles/src/constraint_optimizer.rs b/autoprecompiles/src/constraint_optimizer.rs index 225329ac5b..90d418de21 100644 --- a/autoprecompiles/src/constraint_optimizer.rs +++ b/autoprecompiles/src/constraint_optimizer.rs @@ -23,6 +23,7 @@ use crate::{ low_degree_bus_interaction_optimizer::LowDegreeBusInteractionOptimizer, memory_optimizer::{optimize_memory, MemoryBusInteraction}, range_constraint_optimizer::RangeConstraintHandler, + rule_based_optimizer::rule_based_optimization, stats_logger::StatsLogger, }; @@ -61,6 +62,9 @@ pub fn optimize_constraints< // Index the constraint system for the first time let constraint_system = IndexedConstraintSystem::from(constraint_system); + let constraint_system = rule_based_optimization(constraint_system); + stats_logger.log("rule-based optimization", &constraint_system); + let constraint_system = solver_based_optimization(constraint_system, solver)?; stats_logger.log("solver-based optimization", &constraint_system); diff --git a/autoprecompiles/src/optimizer.rs b/autoprecompiles/src/optimizer.rs index d71485ca50..16e88464e3 100644 --- a/autoprecompiles/src/optimizer.rs +++ b/autoprecompiles/src/optimizer.rs @@ -7,7 +7,7 @@ use powdr_constraint_solver::constraint_system::{ AlgebraicConstraint, ComputationMethod, DerivedVariable, }; use powdr_constraint_solver::inliner::{self, inline_everything_below_degree_bound}; -use powdr_constraint_solver::solver::{self, new_solver}; +use powdr_constraint_solver::solver::new_solver; use powdr_constraint_solver::{ constraint_system::{BusInteraction, ConstraintSystem}, grouped_expression::GroupedExpression, @@ -16,7 +16,6 @@ use powdr_number::FieldElement; use crate::constraint_optimizer::trivial_simplifications; use crate::range_constraint_optimizer::optimize_range_constraints; -use crate::rule_based_optimizer::rule_based_optimization; use crate::{ adapter::Adapter, constraint_optimizer::optimize_constraints, @@ -40,10 +39,7 @@ pub fn optimize( stats_logger.log("exec bus optimization", &machine); } - let constraint_system = symbolic_machine_to_constraint_system(machine); - - let mut constraint_system = rule_based_optimization(constraint_system); - stats_logger.log("rule-based optimization", &constraint_system); + let mut constraint_system = symbolic_machine_to_constraint_system(machine); let mut solver = new_solver(constraint_system.clone(), bus_interaction_handler.clone()); loop { diff --git a/autoprecompiles/src/rule_based_optimizer.rs b/autoprecompiles/src/rule_based_optimizer.rs index ae0cf8c300..136b9b3231 100644 --- a/autoprecompiles/src/rule_based_optimizer.rs +++ b/autoprecompiles/src/rule_based_optimizer.rs @@ -1,10 +1,10 @@ +#![allow(clippy::iter_over_hash_type)] use std::{collections::HashMap, fmt::Display, hash::Hash}; use itertools::Itertools; use powdr_constraint_solver::{ - constraint_system::ConstraintSystem, grouped_expression::{GroupedExpression, GroupedExpressionComponent}, - indexed_constraint_system::apply_substitutions, + indexed_constraint_system::IndexedConstraintSystem, }; use powdr_number::{BabyBearField, FieldElement, LargeInt}; @@ -45,44 +45,64 @@ crepe! { for value in std::iter::once(-offset / coeff); } -fn linear_components<'a>(expr: &'a GroupedExpression) -> Vec<(F, Var)> { +fn linear_components(expr: &GroupedExpression) -> Vec<(F, Var)> { expr.linear_components().map(|(v, c)| (*c, *v)).collect() } pub fn rule_based_optimization( - system: ConstraintSystem, -) -> ConstraintSystem { + mut system: IndexedConstraintSystem, +) -> IndexedConstraintSystem { if T::modulus().to_arbitrary_integer() != BabyBearField::modulus().to_arbitrary_integer() { return system; } + let start = std::time::Instant::now(); let mut rt = Crepe::new(); let mut var_mapper = Default::default(); let transformed_expressions = system + .system() .algebraic_constraints .iter() .map(|c| transform_grouped_expression(&c.expression, &mut var_mapper)) .collect_vec(); + let transform_end = std::time::Instant::now(); - rt.extend(transformed_expressions.iter().map(|e| Constraint(e))); + rt.extend(transformed_expressions.iter().map(Constraint)); + + let insert_end = std::time::Instant::now(); let (assignments,) = rt.run(); - for Assignment(var, value) in &assignments { - println!( - "Inferred assignment: {} = {}", - var_mapper.backward(*var).unwrap(), - value - ); - } - apply_substitutions( - system, - assignments.into_iter().map(|Assignment(var, value)| { + let run_end = std::time::Instant::now(); + for (var, value) in assignments + .into_iter() + .map(|Assignment(var, value)| { ( - var_mapper.backward(var).unwrap().clone(), - GroupedExpression::from_number(T::from(value.to_arbitrary_integer())), + var_mapper.backward(&var), + T::from(value.to_arbitrary_integer()), ) - }), - ) + }) + .sorted() + { + log::trace!("Rule-based assignment: {var} = {value}",); + system.substitute_by_known(var, &value); + } + let substitution_end = std::time::Instant::now(); + + log::debug!( + "Rule-based optimization timings:\n\ + Transform: {}\n\ + Insert: {}\n\ + Run: {}\n\ + Substitution: {}\n\ + Total: {}", + (transform_end - start).as_secs_f32(), + (insert_end - transform_end).as_secs_f32(), + (run_end - insert_end).as_secs_f32(), + (substitution_end - run_end).as_secs_f32(), + (substitution_end - start).as_secs_f32(), + ); + + system } struct VarMapper { @@ -102,20 +122,20 @@ impl Default for VarMapper { } impl VarMapper { - fn forward(&mut self, v: V) -> Var { - if let Some(var) = self.forward.get(&v) { + fn forward(&mut self, v: &V) -> Var { + if let Some(var) = self.forward.get(v) { *var } else { let var = Var(self.next_id); self.forward.insert(v.clone(), var); - self.backward.insert(var, v); + self.backward.insert(var, v.clone()); self.next_id += 1; var } } - fn backward(&self, var: Var) -> Option<&V> { - self.backward.get(&var) + fn backward(&self, var: &Var) -> &V { + self.backward.get(var).unwrap() } } @@ -131,7 +151,7 @@ fn transform_grouped_expression { - GroupedExpression::from_unknown_variable(var_mapper.forward(v.clone())) + GroupedExpression::from_unknown_variable(var_mapper.forward(&v)) * BabyBearField::from(c.to_arbitrary_integer()) } GroupedExpressionComponent::Constant(c) => { From 8e796e7dd1897a4a78f7ab3af1eaeb3441a7d44c Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 13 Nov 2025 21:36:36 +0000 Subject: [PATCH 05/60] Use info for timing. --- autoprecompiles/src/rule_based_optimizer.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/autoprecompiles/src/rule_based_optimizer.rs b/autoprecompiles/src/rule_based_optimizer.rs index 136b9b3231..8bb83b49ed 100644 --- a/autoprecompiles/src/rule_based_optimizer.rs +++ b/autoprecompiles/src/rule_based_optimizer.rs @@ -88,7 +88,7 @@ pub fn rule_based_optimization Date: Fri, 14 Nov 2025 12:27:03 +0000 Subject: [PATCH 06/60] Add bus interactions and range constraints --- autoprecompiles/src/constraint_optimizer.rs | 3 +- autoprecompiles/src/rule_based_optimizer.rs | 96 +++++++++++++++++---- 2 files changed, 82 insertions(+), 17 deletions(-) diff --git a/autoprecompiles/src/constraint_optimizer.rs b/autoprecompiles/src/constraint_optimizer.rs index 90d418de21..2a35af9a14 100644 --- a/autoprecompiles/src/constraint_optimizer.rs +++ b/autoprecompiles/src/constraint_optimizer.rs @@ -62,7 +62,8 @@ pub fn optimize_constraints< // Index the constraint system for the first time let constraint_system = IndexedConstraintSystem::from(constraint_system); - let constraint_system = rule_based_optimization(constraint_system); + let constraint_system = + rule_based_optimization(constraint_system, bus_interaction_handler.clone()); stats_logger.log("rule-based optimization", &constraint_system); let constraint_system = solver_based_optimization(constraint_system, solver)?; diff --git a/autoprecompiles/src/rule_based_optimizer.rs b/autoprecompiles/src/rule_based_optimizer.rs index 8bb83b49ed..85167fab85 100644 --- a/autoprecompiles/src/rule_based_optimizer.rs +++ b/autoprecompiles/src/rule_based_optimizer.rs @@ -1,15 +1,19 @@ #![allow(clippy::iter_over_hash_type)] +#![allow(for_loops_over_fallibles)] use std::{collections::HashMap, fmt::Display, hash::Hash}; use itertools::Itertools; use powdr_constraint_solver::{ - grouped_expression::{GroupedExpression, GroupedExpressionComponent}, + constraint_system::{BusInteraction, BusInteractionHandler}, + grouped_expression::{GroupedExpression, GroupedExpressionComponent, NoRangeConstraints}, indexed_constraint_system::IndexedConstraintSystem, }; use powdr_number::{BabyBearField, FieldElement, LargeInt}; use crepe::crepe; +use crate::range_constraint_optimizer::RangeConstraintHandler; + type F = BabyBearField; #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] @@ -17,32 +21,44 @@ struct Var(u32); crepe! { @input - struct Constraint<'a>(&'a GroupedExpression); + struct AlgebraicConstraint<'a>(&'a GroupedExpression); + + @input + struct BusInteractionConstraint<'a>(&'a BusInteraction>); + + @input + struct RangeConstraintOnExpression<'a>(&'a GroupedExpression, F, F); + + struct Expression<'a>(&'a GroupedExpression); + Expression(e) <- AlgebraicConstraint(e); + Expression(e) <- BusInteractionConstraint(bus_inter), for e in bus_inter.fields(); + + struct IsSimpleVar<'a>(&'a GroupedExpression, Var); + IsSimpleVar(e, v) <- Expression(e), for v in e.try_to_simple_unknown(); + + @output + struct RangeConstraint(Var, F, F); struct IsAffine<'a>(&'a GroupedExpression); - // TODO it should not be required that it is a constraint, but - // we cannot get expression from anywhere else. - IsAffine(e) <- Constraint(e), (e.is_affine()); + IsAffine(e) <- Expression(e), (e.is_affine()); struct ExprHasLinearComponent<'a>(&'a GroupedExpression, F, Var); ExprHasLinearComponent(e, coeff, var) <- IsAffine(e), for (coeff, var) in linear_components(e); struct LinearComponentCount<'a>(&'a GroupedExpression, usize); - LinearComponentCount(e, count) <- IsAffine(e), for count in std::iter::once(e.linear_components().count()); + LinearComponentCount(e, e.linear_components().count()) <- IsAffine(e); struct AffineExpression<'a>(&'a GroupedExpression, F, Var, F); - AffineExpression(e, coeff, var, offset) <- + AffineExpression(e, coeff, var, *e.constant_offset()) <- IsAffine(e), LinearComponentCount(e, 1), - ExprHasLinearComponent(e, coeff, var), - for offset in std::iter::once(*e.constant_offset()); + ExprHasLinearComponent(e, coeff, var); @output struct Assignment(Var, F); - Assignment(var, value) <- - Constraint(e), - AffineExpression(e, coeff, var, offset), - for value in std::iter::once(-offset / coeff); + Assignment(var, -offset / coeff) <- + AlgebraicConstraint(e), + AffineExpression(e, coeff, var, offset); } fn linear_components(expr: &GroupedExpression) -> Vec<(F, Var)> { @@ -51,6 +67,7 @@ fn linear_components(expr: &GroupedExpression) -> Vec<(F, Var)> { pub fn rule_based_optimization( mut system: IndexedConstraintSystem, + bus_interaction_handler: impl BusInteractionHandler + Clone, ) -> IndexedConstraintSystem { if T::modulus().to_arbitrary_integer() != BabyBearField::modulus().to_arbitrary_integer() { return system; @@ -65,14 +82,61 @@ pub fn rule_based_optimization>> = system + .system() + .bus_interactions + .iter() + .map(|bus_inter| { + bus_inter + .fields() + .map(|f| transform_grouped_expression(f, &mut var_mapper)) + .collect() + }) + .collect_vec(); + // TODO we should do that inside the system, but the generic range constraint + // handler makes it difficult. + let range_constraints = system + .system() + .bus_interactions + .iter() + .enumerate() + .flat_map(|(i, bus_interaction)| { + let range_constraints = bus_interaction_handler + .handle_bus_interaction(bus_interaction.to_range_constraints(&NoRangeConstraints)) + .fields() + .cloned() + .collect_vec(); + bus_interactions[i] + .fields() + .zip_eq(range_constraints) + .map(|(expr, rc)| { + let (min, max) = rc.range(); + RangeConstraintOnExpression( + &expr, + BabyBearField::from(min.to_arbitrary_integer()), + BabyBearField::from(max.to_arbitrary_integer()), + ) + }) + }) + .collect_vec(); let transform_end = std::time::Instant::now(); - rt.extend(transformed_expressions.iter().map(Constraint)); + rt.extend(transformed_expressions.iter().map(AlgebraicConstraint)); + rt.extend(bus_interactions.iter().map(BusInteractionConstraint)); + rt.extend(range_constraints); let insert_end = std::time::Instant::now(); - let (assignments,) = rt.run(); + let (rcs, assignments) = rt.run(); let run_end = std::time::Instant::now(); + for RangeConstraint(var, min, max) in rcs { + log::info!( + "Rule-based range constraint: {} in [{}, {}]", + var_mapper.backward(&var), + min, + max + ); + } for (var, value) in assignments .into_iter() .map(|Assignment(var, value)| { @@ -83,7 +147,7 @@ pub fn rule_based_optimization Date: Fri, 14 Nov 2025 13:04:20 +0000 Subject: [PATCH 07/60] Discover boolean range constraints. --- autoprecompiles/src/rule_based_optimizer.rs | 26 ++++++++++++++++--- .../src/solver/quadratic_equivalences.rs | 4 +++ 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/autoprecompiles/src/rule_based_optimizer.rs b/autoprecompiles/src/rule_based_optimizer.rs index 85167fab85..593478584f 100644 --- a/autoprecompiles/src/rule_based_optimizer.rs +++ b/autoprecompiles/src/rule_based_optimizer.rs @@ -30,14 +30,31 @@ crepe! { struct RangeConstraintOnExpression<'a>(&'a GroupedExpression, F, F); struct Expression<'a>(&'a GroupedExpression); + // TODO question to answer: Does this treat different instances of the same expression as equal? Expression(e) <- AlgebraicConstraint(e); Expression(e) <- BusInteractionConstraint(bus_inter), for e in bus_inter.fields(); + Expression(q) <- Expression(e), for q in e.quadratic_components().iter().flat_map(|(l, r)| [l, r].into_iter()); struct IsSimpleVar<'a>(&'a GroupedExpression, Var); IsSimpleVar(e, v) <- Expression(e), for v in e.try_to_simple_unknown(); + struct SimpleQuadratic<'a>(&'a GroupedExpression, &'a GroupedExpression, &'a GroupedExpression); + SimpleQuadratic(q, l, r) <- Expression(q), for (l, r) in q.quadratic_components(); + SimpleQuadratic(q, l, r) <- SimpleQuadratic(q, r, l); + @output struct RangeConstraint(Var, F, F); + RangeConstraint(v, min, max) <- + RangeConstraintOnExpression(e, min, max), + IsSimpleVar(e, v); + // TODO wait a second. We can craete range constraints on expressions for all + // algebraic constraints. Then we just work on range constraints on expressions + // instead of algebraic constraints. Might be more difficult with the scaling, though. + RangeConstraint(v, x1, x1 + F::from(1)) <- + AlgebraicConstraint(e), + SimpleQuadratic(e, l, r), + Solvable(l, v, x1), + Solvable(r, v, x1 + F::from(1)); struct IsAffine<'a>(&'a GroupedExpression); IsAffine(e) <- Expression(e), (e.is_affine()); @@ -54,11 +71,14 @@ crepe! { LinearComponentCount(e, 1), ExprHasLinearComponent(e, coeff, var); + struct Solvable<'a>(&'a GroupedExpression, Var, F); + Solvable(e, var, -offset / coeff) <- + AffineExpression(e, coeff, var, offset); + + @output struct Assignment(Var, F); - Assignment(var, -offset / coeff) <- - AlgebraicConstraint(e), - AffineExpression(e, coeff, var, offset); + Assignment(var, v) <- AlgebraicConstraint(e), Solvable(e, var, v); } fn linear_components(expr: &GroupedExpression) -> Vec<(F, Var)> { diff --git a/constraint-solver/src/solver/quadratic_equivalences.rs b/constraint-solver/src/solver/quadratic_equivalences.rs index 3e54470166..b691120978 100644 --- a/constraint-solver/src/solver/quadratic_equivalences.rs +++ b/constraint-solver/src/solver/quadratic_equivalences.rs @@ -87,6 +87,10 @@ fn process_quadratic_equality_candidate_pair< // - X = -A - offset and Y = -A - offset // Since `A` has to have some value, we can conclude `X = Y`. + println!( + "Found quadratic equality {} = {} from {}, {rc2}", + c1_var, c2_var, rc1, + ); Some((c1_var.clone(), c2_var.clone())) } From 337415f011a77f33d96c40984a853480762219ad Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 14 Nov 2025 13:45:41 +0000 Subject: [PATCH 08/60] Add all expressions that are lacking a single variable. --- autoprecompiles/src/rule_based_optimizer.rs | 60 +++++++++++++++---- .../src/solver/quadratic_equivalences.rs | 8 +-- 2 files changed, 51 insertions(+), 17 deletions(-) diff --git a/autoprecompiles/src/rule_based_optimizer.rs b/autoprecompiles/src/rule_based_optimizer.rs index 593478584f..73b13077f1 100644 --- a/autoprecompiles/src/rule_based_optimizer.rs +++ b/autoprecompiles/src/rule_based_optimizer.rs @@ -1,6 +1,10 @@ #![allow(clippy::iter_over_hash_type)] #![allow(for_loops_over_fallibles)] -use std::{collections::HashMap, fmt::Display, hash::Hash}; +use std::{ + collections::{HashMap, HashSet}, + fmt::Display, + hash::Hash, +}; use itertools::Itertools; use powdr_constraint_solver::{ @@ -29,18 +33,22 @@ crepe! { @input struct RangeConstraintOnExpression<'a>(&'a GroupedExpression, F, F); + @input + struct InputExpression<'a>(&'a GroupedExpression); + struct Expression<'a>(&'a GroupedExpression); // TODO question to answer: Does this treat different instances of the same expression as equal? Expression(e) <- AlgebraicConstraint(e); Expression(e) <- BusInteractionConstraint(bus_inter), for e in bus_inter.fields(); Expression(q) <- Expression(e), for q in e.quadratic_components().iter().flat_map(|(l, r)| [l, r].into_iter()); + Expression(e) <- InputExpression(e); struct IsSimpleVar<'a>(&'a GroupedExpression, Var); IsSimpleVar(e, v) <- Expression(e), for v in e.try_to_simple_unknown(); - struct SimpleQuadratic<'a>(&'a GroupedExpression, &'a GroupedExpression, &'a GroupedExpression); - SimpleQuadratic(q, l, r) <- Expression(q), for (l, r) in q.quadratic_components(); - SimpleQuadratic(q, l, r) <- SimpleQuadratic(q, r, l); + struct Product<'a>(&'a GroupedExpression, &'a GroupedExpression, &'a GroupedExpression); + Product(q, l, r) <- Expression(q), for (l, r) in q.quadratic_components(); + Product(q, l, r) <- Product(q, r, l); @output struct RangeConstraint(Var, F, F); @@ -52,7 +60,7 @@ crepe! { // instead of algebraic constraints. Might be more difficult with the scaling, though. RangeConstraint(v, x1, x1 + F::from(1)) <- AlgebraicConstraint(e), - SimpleQuadratic(e, l, r), + Product(e, l, r), Solvable(l, v, x1), Solvable(r, v, x1 + F::from(1)); @@ -139,24 +147,31 @@ pub fn rule_based_optimization) -> HashSet> { + let mut result = expr + .quadratic_components() + .iter() + .flat_map(|(l, r)| { + extract_single_vars(l) + .into_iter() + .chain(extract_single_vars(r).into_iter()) + }) + .collect::>(); + result.extend( + expr.linear_components().map(|(v, c)| { + expr.clone() - GroupedExpression::from_unknown_variable(v.clone()) * (*c) + }), + ); + result +} diff --git a/constraint-solver/src/solver/quadratic_equivalences.rs b/constraint-solver/src/solver/quadratic_equivalences.rs index b691120978..0c96a2cf2c 100644 --- a/constraint-solver/src/solver/quadratic_equivalences.rs +++ b/constraint-solver/src/solver/quadratic_equivalences.rs @@ -73,8 +73,8 @@ fn process_quadratic_equality_candidate_pair< // Now the only remaining check is to see if the affine expressions are the same. // This could have been the first step, but it is rather expensive, so we do it last. - if c1.expr - GroupedExpression::from_unknown_variable(c1_var.clone()) - != c2.expr - GroupedExpression::from_unknown_variable(c2_var.clone()) + if c1.expr.clone() - GroupedExpression::from_unknown_variable(c1_var.clone()) + != c2.expr.clone() - GroupedExpression::from_unknown_variable(c2_var.clone()) { return None; } @@ -88,8 +88,8 @@ fn process_quadratic_equality_candidate_pair< // Since `A` has to have some value, we can conclude `X = Y`. println!( - "Found quadratic equality {} = {} from {}, {rc2}", - c1_var, c2_var, rc1, + "Found quadratic equality {} = {} from ({} - {}) * ({}) and ({} - {}) * ({})", + c1_var, c2_var, c1.expr, c1.offset, c1.expr, c2.expr, c2.offset, c2.expr ); Some((c1_var.clone(), c2_var.clone())) } From 419f9d5012c390fd889412e172c6318a65f1040f Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 14 Nov 2025 16:48:44 +0000 Subject: [PATCH 09/60] Try to split out single variables. --- autoprecompiles/src/rule_based_optimizer.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/autoprecompiles/src/rule_based_optimizer.rs b/autoprecompiles/src/rule_based_optimizer.rs index 73b13077f1..d96d146669 100644 --- a/autoprecompiles/src/rule_based_optimizer.rs +++ b/autoprecompiles/src/rule_based_optimizer.rs @@ -14,6 +14,8 @@ use powdr_constraint_solver::{ }; use powdr_number::{BabyBearField, FieldElement, LargeInt}; +use num_traits::Zero; + use crepe::crepe; use crate::range_constraint_optimizer::RangeConstraintHandler; @@ -64,9 +66,24 @@ crepe! { Solvable(l, v, x1), Solvable(r, v, x1 + F::from(1)); + struct IsZero<'a>(&'a GroupedExpression); + IsZero(e) <- Expression(e), (e.is_zero()); + struct IsAffine<'a>(&'a GroupedExpression); IsAffine(e) <- Expression(e), (e.is_affine()); + // struct DestructureAffine<'a>(&'a GroupedExpression, F, Var, &'a GroupedExpression); + // DestructureAffine(e, coeff, var, rest) <- + // IsAffine(e), + // Expression(rest), + // for (coeff, var) in linear_components(e), + // (rest == &(e - &(GroupedExpression::from_unknown_variable(var.clone()) * coeff))); + + // struct AffineExpression<'a>(&'a GroupedExpression, F, Var, F); + // AffineExpression(e, coeff, var, offset) <- + // DestructureAffine(e, coeff, var, rest), + // for offset in rest.try_to_number(); + struct ExprHasLinearComponent<'a>(&'a GroupedExpression, F, Var); ExprHasLinearComponent(e, coeff, var) <- IsAffine(e), for (coeff, var) in linear_components(e); From 55aa6105090e247a358df3d071a01a61713eb3c8 Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 14 Nov 2025 18:12:53 +0100 Subject: [PATCH 10/60] Extract single variable. --- autoprecompiles/src/rule_based_optimizer.rs | 62 +++++++------------ ...icting_constraints_in_bus_interaction.cobr | 12 ++++ 2 files changed, 36 insertions(+), 38 deletions(-) create mode 100644 openvm/tests/conflicting_constraints_in_bus_interaction.cobr diff --git a/autoprecompiles/src/rule_based_optimizer.rs b/autoprecompiles/src/rule_based_optimizer.rs index d96d146669..7ea2eff7ba 100644 --- a/autoprecompiles/src/rule_based_optimizer.rs +++ b/autoprecompiles/src/rule_based_optimizer.rs @@ -36,14 +36,15 @@ crepe! { struct RangeConstraintOnExpression<'a>(&'a GroupedExpression, F, F); @input - struct InputExpression<'a>(&'a GroupedExpression); + struct DestructureAffine<'a>(&'a GroupedExpression, F, Var, &'a GroupedExpression); struct Expression<'a>(&'a GroupedExpression); // TODO question to answer: Does this treat different instances of the same expression as equal? Expression(e) <- AlgebraicConstraint(e); Expression(e) <- BusInteractionConstraint(bus_inter), for e in bus_inter.fields(); Expression(q) <- Expression(e), for q in e.quadratic_components().iter().flat_map(|(l, r)| [l, r].into_iter()); - Expression(e) <- InputExpression(e); + Expression(e) <- DestructureAffine(_, _, _, e); + Expression(e) <- DestructureAffine(e, _, _, _); struct IsSimpleVar<'a>(&'a GroupedExpression, Var); IsSimpleVar(e, v) <- Expression(e), for v in e.try_to_simple_unknown(); @@ -72,44 +73,20 @@ crepe! { struct IsAffine<'a>(&'a GroupedExpression); IsAffine(e) <- Expression(e), (e.is_affine()); - // struct DestructureAffine<'a>(&'a GroupedExpression, F, Var, &'a GroupedExpression); - // DestructureAffine(e, coeff, var, rest) <- - // IsAffine(e), - // Expression(rest), - // for (coeff, var) in linear_components(e), - // (rest == &(e - &(GroupedExpression::from_unknown_variable(var.clone()) * coeff))); - - // struct AffineExpression<'a>(&'a GroupedExpression, F, Var, F); - // AffineExpression(e, coeff, var, offset) <- - // DestructureAffine(e, coeff, var, rest), - // for offset in rest.try_to_number(); - - struct ExprHasLinearComponent<'a>(&'a GroupedExpression, F, Var); - ExprHasLinearComponent(e, coeff, var) <- IsAffine(e), for (coeff, var) in linear_components(e); - - struct LinearComponentCount<'a>(&'a GroupedExpression, usize); - LinearComponentCount(e, e.linear_components().count()) <- IsAffine(e); - struct AffineExpression<'a>(&'a GroupedExpression, F, Var, F); - AffineExpression(e, coeff, var, *e.constant_offset()) <- - IsAffine(e), - LinearComponentCount(e, 1), - ExprHasLinearComponent(e, coeff, var); + AffineExpression(e, coeff, var, offset) <- + DestructureAffine(e, coeff, var, rest), + for offset in rest.try_to_number(); struct Solvable<'a>(&'a GroupedExpression, Var, F); Solvable(e, var, -offset / coeff) <- AffineExpression(e, coeff, var, offset); - @output struct Assignment(Var, F); Assignment(var, v) <- AlgebraicConstraint(e), Solvable(e, var, v); } -fn linear_components(expr: &GroupedExpression) -> Vec<(F, Var)> { - expr.linear_components().map(|(v, c)| (*c, *v)).collect() -} - pub fn rule_based_optimization( mut system: IndexedConstraintSystem, bus_interaction_handler: impl BusInteractionHandler + Clone, @@ -165,7 +142,7 @@ pub fn rule_based_optimization) -> HashSet> { +fn extract_single_vars( + expr: &GroupedExpression, +) -> HashSet<(GroupedExpression, F, Var, GroupedExpression)> { let mut result = expr .quadratic_components() .iter() @@ -288,10 +271,13 @@ fn extract_single_vars(expr: &GroupedExpression) -> HashSet>(); - result.extend( - expr.linear_components().map(|(v, c)| { - expr.clone() - GroupedExpression::from_unknown_variable(v.clone()) * (*c) - }), - ); + result.extend(expr.linear_components().map(|(v, c)| { + ( + expr.clone(), + *c, + *v, + expr.clone() - GroupedExpression::from_unknown_variable(v.clone()) * (*c), + ) + })); result } diff --git a/openvm/tests/conflicting_constraints_in_bus_interaction.cobr b/openvm/tests/conflicting_constraints_in_bus_interaction.cobr new file mode 100644 index 0000000000..6eaa0ae0e5 --- /dev/null +++ b/openvm/tests/conflicting_constraints_in_bus_interaction.cobr @@ -0,0 +1,12 @@ +¢kconstraints˜¡dexpr¡oBinaryOperation£dleft¡iReference£dnameqopcode_sll_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnameqopcode_sll_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡fNumber¡dexpr¡oBinaryOperation£dleft¡iReference£dnameqopcode_srl_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnameqopcode_srl_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡fNumber¡dexpr¡oBinaryOperation£dleft¡iReference£dnameqopcode_sra_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnameqopcode_sra_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡fNumber¡dexpr¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_sll_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_srl_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sra_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_sll_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_srl_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sra_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡fNumber¡dexpr¡oBinaryOperation£dleft¡iReference£dnameubit_shift_marker__0_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnameubit_shift_marker__0_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡fNumber¡dexpr¡oBinaryOperation£dleft¡iReference£dnameubit_shift_marker__0_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnameubit_multiplier_left_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡iReference£dnameqopcode_sll_flag_0gpoly_id¢bideptypeiCommitteddnextô¡dexpr¡oBinaryOperation£dleft¡iReference£dnameubit_shift_marker__0_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnamevbit_multiplier_right_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡iReference£dnameqopcode_srl_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sra_flag_0gpoly_id¢bideptypeiCommitteddnextô¡dexpr¡oBinaryOperation£dleft¡iReference£dnameubit_shift_marker__1_0gpoly_id¢bid eptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnameubit_shift_marker__1_0gpoly_id¢bid eptypeiCommitteddnextôbopcSuberight¡fNumber¡dexpr¡oBinaryOperation£dleft¡iReference£dnameubit_shift_marker__1_0gpoly_id¢bid eptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnameubit_multiplier_left_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡fNumberbopcMuleright¡iReference£dnameqopcode_sll_flag_0gpoly_id¢bideptypeiCommitteddnextô¡dexpr¡oBinaryOperation£dleft¡iReference£dnameubit_shift_marker__1_0gpoly_id¢bid eptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnamevbit_multiplier_right_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡fNumberbopcMuleright¡oBinaryOperation£dleft¡iReference£dnameqopcode_srl_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sra_flag_0gpoly_id¢bideptypeiCommitteddnextô¡dexpr¡oBinaryOperation£dleft¡iReference£dnameubit_shift_marker__2_0gpoly_id¢bid +eptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnameubit_shift_marker__2_0gpoly_id¢bid +eptypeiCommitteddnextôbopcSuberight¡fNumber¡dexpr¡oBinaryOperation£dleft¡iReference£dnameubit_shift_marker__2_0gpoly_id¢bid +eptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnameubit_multiplier_left_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡fNumberbopcMuleright¡iReference£dnameqopcode_sll_flag_0gpoly_id¢bideptypeiCommitteddnextô¡dexpr¡oBinaryOperation£dleft¡iReference£dnameubit_shift_marker__2_0gpoly_id¢bid +eptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnamevbit_multiplier_right_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡fNumberbopcMuleright¡oBinaryOperation£dleft¡iReference£dnameqopcode_srl_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sra_flag_0gpoly_id¢bideptypeiCommitteddnextô¡dexpr¡oBinaryOperation£dleft¡iReference£dnameubit_shift_marker__3_0gpoly_id¢bid eptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnameubit_shift_marker__3_0gpoly_id¢bid eptypeiCommitteddnextôbopcSuberight¡fNumber¡dexpr¡oBinaryOperation£dleft¡iReference£dnameubit_shift_marker__3_0gpoly_id¢bid eptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnameubit_multiplier_left_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡fNumberbopcMuleright¡iReference£dnameqopcode_sll_flag_0gpoly_id¢bideptypeiCommitteddnextô¡dexpr¡oBinaryOperation£dleft¡iReference£dnameubit_shift_marker__3_0gpoly_id¢bid eptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnamevbit_multiplier_right_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡fNumberbopcMuleright¡oBinaryOperation£dleft¡iReference£dnameqopcode_srl_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sra_flag_0gpoly_id¢bideptypeiCommitteddnextô¡dexpr¡oBinaryOperation£dleft¡iReference£dnameubit_shift_marker__4_0gpoly_id¢bid eptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnameubit_shift_marker__4_0gpoly_id¢bid eptypeiCommitteddnextôbopcSuberight¡fNumber¡dexpr¡oBinaryOperation£dleft¡iReference£dnameubit_shift_marker__4_0gpoly_id¢bid eptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnameubit_multiplier_left_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡fNumberbopcMuleright¡iReference£dnameqopcode_sll_flag_0gpoly_id¢bideptypeiCommitteddnextô¡dexpr¡oBinaryOperation£dleft¡iReference£dnameubit_shift_marker__4_0gpoly_id¢bid eptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnamevbit_multiplier_right_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡fNumberbopcMuleright¡oBinaryOperation£dleft¡iReference£dnameqopcode_srl_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sra_flag_0gpoly_id¢bideptypeiCommitteddnextô¡dexpr¡oBinaryOperation£dleft¡iReference£dnameubit_shift_marker__5_0gpoly_id¢bid eptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnameubit_shift_marker__5_0gpoly_id¢bid eptypeiCommitteddnextôbopcSuberight¡fNumber¡dexpr¡oBinaryOperation£dleft¡iReference£dnameubit_shift_marker__5_0gpoly_id¢bid eptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnameubit_multiplier_left_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡fNumber bopcMuleright¡iReference£dnameqopcode_sll_flag_0gpoly_id¢bideptypeiCommitteddnextô¡dexpr¡oBinaryOperation£dleft¡iReference£dnameubit_shift_marker__5_0gpoly_id¢bid eptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnamevbit_multiplier_right_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡fNumber bopcMuleright¡oBinaryOperation£dleft¡iReference£dnameqopcode_srl_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sra_flag_0gpoly_id¢bideptypeiCommitteddnextô¡dexpr¡oBinaryOperation£dleft¡iReference£dnameubit_shift_marker__6_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnameubit_shift_marker__6_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡fNumber¡dexpr¡oBinaryOperation£dleft¡iReference£dnameubit_shift_marker__6_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnameubit_multiplier_left_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡fNumber@bopcMuleright¡iReference£dnameqopcode_sll_flag_0gpoly_id¢bideptypeiCommitteddnextô¡dexpr¡oBinaryOperation£dleft¡iReference£dnameubit_shift_marker__6_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnamevbit_multiplier_right_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡fNumber@bopcMuleright¡oBinaryOperation£dleft¡iReference£dnameqopcode_srl_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sra_flag_0gpoly_id¢bideptypeiCommitteddnextô¡dexpr¡oBinaryOperation£dleft¡iReference£dnameubit_shift_marker__7_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnameubit_shift_marker__7_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡fNumber¡dexpr¡oBinaryOperation£dleft¡iReference£dnameubit_shift_marker__7_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnameubit_multiplier_left_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡fNumber€bopcMuleright¡iReference£dnameqopcode_sll_flag_0gpoly_id¢bideptypeiCommitteddnextô¡dexpr¡oBinaryOperation£dleft¡iReference£dnameubit_shift_marker__7_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnamevbit_multiplier_right_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡fNumber€bopcMuleright¡oBinaryOperation£dleft¡iReference£dnameqopcode_srl_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sra_flag_0gpoly_id¢bideptypeiCommitteddnextô¡dexpr¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_sll_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_srl_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sra_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameubit_shift_marker__0_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnameubit_shift_marker__1_0gpoly_id¢bid eptypeiCommitteddnextôbopcAdderight¡iReference£dnameubit_shift_marker__2_0gpoly_id¢bid +eptypeiCommitteddnextôbopcAdderight¡iReference£dnameubit_shift_marker__3_0gpoly_id¢bid eptypeiCommitteddnextôbopcAdderight¡iReference£dnameubit_shift_marker__4_0gpoly_id¢bid eptypeiCommitteddnextôbopcAdderight¡iReference£dnameubit_shift_marker__5_0gpoly_id¢bid eptypeiCommitteddnextôbopcAdderight¡iReference£dnameubit_shift_marker__6_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnameubit_shift_marker__7_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡fNumber¡dexpr¡oBinaryOperation£dleft¡iReference£dnamevlimb_shift_marker__0_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnamevlimb_shift_marker__0_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡fNumber¡dexpr¡oBinaryOperation£dleft¡iReference£dnamevlimb_shift_marker__0_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnamefa__0_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnameqopcode_sll_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnamefb__0_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnameubit_multiplier_left_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡fNumberbopcMuleright¡iReference£dnametbit_shift_carry__0_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnameqopcode_sll_flag_0gpoly_id¢bideptypeiCommitteddnextô¡dexpr¡oBinaryOperation£dleft¡iReference£dnamevlimb_shift_marker__0_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnamefa__0_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnamevbit_multiplier_right_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnametbit_shift_carry__1_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnameqopcode_srl_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sra_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡fNumberbopcAdderight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_srl_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sra_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnamefb__0_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡iReference£dnametbit_shift_carry__0_0gpoly_id¢bideptypeiCommitteddnextô¡dexpr¡oBinaryOperation£dleft¡iReference£dnamevlimb_shift_marker__0_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnamefa__1_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnameqopcode_sll_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnametbit_shift_carry__0_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnameqopcode_sll_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡oBinaryOperation£dleft¡iReference£dnamefb__1_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnameubit_multiplier_left_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡fNumberbopcMuleright¡iReference£dnametbit_shift_carry__1_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnameqopcode_sll_flag_0gpoly_id¢bideptypeiCommitteddnextô¡dexpr¡oBinaryOperation£dleft¡iReference£dnamevlimb_shift_marker__0_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnamefa__1_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnamevbit_multiplier_right_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnametbit_shift_carry__2_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnameqopcode_srl_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sra_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡fNumberbopcAdderight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_srl_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sra_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnamefb__1_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡iReference£dnametbit_shift_carry__1_0gpoly_id¢bideptypeiCommitteddnextô¡dexpr¡oBinaryOperation£dleft¡iReference£dnamevlimb_shift_marker__0_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnamefa__2_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnameqopcode_sll_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnametbit_shift_carry__1_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnameqopcode_sll_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡oBinaryOperation£dleft¡iReference£dnamefb__2_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnameubit_multiplier_left_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡fNumberbopcMuleright¡iReference£dnametbit_shift_carry__2_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnameqopcode_sll_flag_0gpoly_id¢bideptypeiCommitteddnextô¡dexpr¡oBinaryOperation£dleft¡iReference£dnamevlimb_shift_marker__0_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnamefa__2_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnamevbit_multiplier_right_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnametbit_shift_carry__3_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnameqopcode_srl_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sra_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡fNumberbopcAdderight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_srl_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sra_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnamefb__2_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡iReference£dnametbit_shift_carry__2_0gpoly_id¢bideptypeiCommitteddnextô¡dexpr¡oBinaryOperation£dleft¡iReference£dnamevlimb_shift_marker__0_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnamefa__3_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnameqopcode_sll_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnametbit_shift_carry__2_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnameqopcode_sll_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡oBinaryOperation£dleft¡iReference£dnamefb__3_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnameubit_multiplier_left_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡fNumberbopcMuleright¡iReference£dnametbit_shift_carry__3_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnameqopcode_sll_flag_0gpoly_id¢bideptypeiCommitteddnextô¡dexpr¡oBinaryOperation£dleft¡iReference£dnamevlimb_shift_marker__0_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnamefa__3_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnamevbit_multiplier_right_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnamehb_sign_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnamevbit_multiplier_right_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡fNumberbopcMuleright¡fNumberbopcAdderight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_srl_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sra_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnamefb__3_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡iReference£dnametbit_shift_carry__3_0gpoly_id¢bideptypeiCommitteddnextô¡dexpr¡oBinaryOperation£dleft¡iReference£dnamevlimb_shift_marker__1_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnamevlimb_shift_marker__1_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡fNumber¡dexpr¡oBinaryOperation£dleft¡iReference£dnamevlimb_shift_marker__1_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnamefa__0_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnameqopcode_sll_flag_0gpoly_id¢bideptypeiCommitteddnextô¡dexpr¡oBinaryOperation£dleft¡iReference£dnamevlimb_shift_marker__1_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnamefa__0_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnamevbit_multiplier_right_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnametbit_shift_carry__2_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnameqopcode_srl_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sra_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡fNumberbopcAdderight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_srl_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sra_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnamefb__1_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡iReference£dnametbit_shift_carry__1_0gpoly_id¢bideptypeiCommitteddnextô¡dexpr¡oBinaryOperation£dleft¡iReference£dnamevlimb_shift_marker__1_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnamefa__1_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnameqopcode_sll_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnamefb__0_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnameubit_multiplier_left_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡fNumberbopcMuleright¡iReference£dnametbit_shift_carry__0_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnameqopcode_sll_flag_0gpoly_id¢bideptypeiCommitteddnextô¡dexpr¡oBinaryOperation£dleft¡iReference£dnamevlimb_shift_marker__1_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnamefa__1_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnamevbit_multiplier_right_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnametbit_shift_carry__3_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnameqopcode_srl_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sra_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡fNumberbopcAdderight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_srl_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sra_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnamefb__2_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡iReference£dnametbit_shift_carry__2_0gpoly_id¢bideptypeiCommitteddnextô¡dexpr¡oBinaryOperation£dleft¡iReference£dnamevlimb_shift_marker__1_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnamefa__2_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnameqopcode_sll_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnametbit_shift_carry__0_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnameqopcode_sll_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡oBinaryOperation£dleft¡iReference£dnamefb__1_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnameubit_multiplier_left_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡fNumberbopcMuleright¡iReference£dnametbit_shift_carry__1_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnameqopcode_sll_flag_0gpoly_id¢bideptypeiCommitteddnextô¡dexpr¡oBinaryOperation£dleft¡iReference£dnamevlimb_shift_marker__1_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnamefa__2_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnamevbit_multiplier_right_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnamehb_sign_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnamevbit_multiplier_right_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡fNumberbopcMuleright¡fNumberbopcAdderight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_srl_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sra_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnamefb__3_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡iReference£dnametbit_shift_carry__3_0gpoly_id¢bideptypeiCommitteddnextô¡dexpr¡oBinaryOperation£dleft¡iReference£dnamevlimb_shift_marker__1_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnamefa__3_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnameqopcode_sll_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnametbit_shift_carry__1_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnameqopcode_sll_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡oBinaryOperation£dleft¡iReference£dnamefb__2_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnameubit_multiplier_left_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡fNumberbopcMuleright¡iReference£dnametbit_shift_carry__2_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnameqopcode_sll_flag_0gpoly_id¢bideptypeiCommitteddnextô¡dexpr¡oBinaryOperation£dleft¡iReference£dnamevlimb_shift_marker__1_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnamefa__3_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnameqopcode_srl_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sra_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡iReference£dnamehb_sign_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡fNumberÿ¡dexpr¡oBinaryOperation£dleft¡iReference£dnamevlimb_shift_marker__2_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnamevlimb_shift_marker__2_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡fNumber¡dexpr¡oBinaryOperation£dleft¡iReference£dnamevlimb_shift_marker__2_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnamefa__0_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnameqopcode_sll_flag_0gpoly_id¢bideptypeiCommitteddnextô¡dexpr¡oBinaryOperation£dleft¡iReference£dnamevlimb_shift_marker__2_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnamefa__0_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnamevbit_multiplier_right_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnametbit_shift_carry__3_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnameqopcode_srl_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sra_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡fNumberbopcAdderight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_srl_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sra_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnamefb__2_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡iReference£dnametbit_shift_carry__2_0gpoly_id¢bideptypeiCommitteddnextô¡dexpr¡oBinaryOperation£dleft¡iReference£dnamevlimb_shift_marker__2_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnamefa__1_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnameqopcode_sll_flag_0gpoly_id¢bideptypeiCommitteddnextô¡dexpr¡oBinaryOperation£dleft¡iReference£dnamevlimb_shift_marker__2_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnamefa__1_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnamevbit_multiplier_right_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnamehb_sign_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnamevbit_multiplier_right_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡fNumberbopcMuleright¡fNumberbopcAdderight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_srl_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sra_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnamefb__3_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡iReference£dnametbit_shift_carry__3_0gpoly_id¢bideptypeiCommitteddnextô¡dexpr¡oBinaryOperation£dleft¡iReference£dnamevlimb_shift_marker__2_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnamefa__2_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnameqopcode_sll_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnamefb__0_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnameubit_multiplier_left_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡fNumberbopcMuleright¡iReference£dnametbit_shift_carry__0_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnameqopcode_sll_flag_0gpoly_id¢bideptypeiCommitteddnextô¡dexpr¡oBinaryOperation£dleft¡iReference£dnamevlimb_shift_marker__2_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnamefa__2_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnameqopcode_srl_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sra_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡iReference£dnamehb_sign_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡fNumberÿ¡dexpr¡oBinaryOperation£dleft¡iReference£dnamevlimb_shift_marker__2_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnamefa__3_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnameqopcode_sll_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnametbit_shift_carry__0_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnameqopcode_sll_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡oBinaryOperation£dleft¡iReference£dnamefb__1_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnameubit_multiplier_left_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡fNumberbopcMuleright¡iReference£dnametbit_shift_carry__1_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnameqopcode_sll_flag_0gpoly_id¢bideptypeiCommitteddnextô¡dexpr¡oBinaryOperation£dleft¡iReference£dnamevlimb_shift_marker__2_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnamefa__3_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnameqopcode_srl_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sra_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡iReference£dnamehb_sign_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡fNumberÿ¡dexpr¡oBinaryOperation£dleft¡iReference£dnamevlimb_shift_marker__3_0gpoly_id¢bid eptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnamevlimb_shift_marker__3_0gpoly_id¢bid eptypeiCommitteddnextôbopcSuberight¡fNumber¡dexpr¡oBinaryOperation£dleft¡iReference£dnamevlimb_shift_marker__3_0gpoly_id¢bid eptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnamefa__0_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnameqopcode_sll_flag_0gpoly_id¢bideptypeiCommitteddnextô¡dexpr¡oBinaryOperation£dleft¡iReference£dnamevlimb_shift_marker__3_0gpoly_id¢bid eptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnamefa__0_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnamevbit_multiplier_right_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnamehb_sign_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnamevbit_multiplier_right_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡fNumberbopcMuleright¡fNumberbopcAdderight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_srl_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sra_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnamefb__3_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡iReference£dnametbit_shift_carry__3_0gpoly_id¢bideptypeiCommitteddnextô¡dexpr¡oBinaryOperation£dleft¡iReference£dnamevlimb_shift_marker__3_0gpoly_id¢bid eptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnamefa__1_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnameqopcode_sll_flag_0gpoly_id¢bideptypeiCommitteddnextô¡dexpr¡oBinaryOperation£dleft¡iReference£dnamevlimb_shift_marker__3_0gpoly_id¢bid eptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnamefa__1_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnameqopcode_srl_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sra_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡iReference£dnamehb_sign_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡fNumberÿ¡dexpr¡oBinaryOperation£dleft¡iReference£dnamevlimb_shift_marker__3_0gpoly_id¢bid eptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnamefa__2_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnameqopcode_sll_flag_0gpoly_id¢bideptypeiCommitteddnextô¡dexpr¡oBinaryOperation£dleft¡iReference£dnamevlimb_shift_marker__3_0gpoly_id¢bid eptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnamefa__2_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnameqopcode_srl_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sra_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡iReference£dnamehb_sign_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡fNumberÿ¡dexpr¡oBinaryOperation£dleft¡iReference£dnamevlimb_shift_marker__3_0gpoly_id¢bid eptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnamefa__3_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnameqopcode_sll_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnamefb__0_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnameubit_multiplier_left_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡fNumberbopcMuleright¡iReference£dnametbit_shift_carry__0_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnameqopcode_sll_flag_0gpoly_id¢bideptypeiCommitteddnextô¡dexpr¡oBinaryOperation£dleft¡iReference£dnamevlimb_shift_marker__3_0gpoly_id¢bid eptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnamefa__3_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnameqopcode_srl_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sra_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡iReference£dnamehb_sign_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡fNumberÿ¡dexpr¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_sll_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_srl_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sra_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnamevlimb_shift_marker__0_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnamevlimb_shift_marker__1_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnamevlimb_shift_marker__2_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnamevlimb_shift_marker__3_0gpoly_id¢bid eptypeiCommitteddnextôbopcSuberight¡fNumber¡dexpr¡oBinaryOperation£dleft¡iReference£dnamehb_sign_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnamehb_sign_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡fNumber¡dexpr¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡fNumberbopcSuberight¡iReference£dnameqopcode_sra_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnamehb_sign_0gpoly_id¢bideptypeiCommitteddnextô¡dexpr¡fNumber¡dexpr¡oBinaryOperation£dleft¡fNumberbopcSuberight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnamefc__0_0gpoly_id¢bid#eptypeiCommitteddnextôbopcAdderight¡oBinaryOperation£dleft¡iReference£dnamefc__1_0gpoly_id¢bid$eptypeiCommitteddnextôbopcMuleright¡fNumberbopcAdderight¡oBinaryOperation£dleft¡iReference£dnamefc__2_0gpoly_id¢bid%eptypeiCommitteddnextôbopcMuleright¡fNumber¡dexpr¡oBinaryOperation£dleft¡iReference£dnamefc__2_0gpoly_id¢bid%eptypeiCommitteddnextôbopcSuberight¡iReference£dnamefc__3_0gpoly_id¢bid&eptypeiCommitteddnextô¡dexpr¡oBinaryOperation£dleft¡iReference£dnamefc__2_0gpoly_id¢bid%eptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡fNumberÿbopcSuberight¡iReference£dnamefc__2_0gpoly_id¢bid%eptypeiCommitteddnextô¡dexpr¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_sll_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_srl_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sra_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnamewfrom_state__timestamp_0gpoly_id¢bid'eptypeiCommitteddnextôbopcSuberight¡iReference£dnamex$reads_aux__0__base__prev_timestamp_0gpoly_id¢bid(eptypeiCommitteddnextôbopcSuberight¡fNumberbopcSuberight¡oBinaryOperation£dleft¡iReference£dnamex7reads_aux__0__base__timestamp_lt_aux__lower_decomp__0_0gpoly_id¢bid)eptypeiCommitteddnextôbopcAdderight¡oBinaryOperation£dleft¡iReference£dnamex7reads_aux__0__base__timestamp_lt_aux__lower_decomp__1_0gpoly_id¢bid*eptypeiCommitteddnextôbopcMuleright¡fNumber¡dexpr¡fNumber¡dexpr¡fNumber¡dexpr¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_sll_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_srl_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sra_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnamewfrom_state__timestamp_0gpoly_id¢bid'eptypeiCommitteddnextôbopcAdderight¡fNumberbopcSuberight¡iReference£dnamex"writes_aux__base__prev_timestamp_0gpoly_id¢bid.eptypeiCommitteddnextôbopcSuberight¡fNumberbopcSuberight¡oBinaryOperation£dleft¡iReference£dnamex5writes_aux__base__timestamp_lt_aux__lower_decomp__0_0gpoly_id¢bid/eptypeiCommitteddnextôbopcAdderight¡oBinaryOperation£dleft¡iReference£dnamex5writes_aux__base__timestamp_lt_aux__lower_decomp__1_0gpoly_id¢bid0eptypeiCommitteddnextôbopcMuleright¡fNumber¡dexpr¡oBinaryOperation£dleft¡nUnaryOperation¢bopeMinusdexpr¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_sll_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_srl_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sra_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡fNumber¡dexpr¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡fNumberbopcAdderight¡oBinaryOperation£dleft¡iReference£dnameqopcode_srl_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡oBinaryOperation£dleft¡iReference£dnameqopcode_sra_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡fNumberbopcSuberight¡fNumber¡dexpr¡oBinaryOperation£dleft¡iReference£dnameqopcode_slt_flag_1gpoly_id¢bid8eptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnameqopcode_slt_flag_1gpoly_id¢bid8eptypeiCommitteddnextôbopcSuberight¡fNumber¡dexpr¡oBinaryOperation£dleft¡iReference£dnameropcode_sltu_flag_1gpoly_id¢bid9eptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnameropcode_sltu_flag_1gpoly_id¢bid9eptypeiCommitteddnextôbopcSuberight¡fNumber¡dexpr¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_slt_flag_1gpoly_id¢bid8eptypeiCommitteddnextôbopcAdderight¡iReference£dnameropcode_sltu_flag_1gpoly_id¢bid9eptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_slt_flag_1gpoly_id¢bid8eptypeiCommitteddnextôbopcAdderight¡iReference£dnameropcode_sltu_flag_1gpoly_id¢bid9eptypeiCommitteddnextôbopcSuberight¡fNumber¡dexpr¡oBinaryOperation£dleft¡iReference£dnamelcmp_result_1gpoly_id¢bid:eptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnamelcmp_result_1gpoly_id¢bid:eptypeiCommitteddnextôbopcSuberight¡fNumber¡dexpr¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnamefb__3_1gpoly_id¢bid;eptypeiCommitteddnextôbopcSuberight¡iReference£dnameib_msb_f_1gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡fNumberbopcSuberight¡oBinaryOperation£dleft¡iReference£dnamefc__3_1gpoly_id¢bid=eptypeiCommitteddnextôbopcSuberight¡iReference£dnameic_msb_f_1gpoly_id¢bid>eptypeiCommitteddnextô¡dexpr¡oBinaryOperation£dleft¡iReference£dnamepdiff_marker__3_1gpoly_id¢bid?eptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnamepdiff_marker__3_1gpoly_id¢bid?eptypeiCommitteddnextôbopcSuberight¡fNumber¡dexpr¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡fNumberbopcSuberight¡iReference£dnamepdiff_marker__3_1gpoly_id¢bid?eptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameic_msb_f_1gpoly_id¢bid>eptypeiCommitteddnextôbopcSuberight¡iReference£dnameib_msb_f_1gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡iReference£dnameib_msb_f_1gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡oBinaryOperation£dleft¡fNumber€bopcMuleright¡iReference£dnameqopcode_slt_flag_1gpoly_id¢bid8eptypeiCommitteddnextô¡fNumber¡fNumbernoriginal_index¥dkinddSendbiddmult¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnamepdiff_marker__3_1gpoly_id¢bid?eptypeiCommitteddnextôbopcAdderight¡iReference£dnamepdiff_marker__2_1gpoly_id¢bidAeptypeiCommitteddnextôbopcAdderight¡iReference£dnamepdiff_marker__1_1gpoly_id¢bidDeptypeiCommitteddnextôbopcAdderight¡iReference£dnamepdiff_marker__0_1gpoly_id¢bidGeptypeiCommitteddnextôdargs„¡oBinaryOperation£dleft¡iReference£dnamejdiff_val_1gpoly_id¢bid@eptypeiCommitteddnextôbopcSuberight¡fNumber¡fNumber¡fNumber¡fNumbernoriginal_index¥dkinddSendbiddmult¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_slt_flag_1gpoly_id¢bid8eptypeiCommitteddnextôbopcAdderight¡iReference£dnameropcode_sltu_flag_1gpoly_id¢bid9eptypeiCommitteddnextôbopcSuberight¡fNumberdargs„¡iReference£dnamefc__0_1gpoly_id¢bidHeptypeiCommitteddnextô¡iReference£dnamefc__1_1gpoly_id¢bidEeptypeiCommitteddnextô¡fNumber¡fNumbernoriginal_index¥dkinddSendbiddmult¡oBinaryOperation£dleft¡iReference£dnameqopcode_slt_flag_1gpoly_id¢bid8eptypeiCommitteddnextôbopcAdderight¡iReference£dnameropcode_sltu_flag_1gpoly_id¢bid9eptypeiCommitteddnextôdargs‚¡iReference£dnamex7reads_aux__0__base__timestamp_lt_aux__lower_decomp__0_1gpoly_id¢bidNeptypeiCommitteddnextô¡fNumbernoriginal_index¥dkinddSendbiddmult¡oBinaryOperation£dleft¡iReference£dnameqopcode_slt_flag_1gpoly_id¢bid8eptypeiCommitteddnextôbopcAdderight¡iReference£dnameropcode_sltu_flag_1gpoly_id¢bid9eptypeiCommitteddnextôdargs‚¡iReference£dnamex7reads_aux__0__base__timestamp_lt_aux__lower_decomp__1_1gpoly_id¢bidOeptypeiCommitteddnextô¡fNumber noriginal_index¥dkinddSendbiddmult¡oBinaryOperation£dleft¡fNumberxbopcMuleright¡oBinaryOperation£dleft¡iReference£dnameqopcode_slt_flag_1gpoly_id¢bid8eptypeiCommitteddnextôbopcAdderight¡iReference£dnameropcode_sltu_flag_1gpoly_id¢bid9eptypeiCommitteddnextôdargs‡¡fNumber¡fNumber¡iReference£dnamefb__0_1gpoly_id¢bidIeptypeiCommitteddnextô¡iReference£dnamefb__1_1gpoly_id¢bidFeptypeiCommitteddnextô¡iReference£dnamefb__2_1gpoly_id¢bidCeptypeiCommitteddnextô¡iReference£dnamefb__3_1gpoly_id¢bid;eptypeiCommitteddnextô¡iReference£dnamex$reads_aux__0__base__prev_timestamp_1gpoly_id¢bidMeptypeiCommitteddnextônoriginal_index¥dkinddSendbiddmult¡oBinaryOperation£dleft¡iReference£dnameqopcode_slt_flag_1gpoly_id¢bid8eptypeiCommitteddnextôbopcAdderight¡iReference£dnameropcode_sltu_flag_1gpoly_id¢bid9eptypeiCommitteddnextôdargs‡¡fNumber¡fNumber¡iReference£dnamefb__0_1gpoly_id¢bidIeptypeiCommitteddnextô¡iReference£dnamefb__1_1gpoly_id¢bidFeptypeiCommitteddnextô¡iReference£dnamefb__2_1gpoly_id¢bidCeptypeiCommitteddnextô¡iReference£dnamefb__3_1gpoly_id¢bid;eptypeiCommitteddnextô¡iReference£dnamewfrom_state__timestamp_1gpoly_id¢bidLeptypeiCommitteddnextônoriginal_index¥dkinddSendbiddmult¡fNumberdargs‚¡iReference£dnamex7reads_aux__1__base__timestamp_lt_aux__lower_decomp__0_1gpoly_id¢bidQeptypeiCommitteddnextô¡fNumbernoriginal_index¥dkinddSendbiddmult¡fNumberdargs‚¡iReference£dnamex7reads_aux__1__base__timestamp_lt_aux__lower_decomp__1_1gpoly_id¢bidReptypeiCommitteddnextô¡fNumber noriginal_index ¥dkinddSendbiddmult¡fNumberxdargs‡¡fNumber¡fNumber(¡iReference£dnamefc__0_1gpoly_id¢bidHeptypeiCommitteddnextô¡iReference£dnamefc__1_1gpoly_id¢bidEeptypeiCommitteddnextô¡iReference£dnamefc__2_1gpoly_id¢bidBeptypeiCommitteddnextô¡iReference£dnamefc__3_1gpoly_id¢bid=eptypeiCommitteddnextô¡iReference£dnamex$reads_aux__1__base__prev_timestamp_1gpoly_id¢bidPeptypeiCommitteddnextônoriginal_index!¥dkinddSendbiddmult¡fNumberdargs‡¡fNumber¡fNumber(¡iReference£dnamefc__0_1gpoly_id¢bidHeptypeiCommitteddnextô¡iReference£dnamefc__1_1gpoly_id¢bidEeptypeiCommitteddnextô¡iReference£dnamefc__2_1gpoly_id¢bidBeptypeiCommitteddnextô¡iReference£dnamefc__3_1gpoly_id¢bid=eptypeiCommitteddnextô¡oBinaryOperation£dleft¡iReference£dnamewfrom_state__timestamp_1gpoly_id¢bidLeptypeiCommitteddnextôbopcAdderight¡fNumbernoriginal_index"¥dkinddSendbiddmult¡oBinaryOperation£dleft¡iReference£dnameqopcode_slt_flag_1gpoly_id¢bid8eptypeiCommitteddnextôbopcAdderight¡iReference£dnameropcode_sltu_flag_1gpoly_id¢bid9eptypeiCommitteddnextôdargs‚¡iReference£dnamex5writes_aux__base__timestamp_lt_aux__lower_decomp__0_1gpoly_id¢bidTeptypeiCommitteddnextô¡fNumbernoriginal_index#¥dkinddSendbiddmult¡oBinaryOperation£dleft¡iReference£dnameqopcode_slt_flag_1gpoly_id¢bid8eptypeiCommitteddnextôbopcAdderight¡iReference£dnameropcode_sltu_flag_1gpoly_id¢bid9eptypeiCommitteddnextôdargs‚¡iReference£dnamex5writes_aux__base__timestamp_lt_aux__lower_decomp__1_1gpoly_id¢bidUeptypeiCommitteddnextô¡fNumber noriginal_index$¥dkinddSendbiddmult¡oBinaryOperation£dleft¡fNumberxbopcMuleright¡oBinaryOperation£dleft¡iReference£dnameqopcode_slt_flag_1gpoly_id¢bid8eptypeiCommitteddnextôbopcAdderight¡iReference£dnameropcode_sltu_flag_1gpoly_id¢bid9eptypeiCommitteddnextôdargs‡¡fNumber¡fNumber(¡iReference£dnamexwrites_aux__prev_data__0_1gpoly_id¢bidXeptypeiCommitteddnextô¡iReference£dnamexwrites_aux__prev_data__1_1gpoly_id¢bidYeptypeiCommitteddnextô¡iReference£dnamexwrites_aux__prev_data__2_1gpoly_id¢bidZeptypeiCommitteddnextô¡iReference£dnamexwrites_aux__prev_data__3_1gpoly_id¢bid[eptypeiCommitteddnextô¡iReference£dnamex"writes_aux__base__prev_timestamp_1gpoly_id¢bidSeptypeiCommitteddnextônoriginal_index%¥dkinddSendbiddmult¡oBinaryOperation£dleft¡iReference£dnameqopcode_slt_flag_1gpoly_id¢bid8eptypeiCommitteddnextôbopcAdderight¡iReference£dnameropcode_sltu_flag_1gpoly_id¢bid9eptypeiCommitteddnextôdargs‡¡fNumber¡fNumber(¡iReference£dnamelcmp_result_1gpoly_id¢bid:eptypeiCommitteddnextô¡fNumber¡fNumber¡fNumber¡oBinaryOperation£dleft¡iReference£dnamewfrom_state__timestamp_1gpoly_id¢bidLeptypeiCommitteddnextôbopcAdderight¡fNumbernoriginal_index&¥dkinddSendbiddmult¡oBinaryOperation£dleft¡iReference£dnameqopcode_slt_flag_1gpoly_id¢bid8eptypeiCommitteddnextôbopcAdderight¡iReference£dnameropcode_sltu_flag_1gpoly_id¢bid9eptypeiCommitteddnextôdargs‰¡iReference£dnamepfrom_state__pc_1gpoly_id¢bid\eptypeiCommitteddnextô¡oBinaryOperation£dleft¡iReference£dnameropcode_sltu_flag_1gpoly_id¢bid9eptypeiCommitteddnextôbopcAdderight¡fNumber¡fNumber(¡fNumber¡fNumber(¡fNumber¡fNumber¡fNumber¡fNumbernoriginal_index'¥dkinddSendbiddmult¡nUnaryOperation¢bopeMinusdexpr¡oBinaryOperation£dleft¡iReference£dnameqopcode_slt_flag_1gpoly_id¢bid8eptypeiCommitteddnextôbopcAdderight¡iReference£dnameropcode_sltu_flag_1gpoly_id¢bid9eptypeiCommitteddnextôdargs‚¡iReference£dnamepfrom_state__pc_1gpoly_id¢bid\eptypeiCommitteddnextô¡iReference£dnamewfrom_state__timestamp_1gpoly_id¢bidLeptypeiCommitteddnextônoriginal_index(¥dkinddSendbiddmult¡oBinaryOperation£dleft¡iReference£dnameqopcode_slt_flag_1gpoly_id¢bid8eptypeiCommitteddnextôbopcAdderight¡iReference£dnameropcode_sltu_flag_1gpoly_id¢bid9eptypeiCommitteddnextôdargs‚¡oBinaryOperation£dleft¡iReference£dnamepfrom_state__pc_1gpoly_id¢bid\eptypeiCommitteddnextôbopcAdderight¡fNumber¡oBinaryOperation£dleft¡iReference£dnamewfrom_state__timestamp_1gpoly_id¢bidLeptypeiCommitteddnextôbopcAdderight¡fNumbernoriginal_index)¥dkinddSendbiddmult¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_add_flag_2gpoly_id¢bid]eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sub_flag_2gpoly_id¢bid^eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_xor_flag_2gpoly_id¢bid_eptypeiCommitteddnextôbopcAdderight¡iReference£dnamepopcode_or_flag_2gpoly_id¢bid`eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_and_flag_2gpoly_id¢bidaeptypeiCommitteddnextôdargs„¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡fNumberbopcSuberight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_xor_flag_2gpoly_id¢bid_eptypeiCommitteddnextôbopcAdderight¡iReference£dnamepopcode_or_flag_2gpoly_id¢bid`eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_and_flag_2gpoly_id¢bidaeptypeiCommitteddnextôbopcMuleright¡iReference£dnamefa__0_2gpoly_id¢biddeptypeiCommitteddnextôbopcAdderight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_xor_flag_2gpoly_id¢bid_eptypeiCommitteddnextôbopcAdderight¡iReference£dnamepopcode_or_flag_2gpoly_id¢bid`eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_and_flag_2gpoly_id¢bidaeptypeiCommitteddnextôbopcMuleright¡iReference£dnamefb__0_2gpoly_id¢bidbeptypeiCommitteddnextô¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡fNumberbopcSuberight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_xor_flag_2gpoly_id¢bid_eptypeiCommitteddnextôbopcAdderight¡iReference£dnamepopcode_or_flag_2gpoly_id¢bid`eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_and_flag_2gpoly_id¢bidaeptypeiCommitteddnextôbopcMuleright¡iReference£dnamefa__0_2gpoly_id¢biddeptypeiCommitteddnextôbopcAdderight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_xor_flag_2gpoly_id¢bid_eptypeiCommitteddnextôbopcAdderight¡iReference£dnamepopcode_or_flag_2gpoly_id¢bid`eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_and_flag_2gpoly_id¢bidaeptypeiCommitteddnextôbopcMuleright¡iReference£dnamefc__0_2gpoly_id¢bidceptypeiCommitteddnextô¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_xor_flag_2gpoly_id¢bid_eptypeiCommitteddnextôbopcMuleright¡iReference£dnamefa__0_2gpoly_id¢biddeptypeiCommitteddnextôbopcAdderight¡oBinaryOperation£dleft¡iReference£dnamepopcode_or_flag_2gpoly_id¢bid`eptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡fNumberbopcMuleright¡iReference£dnamefa__0_2gpoly_id¢biddeptypeiCommitteddnextôbopcSuberight¡iReference£dnamefb__0_2gpoly_id¢bidbeptypeiCommitteddnextôbopcSuberight¡iReference£dnamefc__0_2gpoly_id¢bidceptypeiCommitteddnextôbopcAdderight¡oBinaryOperation£dleft¡iReference£dnameqopcode_and_flag_2gpoly_id¢bidaeptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnamefb__0_2gpoly_id¢bidbeptypeiCommitteddnextôbopcAdderight¡iReference£dnamefc__0_2gpoly_id¢bidceptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡fNumberbopcMuleright¡iReference£dnamefa__0_2gpoly_id¢biddeptypeiCommitteddnextô¡fNumbernoriginal_index*¥dkinddSendbiddmult¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_add_flag_2gpoly_id¢bid]eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sub_flag_2gpoly_id¢bid^eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_xor_flag_2gpoly_id¢bid_eptypeiCommitteddnextôbopcAdderight¡iReference£dnamepopcode_or_flag_2gpoly_id¢bid`eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_and_flag_2gpoly_id¢bidaeptypeiCommitteddnextôdargs„¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡fNumberbopcSuberight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_xor_flag_2gpoly_id¢bid_eptypeiCommitteddnextôbopcAdderight¡iReference£dnamepopcode_or_flag_2gpoly_id¢bid`eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_and_flag_2gpoly_id¢bidaeptypeiCommitteddnextôbopcMuleright¡iReference£dnamefa__1_2gpoly_id¢bidgeptypeiCommitteddnextôbopcAdderight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_xor_flag_2gpoly_id¢bid_eptypeiCommitteddnextôbopcAdderight¡iReference£dnamepopcode_or_flag_2gpoly_id¢bid`eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_and_flag_2gpoly_id¢bidaeptypeiCommitteddnextôbopcMuleright¡iReference£dnamefb__1_2gpoly_id¢bideeptypeiCommitteddnextô¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡fNumberbopcSuberight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_xor_flag_2gpoly_id¢bid_eptypeiCommitteddnextôbopcAdderight¡iReference£dnamepopcode_or_flag_2gpoly_id¢bid`eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_and_flag_2gpoly_id¢bidaeptypeiCommitteddnextôbopcMuleright¡iReference£dnamefa__1_2gpoly_id¢bidgeptypeiCommitteddnextôbopcAdderight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_xor_flag_2gpoly_id¢bid_eptypeiCommitteddnextôbopcAdderight¡iReference£dnamepopcode_or_flag_2gpoly_id¢bid`eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_and_flag_2gpoly_id¢bidaeptypeiCommitteddnextôbopcMuleright¡iReference£dnamefc__1_2gpoly_id¢bidfeptypeiCommitteddnextô¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_xor_flag_2gpoly_id¢bid_eptypeiCommitteddnextôbopcMuleright¡iReference£dnamefa__1_2gpoly_id¢bidgeptypeiCommitteddnextôbopcAdderight¡oBinaryOperation£dleft¡iReference£dnamepopcode_or_flag_2gpoly_id¢bid`eptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡fNumberbopcMuleright¡iReference£dnamefa__1_2gpoly_id¢bidgeptypeiCommitteddnextôbopcSuberight¡iReference£dnamefb__1_2gpoly_id¢bideeptypeiCommitteddnextôbopcSuberight¡iReference£dnamefc__1_2gpoly_id¢bidfeptypeiCommitteddnextôbopcAdderight¡oBinaryOperation£dleft¡iReference£dnameqopcode_and_flag_2gpoly_id¢bidaeptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnamefb__1_2gpoly_id¢bideeptypeiCommitteddnextôbopcAdderight¡iReference£dnamefc__1_2gpoly_id¢bidfeptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡fNumberbopcMuleright¡iReference£dnamefa__1_2gpoly_id¢bidgeptypeiCommitteddnextô¡fNumbernoriginal_index+¥dkinddSendbiddmult¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_add_flag_2gpoly_id¢bid]eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sub_flag_2gpoly_id¢bid^eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_xor_flag_2gpoly_id¢bid_eptypeiCommitteddnextôbopcAdderight¡iReference£dnamepopcode_or_flag_2gpoly_id¢bid`eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_and_flag_2gpoly_id¢bidaeptypeiCommitteddnextôdargs„¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡fNumberbopcSuberight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_xor_flag_2gpoly_id¢bid_eptypeiCommitteddnextôbopcAdderight¡iReference£dnamepopcode_or_flag_2gpoly_id¢bid`eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_and_flag_2gpoly_id¢bidaeptypeiCommitteddnextôbopcMuleright¡iReference£dnamefa__2_2gpoly_id¢bidjeptypeiCommitteddnextôbopcAdderight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_xor_flag_2gpoly_id¢bid_eptypeiCommitteddnextôbopcAdderight¡iReference£dnamepopcode_or_flag_2gpoly_id¢bid`eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_and_flag_2gpoly_id¢bidaeptypeiCommitteddnextôbopcMuleright¡iReference£dnamefb__2_2gpoly_id¢bidheptypeiCommitteddnextô¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡fNumberbopcSuberight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_xor_flag_2gpoly_id¢bid_eptypeiCommitteddnextôbopcAdderight¡iReference£dnamepopcode_or_flag_2gpoly_id¢bid`eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_and_flag_2gpoly_id¢bidaeptypeiCommitteddnextôbopcMuleright¡iReference£dnamefa__2_2gpoly_id¢bidjeptypeiCommitteddnextôbopcAdderight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_xor_flag_2gpoly_id¢bid_eptypeiCommitteddnextôbopcAdderight¡iReference£dnamepopcode_or_flag_2gpoly_id¢bid`eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_and_flag_2gpoly_id¢bidaeptypeiCommitteddnextôbopcMuleright¡iReference£dnamefc__2_2gpoly_id¢bidieptypeiCommitteddnextô¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_xor_flag_2gpoly_id¢bid_eptypeiCommitteddnextôbopcMuleright¡iReference£dnamefa__2_2gpoly_id¢bidjeptypeiCommitteddnextôbopcAdderight¡oBinaryOperation£dleft¡iReference£dnamepopcode_or_flag_2gpoly_id¢bid`eptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡fNumberbopcMuleright¡iReference£dnamefa__2_2gpoly_id¢bidjeptypeiCommitteddnextôbopcSuberight¡iReference£dnamefb__2_2gpoly_id¢bidheptypeiCommitteddnextôbopcSuberight¡iReference£dnamefc__2_2gpoly_id¢bidieptypeiCommitteddnextôbopcAdderight¡oBinaryOperation£dleft¡iReference£dnameqopcode_and_flag_2gpoly_id¢bidaeptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnamefb__2_2gpoly_id¢bidheptypeiCommitteddnextôbopcAdderight¡iReference£dnamefc__2_2gpoly_id¢bidieptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡fNumberbopcMuleright¡iReference£dnamefa__2_2gpoly_id¢bidjeptypeiCommitteddnextô¡fNumbernoriginal_index,¥dkinddSendbiddmult¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_add_flag_2gpoly_id¢bid]eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sub_flag_2gpoly_id¢bid^eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_xor_flag_2gpoly_id¢bid_eptypeiCommitteddnextôbopcAdderight¡iReference£dnamepopcode_or_flag_2gpoly_id¢bid`eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_and_flag_2gpoly_id¢bidaeptypeiCommitteddnextôdargs„¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡fNumberbopcSuberight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_xor_flag_2gpoly_id¢bid_eptypeiCommitteddnextôbopcAdderight¡iReference£dnamepopcode_or_flag_2gpoly_id¢bid`eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_and_flag_2gpoly_id¢bidaeptypeiCommitteddnextôbopcMuleright¡iReference£dnamefa__3_2gpoly_id¢bidmeptypeiCommitteddnextôbopcAdderight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_xor_flag_2gpoly_id¢bid_eptypeiCommitteddnextôbopcAdderight¡iReference£dnamepopcode_or_flag_2gpoly_id¢bid`eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_and_flag_2gpoly_id¢bidaeptypeiCommitteddnextôbopcMuleright¡iReference£dnamefb__3_2gpoly_id¢bidkeptypeiCommitteddnextô¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡fNumberbopcSuberight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_xor_flag_2gpoly_id¢bid_eptypeiCommitteddnextôbopcAdderight¡iReference£dnamepopcode_or_flag_2gpoly_id¢bid`eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_and_flag_2gpoly_id¢bidaeptypeiCommitteddnextôbopcMuleright¡iReference£dnamefa__3_2gpoly_id¢bidmeptypeiCommitteddnextôbopcAdderight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_xor_flag_2gpoly_id¢bid_eptypeiCommitteddnextôbopcAdderight¡iReference£dnamepopcode_or_flag_2gpoly_id¢bid`eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_and_flag_2gpoly_id¢bidaeptypeiCommitteddnextôbopcMuleright¡iReference£dnamefc__3_2gpoly_id¢bidleptypeiCommitteddnextô¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_xor_flag_2gpoly_id¢bid_eptypeiCommitteddnextôbopcMuleright¡iReference£dnamefa__3_2gpoly_id¢bidmeptypeiCommitteddnextôbopcAdderight¡oBinaryOperation£dleft¡iReference£dnamepopcode_or_flag_2gpoly_id¢bid`eptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡fNumberbopcMuleright¡iReference£dnamefa__3_2gpoly_id¢bidmeptypeiCommitteddnextôbopcSuberight¡iReference£dnamefb__3_2gpoly_id¢bidkeptypeiCommitteddnextôbopcSuberight¡iReference£dnamefc__3_2gpoly_id¢bidleptypeiCommitteddnextôbopcAdderight¡oBinaryOperation£dleft¡iReference£dnameqopcode_and_flag_2gpoly_id¢bidaeptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnamefb__3_2gpoly_id¢bidkeptypeiCommitteddnextôbopcAdderight¡iReference£dnamefc__3_2gpoly_id¢bidleptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡fNumberbopcMuleright¡iReference£dnamefa__3_2gpoly_id¢bidmeptypeiCommitteddnextô¡fNumbernoriginal_index-¥dkinddSendbiddmult¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_add_flag_2gpoly_id¢bid]eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sub_flag_2gpoly_id¢bid^eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_xor_flag_2gpoly_id¢bid_eptypeiCommitteddnextôbopcAdderight¡iReference£dnamepopcode_or_flag_2gpoly_id¢bid`eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_and_flag_2gpoly_id¢bidaeptypeiCommitteddnextôdargs„¡iReference£dnamefc__0_2gpoly_id¢bidceptypeiCommitteddnextô¡iReference£dnamefc__1_2gpoly_id¢bidfeptypeiCommitteddnextô¡fNumber¡fNumbernoriginal_index.¥dkinddSendbiddmult¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_add_flag_2gpoly_id¢bid]eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sub_flag_2gpoly_id¢bid^eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_xor_flag_2gpoly_id¢bid_eptypeiCommitteddnextôbopcAdderight¡iReference£dnamepopcode_or_flag_2gpoly_id¢bid`eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_and_flag_2gpoly_id¢bidaeptypeiCommitteddnextôdargs‚¡iReference£dnamex7reads_aux__0__base__timestamp_lt_aux__lower_decomp__0_2gpoly_id¢bidreptypeiCommitteddnextô¡fNumbernoriginal_index/¥dkinddSendbiddmult¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_add_flag_2gpoly_id¢bid]eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sub_flag_2gpoly_id¢bid^eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_xor_flag_2gpoly_id¢bid_eptypeiCommitteddnextôbopcAdderight¡iReference£dnamepopcode_or_flag_2gpoly_id¢bid`eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_and_flag_2gpoly_id¢bidaeptypeiCommitteddnextôdargs‚¡iReference£dnamex7reads_aux__0__base__timestamp_lt_aux__lower_decomp__1_2gpoly_id¢bidseptypeiCommitteddnextô¡fNumber noriginal_index0¥dkinddSendbiddmult¡oBinaryOperation£dleft¡fNumberxbopcMuleright¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_add_flag_2gpoly_id¢bid]eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sub_flag_2gpoly_id¢bid^eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_xor_flag_2gpoly_id¢bid_eptypeiCommitteddnextôbopcAdderight¡iReference£dnamepopcode_or_flag_2gpoly_id¢bid`eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_and_flag_2gpoly_id¢bidaeptypeiCommitteddnextôdargs‡¡fNumber¡fNumber(¡iReference£dnamefb__0_2gpoly_id¢bidbeptypeiCommitteddnextô¡iReference£dnamefb__1_2gpoly_id¢bideeptypeiCommitteddnextô¡iReference£dnamefb__2_2gpoly_id¢bidheptypeiCommitteddnextô¡iReference£dnamefb__3_2gpoly_id¢bidkeptypeiCommitteddnextô¡iReference£dnamex$reads_aux__0__base__prev_timestamp_2gpoly_id¢bidqeptypeiCommitteddnextônoriginal_index1¥dkinddSendbiddmult¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_add_flag_2gpoly_id¢bid]eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sub_flag_2gpoly_id¢bid^eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_xor_flag_2gpoly_id¢bid_eptypeiCommitteddnextôbopcAdderight¡iReference£dnamepopcode_or_flag_2gpoly_id¢bid`eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_and_flag_2gpoly_id¢bidaeptypeiCommitteddnextôdargs‡¡fNumber¡fNumber(¡iReference£dnamefb__0_2gpoly_id¢bidbeptypeiCommitteddnextô¡iReference£dnamefb__1_2gpoly_id¢bideeptypeiCommitteddnextô¡iReference£dnamefb__2_2gpoly_id¢bidheptypeiCommitteddnextô¡iReference£dnamefb__3_2gpoly_id¢bidkeptypeiCommitteddnextô¡iReference£dnamewfrom_state__timestamp_2gpoly_id¢bidpeptypeiCommitteddnextônoriginal_index2¥dkinddSendbiddmult¡fNumberdargs‚¡iReference£dnamex7reads_aux__1__base__timestamp_lt_aux__lower_decomp__0_2gpoly_id¢bidueptypeiCommitteddnextô¡fNumbernoriginal_index3¥dkinddSendbiddmult¡fNumberdargs‚¡iReference£dnamex7reads_aux__1__base__timestamp_lt_aux__lower_decomp__1_2gpoly_id¢bidveptypeiCommitteddnextô¡fNumber noriginal_index4¥dkinddSendbiddmult¡fNumberdargs‡¡fNumber¡fNumber¡iReference£dnamefc__0_2gpoly_id¢bidceptypeiCommitteddnextô¡iReference£dnamefc__1_2gpoly_id¢bidfeptypeiCommitteddnextô¡iReference£dnamefc__2_2gpoly_id¢bidieptypeiCommitteddnextô¡iReference£dnamefc__3_2gpoly_id¢bidleptypeiCommitteddnextô¡iReference£dnamex$reads_aux__1__base__prev_timestamp_2gpoly_id¢bidteptypeiCommitteddnextônoriginal_index5¥dkinddSendbiddmult¡fNumberdargs‡¡fNumber¡fNumber¡iReference£dnamefc__0_2gpoly_id¢bidceptypeiCommitteddnextô¡iReference£dnamefc__1_2gpoly_id¢bidfeptypeiCommitteddnextô¡iReference£dnamefc__2_2gpoly_id¢bidieptypeiCommitteddnextô¡iReference£dnamefc__3_2gpoly_id¢bidleptypeiCommitteddnextô¡oBinaryOperation£dleft¡iReference£dnamewfrom_state__timestamp_2gpoly_id¢bidpeptypeiCommitteddnextôbopcAdderight¡fNumbernoriginal_index6¥dkinddSendbiddmult¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_add_flag_2gpoly_id¢bid]eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sub_flag_2gpoly_id¢bid^eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_xor_flag_2gpoly_id¢bid_eptypeiCommitteddnextôbopcAdderight¡iReference£dnamepopcode_or_flag_2gpoly_id¢bid`eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_and_flag_2gpoly_id¢bidaeptypeiCommitteddnextôdargs‚¡iReference£dnamex5writes_aux__base__timestamp_lt_aux__lower_decomp__0_2gpoly_id¢bidxeptypeiCommitteddnextô¡fNumbernoriginal_index7¥dkinddSendbiddmult¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_add_flag_2gpoly_id¢bid]eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sub_flag_2gpoly_id¢bid^eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_xor_flag_2gpoly_id¢bid_eptypeiCommitteddnextôbopcAdderight¡iReference£dnamepopcode_or_flag_2gpoly_id¢bid`eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_and_flag_2gpoly_id¢bidaeptypeiCommitteddnextôdargs‚¡iReference£dnamex5writes_aux__base__timestamp_lt_aux__lower_decomp__1_2gpoly_id¢bidyeptypeiCommitteddnextô¡fNumber noriginal_index8¥dkinddSendbiddmult¡oBinaryOperation£dleft¡fNumberxbopcMuleright¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_add_flag_2gpoly_id¢bid]eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sub_flag_2gpoly_id¢bid^eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_xor_flag_2gpoly_id¢bid_eptypeiCommitteddnextôbopcAdderight¡iReference£dnamepopcode_or_flag_2gpoly_id¢bid`eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_and_flag_2gpoly_id¢bidaeptypeiCommitteddnextôdargs‡¡fNumber¡fNumber(¡iReference£dnamexwrites_aux__prev_data__0_2gpoly_id¢bid|eptypeiCommitteddnextô¡iReference£dnamexwrites_aux__prev_data__1_2gpoly_id¢bid}eptypeiCommitteddnextô¡iReference£dnamexwrites_aux__prev_data__2_2gpoly_id¢bid~eptypeiCommitteddnextô¡iReference£dnamexwrites_aux__prev_data__3_2gpoly_id¢bideptypeiCommitteddnextô¡iReference£dnamex"writes_aux__base__prev_timestamp_2gpoly_id¢bidweptypeiCommitteddnextônoriginal_index9¥dkinddSendbiddmult¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_add_flag_2gpoly_id¢bid]eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sub_flag_2gpoly_id¢bid^eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_xor_flag_2gpoly_id¢bid_eptypeiCommitteddnextôbopcAdderight¡iReference£dnamepopcode_or_flag_2gpoly_id¢bid`eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_and_flag_2gpoly_id¢bidaeptypeiCommitteddnextôdargs‡¡fNumber¡fNumber(¡iReference£dnamefa__0_2gpoly_id¢biddeptypeiCommitteddnextô¡iReference£dnamefa__1_2gpoly_id¢bidgeptypeiCommitteddnextô¡iReference£dnamefa__2_2gpoly_id¢bidjeptypeiCommitteddnextô¡iReference£dnamefa__3_2gpoly_id¢bidmeptypeiCommitteddnextô¡oBinaryOperation£dleft¡iReference£dnamewfrom_state__timestamp_2gpoly_id¢bidpeptypeiCommitteddnextôbopcAdderight¡fNumbernoriginal_index:¥dkinddSendbiddmult¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_add_flag_2gpoly_id¢bid]eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sub_flag_2gpoly_id¢bid^eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_xor_flag_2gpoly_id¢bid_eptypeiCommitteddnextôbopcAdderight¡iReference£dnamepopcode_or_flag_2gpoly_id¢bid`eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_and_flag_2gpoly_id¢bidaeptypeiCommitteddnextôdargs‰¡iReference£dnamepfrom_state__pc_2gpoly_id¢bid€eptypeiCommitteddnextô¡oBinaryOperation£dleft¡fNumberbopcAdderight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_sub_flag_2gpoly_id¢bid^eptypeiCommitteddnextôbopcAdderight¡oBinaryOperation£dleft¡iReference£dnameqopcode_xor_flag_2gpoly_id¢bid_eptypeiCommitteddnextôbopcMuleright¡fNumberbopcAdderight¡oBinaryOperation£dleft¡iReference£dnamepopcode_or_flag_2gpoly_id¢bid`eptypeiCommitteddnextôbopcMuleright¡fNumberbopcAdderight¡oBinaryOperation£dleft¡iReference£dnameqopcode_and_flag_2gpoly_id¢bidaeptypeiCommitteddnextôbopcMuleright¡fNumber¡fNumber(¡fNumber(¡fNumber¡fNumber¡fNumber¡fNumber¡fNumbernoriginal_index;¥dkinddSendbiddmult¡nUnaryOperation¢bopeMinusdexpr¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_add_flag_2gpoly_id¢bid]eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sub_flag_2gpoly_id¢bid^eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_xor_flag_2gpoly_id¢bid_eptypeiCommitteddnextôbopcAdderight¡iReference£dnamepopcode_or_flag_2gpoly_id¢bid`eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_and_flag_2gpoly_id¢bidaeptypeiCommitteddnextôdargs‚¡iReference£dnamepfrom_state__pc_2gpoly_id¢bid€eptypeiCommitteddnextô¡iReference£dnamewfrom_state__timestamp_2gpoly_id¢bidpeptypeiCommitteddnextônoriginal_index<¥dkinddSendbiddmult¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_add_flag_2gpoly_id¢bid]eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sub_flag_2gpoly_id¢bid^eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_xor_flag_2gpoly_id¢bid_eptypeiCommitteddnextôbopcAdderight¡iReference£dnamepopcode_or_flag_2gpoly_id¢bid`eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_and_flag_2gpoly_id¢bidaeptypeiCommitteddnextôdargs‚¡oBinaryOperation£dleft¡iReference£dnamepfrom_state__pc_2gpoly_id¢bid€eptypeiCommitteddnextôbopcAdderight¡fNumber¡oBinaryOperation£dleft¡iReference£dnamewfrom_state__timestamp_2gpoly_id¢bidpeptypeiCommitteddnextôbopcAdderight¡fNumbernoriginal_index=¥dkinddSendbiddmult¡oBinaryOperation£dleft¡iReference£dnamehis_lui_3gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnamehis_jal_3gpoly_id¢bid‚eptypeiCommitteddnextôdargs„¡iReference£dnamelrd_data__0_3gpoly_id¢bidƒeptypeiCommitteddnextô¡iReference£dnamelrd_data__1_3gpoly_id¢bid„eptypeiCommitteddnextô¡fNumber¡fNumbernoriginal_index>¥dkinddSendbiddmult¡oBinaryOperation£dleft¡iReference£dnamehis_lui_3gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnamehis_jal_3gpoly_id¢bid‚eptypeiCommitteddnextôdargs„¡iReference£dnamelrd_data__2_3gpoly_id¢bid…eptypeiCommitteddnextô¡iReference£dnamelrd_data__3_3gpoly_id¢bid†eptypeiCommitteddnextô¡fNumber¡fNumbernoriginal_index?¥dkinddSendbiddmult¡iReference£dnamehis_jal_3gpoly_id¢bid‚eptypeiCommitteddnextôdargs„¡iReference£dnamelrd_data__3_3gpoly_id¢bid†eptypeiCommitteddnextô¡fNumberÀ¡oBinaryOperation£dleft¡iReference£dnamelrd_data__3_3gpoly_id¢bid†eptypeiCommitteddnextôbopcAdderight¡fNumberÀ¡fNumbernoriginal_index@¥dkinddSendbiddmult¡fNumberdargs‚¡iReference£dnamex=inner__rd_aux_cols__base__timestamp_lt_aux__lower_decomp__0_3gpoly_id¢bidŒeptypeiCommitteddnextô¡fNumbernoriginal_indexA¥dkinddSendbiddmult¡fNumberdargs‚¡iReference£dnamex=inner__rd_aux_cols__base__timestamp_lt_aux__lower_decomp__1_3gpoly_id¢bideptypeiCommitteddnextô¡fNumber noriginal_indexB¥dkinddSendbiddmult¡fNumberdargs‡¡fNumber¡fNumber¡iReference£dnamex"inner__rd_aux_cols__prev_data__0_3gpoly_id¢bideptypeiCommitteddnextô¡iReference£dnamex"inner__rd_aux_cols__prev_data__1_3gpoly_id¢bideptypeiCommitteddnextô¡iReference£dnamex"inner__rd_aux_cols__prev_data__2_3gpoly_id¢bid‘eptypeiCommitteddnextô¡iReference£dnamex"inner__rd_aux_cols__prev_data__3_3gpoly_id¢bid’eptypeiCommitteddnextô¡iReference£dnamex*inner__rd_aux_cols__base__prev_timestamp_3gpoly_id¢bid‹eptypeiCommitteddnextônoriginal_indexC¥dkinddSendbiddmult¡fNumberdargs‡¡fNumber¡fNumber¡iReference£dnamelrd_data__0_3gpoly_id¢bidƒeptypeiCommitteddnextô¡iReference£dnamelrd_data__1_3gpoly_id¢bid„eptypeiCommitteddnextô¡iReference£dnamelrd_data__2_3gpoly_id¢bid…eptypeiCommitteddnextô¡iReference£dnamelrd_data__3_3gpoly_id¢bid†eptypeiCommitteddnextô¡iReference£dnamexinner__from_state__timestamp_3gpoly_id¢bidŠeptypeiCommitteddnextônoriginal_indexD¥dkinddSendbiddmult¡oBinaryOperation£dleft¡iReference£dnamehis_lui_3gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnamehis_jal_3gpoly_id¢bid‚eptypeiCommitteddnextôdargs‰¡iReference£dnamewinner__from_state__pc_3gpoly_id¢bidˆeptypeiCommitteddnextô¡oBinaryOperation£dleft¡fNumber0bopcAdderight¡iReference£dnamehis_lui_3gpoly_id¢bideptypeiCommitteddnextô¡fNumber¡fNumber¡fNumberwÿýñ¡fNumber¡fNumber¡fNumber¡fNumbernoriginal_indexE¥dkinddSendbiddmult¡nUnaryOperation¢bopeMinusdexpr¡oBinaryOperation£dleft¡iReference£dnamehis_lui_3gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnamehis_jal_3gpoly_id¢bid‚eptypeiCommitteddnextôdargs‚¡iReference£dnamewinner__from_state__pc_3gpoly_id¢bidˆeptypeiCommitteddnextô¡iReference£dnamexinner__from_state__timestamp_3gpoly_id¢bidŠeptypeiCommitteddnextônoriginal_indexF¥dkinddSendbiddmult¡oBinaryOperation£dleft¡iReference£dnamehis_lui_3gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnamehis_jal_3gpoly_id¢bid‚eptypeiCommitteddnextôdargs‚¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnamewinner__from_state__pc_3gpoly_id¢bidˆeptypeiCommitteddnextôbopcAdderight¡oBinaryOperation£dleft¡iReference£dnamehis_lui_3gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡fNumberbopcAdderight¡oBinaryOperation£dleft¡iReference£dnamehis_jal_3gpoly_id¢bid‚eptypeiCommitteddnextôbopcMuleright¡fNumberwÿýñ¡oBinaryOperation£dleft¡iReference£dnamexinner__from_state__timestamp_3gpoly_id¢bidŠeptypeiCommitteddnextôbopcAdderight¡fNumbernoriginal_indexG \ No newline at end of file From d171dc8d5c8c29ef968796db2827e6c2a2f534ef Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 18 Nov 2025 16:30:26 +0000 Subject: [PATCH 11/60] Compute quadratic equivalences. --- autoprecompiles/Cargo.toml | 1 + autoprecompiles/src/rule_based_optimizer.rs | 307 ++++++++++++++++---- 2 files changed, 257 insertions(+), 51 deletions(-) diff --git a/autoprecompiles/Cargo.toml b/autoprecompiles/Cargo.toml index fa7e8f6725..6123edfd8b 100644 --- a/autoprecompiles/Cargo.toml +++ b/autoprecompiles/Cargo.toml @@ -24,6 +24,7 @@ strum = { version = "0.27.0", features = ["derive"] } metrics = "0.23.0" crepe = "0.1.8" +slab = "0.4.11" [package.metadata.cargo-udeps.ignore] development = ["env_logger"] diff --git a/autoprecompiles/src/rule_based_optimizer.rs b/autoprecompiles/src/rule_based_optimizer.rs index 7ea2eff7ba..8ed707cedb 100644 --- a/autoprecompiles/src/rule_based_optimizer.rs +++ b/autoprecompiles/src/rule_based_optimizer.rs @@ -1,22 +1,26 @@ #![allow(clippy::iter_over_hash_type)] #![allow(for_loops_over_fallibles)] use std::{ + cell::RefCell, collections::{HashMap, HashSet}, fmt::Display, hash::Hash, + ops::Index, }; -use itertools::Itertools; +use itertools::{EitherOrBoth, Itertools}; use powdr_constraint_solver::{ constraint_system::{BusInteraction, BusInteractionHandler}, grouped_expression::{GroupedExpression, GroupedExpressionComponent, NoRangeConstraints}, indexed_constraint_system::IndexedConstraintSystem, + runtime_constant::VarTransformable, }; use powdr_number::{BabyBearField, FieldElement, LargeInt}; use num_traits::Zero; use crepe::crepe; +use slab::Slab; use crate::range_constraint_optimizer::RangeConstraintHandler; @@ -25,66 +29,254 @@ type F = BabyBearField; #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] struct Var(u32); -crepe! { - @input - struct AlgebraicConstraint<'a>(&'a GroupedExpression); +impl Display for Var { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "v{}", self.0) + } +} - @input - struct BusInteractionConstraint<'a>(&'a BusInteraction>); +struct DB { + expressions: Vec>, + reverse: HashMap, usize>, +} + +impl Index for DB { + type Output = GroupedExpression; + fn index(&self, index: Expr) -> &Self::Output { + &self.expressions[index.0] + } +} + +struct System { + expressions: RefCell, + var_to_string: Option>, +} + +impl Default for System { + fn default() -> Self { + Self { + expressions: RefCell::new(DB { + expressions: Vec::new(), + reverse: HashMap::new(), + }), + var_to_string: None, + } + } +} + +impl PartialEq for System { + fn eq(&self, other: &Self) -> bool { + // TODO change this as soon as we have different systems + true + } +} + +impl Eq for System {} + +impl PartialOrd for System { + fn partial_cmp(&self, other: &Self) -> Option { + // TODO change this as soon as we have different systems + Some(std::cmp::Ordering::Equal) + } +} + +impl Ord for System { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + // TODO change this as soon as we have different systems + std::cmp::Ordering::Equal + } +} + +impl Hash for System { + fn hash(&self, state: &mut H) { + // TODO change this as soon as we have different systems + 0.hash(state); + } +} + +impl System { + pub fn set_var_to_string_mapping(&mut self, mapping: HashMap) { + assert!(self.var_to_string.is_none()); + self.var_to_string = Some(mapping); + } + // pub fn get(&self, expr: Expr) -> &GroupedExpression { + // let b = self.expressions.borrow(); + // b.get(expr.0).unwrap() + // } + + pub fn insert(&self, expr: &GroupedExpression) -> Expr { + let mut db = self.expressions.borrow_mut(); + let id = if let Some(&id) = db.reverse.get(&expr) { + id + } else { + db.expressions.push(expr.clone()); + let id = db.expressions.len() - 1; + db.reverse.insert(expr.clone(), id); + id + }; + Expr(id) + } + // TODO potentially make this a more generic "matches structure" function + pub fn try_to_simple_quadratic(&self, expr: Expr) -> Option<(Expr, Expr)> { + let (l, r) = { + let db = self.expressions.borrow(); + let (l, r) = db[expr].try_as_single_product()?; + (l.clone(), r.clone()) + }; + // TODO eventually, l and r are cloned. + // if we change GroupedExpression to use `Expr` for the recursion, we do not + // have to insert everything multiple times. + Some((self.insert(&l), self.insert(&r))) + } + + pub fn try_to_simple_var(&self, expr: Expr) -> Option { + let db = self.expressions.borrow(); + db[expr].try_to_simple_unknown() + } + + /// Returns Some(C) if `a - b = C' and both are affine. + pub fn constant_difference(&self, a: Expr, b: Expr) -> Option { + println!( + "Checking constant difference between {} and {}", + self.format_expr(a), + self.format_expr(b) + ); + let db = self.expressions.borrow(); + let a = &db[a]; + let b = &db[b]; + (a.is_affine() + && b.is_affine() + && a.linear_components() + .zip(b.linear_components()) + .all(|(x, y)| x == y)) + .then(|| *a.constant_offset() - *b.constant_offset()) + } + + /// Checks if a and b are affine constraints that differ in exactly one variable. + /// TODO scaling + pub fn differ_in_exactly_one_variable(&self, a: Expr, b: Expr) -> Option<(Var, Var)> { + let db = self.expressions.borrow(); + let a = &db[a]; + let b = &db[b]; + if !a.is_affine() || !b.is_affine() { + return None; + } + if a.constant_offset() != b.constant_offset() { + return None; + } + // TODO use merge_join_by? (avoid creating the HashSet) + let left_vars = a + .linear_components() + .map(|(v, _)| *v) + .collect::>(); + let right_vars = b + .linear_components() + .map(|(v, _)| *v) + .collect::>(); + let left_var = left_vars.difference(&right_vars).exactly_one().ok()?; + let right_var = right_vars.difference(&left_vars).exactly_one().ok()?; + if a.coefficient_of_variable_in_affine_part(left_var) + != b.coefficient_of_variable_in_affine_part(right_var) + { + return None; + } + Some((*left_var, *right_var)) + } + + pub fn format_expr(&self, expr: Expr) -> String { + let db = self.expressions.borrow(); + if let Some(var_to_string) = &self.var_to_string { + db[expr] + .transform_var_type(&mut |v| &var_to_string[v]) + .to_string() + } else { + db[expr].to_string() + } + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +struct Expr(usize); + +crepe! { @input - struct RangeConstraintOnExpression<'a>(&'a GroupedExpression, F, F); + struct S<'a>(&'a System); @input - struct DestructureAffine<'a>(&'a GroupedExpression, F, Var, &'a GroupedExpression); + struct AlgebraicConstraint(Expr); - struct Expression<'a>(&'a GroupedExpression); - // TODO question to answer: Does this treat different instances of the same expression as equal? - Expression(e) <- AlgebraicConstraint(e); - Expression(e) <- BusInteractionConstraint(bus_inter), for e in bus_inter.fields(); - Expression(q) <- Expression(e), for q in e.quadratic_components().iter().flat_map(|(l, r)| [l, r].into_iter()); - Expression(e) <- DestructureAffine(_, _, _, e); - Expression(e) <- DestructureAffine(e, _, _, _); + // @input + // struct BusInteractionConstraint<'a>(&'a BusInteraction>); - struct IsSimpleVar<'a>(&'a GroupedExpression, Var); - IsSimpleVar(e, v) <- Expression(e), for v in e.try_to_simple_unknown(); + @input + struct RangeConstraintOnExpression(Expr, F, F); - struct Product<'a>(&'a GroupedExpression, &'a GroupedExpression, &'a GroupedExpression); - Product(q, l, r) <- Expression(q), for (l, r) in q.quadratic_components(); - Product(q, l, r) <- Product(q, r, l); @output struct RangeConstraint(Var, F, F); RangeConstraint(v, min, max) <- RangeConstraintOnExpression(e, min, max), - IsSimpleVar(e, v); + S(sys), + let Some(v) = sys.try_to_simple_var(e); + + // struct Product(Expr, Expr, Expr); + // Product(e, l, r) <- + // AlgebraicConstraint(e), + // S(sys), + // let Some((l, r)) = sys.try_to_simple_quadratic(e); + + // (E, expr, offset) <-> E = (expr) * (expr + offset) is a constraint + struct QuadraticEquivalenceCandidate(Expr, Expr, F); + QuadraticEquivalenceCandidate(e, r, offset) <- + AlgebraicConstraint(e), + S(sys), + let Some((l, r)) = sys.try_to_simple_quadratic(e), + let Some(offset) = sys.constant_difference(l, r); + + struct QuadraticEquivalence(Var, Var); + QuadraticEquivalence(v1, v2) <- + QuadraticEquivalenceCandidate(_, expr1, offset), + QuadraticEquivalenceCandidate(_, expr2, offset), + S(sys), + let Some((v1, v2)) = sys.differ_in_exactly_one_variable(expr1, expr2), + RangeConstraint(v1, min, max), + RangeConstraint(v2, min, max), + (min + offset >= max); // TODO not exactly + + // TODO wait a second. We can craete range constraints on expressions for all // algebraic constraints. Then we just work on range constraints on expressions // instead of algebraic constraints. Might be more difficult with the scaling, though. - RangeConstraint(v, x1, x1 + F::from(1)) <- - AlgebraicConstraint(e), - Product(e, l, r), - Solvable(l, v, x1), - Solvable(r, v, x1 + F::from(1)); + // RangeConstraint(v, x1, x1 + F::from(1)) <- + // AlgebraicConstraint(e), + // Product(e, l, r), + // Solvable(l, v, x1), + // Solvable(r, v, x1 + F::from(1)); - struct IsZero<'a>(&'a GroupedExpression); - IsZero(e) <- Expression(e), (e.is_zero()); + // struct IsZero<'a>(&'a GroupedExpression); + // IsZero(e) <- Expression(e), (e.is_zero()); - struct IsAffine<'a>(&'a GroupedExpression); - IsAffine(e) <- Expression(e), (e.is_affine()); + // struct IsAffine<'a>(&'a GroupedExpression); + // IsAffine(e) <- Expression(e), (e.is_affine()); - struct AffineExpression<'a>(&'a GroupedExpression, F, Var, F); - AffineExpression(e, coeff, var, offset) <- - DestructureAffine(e, coeff, var, rest), - for offset in rest.try_to_number(); + // struct AffineExpression<'a>(&'a GroupedExpression, F, Var, F); + // AffineExpression(e, coeff, var, offset) <- + // DestructureAffine(e, coeff, var, rest), + // for offset in rest.try_to_number(); - struct Solvable<'a>(&'a GroupedExpression, Var, F); - Solvable(e, var, -offset / coeff) <- - AffineExpression(e, coeff, var, offset); + // struct Solvable<'a>(&'a GroupedExpression, Var, F); + // Solvable(e, var, -offset / coeff) <- + // AffineExpression(e, coeff, var, offset); @output struct Assignment(Var, F); - Assignment(var, v) <- AlgebraicConstraint(e), Solvable(e, var, v); + // Assignment(var, v) <- AlgebraicConstraint(e), Solvable(e, var, v); + + @output + struct Equivalence(Var, Var); + Equivalence(v1, v2) <- QuadraticEquivalence(v1, v2); + } pub fn rule_based_optimization( @@ -98,13 +290,16 @@ pub fn rule_based_optimization>> = system + let bus_interactions: Vec> = system .system() .bus_interactions .iter() @@ -112,9 +307,11 @@ pub fn rule_based_optimization { forward: HashMap, backward: HashMap, From d78c3eb13f3384c7b43b225448bb64ed6feb66fd Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 18 Nov 2025 17:24:05 +0000 Subject: [PATCH 12/60] Derive quadratic equivalences. --- autoprecompiles/src/rule_based_optimizer.rs | 119 ++++++++++++------ .../src/solver/quadratic_equivalences.rs | 3 +- 2 files changed, 81 insertions(+), 41 deletions(-) diff --git a/autoprecompiles/src/rule_based_optimizer.rs b/autoprecompiles/src/rule_based_optimizer.rs index 8ed707cedb..0db691a385 100644 --- a/autoprecompiles/src/rule_based_optimizer.rs +++ b/autoprecompiles/src/rule_based_optimizer.rs @@ -117,6 +117,16 @@ impl System { Expr(id) } + pub fn try_to_affine(&self, expr: Expr) -> Option<(F, Var, F)> { + let db = self.expressions.borrow(); + let expr = &db[expr]; + if !expr.is_affine() { + return None; + } + let (var, coeff) = expr.linear_components().exactly_one().ok()?; + Some((*coeff, *var, *expr.constant_offset())) + } + // TODO potentially make this a more generic "matches structure" function pub fn try_to_simple_quadratic(&self, expr: Expr) -> Option<(Expr, Expr)> { let (l, r) = { @@ -137,11 +147,6 @@ impl System { /// Returns Some(C) if `a - b = C' and both are affine. pub fn constant_difference(&self, a: Expr, b: Expr) -> Option { - println!( - "Checking constant difference between {} and {}", - self.format_expr(a), - self.format_expr(b) - ); let db = self.expressions.borrow(); let a = &db[a]; let b = &db[b]; @@ -155,11 +160,16 @@ impl System { /// Checks if a and b are affine constraints that differ in exactly one variable. /// TODO scaling - pub fn differ_in_exactly_one_variable(&self, a: Expr, b: Expr) -> Option<(Var, Var)> { + pub fn differ_in_exactly_one_variable(&self, a_id: Expr, b_id: Expr) -> Option<(Var, Var, F)> { let db = self.expressions.borrow(); - let a = &db[a]; - let b = &db[b]; - if !a.is_affine() || !b.is_affine() { + let a = &db[a_id]; + let b = &db[b_id]; + if !a.is_affine() + || !b.is_affine() + || a.referenced_unknown_variables().count() != b.referenced_unknown_variables().count() + // TODO this is not in the docstring + || a.referenced_unknown_variables().count() < 2 + { return None; } if a.constant_offset() != b.constant_offset() { @@ -176,12 +186,11 @@ impl System { .collect::>(); let left_var = left_vars.difference(&right_vars).exactly_one().ok()?; let right_var = right_vars.difference(&left_vars).exactly_one().ok()?; - if a.coefficient_of_variable_in_affine_part(left_var) - != b.coefficient_of_variable_in_affine_part(right_var) - { + let coeff = *a.coefficient_of_variable_in_affine_part(left_var).unwrap(); + if coeff != *b.coefficient_of_variable_in_affine_part(right_var).unwrap() { return None; } - Some((*left_var, *right_var)) + Some((*left_var, *right_var, coeff)) } pub fn format_expr(&self, expr: Expr) -> String { @@ -194,6 +203,17 @@ impl System { db[expr].to_string() } } + + pub fn format_var(&self, var: Var) -> String { + if let Some(var_to_string) = &self.var_to_string { + var_to_string + .get(&var) + .cloned() + .unwrap_or_else(|| var.to_string()) + } else { + var.to_string() + } + } } #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] @@ -212,6 +232,15 @@ crepe! { @input struct RangeConstraintOnExpression(Expr, F, F); + struct Expression(Expr); + Expression(e) <- AlgebraicConstraint(e); + Expression(e) <- RangeConstraintOnExpression(e, _, _); + + struct AffineExpression(Expr, F, Var, F); + AffineExpression(e, coeff, var, offset) <- + Expression(e), + S(sys), + let Some((coeff, var, offset)) = sys.try_to_affine(e); @output struct RangeConstraint(Var, F, F); @@ -220,18 +249,25 @@ crepe! { S(sys), let Some(v) = sys.try_to_simple_var(e); - // struct Product(Expr, Expr, Expr); - // Product(e, l, r) <- - // AlgebraicConstraint(e), - // S(sys), - // let Some((l, r)) = sys.try_to_simple_quadratic(e); + // TODO conditions on the range constraint + RangeConstraint(v, min / coeff, max / coeff) <- + RangeConstraintOnExpression(e, min, max), + AffineExpression(e, coeff, v, offset), + (offset == F::zero()); + + struct Product(Expr, Expr, Expr); + Product(e, l, r) <- + Expression(e), + S(sys), + let Some((l, r)) = sys.try_to_simple_quadratic(e); + Product(e, r, l) <- Product(e, l, r); // (E, expr, offset) <-> E = (expr) * (expr + offset) is a constraint struct QuadraticEquivalenceCandidate(Expr, Expr, F); QuadraticEquivalenceCandidate(e, r, offset) <- AlgebraicConstraint(e), S(sys), - let Some((l, r)) = sys.try_to_simple_quadratic(e), + Product(e, l, r), let Some(offset) = sys.constant_difference(l, r); struct QuadraticEquivalence(Var, Var); @@ -239,39 +275,35 @@ crepe! { QuadraticEquivalenceCandidate(_, expr1, offset), QuadraticEquivalenceCandidate(_, expr2, offset), S(sys), - let Some((v1, v2)) = sys.differ_in_exactly_one_variable(expr1, expr2), + let Some((v1, v2, coeff)) = sys.differ_in_exactly_one_variable(expr1, expr2), RangeConstraint(v1, min, max), RangeConstraint(v2, min, max), - (min + offset >= max); // TODO not exactly + (min + (offset / coeff) >= max); // TODO not exactly // TODO wait a second. We can craete range constraints on expressions for all // algebraic constraints. Then we just work on range constraints on expressions // instead of algebraic constraints. Might be more difficult with the scaling, though. - // RangeConstraint(v, x1, x1 + F::from(1)) <- - // AlgebraicConstraint(e), - // Product(e, l, r), - // Solvable(l, v, x1), - // Solvable(r, v, x1 + F::from(1)); - // struct IsZero<'a>(&'a GroupedExpression); - // IsZero(e) <- Expression(e), (e.is_zero()); - // struct IsAffine<'a>(&'a GroupedExpression); - // IsAffine(e) <- Expression(e), (e.is_affine()); - // struct AffineExpression<'a>(&'a GroupedExpression, F, Var, F); - // AffineExpression(e, coeff, var, offset) <- - // DestructureAffine(e, coeff, var, rest), - // for offset in rest.try_to_number(); - // struct Solvable<'a>(&'a GroupedExpression, Var, F); - // Solvable(e, var, -offset / coeff) <- - // AffineExpression(e, coeff, var, offset); + struct Solvable(Expr, Var, F); + Solvable(e, var, -offset / coeff) <- + AffineExpression(e, coeff, var, offset); + + // Boolean range constraint + RangeConstraint(v, x1, x1 + F::from(1)) <- + AlgebraicConstraint(e), + Product(e, l, r), + Solvable(l, v, x1), + Solvable(r, v, x1 + F::from(1)); @output struct Assignment(Var, F); - // Assignment(var, v) <- AlgebraicConstraint(e), Solvable(e, var, v); + Assignment(var, v) <- + AlgebraicConstraint(e), + Solvable(e, var, v); @output struct Equivalence(Var, Var); @@ -363,8 +395,8 @@ pub fn rule_based_optimization Date: Tue, 18 Nov 2025 17:45:35 +0000 Subject: [PATCH 13/60] Disable quadratic equivalences. --- constraint-solver/src/solver/base.rs | 17 +++++++++-------- .../src/solver/quadratic_equivalences.rs | 7 +------ 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/constraint-solver/src/solver/base.rs b/constraint-solver/src/solver/base.rs index b81de11f77..5593a4ec31 100644 --- a/constraint-solver/src/solver/base.rs +++ b/constraint-solver/src/solver/base.rs @@ -374,14 +374,15 @@ where /// Tries to find equivalent variables using quadratic constraints. fn try_solve_quadratic_equivalences(&mut self) -> bool { - let equivalences = quadratic_equivalences::find_quadratic_equalities( - self.constraint_system.system().algebraic_constraints(), - &*self, - ); - for (x, y) in &equivalences { - self.apply_assignment(y, &GroupedExpression::from_unknown_variable(x.clone())); - } - !equivalences.is_empty() + false + // let equivalences = quadratic_equivalences::find_quadratic_equalities( + // self.constraint_system.system().algebraic_constraints(), + // &*self, + // ); + // for (x, y) in &equivalences { + // self.apply_assignment(y, &GroupedExpression::from_unknown_variable(x.clone())); + // } + // !equivalences.is_empty() } /// Find groups of variables with a small set of possible assignments. diff --git a/constraint-solver/src/solver/quadratic_equivalences.rs b/constraint-solver/src/solver/quadratic_equivalences.rs index 6fdf97f8ef..4df24ea93a 100644 --- a/constraint-solver/src/solver/quadratic_equivalences.rs +++ b/constraint-solver/src/solver/quadratic_equivalences.rs @@ -87,12 +87,7 @@ fn process_quadratic_equality_candidate_pair< // - X = -A - offset and Y = -A - offset // Since `A` has to have some value, we can conclude `X = Y`. - println!( - "Found quadratic equality {} = {} from ({} - {}) * ({}) and ({} - {}) * ({})", - c1_var, c2_var, c1.expr, c1.offset, c1.expr, c2.expr, c2.offset, c2.expr - ); - None - // Some((c1_var.clone(), c2_var.clone())) + Some((c1_var.clone(), c2_var.clone())) } /// This represents an identity `expr * (expr + offset) = 0`, From c95b6a7b06fb5d14c8631efea415cfd557ebecc2 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 18 Nov 2025 20:49:45 +0000 Subject: [PATCH 14/60] Use real range constraints. --- autoprecompiles/src/rule_based_optimizer.rs | 46 +++++++++++---------- constraint-solver/src/range_constraint.rs | 2 +- 2 files changed, 26 insertions(+), 22 deletions(-) diff --git a/autoprecompiles/src/rule_based_optimizer.rs b/autoprecompiles/src/rule_based_optimizer.rs index 0db691a385..edc9ec15f5 100644 --- a/autoprecompiles/src/rule_based_optimizer.rs +++ b/autoprecompiles/src/rule_based_optimizer.rs @@ -13,10 +13,12 @@ use powdr_constraint_solver::{ constraint_system::{BusInteraction, BusInteractionHandler}, grouped_expression::{GroupedExpression, GroupedExpressionComponent, NoRangeConstraints}, indexed_constraint_system::IndexedConstraintSystem, + range_constraint::RangeConstraint, runtime_constant::VarTransformable, }; use powdr_number::{BabyBearField, FieldElement, LargeInt}; +use num_traits::One; use num_traits::Zero; use crepe::crepe; @@ -230,11 +232,11 @@ crepe! { // struct BusInteractionConstraint<'a>(&'a BusInteraction>); @input - struct RangeConstraintOnExpression(Expr, F, F); + struct RangeConstraintOnExpression(Expr, RangeConstraint); struct Expression(Expr); Expression(e) <- AlgebraicConstraint(e); - Expression(e) <- RangeConstraintOnExpression(e, _, _); + Expression(e) <- RangeConstraintOnExpression(e, _); struct AffineExpression(Expr, F, Var, F); AffineExpression(e, coeff, var, offset) <- @@ -243,17 +245,18 @@ crepe! { let Some((coeff, var, offset)) = sys.try_to_affine(e); @output - struct RangeConstraint(Var, F, F); - RangeConstraint(v, min, max) <- - RangeConstraintOnExpression(e, min, max), - S(sys), - let Some(v) = sys.try_to_simple_var(e); - - // TODO conditions on the range constraint - RangeConstraint(v, min / coeff, max / coeff) <- - RangeConstraintOnExpression(e, min, max), + struct RangeConstraintOnVar(Var, RangeConstraint); + // RC(coeff * var + offset) = rc <=> + // coeff * RC(var) + offset = rc <=> + // RC(var) = (rc - offset) / coeff + RangeConstraintOnVar(v, rc.combine_sum(&RangeConstraint::from_value(offset)).multiple(F::one() / coeff)) <- + RangeConstraintOnExpression(e, rc), AffineExpression(e, coeff, v, offset), - (offset == F::zero()); + (coeff != F::zero()); + + RangeConstraintOnVar(v, v_rc1.conjunction(&v_rc2)) <- + RangeConstraintOnVar(v, v_rc1), + RangeConstraintOnVar(v, v_rc2); struct Product(Expr, Expr, Expr); Product(e, l, r) <- @@ -276,9 +279,9 @@ crepe! { QuadraticEquivalenceCandidate(_, expr2, offset), S(sys), let Some((v1, v2, coeff)) = sys.differ_in_exactly_one_variable(expr1, expr2), - RangeConstraint(v1, min, max), - RangeConstraint(v2, min, max), - (min + (offset / coeff) >= max); // TODO not exactly + RangeConstraintOnVar(v1, rc), + RangeConstraintOnVar(v2, rc), + (rc.is_disjoint(&rc.combine_sum(&RangeConstraint::from_value(offset / coeff)))); // TODO wait a second. We can craete range constraints on expressions for all @@ -293,7 +296,7 @@ crepe! { AffineExpression(e, coeff, var, offset); // Boolean range constraint - RangeConstraint(v, x1, x1 + F::from(1)) <- + RangeConstraintOnVar(v, RangeConstraint::from_range(x1, x1 + F::from(1))) <- AlgebraicConstraint(e), Product(e, l, r), Solvable(l, v, x1), @@ -308,7 +311,6 @@ crepe! { @output struct Equivalence(Var, Var); Equivalence(v1, v2) <- QuadraticEquivalence(v1, v2); - } pub fn rule_based_optimization( @@ -362,11 +364,13 @@ pub fn rule_based_optimization { /// Bit-mask. A value `x` is allowed only if `x & mask == x` (when seen as unsigned integer). mask: T::Integer, From 062a6687385bb470a4b122c0b18670982a8b6ba6 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 18 Nov 2025 21:26:14 +0000 Subject: [PATCH 15/60] Apply substitutions. --- autoprecompiles/src/rule_based_optimizer.rs | 114 +++++++++++++++++--- 1 file changed, 101 insertions(+), 13 deletions(-) diff --git a/autoprecompiles/src/rule_based_optimizer.rs b/autoprecompiles/src/rule_based_optimizer.rs index edc9ec15f5..bc78e59539 100644 --- a/autoprecompiles/src/rule_based_optimizer.rs +++ b/autoprecompiles/src/rule_based_optimizer.rs @@ -2,7 +2,7 @@ #![allow(for_loops_over_fallibles)] use std::{ cell::RefCell, - collections::{HashMap, HashSet}, + collections::{BTreeSet, HashMap, HashSet}, fmt::Display, hash::Hash, ops::Index, @@ -108,17 +108,34 @@ impl System { pub fn insert(&self, expr: &GroupedExpression) -> Expr { let mut db = self.expressions.borrow_mut(); - let id = if let Some(&id) = db.reverse.get(&expr) { - id + if let Some(&id) = db.reverse.get(expr) { + Expr(id) } else { - db.expressions.push(expr.clone()); - let id = db.expressions.len() - 1; - db.reverse.insert(expr.clone(), id); - id - }; + self.insert_owned_new(&mut db, expr.clone()) + } + } + + pub fn insert_owned(&self, expr: GroupedExpression) -> Expr { + let mut db = self.expressions.borrow_mut(); + if let Some(&id) = db.reverse.get(&expr) { + Expr(id) + } else { + self.insert_owned_new(&mut db, expr) + } + } + + fn insert_owned_new(&self, db: &mut DB, expr: GroupedExpression) -> Expr { + db.expressions.push(expr.clone()); + let id = db.expressions.len() - 1; + db.reverse.insert(expr, id); Expr(id) } + pub fn referenced_variable(&self, expr: Expr) -> BTreeSet { + let db = self.expressions.borrow(); + db[expr].referenced_unknown_variables().cloned().collect() + } + pub fn try_to_affine(&self, expr: Expr) -> Option<(F, Var, F)> { let db = self.expressions.borrow(); let expr = &db[expr]; @@ -129,6 +146,10 @@ impl System { Some((*coeff, *var, *expr.constant_offset())) } + pub fn is_zero(&self, expr: Expr) -> bool { + self.expressions.borrow()[expr].is_zero() + } + // TODO potentially make this a more generic "matches structure" function pub fn try_to_simple_quadratic(&self, expr: Expr) -> Option<(Expr, Expr)> { let (l, r) = { @@ -195,6 +216,29 @@ impl System { Some((*left_var, *right_var, coeff)) } + pub fn substitute_by_known(&self, e: Expr, var: Var, value: F) -> Expr { + let expr = { + let db = self.expressions.borrow(); + let mut expr = db[e].clone(); + expr.substitute_by_known(&var, &value); + expr + }; + self.insert_owned(expr) + } + + pub fn substitute_by_var(&self, e: Expr, var: Var, replacement: Var) -> Expr { + let expr = { + let db = self.expressions.borrow(); + let mut expr = db[e].clone(); + expr.substitute_by_unknown( + &var, + &GroupedExpression::from_unknown_variable(replacement), + ); + expr + }; + self.insert_owned(expr) + } + pub fn format_expr(&self, expr: Expr) -> String { let db = self.expressions.borrow(); if let Some(var_to_string) = &self.var_to_string { @@ -226,7 +270,10 @@ crepe! { struct S<'a>(&'a System); @input + struct InitialAlgebraicConstraint(Expr); + struct AlgebraicConstraint(Expr); + AlgebraicConstraint(e) <- InitialAlgebraicConstraint(e); // @input // struct BusInteractionConstraint<'a>(&'a BusInteraction>); @@ -238,6 +285,12 @@ crepe! { Expression(e) <- AlgebraicConstraint(e); Expression(e) <- RangeConstraintOnExpression(e, _); + struct ContainsVariable(Expr, Var); + ContainsVariable(e, v) <- + Expression(e), + S(sys), + for v in sys.referenced_variable(e); + struct AffineExpression(Expr, F, Var, F); AffineExpression(e, coeff, var, offset) <- Expression(e), @@ -288,9 +341,6 @@ crepe! { // algebraic constraints. Then we just work on range constraints on expressions // instead of algebraic constraints. Might be more difficult with the scaling, though. - - - struct Solvable(Expr, Var, F); Solvable(e, var, -offset / coeff) <- AffineExpression(e, coeff, var, offset); @@ -311,6 +361,40 @@ crepe! { @output struct Equivalence(Var, Var); Equivalence(v1, v2) <- QuadraticEquivalence(v1, v2); + + AlgebraicConstraint(sys.substitute_by_known(e, v, val)) <- + S(sys), + AlgebraicConstraint(e), + ContainsVariable(e, v), + Assignment(v, val); + + struct ReplaceAlgebraicConstraintBy(Expr, Expr); + ReplaceAlgebraicConstraintBy(e, sys.substitute_by_known(e, v, val)) <- + S(sys), + AlgebraicConstraint(e), + ContainsVariable(e, v), + Assignment(v, val); + ReplaceAlgebraicConstraintBy(e, sys.substitute_by_var(e, v, v2)) <- + S(sys), + AlgebraicConstraint(e), + ContainsVariable(e, v), + Equivalence(v, v2); + AlgebraicConstraint(e) <- + ReplaceAlgebraicConstraintBy(_, e); + + + // This constraint has been replaced by a different one (or is redundant). + struct AlgebraicConstraintDeleted(Expr); + AlgebraicConstraintDeleted(e) <- + ReplaceAlgebraicConstraintBy(e, _); + AlgebraicConstraintDeleted(e) <- + S(sys), + AlgebraicConstraint(e), + (sys.is_zero(e)); + + @output + struct FinalAlgebraicConstraint(Expr); + FinalAlgebraicConstraint(e) <- AlgebraicConstraint(e), !AlgebraicConstraintDeleted(e); } pub fn rule_based_optimization( @@ -381,7 +465,7 @@ pub fn rule_based_optimization Date: Tue, 18 Nov 2025 22:24:59 +0000 Subject: [PATCH 16/60] Some performance fixes. --- autoprecompiles/src/rule_based_optimizer.rs | 25 ++++++-------- constraint-solver/src/grouped_expression.rs | 38 +++++++++++++++++++++ 2 files changed, 48 insertions(+), 15 deletions(-) diff --git a/autoprecompiles/src/rule_based_optimizer.rs b/autoprecompiles/src/rule_based_optimizer.rs index bc78e59539..031fe7086d 100644 --- a/autoprecompiles/src/rule_based_optimizer.rs +++ b/autoprecompiles/src/rule_based_optimizer.rs @@ -220,7 +220,8 @@ impl System { let expr = { let db = self.expressions.borrow(); let mut expr = db[e].clone(); - expr.substitute_by_known(&var, &value); + // expr.substitute_by_known(&var, &value); + expr.substitute_simple(&var, value); expr }; self.insert_owned(expr) @@ -362,23 +363,17 @@ crepe! { struct Equivalence(Var, Var); Equivalence(v1, v2) <- QuadraticEquivalence(v1, v2); - AlgebraicConstraint(sys.substitute_by_known(e, v, val)) <- - S(sys), - AlgebraicConstraint(e), - ContainsVariable(e, v), - Assignment(v, val); - struct ReplaceAlgebraicConstraintBy(Expr, Expr); ReplaceAlgebraicConstraintBy(e, sys.substitute_by_known(e, v, val)) <- S(sys), AlgebraicConstraint(e), ContainsVariable(e, v), Assignment(v, val); - ReplaceAlgebraicConstraintBy(e, sys.substitute_by_var(e, v, v2)) <- - S(sys), - AlgebraicConstraint(e), - ContainsVariable(e, v), - Equivalence(v, v2); + // ReplaceAlgebraicConstraintBy(e, sys.substitute_by_var(e, v, v2)) <- + // S(sys), + // AlgebraicConstraint(e), + // ContainsVariable(e, v), + // Equivalence(v, v2); AlgebraicConstraint(e) <- ReplaceAlgebraicConstraintBy(_, e); @@ -495,9 +490,9 @@ pub fn rule_based_optimization GroupedExpression { } } +impl GroupedExpression { + pub fn substitute_simple(&mut self, variable: &V, substitution: T) { + if self.linear.contains_key(variable) { + let coeff = self.linear.remove(variable).unwrap(); + self.constant += coeff * substitution; + } + + let mut to_add = GroupedExpression::zero(); + self.quadratic.retain_mut(|(l, r)| { + l.substitute_simple(variable, substitution); + r.substitute_simple(variable, substitution); + match (l.try_to_known(), r.try_to_known()) { + (Some(l), Some(r)) => { + self.constant += l.clone() * r.clone(); + false + } + (Some(l), None) => { + if !l.is_zero() { + to_add += r.clone() * l; + } + false + } + (None, Some(r)) => { + if !r.is_zero() { + to_add += l.clone() * r; + } + false + } + _ => true, + } + }); + // remove_quadratic_terms_adding_to_zero(&mut self.quadratic); + + if !to_add.is_zero() { + *self += to_add; + } + } +} impl, V: Ord + Clone + Eq> GroupedExpression { /// Substitute a variable by a symbolically known expression. The variable can be known or unknown. /// If it was already known, it will be substituted in the known expressions. From 2e787b46763fe4f11c9518ab4d50cadeffffd931 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 19 Nov 2025 15:38:01 +0000 Subject: [PATCH 17/60] Cleanup. --- autoprecompiles/Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/autoprecompiles/Cargo.toml b/autoprecompiles/Cargo.toml index 6123edfd8b..fa7e8f6725 100644 --- a/autoprecompiles/Cargo.toml +++ b/autoprecompiles/Cargo.toml @@ -24,7 +24,6 @@ strum = { version = "0.27.0", features = ["derive"] } metrics = "0.23.0" crepe = "0.1.8" -slab = "0.4.11" [package.metadata.cargo-udeps.ignore] development = ["env_logger"] From 868dbda4fd3b5ea4acc11a52d3f26bdd52b0e445 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 19 Nov 2025 15:38:48 +0000 Subject: [PATCH 18/60] Make range constraints copy. --- .../low_degree_bus_interaction_optimizer.rs | 2 +- .../src/algebraic_constraint/solve.rs | 9 +- constraint-solver/src/range_constraint.rs | 2 +- .../src/solver/constraint_splitter.rs | 82 +++++++++---------- constraint-solver/src/symbolic_expression.rs | 8 +- constraint-solver/tests/solver.rs | 4 +- .../bus_interaction_handler/bitwise_lookup.rs | 10 +-- openvm/src/bus_interaction_handler/memory.rs | 4 +- .../variable_range_checker.rs | 2 +- 9 files changed, 57 insertions(+), 66 deletions(-) diff --git a/autoprecompiles/src/low_degree_bus_interaction_optimizer.rs b/autoprecompiles/src/low_degree_bus_interaction_optimizer.rs index 18f3e01f72..9f51866a02 100644 --- a/autoprecompiles/src/low_degree_bus_interaction_optimizer.rs +++ b/autoprecompiles/src/low_degree_bus_interaction_optimizer.rs @@ -184,7 +184,7 @@ impl< }) .filter(|function| { self.has_few_possible_values( - function.inputs.iter().map(|f| f.range_constraint.clone()), + function.inputs.iter().map(|f| f.range_constraint), MAX_DOMAIN_SIZE, ) }) diff --git a/constraint-solver/src/algebraic_constraint/solve.rs b/constraint-solver/src/algebraic_constraint/solve.rs index 0eba58b8f3..4b03d73b23 100644 --- a/constraint-solver/src/algebraic_constraint/solve.rs +++ b/constraint-solver/src/algebraic_constraint/solve.rs @@ -317,7 +317,7 @@ fn effect_to_range_constraint( effect: &Effect, ) -> Option<(V, RangeConstraint)> { match effect { - Effect::RangeConstraint(var, rc) => Some((var.clone(), rc.clone())), + Effect::RangeConstraint(var, rc) => Some((var.clone(), *rc)), Effect::Assignment(var, value) => Some((var.clone(), value.range_constraint())), _ => None, } @@ -465,8 +465,7 @@ mod tests { let b = Qse::from_unknown_variable("b"); let c = Qse::from_unknown_variable("c"); let z = Qse::from_unknown_variable("Z"); - let range_constraints = - HashMap::from([("a", rc.clone()), ("b", rc.clone()), ("c", rc.clone())]); + let range_constraints = HashMap::from([("a", rc), ("b", rc), ("c", rc)]); // a * 0x100 + b * 0x10000 + c * 0x1000000 + 10 - Z = 0 let ten = constant(10); let constr = @@ -503,7 +502,7 @@ mod tests { let Effect::RangeConstraint(var, rc) = effect else { panic!(); }; - (var, rc.clone()) + (var, *rc) } #[test] @@ -583,7 +582,7 @@ mod tests { fn bool_plus_one_cant_be_zero() { let expr = var("a") + constant(1); let rc = RangeConstraint::from_mask(0x1u64); - let range_constraints = HashMap::from([("a", rc.clone())]); + let range_constraints = HashMap::from([("a", rc)]); assert!(AlgebraicConstraint::assert_zero(&expr) .solve(&range_constraints) .is_err()); diff --git a/constraint-solver/src/range_constraint.rs b/constraint-solver/src/range_constraint.rs index ab6e197322..2966f91b69 100644 --- a/constraint-solver/src/range_constraint.rs +++ b/constraint-solver/src/range_constraint.rs @@ -35,7 +35,7 @@ use powdr_number::{log2_exact, FieldElement, LargeInt}; /// of the full system. /// /// Finally, please be aware that same constraint can have multiple representations. -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] pub struct RangeConstraint { /// Bit-mask. A value `x` is allowed only if `x & mask == x` (when seen as unsigned integer). mask: T::Integer, diff --git a/constraint-solver/src/solver/constraint_splitter.rs b/constraint-solver/src/solver/constraint_splitter.rs index 25d72688a2..f9ed33d7bb 100644 --- a/constraint-solver/src/solver/constraint_splitter.rs +++ b/constraint-solver/src/solver/constraint_splitter.rs @@ -380,10 +380,10 @@ mod test { fn split_simple() { let four_bit_rc = RangeConstraint::from_mask(0xfu32); let rcs = [ - ("x", four_bit_rc.clone()), - ("y", four_bit_rc.clone()), - ("a", four_bit_rc.clone()), - ("b", four_bit_rc.clone()), + ("x", four_bit_rc), + ("y", four_bit_rc), + ("a", four_bit_rc), + ("b", four_bit_rc), ] .into_iter() .collect::>(); @@ -397,13 +397,13 @@ mod test { fn split_multiple() { let four_bit_rc = RangeConstraint::from_mask(0xfu32); let rcs = [ - ("x", four_bit_rc.clone()), - ("y", four_bit_rc.clone()), - ("a", four_bit_rc.clone()), - ("b", four_bit_rc.clone()), - ("r", four_bit_rc.clone()), - ("s", four_bit_rc.clone()), - ("w", four_bit_rc.clone()), + ("x", four_bit_rc), + ("y", four_bit_rc), + ("a", four_bit_rc), + ("b", four_bit_rc), + ("r", four_bit_rc), + ("s", four_bit_rc), + ("w", four_bit_rc), ] .into_iter() .collect::>(); @@ -432,13 +432,9 @@ w = 0" let byte_rc = RangeConstraint::from_mask(0xffu32); let bit_rc = RangeConstraint::from_mask(0x1u32); - let rcs = [ - ("b__3_0", byte_rc.clone()), - ("b_msb_f_0", byte_rc.clone()), - ("x", bit_rc.clone()), - ] - .into_iter() - .collect::>(); + let rcs = [("b__3_0", byte_rc), ("b_msb_f_0", byte_rc), ("x", bit_rc)] + .into_iter() + .collect::>(); let expr1 = var("b__3_0") - var("b_msb_f_0") + constant(256) * var("x"); let items = try_split(expr1, &rcs).unwrap().iter().join("\n"); assert_eq!( @@ -459,13 +455,13 @@ x - 1 = 0" fn split_multiple_with_const() { let four_bit_rc = RangeConstraint::from_mask(0xfu32); let rcs = [ - ("x", four_bit_rc.clone()), - ("y", four_bit_rc.clone()), - ("a", four_bit_rc.clone()), - ("b", four_bit_rc.clone()), - ("r", four_bit_rc.clone()), - ("s", four_bit_rc.clone()), - ("w", four_bit_rc.clone()), + ("x", four_bit_rc), + ("y", four_bit_rc), + ("a", four_bit_rc), + ("b", four_bit_rc), + ("r", four_bit_rc), + ("s", four_bit_rc), + ("w", four_bit_rc), ] .into_iter() .collect::>(); @@ -491,10 +487,10 @@ w - 5 = 0" fn split_limb_decomposition() { let four_bit_rc = RangeConstraint::from_mask(0xfu32); let rcs = [ - ("l0", four_bit_rc.clone()), - ("l1", four_bit_rc.clone()), - ("l2", four_bit_rc.clone()), - ("l3", four_bit_rc.clone()), + ("l0", four_bit_rc), + ("l1", four_bit_rc), + ("l2", four_bit_rc), + ("l3", four_bit_rc), ] .into_iter() .collect::>(); @@ -520,7 +516,7 @@ l3 - 1 = 0" // a__0_12 + 256 * bool_113 - 216 let byte_rc = RangeConstraint::from_mask(0xffu32); let bit_rc = RangeConstraint::from_mask(0x1u32); - let rcs = [("a__0_12", byte_rc.clone()), ("bool_113", bit_rc.clone())] + let rcs = [("a__0_12", byte_rc), ("bool_113", bit_rc)] .into_iter() .collect::>(); let expr1: GroupedExpression = @@ -545,9 +541,9 @@ l3 - 1 = 0" // -(c__1_3) + 256 * (30720 * c__0_3 - c__2_3) = 1226833928 let byte_rc = RangeConstraint::from_mask(0xffu32); let rcs = [ - ("c__0_3", byte_rc.clone()), - ("c__1_3", byte_rc.clone()), - ("c__2_3", byte_rc.clone()), + ("c__0_3", byte_rc), + ("c__1_3", byte_rc), + ("c__2_3", byte_rc), ] .into_iter() .collect::>(); @@ -572,7 +568,7 @@ l3 - 1 = 0" fn wrapping_2() { // bool_17 + 1069547521 * (a__0_0) = 943718400 let bit_rc = RangeConstraint::from_mask(0x1u32); - let rcs = [("bool_17", bit_rc.clone()), ("a__0_0", bit_rc.clone())] + let rcs = [("bool_17", bit_rc), ("a__0_0", bit_rc)] .into_iter() .collect::>(); let expr: GroupedExpression = @@ -589,9 +585,9 @@ l3 - 1 = 0" let bit_rc = RangeConstraint::from_mask(0x1u32); let limb_rc = RangeConstraint::from_mask(0x7fffu32); let rcs = [ - ("bool_103", bit_rc.clone()), - ("to_pc_least_sig_bit_4", bit_rc.clone()), - ("to_pc_limbs__0_4", limb_rc.clone()), + ("bool_103", bit_rc), + ("to_pc_least_sig_bit_4", bit_rc), + ("to_pc_limbs__0_4", limb_rc), ] .into_iter() .collect::>(); @@ -629,7 +625,7 @@ l3 - 1 = 0" #[test] fn split_fail_overlapping() { let four_bit_rc = RangeConstraint::from_mask(0xfu32); - let rcs = [("x", four_bit_rc.clone()), ("y", four_bit_rc.clone())] + let rcs = [("x", four_bit_rc), ("y", four_bit_rc)] .into_iter() .collect::>(); // The RC of x is not tight enough @@ -640,13 +636,9 @@ l3 - 1 = 0" #[test] fn split_fail_not_unique() { let four_bit_rc = RangeConstraint::from_mask(0xfu32); - let rcs = [ - ("x", four_bit_rc.clone()), - ("y", four_bit_rc.clone()), - ("z", four_bit_rc.clone()), - ] - .into_iter() - .collect::>(); + let rcs = [("x", four_bit_rc), ("y", four_bit_rc), ("z", four_bit_rc)] + .into_iter() + .collect::>(); // There are multiple ways to solve the modulo equation. let expr = (var("x") - var("y")) + constant(16) * var("z") - constant(1); assert!(try_split(expr, &rcs).is_none()); diff --git a/constraint-solver/src/symbolic_expression.rs b/constraint-solver/src/symbolic_expression.rs index 81851e7bb9..af5c024b35 100644 --- a/constraint-solver/src/symbolic_expression.rs +++ b/constraint-solver/src/symbolic_expression.rs @@ -173,21 +173,21 @@ impl VarTransformable Some(match self { SymbolicExpression::Concrete(n) => SymbolicExpression::Concrete(*n), SymbolicExpression::Symbol(v, rc) => { - SymbolicExpression::from_symbol(var_transform(v)?, rc.clone()) + SymbolicExpression::from_symbol(var_transform(v)?, *rc) } SymbolicExpression::BinaryOperation(lhs, op, rhs, rc) => { SymbolicExpression::BinaryOperation( Arc::new(lhs.try_transform_var_type(var_transform)?), *op, Arc::new(rhs.try_transform_var_type(var_transform)?), - rc.clone(), + *rc, ) } SymbolicExpression::UnaryOperation(op, inner, rc) => { SymbolicExpression::UnaryOperation( *op, Arc::new(inner.try_transform_var_type(var_transform)?), - rc.clone(), + *rc, ) } }) @@ -449,7 +449,7 @@ impl RuntimeConstant for SymbolicExpression RangeConstraint::from_value(*v), SymbolicExpression::Symbol(.., rc) | SymbolicExpression::BinaryOperation(.., rc) - | SymbolicExpression::UnaryOperation(.., rc) => rc.clone(), + | SymbolicExpression::UnaryOperation(.., rc) => *rc, } } diff --git a/constraint-solver/tests/solver.rs b/constraint-solver/tests/solver.rs index a6c45863d3..f426ea72e7 100644 --- a/constraint-solver/tests/solver.rs +++ b/constraint-solver/tests/solver.rs @@ -155,8 +155,8 @@ impl BusInteractionHandler for TestBusInteractionHandler { ^ b.to_integer().try_into_u64().unwrap(), ); vec![ - bus_interaction.payload[0].clone(), - bus_interaction.payload[1].clone(), + bus_interaction.payload[0], + bus_interaction.payload[1], RangeConstraint::from_value(result), ] } else { diff --git a/openvm/src/bus_interaction_handler/bitwise_lookup.rs b/openvm/src/bus_interaction_handler/bitwise_lookup.rs index 86d11c0355..b3dab101f2 100644 --- a/openvm/src/bus_interaction_handler/bitwise_lookup.rs +++ b/openvm/src/bus_interaction_handler/bitwise_lookup.rs @@ -80,9 +80,9 @@ pub fn bitwise_lookup_pure_range_constraints( data.iter().map(|_| byte_constraint()).collect::>() }; - [address_space.clone(), pointer.clone()] + [*address_space, *pointer] .into_iter() .chain(data) - .chain(std::iter::once(timestamp.clone())) + .chain(std::iter::once(*timestamp)) .collect() } // Otherwise, we can't improve the constraints diff --git a/openvm/src/bus_interaction_handler/variable_range_checker.rs b/openvm/src/bus_interaction_handler/variable_range_checker.rs index e5ecf369c3..771e16b5f1 100644 --- a/openvm/src/bus_interaction_handler/variable_range_checker.rs +++ b/openvm/src/bus_interaction_handler/variable_range_checker.rs @@ -22,7 +22,7 @@ pub fn handle_variable_range_checker( Some(bits_value) if bits_value.to_degree() <= MAX_BITS => { let bits_value = bits_value.to_integer().try_into_u64().unwrap(); let mask = (1u64 << bits_value) - 1; - vec![RangeConstraint::from_mask(mask), bits.clone()] + vec![RangeConstraint::from_mask(mask), *bits] } _ => { vec![ From bd0930f62a04a083e7992704539ed50d625ec142 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 19 Nov 2025 15:38:11 +0000 Subject: [PATCH 19/60] Cleanup. --- autoprecompiles/src/rule_based_optimizer.rs | 46 ++++++++++----------- constraint-solver/src/grouped_expression.rs | 2 +- constraint-solver/src/solver/base.rs | 2 +- 3 files changed, 23 insertions(+), 27 deletions(-) diff --git a/autoprecompiles/src/rule_based_optimizer.rs b/autoprecompiles/src/rule_based_optimizer.rs index 031fe7086d..8c4080bd43 100644 --- a/autoprecompiles/src/rule_based_optimizer.rs +++ b/autoprecompiles/src/rule_based_optimizer.rs @@ -8,7 +8,7 @@ use std::{ ops::Index, }; -use itertools::{EitherOrBoth, Itertools}; +use itertools::Itertools; use powdr_constraint_solver::{ constraint_system::{BusInteraction, BusInteractionHandler}, grouped_expression::{GroupedExpression, GroupedExpressionComponent, NoRangeConstraints}, @@ -18,13 +18,9 @@ use powdr_constraint_solver::{ }; use powdr_number::{BabyBearField, FieldElement, LargeInt}; -use num_traits::One; -use num_traits::Zero; +use num_traits::{One, Zero}; use crepe::crepe; -use slab::Slab; - -use crate::range_constraint_optimizer::RangeConstraintHandler; type F = BabyBearField; @@ -33,7 +29,7 @@ struct Var(u32); impl Display for Var { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "v{}", self.0) + write!(f, "v_{}", self.0) } } @@ -67,7 +63,7 @@ impl Default for System { } impl PartialEq for System { - fn eq(&self, other: &Self) -> bool { + fn eq(&self, _other: &Self) -> bool { // TODO change this as soon as we have different systems true } @@ -76,14 +72,14 @@ impl PartialEq for System { impl Eq for System {} impl PartialOrd for System { - fn partial_cmp(&self, other: &Self) -> Option { + fn partial_cmp(&self, _other: &Self) -> Option { // TODO change this as soon as we have different systems Some(std::cmp::Ordering::Equal) } } impl Ord for System { - fn cmp(&self, other: &Self) -> std::cmp::Ordering { + fn cmp(&self, _other: &Self) -> std::cmp::Ordering { // TODO change this as soon as we have different systems std::cmp::Ordering::Equal } @@ -101,10 +97,6 @@ impl System { assert!(self.var_to_string.is_none()); self.var_to_string = Some(mapping); } - // pub fn get(&self, expr: Expr) -> &GroupedExpression { - // let b = self.expressions.borrow(); - // b.get(expr.0).unwrap() - // } pub fn insert(&self, expr: &GroupedExpression) -> Expr { let mut db = self.expressions.borrow_mut(); @@ -151,7 +143,7 @@ impl System { } // TODO potentially make this a more generic "matches structure" function - pub fn try_to_simple_quadratic(&self, expr: Expr) -> Option<(Expr, Expr)> { + pub fn try_as_single_product(&self, expr: Expr) -> Option<(Expr, Expr)> { let (l, r) = { let db = self.expressions.borrow(); let (l, r) = db[expr].try_as_single_product()?; @@ -163,7 +155,8 @@ impl System { Some((self.insert(&l), self.insert(&r))) } - pub fn try_to_simple_var(&self, expr: Expr) -> Option { + #[allow(dead_code)] + pub fn try_as_single_var(&self, expr: Expr) -> Option { let db = self.expressions.borrow(); db[expr].try_to_simple_unknown() } @@ -181,8 +174,10 @@ impl System { .then(|| *a.constant_offset() - *b.constant_offset()) } - /// Checks if a and b are affine constraints that differ in exactly one variable. - /// TODO scaling + /// If this returns `Some((v1, v2, coeff))`, then `a` and `b` are affine expressions + /// such that `b` is obtained from `a` when replacing `v1` by `v2` and + /// `coeff` is the coefficient of `v1` in `a` (and also of `v2` in `b`) + /// also `a` and `b` have at least two variables each. pub fn differ_in_exactly_one_variable(&self, a_id: Expr, b_id: Expr) -> Option<(Var, Var, F)> { let db = self.expressions.borrow(); let a = &db[a_id]; @@ -190,7 +185,6 @@ impl System { if !a.is_affine() || !b.is_affine() || a.referenced_unknown_variables().count() != b.referenced_unknown_variables().count() - // TODO this is not in the docstring || a.referenced_unknown_variables().count() < 2 { return None; @@ -303,7 +297,7 @@ crepe! { // RC(coeff * var + offset) = rc <=> // coeff * RC(var) + offset = rc <=> // RC(var) = (rc - offset) / coeff - RangeConstraintOnVar(v, rc.combine_sum(&RangeConstraint::from_value(offset)).multiple(F::one() / coeff)) <- + RangeConstraintOnVar(v, rc.combine_sum(&RangeConstraint::from_value(-offset)).multiple(F::one() / coeff)) <- RangeConstraintOnExpression(e, rc), AffineExpression(e, coeff, v, offset), (coeff != F::zero()); @@ -316,7 +310,7 @@ crepe! { Product(e, l, r) <- Expression(e), S(sys), - let Some((l, r)) = sys.try_to_simple_quadratic(e); + let Some((l, r)) = sys.try_as_single_product(e); Product(e, r, l) <- Product(e, l, r); // (E, expr, offset) <-> E = (expr) * (expr + offset) is a constraint @@ -508,11 +502,13 @@ pub fn rule_based_optimization>(); result.extend(expr.linear_components().map(|(v, c)| { @@ -607,7 +603,7 @@ fn extract_single_vars( expr.clone(), *c, *v, - expr.clone() - GroupedExpression::from_unknown_variable(v.clone()) * (*c), + expr.clone() - GroupedExpression::from_unknown_variable(*v) * (*c), ) })); result diff --git a/constraint-solver/src/grouped_expression.rs b/constraint-solver/src/grouped_expression.rs index 47b75b3ab2..d915f3a2b5 100644 --- a/constraint-solver/src/grouped_expression.rs +++ b/constraint-solver/src/grouped_expression.rs @@ -307,7 +307,7 @@ impl GroupedExpression { r.substitute_simple(variable, substitution); match (l.try_to_known(), r.try_to_known()) { (Some(l), Some(r)) => { - self.constant += l.clone() * r.clone(); + self.constant += *l * *r; false } (Some(l), None) => { diff --git a/constraint-solver/src/solver/base.rs b/constraint-solver/src/solver/base.rs index 5593a4ec31..dcab5fa227 100644 --- a/constraint-solver/src/solver/base.rs +++ b/constraint-solver/src/solver/base.rs @@ -12,7 +12,7 @@ use crate::solver::boolean_extractor::BooleanExtractor; use crate::solver::constraint_splitter::try_split_constraint; use crate::solver::linearizer::Linearizer; use crate::solver::var_transformation::Variable; -use crate::solver::{exhaustive_search, quadratic_equivalences, Error, Solver, VariableAssignment}; +use crate::solver::{exhaustive_search, Error, Solver, VariableAssignment}; use crate::utils::possible_concrete_values; use std::collections::{BTreeSet, HashMap, HashSet}; From 1dca1dc5784c0777894f62bc91f4ad138f7fcfd7 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 19 Nov 2025 15:58:49 +0000 Subject: [PATCH 20/60] Improve var diff check. --- autoprecompiles/src/rule_based_optimizer.rs | 35 ++++++++++++--------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/autoprecompiles/src/rule_based_optimizer.rs b/autoprecompiles/src/rule_based_optimizer.rs index 8c4080bd43..fb0479680d 100644 --- a/autoprecompiles/src/rule_based_optimizer.rs +++ b/autoprecompiles/src/rule_based_optimizer.rs @@ -8,7 +8,7 @@ use std::{ ops::Index, }; -use itertools::Itertools; +use itertools::{EitherOrBoth, Itertools}; use powdr_constraint_solver::{ constraint_system::{BusInteraction, BusInteractionHandler}, grouped_expression::{GroupedExpression, GroupedExpressionComponent, NoRangeConstraints}, @@ -192,22 +192,29 @@ impl System { if a.constant_offset() != b.constant_offset() { return None; } - // TODO use merge_join_by? (avoid creating the HashSet) - let left_vars = a + let mut joined = a .linear_components() - .map(|(v, _)| *v) - .collect::>(); - let right_vars = b - .linear_components() - .map(|(v, _)| *v) - .collect::>(); - let left_var = left_vars.difference(&right_vars).exactly_one().ok()?; - let right_var = right_vars.difference(&left_vars).exactly_one().ok()?; - let coeff = *a.coefficient_of_variable_in_affine_part(left_var).unwrap(); - if coeff != *b.coefficient_of_variable_in_affine_part(right_var).unwrap() { + // Join the sorted iterators into another sorted list, + // noting where the items came from. + .merge_join_by(b.linear_components(), Ord::cmp) + // Remove those that are equal in both iterators. + .filter(|either| !matches!(either, EitherOrBoth::Both(_, _))); + let first_diff = joined.next()?; + let second_diff = joined.next()?; + if joined.next() != None { return None; } - Some((*left_var, *right_var, coeff)) + let (left_var, right_var, coeff) = match (first_diff, second_diff) { + (EitherOrBoth::Left((lv, lc)), EitherOrBoth::Right((rv, rc))) + | (EitherOrBoth::Right((rv, rc)), EitherOrBoth::Left((lv, lc))) => { + if lc != rc { + return None; + } + (*lv, *rv, *lc) + } + _ => return None, + }; + Some((left_var, right_var, coeff)) } pub fn substitute_by_known(&self, e: Expr, var: Var, value: F) -> Expr { From 6321c415dbb30de030c8e6331a91683d13e4109f Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 19 Nov 2025 16:16:45 +0000 Subject: [PATCH 21/60] Add some tests. --- autoprecompiles/Cargo.toml | 3 + autoprecompiles/src/rule_based_optimizer.rs | 73 ++++++++++++------- constraint-solver/src/constraint_system.rs | 2 +- .../src/solver/quadratic_equivalences.rs | 1 + 4 files changed, 53 insertions(+), 26 deletions(-) diff --git a/autoprecompiles/Cargo.toml b/autoprecompiles/Cargo.toml index fa7e8f6725..89f0e79fe4 100644 --- a/autoprecompiles/Cargo.toml +++ b/autoprecompiles/Cargo.toml @@ -25,6 +25,9 @@ strum = { version = "0.27.0", features = ["derive"] } metrics = "0.23.0" crepe = "0.1.8" +[dev-dependencies] +expect-test = "1.5.1" + [package.metadata.cargo-udeps.ignore] development = ["env_logger"] diff --git a/autoprecompiles/src/rule_based_optimizer.rs b/autoprecompiles/src/rule_based_optimizer.rs index fb0479680d..6d2a819a32 100644 --- a/autoprecompiles/src/rule_based_optimizer.rs +++ b/autoprecompiles/src/rule_based_optimizer.rs @@ -1,8 +1,6 @@ -#![allow(clippy::iter_over_hash_type)] -#![allow(for_loops_over_fallibles)] use std::{ cell::RefCell, - collections::{BTreeSet, HashMap, HashSet}, + collections::{BTreeSet, HashMap}, fmt::Display, hash::Hash, ops::Index, @@ -228,6 +226,7 @@ impl System { self.insert_owned(expr) } + #[allow(dead_code)] pub fn substitute_by_var(&self, e: Expr, var: Var, replacement: Var) -> Expr { let expr = { let db = self.expressions.borrow(); @@ -241,6 +240,7 @@ impl System { self.insert_owned(expr) } + #[allow(dead_code)] pub fn format_expr(&self, expr: Expr) -> String { let db = self.expressions.borrow(); if let Some(var_to_string) = &self.var_to_string { @@ -252,6 +252,7 @@ impl System { } } + #[allow(dead_code)] pub fn format_var(&self, var: Var) -> String { if let Some(var_to_string) = &self.var_to_string { var_to_string @@ -592,26 +593,48 @@ fn transform_grouped_expression, -) -> HashSet<(GroupedExpression, F, Var, GroupedExpression)> { - let mut result = expr - .quadratic_components() - .iter() - .flat_map(|(l, r)| { - extract_single_vars(l) - .into_iter() - .chain(extract_single_vars(r)) - }) - .collect::>(); - result.extend(expr.linear_components().map(|(v, c)| { - ( - expr.clone(), - *c, - *v, - expr.clone() - GroupedExpression::from_unknown_variable(*v) * (*c), - ) - })); - result +#[cfg(test)] +mod tests { + use expect_test::expect; + use powdr_constraint_solver::{ + algebraic_constraint, constraint_system::DefaultBusInteractionHandler, + }; + + use super::*; + + fn assert_zero( + expr: GroupedExpression, + ) -> algebraic_constraint::AlgebraicConstraint> { + algebraic_constraint::AlgebraicConstraint::assert_zero(expr) + } + + fn var(name: &str) -> GroupedExpression { + GroupedExpression::from_unknown_variable(name.to_string()) + } + + fn constant(value: i64) -> GroupedExpression { + GroupedExpression::from_number(BabyBearField::from(value)) + } + + #[test] + fn test_rule_based_optimization_empty() { + let system: IndexedConstraintSystem = + IndexedConstraintSystem::default(); + let optimized_system = + rule_based_optimization(system, DefaultBusInteractionHandler::default()); + assert_eq!(optimized_system.system().algebraic_constraints.len(), 0); + } + + #[test] + fn test_rule_based_optimization_simple_assignment() { + let mut system = IndexedConstraintSystem::default(); + let x = var("x"); + system.add_algebraic_constraints([ + assert_zero(x * F::from(7) - constant(21)), + assert_zero(var("y") * (var("y") - constant(1)) - var("x")), + ]); + let optimized_system = + rule_based_optimization(system, DefaultBusInteractionHandler::default()); + expect!["(y) * (y - 1) - 3 = 0"].assert_eq(&optimized_system.to_string()); + } } diff --git a/constraint-solver/src/constraint_system.rs b/constraint-solver/src/constraint_system.rs index 3c3a76ebbe..af8a8d04c1 100644 --- a/constraint-solver/src/constraint_system.rs +++ b/constraint-solver/src/constraint_system.rs @@ -310,7 +310,7 @@ pub trait BusInteractionHandler { /// A default bus interaction handler that does nothing. Using it is /// equivalent to ignoring bus interactions. -#[derive(Default)] +#[derive(Default, Clone)] pub struct DefaultBusInteractionHandler { _marker: std::marker::PhantomData, } diff --git a/constraint-solver/src/solver/quadratic_equivalences.rs b/constraint-solver/src/solver/quadratic_equivalences.rs index 4df24ea93a..6833ed253b 100644 --- a/constraint-solver/src/solver/quadratic_equivalences.rs +++ b/constraint-solver/src/solver/quadratic_equivalences.rs @@ -12,6 +12,7 @@ use crate::{ }; /// Given a list of constraints, tries to determine pairs of equivalent variables. +#[allow(dead_code)] pub fn find_quadratic_equalities( constraints: &[AlgebraicConstraint>], range_constraints: impl RangeConstraintProvider, From 671fda3385f1fbb1fddce50c2491f82c12fe5166 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 19 Nov 2025 16:20:59 +0000 Subject: [PATCH 22/60] Fix warnings. --- autoprecompiles/src/rule_based_optimizer.rs | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/autoprecompiles/src/rule_based_optimizer.rs b/autoprecompiles/src/rule_based_optimizer.rs index 6d2a819a32..4b2bdca08e 100644 --- a/autoprecompiles/src/rule_based_optimizer.rs +++ b/autoprecompiles/src/rule_based_optimizer.rs @@ -1,3 +1,7 @@ +#![allow(clippy::iter_over_hash_type)] +// This is about a warning about interior mutability for the key +// `S`. We need it and it is probably fine. +#![allow(clippy::mutable_key_type)] use std::{ cell::RefCell, collections::{BTreeSet, HashMap}, @@ -70,9 +74,8 @@ impl PartialEq for System { impl Eq for System {} impl PartialOrd for System { - fn partial_cmp(&self, _other: &Self) -> Option { - // TODO change this as soon as we have different systems - Some(std::cmp::Ordering::Equal) + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) } } @@ -199,7 +202,7 @@ impl System { .filter(|either| !matches!(either, EitherOrBoth::Both(_, _))); let first_diff = joined.next()?; let second_diff = joined.next()?; - if joined.next() != None { + if joined.next().is_some() { return None; } let (left_var, right_var, coeff) = match (first_diff, second_diff) { @@ -488,10 +491,10 @@ pub fn rule_based_optimization Date: Wed, 19 Nov 2025 17:05:17 +0000 Subject: [PATCH 23/60] Test. --- autoprecompiles/src/rule_based_optimizer.rs | 125 ++++++++++++++++++ constraint-solver/src/solver/base.rs | 20 +-- .../src/solver/quadratic_equivalences.rs | 2 + 3 files changed, 137 insertions(+), 10 deletions(-) diff --git a/autoprecompiles/src/rule_based_optimizer.rs b/autoprecompiles/src/rule_based_optimizer.rs index 4b2bdca08e..2b129c781f 100644 --- a/autoprecompiles/src/rule_based_optimizer.rs +++ b/autoprecompiles/src/rule_based_optimizer.rs @@ -404,6 +404,7 @@ pub fn rule_based_optimization( + payload: &[RangeConstraint], + ) -> Vec> { + const MAX_BITS: u64 = 25; + // See: https://github.com/openvm-org/openvm/blob/v1.0.0/crates/circuits/primitives/src/var_range/bus.rs + // Expects (x, bits), where `x` is in the range [0, 2^bits - 1] + let [_x, bits] = payload else { + panic!("Expected arguments (x, bits)"); + }; + match bits.try_to_single_value() { + Some(bits_value) if bits_value.to_degree() <= MAX_BITS => { + let bits_value = bits_value.to_integer().try_into_u64().unwrap(); + let mask = (1u64 << bits_value) - 1; + vec![RangeConstraint::from_mask(mask), *bits] + } + _ => { + vec![ + RangeConstraint::from_mask((1u64 << MAX_BITS) - 1), + RangeConstraint::from_range(T::from(0), T::from(MAX_BITS)), + ] + } + } + } + + fn try_handle_bus_interaction( + bus_interaction: &BusInteraction>, + ) -> Option>> { + let mult = bus_interaction.multiplicity.try_to_single_value()?; + if mult == Zero::zero() { + return None; + } + let bus_id = bus_interaction + .bus_id + .try_to_single_value()? + .to_integer() + .try_into_u64()?; + let payload_constraints = match bus_id { + 3 => handle_variable_range_checker(&bus_interaction.payload), + _ => return None, + }; + Some(BusInteraction { + payload: payload_constraints, + ..bus_interaction.clone() + }) + } + + #[derive(Clone)] + struct TestBusInteractionHandler; + + impl BusInteractionHandler for TestBusInteractionHandler { + fn handle_bus_interaction( + &self, + bus_interaction: BusInteraction>, + ) -> BusInteraction> { + try_handle_bus_interaction(&bus_interaction).unwrap_or(bus_interaction) + } + } + + fn bit_constraint( + variable: &str, + bits: u32, + ) -> BusInteraction> { + BusInteraction { + bus_id: constant(3), + payload: vec![var(variable), constant(bits as i64)], + multiplicity: constant(1), + } + } + #[test] fn test_rule_based_optimization_empty() { let system: IndexedConstraintSystem = @@ -640,4 +711,58 @@ mod tests { rule_based_optimization(system, DefaultBusInteractionHandler::default()); expect!["(y) * (y - 1) - 3 = 0"].assert_eq(&optimized_system.to_string()); } + + #[test] + fn test_rule_based_optimization_quadratic_equality() { + let mut system = IndexedConstraintSystem::default(); + system.add_algebraic_constraints([ + assert_zero( + (constant(30720) * var("rs1_data__0_1") + constant(7864320) * var("rs1_data__1_1") + - constant(30720) * var("mem_ptr_limbs__0_1") + + constant(737280)) + * (constant(30720) * var("rs1_data__0_1") + + constant(7864320) * var("rs1_data__1_1") + - constant(30720) * var("mem_ptr_limbs__0_1") + + constant(737281)), + ), + assert_zero( + (constant(30720) * var("rs1_data__0_1") + constant(7864320) * var("rs1_data__1_1") + - constant(30720) * var("mem_ptr_limbs__0_2") + + constant(737280)) + * (constant(30720) * var("rs1_data__0_1") + + constant(7864320) * var("rs1_data__1_1") + - constant(30720) * var("mem_ptr_limbs__0_2") + + constant(737281)), + ), + ]); + system.add_bus_interactions([ + bit_constraint("rs1_data__0_1", 8), + bit_constraint("rs1_data__1_1", 8), + BusInteraction { + bus_id: constant(3), + multiplicity: constant(1), + payload: vec![ + constant(-503316480) * var("mem_ptr_limbs__0_1"), + constant(14), + ], + }, + BusInteraction { + bus_id: constant(3), + multiplicity: constant(1), + payload: vec![ + constant(-503316480) * var("mem_ptr_limbs__0_2"), + constant(14), + ], + }, + ]); + let optimized_system = rule_based_optimization(system, TestBusInteractionHandler); + // Note that in the system below, mem_ptr_limbs__0_2 has been eliminated + expect![[r#" + (30720 * mem_ptr_limbs__0_1 - 30720 * rs1_data__0_1 - 7864320 * rs1_data__1_1 - 737280) * (30720 * mem_ptr_limbs__0_1 - 30720 * rs1_data__0_1 - 7864320 * rs1_data__1_1 - 737281) = 0 + (30720 * mem_ptr_limbs__0_1 - 30720 * rs1_data__0_1 - 7864320 * rs1_data__1_1 - 737280) * (30720 * mem_ptr_limbs__0_1 - 30720 * rs1_data__0_1 - 7864320 * rs1_data__1_1 - 737281) = 0 + BusInteraction { bus_id: 3, multiplicity: 1, payload: rs1_data__0_1, 8 } + BusInteraction { bus_id: 3, multiplicity: 1, payload: rs1_data__1_1, 8 } + BusInteraction { bus_id: 3, multiplicity: 1, payload: -(503316480 * mem_ptr_limbs__0_1), 14 } + BusInteraction { bus_id: 3, multiplicity: 1, payload: -(503316480 * mem_ptr_limbs__0_1), 14 }"#]].assert_eq(&optimized_system.to_string()); + } } diff --git a/constraint-solver/src/solver/base.rs b/constraint-solver/src/solver/base.rs index dcab5fa227..b5e2231d89 100644 --- a/constraint-solver/src/solver/base.rs +++ b/constraint-solver/src/solver/base.rs @@ -12,7 +12,7 @@ use crate::solver::boolean_extractor::BooleanExtractor; use crate::solver::constraint_splitter::try_split_constraint; use crate::solver::linearizer::Linearizer; use crate::solver::var_transformation::Variable; -use crate::solver::{exhaustive_search, Error, Solver, VariableAssignment}; +use crate::solver::{exhaustive_search, quadratic_equivalences, Error, Solver, VariableAssignment}; use crate::utils::possible_concrete_values; use std::collections::{BTreeSet, HashMap, HashSet}; @@ -374,15 +374,15 @@ where /// Tries to find equivalent variables using quadratic constraints. fn try_solve_quadratic_equivalences(&mut self) -> bool { - false - // let equivalences = quadratic_equivalences::find_quadratic_equalities( - // self.constraint_system.system().algebraic_constraints(), - // &*self, - // ); - // for (x, y) in &equivalences { - // self.apply_assignment(y, &GroupedExpression::from_unknown_variable(x.clone())); - // } - // !equivalences.is_empty() + // false + let equivalences = quadratic_equivalences::find_quadratic_equalities( + self.constraint_system.system().algebraic_constraints(), + &*self, + ); + for (x, y) in &equivalences { + self.apply_assignment(y, &GroupedExpression::from_unknown_variable(x.clone())); + } + !equivalences.is_empty() } /// Find groups of variables with a small set of possible assignments. diff --git a/constraint-solver/src/solver/quadratic_equivalences.rs b/constraint-solver/src/solver/quadratic_equivalences.rs index 6833ed253b..bf299716d4 100644 --- a/constraint-solver/src/solver/quadratic_equivalences.rs +++ b/constraint-solver/src/solver/quadratic_equivalences.rs @@ -80,6 +80,8 @@ fn process_quadratic_equality_candidate_pair< return None; } + println!("XXX {c1_var}, {c2_var},\n{}\n{}", c1.expr, c2.expr); + // Now we have `(X + A) * (X + A + offset) = 0` and `(Y + A) * (Y + A + offset) = 0` // Furthermore, the range constraints of `X` and `Y` are such that for both identities, // the two alternatives can never be satisfied at the same time. Since both variables From 25f270d60b969f898afdd62ff336ba0cafe1a2b3 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 19 Nov 2025 17:55:29 +0000 Subject: [PATCH 24/60] Provide range constraints from the start. --- autoprecompiles/src/constraint_optimizer.rs | 2 +- autoprecompiles/src/rule_based_optimizer.rs | 150 ++++++++++-------- .../src/solver/quadratic_equivalences.rs | 3 +- 3 files changed, 87 insertions(+), 68 deletions(-) diff --git a/autoprecompiles/src/constraint_optimizer.rs b/autoprecompiles/src/constraint_optimizer.rs index 2a35af9a14..abef421528 100644 --- a/autoprecompiles/src/constraint_optimizer.rs +++ b/autoprecompiles/src/constraint_optimizer.rs @@ -63,7 +63,7 @@ pub fn optimize_constraints< let constraint_system = IndexedConstraintSystem::from(constraint_system); let constraint_system = - rule_based_optimization(constraint_system, bus_interaction_handler.clone()); + rule_based_optimization(constraint_system, &*solver, bus_interaction_handler.clone()); stats_logger.log("rule-based optimization", &constraint_system); let constraint_system = solver_based_optimization(constraint_system, solver)?; diff --git a/autoprecompiles/src/rule_based_optimizer.rs b/autoprecompiles/src/rule_based_optimizer.rs index 2b129c781f..7371833aea 100644 --- a/autoprecompiles/src/rule_based_optimizer.rs +++ b/autoprecompiles/src/rule_based_optimizer.rs @@ -13,7 +13,9 @@ use std::{ use itertools::{EitherOrBoth, Itertools}; use powdr_constraint_solver::{ constraint_system::{BusInteraction, BusInteractionHandler}, - grouped_expression::{GroupedExpression, GroupedExpressionComponent, NoRangeConstraints}, + grouped_expression::{ + GroupedExpression, GroupedExpressionComponent, NoRangeConstraints, RangeConstraintProvider, + }, indexed_constraint_system::IndexedConstraintSystem, range_constraint::RangeConstraint, runtime_constant::VarTransformable, @@ -278,6 +280,9 @@ crepe! { @input struct InitialAlgebraicConstraint(Expr); + @input + struct InitialRangeConstraintOnVar(Var, RangeConstraint); + struct AlgebraicConstraint(Expr); AlgebraicConstraint(e) <- InitialAlgebraicConstraint(e); @@ -305,6 +310,7 @@ crepe! { @output struct RangeConstraintOnVar(Var, RangeConstraint); + RangeConstraintOnVar(v, rc) <- InitialRangeConstraintOnVar(v, rc); // RC(coeff * var + offset) = rc <=> // coeff * RC(var) + offset = rc <=> // RC(var) = (rc - offset) / coeff @@ -374,11 +380,11 @@ crepe! { AlgebraicConstraint(e), ContainsVariable(e, v), Assignment(v, val); - // ReplaceAlgebraicConstraintBy(e, sys.substitute_by_var(e, v, v2)) <- - // S(sys), - // AlgebraicConstraint(e), - // ContainsVariable(e, v), - // Equivalence(v, v2); + ReplaceAlgebraicConstraintBy(e, sys.substitute_by_var(e, v, v2)) <- + S(sys), + AlgebraicConstraint(e), + ContainsVariable(e, v), + Equivalence(v, v2); AlgebraicConstraint(e) <- ReplaceAlgebraicConstraintBy(_, e); @@ -399,12 +405,13 @@ crepe! { pub fn rule_based_optimization( mut system: IndexedConstraintSystem, + range_constraints: impl RangeConstraintProvider, bus_interaction_handler: impl BusInteractionHandler + Clone, ) -> IndexedConstraintSystem { if T::modulus().to_arbitrary_integer() != BabyBearField::modulus().to_arbitrary_integer() { return system; } - println!("{system}"); + // println!("{system}"); let start = std::time::Instant::now(); let mut rt = Crepe::new(); @@ -430,10 +437,18 @@ pub fn rule_based_optimization( + rc: &RangeConstraint, +) -> RangeConstraint { + let (min, max) = rc.range(); + let mask = *rc.mask(); + RangeConstraint::from_range( + BabyBearField::from(min.to_arbitrary_integer()), + BabyBearField::from(max.to_arbitrary_integer()), + ) + .conjunction(&RangeConstraint::from_mask(mask.try_into_u64().unwrap())) +} + #[cfg(test)] mod tests { use expect_test::expect; use powdr_constraint_solver::{ algebraic_constraint, constraint_system::DefaultBusInteractionHandler, - runtime_constant::RuntimeConstant, }; use super::*; @@ -613,11 +632,11 @@ mod tests { algebraic_constraint::AlgebraicConstraint::assert_zero(expr) } - fn var(name: &str) -> GroupedExpression { + fn v(name: &str) -> GroupedExpression { GroupedExpression::from_unknown_variable(name.to_string()) } - fn constant(value: i64) -> GroupedExpression { + fn c(value: i64) -> GroupedExpression { GroupedExpression::from_number(BabyBearField::from(value)) } @@ -684,9 +703,9 @@ mod tests { bits: u32, ) -> BusInteraction> { BusInteraction { - bus_id: constant(3), - payload: vec![var(variable), constant(bits as i64)], - multiplicity: constant(1), + bus_id: c(3), + payload: vec![v(variable), c(bits as i64)], + multiplicity: c(1), } } @@ -694,21 +713,27 @@ mod tests { fn test_rule_based_optimization_empty() { let system: IndexedConstraintSystem = IndexedConstraintSystem::default(); - let optimized_system = - rule_based_optimization(system, DefaultBusInteractionHandler::default()); + let optimized_system = rule_based_optimization( + system, + NoRangeConstraints, + DefaultBusInteractionHandler::default(), + ); assert_eq!(optimized_system.system().algebraic_constraints.len(), 0); } #[test] fn test_rule_based_optimization_simple_assignment() { let mut system = IndexedConstraintSystem::default(); - let x = var("x"); + let x = v("x"); system.add_algebraic_constraints([ - assert_zero(x * F::from(7) - constant(21)), - assert_zero(var("y") * (var("y") - constant(1)) - var("x")), + assert_zero(x * F::from(7) - c(21)), + assert_zero(v("y") * (v("y") - c(1)) - v("x")), ]); - let optimized_system = - rule_based_optimization(system, DefaultBusInteractionHandler::default()); + let optimized_system = rule_based_optimization( + system, + NoRangeConstraints, + DefaultBusInteractionHandler::default(), + ); expect!["(y) * (y - 1) - 3 = 0"].assert_eq(&optimized_system.to_string()); } @@ -717,45 +742,38 @@ mod tests { let mut system = IndexedConstraintSystem::default(); system.add_algebraic_constraints([ assert_zero( - (constant(30720) * var("rs1_data__0_1") + constant(7864320) * var("rs1_data__1_1") - - constant(30720) * var("mem_ptr_limbs__0_1") - + constant(737280)) - * (constant(30720) * var("rs1_data__0_1") - + constant(7864320) * var("rs1_data__1_1") - - constant(30720) * var("mem_ptr_limbs__0_1") - + constant(737281)), + (c(30720) * v("rs1_data__0_1") + c(7864320) * v("rs1_data__1_1") + - c(30720) * v("mem_ptr_limbs__0_1") + + c(737280)) + * (c(30720) * v("rs1_data__0_1") + c(7864320) * v("rs1_data__1_1") + - c(30720) * v("mem_ptr_limbs__0_1") + + c(737281)), ), assert_zero( - (constant(30720) * var("rs1_data__0_1") + constant(7864320) * var("rs1_data__1_1") - - constant(30720) * var("mem_ptr_limbs__0_2") - + constant(737280)) - * (constant(30720) * var("rs1_data__0_1") - + constant(7864320) * var("rs1_data__1_1") - - constant(30720) * var("mem_ptr_limbs__0_2") - + constant(737281)), + (c(30720) * v("rs1_data__0_1") + c(7864320) * v("rs1_data__1_1") + - c(30720) * v("mem_ptr_limbs__0_2") + + c(737280)) + * (c(30720) * v("rs1_data__0_1") + c(7864320) * v("rs1_data__1_1") + - c(30720) * v("mem_ptr_limbs__0_2") + + c(737281)), ), ]); system.add_bus_interactions([ bit_constraint("rs1_data__0_1", 8), bit_constraint("rs1_data__1_1", 8), BusInteraction { - bus_id: constant(3), - multiplicity: constant(1), - payload: vec![ - constant(-503316480) * var("mem_ptr_limbs__0_1"), - constant(14), - ], + bus_id: c(3), + multiplicity: c(1), + payload: vec![c(-503316480) * v("mem_ptr_limbs__0_1"), c(14)], }, BusInteraction { - bus_id: constant(3), - multiplicity: constant(1), - payload: vec![ - constant(-503316480) * var("mem_ptr_limbs__0_2"), - constant(14), - ], + bus_id: c(3), + multiplicity: c(1), + payload: vec![c(-503316480) * v("mem_ptr_limbs__0_2"), c(14)], }, ]); - let optimized_system = rule_based_optimization(system, TestBusInteractionHandler); + let optimized_system = + rule_based_optimization(system, NoRangeConstraints, TestBusInteractionHandler); // Note that in the system below, mem_ptr_limbs__0_2 has been eliminated expect![[r#" (30720 * mem_ptr_limbs__0_1 - 30720 * rs1_data__0_1 - 7864320 * rs1_data__1_1 - 737280) * (30720 * mem_ptr_limbs__0_1 - 30720 * rs1_data__0_1 - 7864320 * rs1_data__1_1 - 737281) = 0 diff --git a/constraint-solver/src/solver/quadratic_equivalences.rs b/constraint-solver/src/solver/quadratic_equivalences.rs index bf299716d4..1848eaeecf 100644 --- a/constraint-solver/src/solver/quadratic_equivalences.rs +++ b/constraint-solver/src/solver/quadratic_equivalences.rs @@ -90,7 +90,8 @@ fn process_quadratic_equality_candidate_pair< // - X = -A - offset and Y = -A - offset // Since `A` has to have some value, we can conclude `X = Y`. - Some((c1_var.clone(), c2_var.clone())) + None + // Some((c1_var.clone(), c2_var.clone())) } /// This represents an identity `expr * (expr + offset) = 0`, From a2d765a5737f1de7141064d198074e95b1522ae9 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 19 Nov 2025 18:00:24 +0000 Subject: [PATCH 25/60] fix compile. --- autoprecompiles/src/rule_based_optimizer.rs | 2 +- constraint-solver/src/solver/quadratic_equivalences.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/autoprecompiles/src/rule_based_optimizer.rs b/autoprecompiles/src/rule_based_optimizer.rs index 7371833aea..b3eb06ba0d 100644 --- a/autoprecompiles/src/rule_based_optimizer.rs +++ b/autoprecompiles/src/rule_based_optimizer.rs @@ -526,7 +526,7 @@ pub fn rule_based_optimization Date: Wed, 19 Nov 2025 18:02:51 +0000 Subject: [PATCH 26/60] Fix performance problem and re-enable quadratic equiv. --- autoprecompiles/src/rule_based_optimizer.rs | 10 +++++----- constraint-solver/src/solver/quadratic_equivalences.rs | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/autoprecompiles/src/rule_based_optimizer.rs b/autoprecompiles/src/rule_based_optimizer.rs index b3eb06ba0d..81572a19e3 100644 --- a/autoprecompiles/src/rule_based_optimizer.rs +++ b/autoprecompiles/src/rule_based_optimizer.rs @@ -380,11 +380,11 @@ crepe! { AlgebraicConstraint(e), ContainsVariable(e, v), Assignment(v, val); - ReplaceAlgebraicConstraintBy(e, sys.substitute_by_var(e, v, v2)) <- - S(sys), - AlgebraicConstraint(e), - ContainsVariable(e, v), - Equivalence(v, v2); + // ReplaceAlgebraicConstraintBy(e, sys.substitute_by_var(e, v, v2)) <- + // S(sys), + // AlgebraicConstraint(e), + // ContainsVariable(e, v), + // Equivalence(v, v2); AlgebraicConstraint(e) <- ReplaceAlgebraicConstraintBy(_, e); diff --git a/constraint-solver/src/solver/quadratic_equivalences.rs b/constraint-solver/src/solver/quadratic_equivalences.rs index 5c8779763c..81942214ee 100644 --- a/constraint-solver/src/solver/quadratic_equivalences.rs +++ b/constraint-solver/src/solver/quadratic_equivalences.rs @@ -90,8 +90,8 @@ fn process_quadratic_equality_candidate_pair< // - X = -A - offset and Y = -A - offset // Since `A` has to have some value, we can conclude `X = Y`. - None - // Some((c1_var.clone(), c2_var.clone())) + // None + Some((c1_var.clone(), c2_var.clone())) } /// This represents an identity `expr * (expr + offset) = 0`, From 3f82d4cc7526b29222a6ad47fb3a151f23e1c291 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 19 Nov 2025 22:10:44 +0000 Subject: [PATCH 27/60] Fix performance issue. --- autoprecompiles/src/rule_based_optimizer.rs | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/autoprecompiles/src/rule_based_optimizer.rs b/autoprecompiles/src/rule_based_optimizer.rs index 81572a19e3..07c53d869c 100644 --- a/autoprecompiles/src/rule_based_optimizer.rs +++ b/autoprecompiles/src/rule_based_optimizer.rs @@ -131,6 +131,12 @@ impl System { db[expr].referenced_unknown_variables().cloned().collect() } + pub fn affine_var_count(&self, expr: Expr) -> Option { + let db = self.expressions.borrow(); + let expr = &db[expr]; + expr.is_affine().then(|| expr.linear_components().count()) + } + pub fn try_to_affine(&self, expr: Expr) -> Option<(F, Var, F)> { let db = self.expressions.borrow(); let expr = &db[expr]; @@ -336,6 +342,7 @@ crepe! { AlgebraicConstraint(e), S(sys), Product(e, l, r), + ({sys.affine_var_count(l).unwrap_or(0) > 1 && sys.affine_var_count(r).unwrap_or(0) > 1}), let Some(offset) = sys.constant_difference(l, r); struct QuadraticEquivalence(Var, Var); @@ -380,11 +387,12 @@ crepe! { AlgebraicConstraint(e), ContainsVariable(e, v), Assignment(v, val); - // ReplaceAlgebraicConstraintBy(e, sys.substitute_by_var(e, v, v2)) <- - // S(sys), - // AlgebraicConstraint(e), - // ContainsVariable(e, v), - // Equivalence(v, v2); + ReplaceAlgebraicConstraintBy(e, sys.substitute_by_var(e, v, v2)) <- + S(sys), + AlgebraicConstraint(e), + ContainsVariable(e, v), + Equivalence(v, v2); + AlgebraicConstraint(e) <- ReplaceAlgebraicConstraintBy(_, e); From 78b284230a2c652df75c753ac66280f2d53f1fb9 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 20 Nov 2025 14:20:33 +0000 Subject: [PATCH 28/60] Do not actually substitute assignments. --- autoprecompiles/src/rule_based_optimizer.rs | 76 +++++++++++---------- 1 file changed, 39 insertions(+), 37 deletions(-) diff --git a/autoprecompiles/src/rule_based_optimizer.rs b/autoprecompiles/src/rule_based_optimizer.rs index 07c53d869c..60e3af717e 100644 --- a/autoprecompiles/src/rule_based_optimizer.rs +++ b/autoprecompiles/src/rule_based_optimizer.rs @@ -147,6 +147,7 @@ impl System { Some((*coeff, *var, *expr.constant_offset())) } + #[allow(dead_code)] pub fn is_zero(&self, expr: Expr) -> bool { self.expressions.borrow()[expr].is_zero() } @@ -226,6 +227,7 @@ impl System { Some((left_var, right_var, coeff)) } + #[allow(dead_code)] pub fn substitute_by_known(&self, e: Expr, var: Var, value: F) -> Expr { let expr = { let db = self.expressions.borrow(); @@ -381,34 +383,34 @@ crepe! { struct Equivalence(Var, Var); Equivalence(v1, v2) <- QuadraticEquivalence(v1, v2); - struct ReplaceAlgebraicConstraintBy(Expr, Expr); - ReplaceAlgebraicConstraintBy(e, sys.substitute_by_known(e, v, val)) <- - S(sys), - AlgebraicConstraint(e), - ContainsVariable(e, v), - Assignment(v, val); - ReplaceAlgebraicConstraintBy(e, sys.substitute_by_var(e, v, v2)) <- - S(sys), - AlgebraicConstraint(e), - ContainsVariable(e, v), - Equivalence(v, v2); - - AlgebraicConstraint(e) <- - ReplaceAlgebraicConstraintBy(_, e); - - - // This constraint has been replaced by a different one (or is redundant). - struct AlgebraicConstraintDeleted(Expr); - AlgebraicConstraintDeleted(e) <- - ReplaceAlgebraicConstraintBy(e, _); - AlgebraicConstraintDeleted(e) <- - S(sys), - AlgebraicConstraint(e), - (sys.is_zero(e)); - - @output - struct FinalAlgebraicConstraint(Expr); - FinalAlgebraicConstraint(e) <- AlgebraicConstraint(e), !AlgebraicConstraintDeleted(e); + // struct ReplaceAlgebraicConstraintBy(Expr, Expr); + // ReplaceAlgebraicConstraintBy(e, sys.substitute_by_known(e, v, val)) <- + // S(sys), + // AlgebraicConstraint(e), + // ContainsVariable(e, v), + // Assignment(v, val); + // ReplaceAlgebraicConstraintBy(e, sys.substitute_by_var(e, v, v2)) <- + // S(sys), + // AlgebraicConstraint(e), + // ContainsVariable(e, v), + // Equivalence(v, v2); + + // AlgebraicConstraint(e) <- + // ReplaceAlgebraicConstraintBy(_, e); + + + // // This constraint has been replaced by a different one (or is redundant). + // struct AlgebraicConstraintDeleted(Expr); + // AlgebraicConstraintDeleted(e) <- + // ReplaceAlgebraicConstraintBy(e, _); + // AlgebraicConstraintDeleted(e) <- + // S(sys), + // AlgebraicConstraint(e), + // (sys.is_zero(e)); + + // @output + // struct FinalAlgebraicConstraint(Expr); + // FinalAlgebraicConstraint(e) <- AlgebraicConstraint(e), !AlgebraicConstraintDeleted(e); } pub fn rule_based_optimization( @@ -500,7 +502,7 @@ pub fn rule_based_optimization Date: Thu, 20 Nov 2025 14:44:06 +0000 Subject: [PATCH 29/60] Update expectations. --- .../tests/apc_snapshots/single_instructions/single_loadhu.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/openvm/tests/apc_snapshots/single_instructions/single_loadhu.txt b/openvm/tests/apc_snapshots/single_instructions/single_loadhu.txt index e028e91d03..90a0c9618b 100644 --- a/openvm/tests/apc_snapshots/single_instructions/single_loadhu.txt +++ b/openvm/tests/apc_snapshots/single_instructions/single_loadhu.txt @@ -4,7 +4,7 @@ Instructions: APC advantage: - Main columns: 41 -> 18 (2.28x reduction) - Bus interactions: 17 -> 12 (1.42x reduction) - - Constraints: 25 -> 9 (2.78x reduction) + - Constraints: 25 -> 8 (3.12x reduction) Symbolic machine using 18 unique main columns: from_state__timestamp_0 @@ -52,5 +52,4 @@ flags__2_0 * ((flags__2_0 - 1) * (flags__2_0 - 2)) = 0 (30720 * mem_ptr_limbs__0_0 - (30720 * rs1_data__0_0 + 7864320 * rs1_data__1_0 + 675840 * is_valid)) * (30720 * mem_ptr_limbs__0_0 - (30720 * rs1_data__0_0 + 7864320 * rs1_data__1_0 + 675841)) = 0 (943718400 * rs1_data__0_0 + 30720 * mem_ptr_limbs__1_0 + 629145590 * is_valid - (120 * rs1_data__1_0 + 30720 * rs1_data__2_0 + 7864320 * rs1_data__3_0 + 943718400 * mem_ptr_limbs__0_0)) * (943718400 * rs1_data__0_0 + 30720 * mem_ptr_limbs__1_0 + 629145589 - (120 * rs1_data__1_0 + 30720 * rs1_data__2_0 + 7864320 * rs1_data__3_0 + 943718400 * mem_ptr_limbs__0_0)) = 0 flags__1_0 * (flags__1_0 - 1) + flags__2_0 * (flags__2_0 - 1) + 5 * flags__1_0 * flags__2_0 - (flags__1_0 * (flags__1_0 + flags__2_0 - 2) + flags__2_0 * (flags__1_0 + flags__2_0 - 2) + 2 * is_valid) = 0 -flags__1_0 * flags__2_0 = 0 is_valid * (is_valid - 1) = 0 \ No newline at end of file From 58f8e69d192c40296e9e063f89c8ff8fd8631198 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 20 Nov 2025 17:51:04 +0000 Subject: [PATCH 30/60] Combine free variables, WIP. --- autoprecompiles/src/rule_based_optimizer.rs | 265 ++++++++++++++++---- constraint-solver/src/grouped_expression.rs | 20 ++ 2 files changed, 242 insertions(+), 43 deletions(-) diff --git a/autoprecompiles/src/rule_based_optimizer.rs b/autoprecompiles/src/rule_based_optimizer.rs index 60e3af717e..e0afb5c2c8 100644 --- a/autoprecompiles/src/rule_based_optimizer.rs +++ b/autoprecompiles/src/rule_based_optimizer.rs @@ -4,7 +4,7 @@ #![allow(clippy::mutable_key_type)] use std::{ cell::RefCell, - collections::{BTreeSet, HashMap}, + collections::{BTreeSet, HashMap, HashSet}, fmt::Display, hash::Hash, ops::Index, @@ -52,6 +52,11 @@ impl Index for DB { struct System { expressions: RefCell, var_to_string: Option>, + + /// Variables that only occurr once in the system + /// (also only once in the constraint they occur in). + single_occurrence_variables: HashSet, + range_constraints_on_vars: HashMap>, } impl Default for System { @@ -62,6 +67,8 @@ impl Default for System { reverse: HashMap::new(), }), var_to_string: None, + single_occurrence_variables: HashSet::new(), + range_constraints_on_vars: HashMap::new(), } } } @@ -96,11 +103,24 @@ impl Hash for System { } impl System { + pub fn set_single_occurrence_variables(&mut self, vars: HashSet) { + assert!(self.single_occurrence_variables.is_empty()); + self.single_occurrence_variables = vars; + } + pub fn set_var_to_string_mapping(&mut self, mapping: HashMap) { assert!(self.var_to_string.is_none()); self.var_to_string = Some(mapping); } + pub fn set_range_constraint_on_var( + &mut self, + rcs: impl Iterator)>, + ) { + assert!(self.range_constraints_on_vars.is_empty()); + self.range_constraints_on_vars = rcs.collect(); + } + pub fn insert(&self, expr: &GroupedExpression) -> Expr { let mut db = self.expressions.borrow_mut(); if let Some(&id) = db.reverse.get(expr) { @@ -126,11 +146,22 @@ impl System { Expr(id) } - pub fn referenced_variable(&self, expr: Expr) -> BTreeSet { + /// Extract an Expr into a free GroupedExpression. + /// This is expensive since it clones the expression. + pub fn extract(&self, expr: Expr) -> GroupedExpression { + let db = self.expressions.borrow(); + db[expr].clone() + } + + pub fn referenced_variables(&self, expr: Expr) -> BTreeSet { let db = self.expressions.borrow(); db[expr].referenced_unknown_variables().cloned().collect() } + pub fn single_occurrence_variables(&self) -> impl Iterator { + self.single_occurrence_variables.iter() + } + pub fn affine_var_count(&self, expr: Expr) -> Option { let db = self.expressions.borrow(); let expr = &db[expr]; @@ -147,6 +178,37 @@ impl System { Some((*coeff, *var, *expr.constant_offset())) } + /// If `expr` contains `var` exactly once in an affine way, + /// returns `Some((var, coeff, rest))` where `expr = coeff * var + rest`. + /// + /// This is relatively expensive because it needs to construct a new Expr. + pub fn try_extract_affine_var(&self, expr: Expr, var: Var) -> Option<(F, Expr)> { + let db = self.expressions.borrow(); + let (coeff, rest) = db[expr].try_extract_affine_var(var)?; + Some((coeff, self.insert_owned(rest))) + } + + pub fn is_affine(&self, expr: Expr) -> bool { + let db = self.expressions.borrow(); + db[expr].is_affine() + } + + pub fn on_expr( + &self, + expr: Expr, + args: Args, + f: impl Fn(&GroupedExpression, Args) -> Ret, + ) -> Ret { + let db = self.expressions.borrow(); + let expr = &db[expr]; + f(expr, args) + } + + pub fn range_constraint_on_expr(&self, expr: Expr) -> RangeConstraint { + let db = self.expressions.borrow(); + db[expr].range_constraint(self) + } + #[allow(dead_code)] pub fn is_zero(&self, expr: Expr) -> bool { self.expressions.borrow()[expr].is_zero() @@ -278,6 +340,15 @@ impl System { } } +impl RangeConstraintProvider for System { + fn get(&self, var: &Var) -> RangeConstraint { + self.range_constraints_on_vars + .get(var) + .cloned() + .unwrap_or(RangeConstraint::unconstrained()) + } +} + #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] struct Expr(usize); @@ -297,18 +368,23 @@ crepe! { // @input // struct BusInteractionConstraint<'a>(&'a BusInteraction>); - @input struct RangeConstraintOnExpression(Expr, RangeConstraint); struct Expression(Expr); Expression(e) <- AlgebraicConstraint(e); Expression(e) <- RangeConstraintOnExpression(e, _); + RangeConstraintOnExpression(e, rc) <- + S(sys), + Expression(e), + let rc = sys.range_constraint_on_expr(e); + + struct ContainsVariable(Expr, Var); ContainsVariable(e, v) <- Expression(e), S(sys), - for v in sys.referenced_variable(e); + for v in sys.referenced_variables(e); struct AffineExpression(Expr, F, Var, F); AffineExpression(e, coeff, var, offset) <- @@ -357,6 +433,110 @@ crepe! { RangeConstraintOnVar(v2, rc), (rc.is_disjoint(&rc.combine_sum(&RangeConstraint::from_value(offset / coeff)))); + @output + struct ReplaceAlgebraicConstraintBy(Expr, Expr); + + // Combine multiple variables that only occur in the same algebraic constraint. + // + // Assume we have an algebraic constraint of the form `X * V1 + Y * V2 = R`, + // where `V1` and `V2` only occur in this constraint and only once. + // The only combination of values for `X`, `Y` and `R` where this is _not_ satisfiable + // is `X = 0`, `Y = 0`, `R != 0`. So the constraint is equivalent to the statement + // `(X = 0 and Y = 0) -> R = 0`. + // + // Considering the simpler case where both `X` and `Y` are non-negative such that + // `X + Y` does not wrap. + // Then `X = 0 and Y = 0` is equivalent to `X + Y = 0`. So we can replace the constraint + // by `(X + Y) * V3 = C`, where `V3` is a new variable that only occurs here. + // + // For the general case, where e.g. `X` can be negative, we replace it by `X * X`, + // if that value is still small enough. + + // TODO here we replace V1 by V2, but we should actually replace it by + // a new variable. + // TODO make this iterate nicely. + + struct SingleOccurrenceVariable(Expr, Var); + SingleOccurrenceVariable(e, v) <- + S(sys), + for v in sys.single_occurrence_variables().cloned(), + AlgebraicConstraint(e), + ContainsVariable(e, v2), + (v == v2) + ; + + struct HasProductSummand(Expr, Expr, Expr); + HasProductSummand(e, l, r) <- + S(sys), + Expression(e), + (!sys.on_expr(e, (), |e, _| e.is_affine())), + for (l, r) in sys.extract(e).into_summands().filter_map(|s| { + if let GroupedExpressionComponent::Quadratic(l, r) = s { + Some((sys.insert_owned(l), sys.insert_owned(r))) + } else { + None + } + }); + HasProductSummand(e, r, l) <- HasProductSummand(e, l, r); + Expression(l) <- HasProductSummand(_, l, _); + Expression(r) <- HasProductSummand(_, _, r); + + ReplaceAlgebraicConstraintBy(e, replacement) <- + S(sys), + SingleOccurrenceVariable(e, v1), + SingleOccurrenceVariable(e, v2), + (v1 < v2), + AlgebraicConstraint(e), + HasProductSummand(e, x1, v1_), + AffineExpression(v1_, coeff1, v1, offset1), + (offset1.is_zero()), + HasProductSummand(e, x2, v2_), + (x2 != v1_), + (x1 != v2_), + AffineExpression(v2_, coeff2, v2, offset2), + (offset2.is_zero()), + // Here, we have e = coeff1 * v1 * x1 + coeff2 * v2 * x2 + ... + RangeConstraintOnExpression(x1, rc1), + RangeConstraintOnExpression(x2, rc2), + ({println!("XXX Considering combining {v1} and {v2} in constraint {}", sys.format_expr(e)); true}), + ({println!("XXX RC: {}: {}, {}: {}", sys.format_expr(x1), rc1, sys.format_expr(x2), rc2); true}), + // TODO Continue here. This is working, but we only have examples where + // the RC can also be negative. + (rc1.range().0.is_zero() && rc2.range().0.is_zero()), + (rc1.multiple(coeff1).range().1 < F::from(-1) / 2.into()), + (rc2.multiple(coeff2).range().1 < F::from(-1) / 2.into()), + let replacement = { + // TODO it could be that only subtracting sys.extract(v1_) * sys.extract(x1) works. + let e = sys.extract(e); + let x1 = sys.extract(x1); + let x2 = sys.extract(x2); + let r = e.clone() - x1.clone() * sys.extract(v1_) - x2.clone() * sys.extract(v2_); + println!("Replacement residual of {e}:\n{r}"); + let replacement = r + GroupedExpression::from_unknown_variable(v1) * (x1 * coeff1 + x2 * coeff2); + sys.insert_owned(replacement) + }; + + // } + // let Some((coeff2, rest)) = sys.try_extract_affine_var(e2, v2), + // let replacement_expr = { + // let db = sys.expressions.borrow(); + // let expr = &db[e]; + // let new_var = Var(1000000 + v1.0 + v2.0); // just a large number to avoid collisions + // let replaced = expr + // .substitute_by_unknown( + // &v1, + // &GroupedExpression::from_unknown_variable(new_var), + // ) + // .substitute_by_unknown( + // &v2, + // &GroupedExpression::from_unknown_variable(new_var), + // ); + // replaced * GroupedExpression::from_number(coeff1 + coeff2) + // }, + // let replacement = sys.insert_owned(replacement_expr); + + + // TODO wait a second. We can craete range constraints on expressions for all // algebraic constraints. Then we just work on range constraints on expressions @@ -383,7 +563,8 @@ crepe! { struct Equivalence(Var, Var); Equivalence(v1, v2) <- QuadraticEquivalence(v1, v2); - // struct ReplaceAlgebraicConstraintBy(Expr, Expr); + // Do not do this because it is rather expensive. + // ReplaceAlgebraicConstraintBy(e, sys.substitute_by_known(e, v, val)) <- // S(sys), // AlgebraicConstraint(e), @@ -395,22 +576,22 @@ crepe! { // ContainsVariable(e, v), // Equivalence(v, v2); - // AlgebraicConstraint(e) <- - // ReplaceAlgebraicConstraintBy(_, e); + AlgebraicConstraint(e) <- + ReplaceAlgebraicConstraintBy(_, e); // // This constraint has been replaced by a different one (or is redundant). - // struct AlgebraicConstraintDeleted(Expr); - // AlgebraicConstraintDeleted(e) <- - // ReplaceAlgebraicConstraintBy(e, _); - // AlgebraicConstraintDeleted(e) <- - // S(sys), - // AlgebraicConstraint(e), - // (sys.is_zero(e)); + struct AlgebraicConstraintDeleted(Expr); + AlgebraicConstraintDeleted(e) <- + ReplaceAlgebraicConstraintBy(e, _); + AlgebraicConstraintDeleted(e) <- + S(sys), + AlgebraicConstraint(e), + (sys.is_zero(e)); - // @output - // struct FinalAlgebraicConstraint(Expr); - // FinalAlgebraicConstraint(e) <- AlgebraicConstraint(e), !AlgebraicConstraintDeleted(e); + @output + struct FinalAlgebraicConstraint(Expr); + FinalAlgebraicConstraint(e) <- AlgebraicConstraint(e), !AlgebraicConstraintDeleted(e); } pub fn rule_based_optimization( @@ -456,29 +637,6 @@ pub fn rule_based_optimization GroupedExpression { self.linear.get(var) } + /// If `self` contains `var` exactly once in an affine way, + /// returns `Some((coeff, rest))` where `self = coeff * var + rest`. + /// + /// This is relatively expensive because it needs to construct a new + /// GroupedExpression. + pub fn try_extract_affine_var(&self, var: V) -> Option<(T, Self)> { + if self + .referenced_unknown_variables() + .filter(|v| *v == &var) + .count() + != 1 + { + return None; + } + let coeff = self.linear.get(&var)?.clone(); + let mut rest = self.clone(); + rest.linear.remove(&var); + Some((coeff, rest)) + } + /// Returns the range constraint of the full expression. pub fn range_constraint( &self, From 73a21aed70ce3ac40faea2b6e6dde2f3cff4270f Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 24 Nov 2025 16:34:10 +0000 Subject: [PATCH 31/60] Iterate and cleanup. --- autoprecompiles/src/rule_based_optimizer.rs | 546 +++++++++--------- .../src/algebraic_constraint/solve.rs | 10 +- constraint-solver/src/grouped_expression.rs | 45 +- constraint-solver/src/range_constraint.rs | 16 + openvm/src/lib.rs | 24 +- .../complex/load_two_bytes_compare.txt | 21 +- .../apc_snapshots/complex/memcpy_block.txt | 2 +- .../single_instructions/single_bne.txt | 15 +- 8 files changed, 370 insertions(+), 309 deletions(-) diff --git a/autoprecompiles/src/rule_based_optimizer.rs b/autoprecompiles/src/rule_based_optimizer.rs index e0afb5c2c8..c71da67c4c 100644 --- a/autoprecompiles/src/rule_based_optimizer.rs +++ b/autoprecompiles/src/rule_based_optimizer.rs @@ -12,10 +12,9 @@ use std::{ use itertools::{EitherOrBoth, Itertools}; use powdr_constraint_solver::{ - constraint_system::{BusInteraction, BusInteractionHandler}, - grouped_expression::{ - GroupedExpression, GroupedExpressionComponent, NoRangeConstraints, RangeConstraintProvider, - }, + algebraic_constraint, + constraint_system::{BusInteraction, BusInteractionHandler, ConstraintSystem}, + grouped_expression::{GroupedExpression, GroupedExpressionComponent, RangeConstraintProvider}, indexed_constraint_system::IndexedConstraintSystem, range_constraint::RangeConstraint, runtime_constant::VarTransformable, @@ -37,21 +36,47 @@ impl Display for Var { } } -struct DB { +#[derive(Default)] +struct ExpressionDB { expressions: Vec>, reverse: HashMap, usize>, } -impl Index for DB { +impl Index for ExpressionDB { type Output = GroupedExpression; fn index(&self, index: Expr) -> &Self::Output { &self.expressions[index.0] } } +impl ExpressionDB { + fn insert_owned_new(&mut self, expr: GroupedExpression) -> Expr { + self.expressions.push(expr.clone()); + let id = self.expressions.len() - 1; + self.reverse.insert(expr, id); + Expr(id) + } + + pub fn insert(&mut self, expr: &GroupedExpression) -> Expr { + if let Some(&id) = self.reverse.get(expr) { + Expr(id) + } else { + self.insert_owned_new(expr.clone()) + } + } + + pub fn insert_owned(&mut self, expr: GroupedExpression) -> Expr { + if let Some(&id) = self.reverse.get(&expr) { + Expr(id) + } else { + self.insert_owned_new(expr) + } + } +} + struct System { - expressions: RefCell, - var_to_string: Option>, + expressions: RefCell, + var_to_string: HashMap, /// Variables that only occurr once in the system /// (also only once in the constraint they occur in). @@ -59,16 +84,18 @@ struct System { range_constraints_on_vars: HashMap>, } -impl Default for System { - fn default() -> Self { +impl System { + fn new( + expressions: ExpressionDB, + var_to_string: HashMap, + single_occurrence_variables: HashSet, + range_constraints_on_vars: HashMap>, + ) -> Self { Self { - expressions: RefCell::new(DB { - expressions: Vec::new(), - reverse: HashMap::new(), - }), - var_to_string: None, - single_occurrence_variables: HashSet::new(), - range_constraints_on_vars: HashMap::new(), + expressions: RefCell::new(expressions), + var_to_string, + single_occurrence_variables, + range_constraints_on_vars, } } } @@ -103,54 +130,18 @@ impl Hash for System { } impl System { - pub fn set_single_occurrence_variables(&mut self, vars: HashSet) { - assert!(self.single_occurrence_variables.is_empty()); - self.single_occurrence_variables = vars; - } - - pub fn set_var_to_string_mapping(&mut self, mapping: HashMap) { - assert!(self.var_to_string.is_none()); - self.var_to_string = Some(mapping); - } - - pub fn set_range_constraint_on_var( - &mut self, - rcs: impl Iterator)>, - ) { - assert!(self.range_constraints_on_vars.is_empty()); - self.range_constraints_on_vars = rcs.collect(); - } - pub fn insert(&self, expr: &GroupedExpression) -> Expr { - let mut db = self.expressions.borrow_mut(); - if let Some(&id) = db.reverse.get(expr) { - Expr(id) - } else { - self.insert_owned_new(&mut db, expr.clone()) - } + self.expressions.borrow_mut().insert(expr) } pub fn insert_owned(&self, expr: GroupedExpression) -> Expr { - let mut db = self.expressions.borrow_mut(); - if let Some(&id) = db.reverse.get(&expr) { - Expr(id) - } else { - self.insert_owned_new(&mut db, expr) - } - } - - fn insert_owned_new(&self, db: &mut DB, expr: GroupedExpression) -> Expr { - db.expressions.push(expr.clone()); - let id = db.expressions.len() - 1; - db.reverse.insert(expr, id); - Expr(id) + self.expressions.borrow_mut().insert_owned(expr) } /// Extract an Expr into a free GroupedExpression. /// This is expensive since it clones the expression. pub fn extract(&self, expr: Expr) -> GroupedExpression { - let db = self.expressions.borrow(); - db[expr].clone() + self.expressions.borrow()[expr].clone() } pub fn referenced_variables(&self, expr: Expr) -> BTreeSet { @@ -178,21 +169,6 @@ impl System { Some((*coeff, *var, *expr.constant_offset())) } - /// If `expr` contains `var` exactly once in an affine way, - /// returns `Some((var, coeff, rest))` where `expr = coeff * var + rest`. - /// - /// This is relatively expensive because it needs to construct a new Expr. - pub fn try_extract_affine_var(&self, expr: Expr, var: Var) -> Option<(F, Expr)> { - let db = self.expressions.borrow(); - let (coeff, rest) = db[expr].try_extract_affine_var(var)?; - Some((coeff, self.insert_owned(rest))) - } - - pub fn is_affine(&self, expr: Expr) -> bool { - let db = self.expressions.borrow(); - db[expr].is_affine() - } - pub fn on_expr( &self, expr: Expr, @@ -227,12 +203,6 @@ impl System { Some((self.insert(&l), self.insert(&r))) } - #[allow(dead_code)] - pub fn try_as_single_var(&self, expr: Expr) -> Option { - let db = self.expressions.borrow(); - db[expr].try_to_simple_unknown() - } - /// Returns Some(C) if `a - b = C' and both are affine. pub fn constant_difference(&self, a: Expr, b: Expr) -> Option { let db = self.expressions.borrow(); @@ -318,25 +288,17 @@ impl System { #[allow(dead_code)] pub fn format_expr(&self, expr: Expr) -> String { let db = self.expressions.borrow(); - if let Some(var_to_string) = &self.var_to_string { - db[expr] - .transform_var_type(&mut |v| &var_to_string[v]) - .to_string() - } else { - db[expr].to_string() - } + db[expr] + .transform_var_type(&mut |v| &self.var_to_string[v]) + .to_string() } #[allow(dead_code)] pub fn format_var(&self, var: Var) -> String { - if let Some(var_to_string) = &self.var_to_string { - var_to_string - .get(&var) - .cloned() - .unwrap_or_else(|| var.to_string()) - } else { - var.to_string() - } + self.var_to_string + .get(&var) + .cloned() + .unwrap_or_else(|| var.to_string()) } } @@ -359,9 +321,6 @@ crepe! { @input struct InitialAlgebraicConstraint(Expr); - @input - struct InitialRangeConstraintOnVar(Var, RangeConstraint); - struct AlgebraicConstraint(Expr); AlgebraicConstraint(e) <- InitialAlgebraicConstraint(e); @@ -392,9 +351,8 @@ crepe! { S(sys), let Some((coeff, var, offset)) = sys.try_to_affine(e); - @output struct RangeConstraintOnVar(Var, RangeConstraint); - RangeConstraintOnVar(v, rc) <- InitialRangeConstraintOnVar(v, rc); + RangeConstraintOnVar(v, rc) <- S(sys), ContainsVariable(_, v), let rc = sys.get(&v); // RC(coeff * var + offset) = rc <=> // coeff * RC(var) + offset = rc <=> // RC(var) = (rc - offset) / coeff @@ -433,7 +391,6 @@ crepe! { RangeConstraintOnVar(v2, rc), (rc.is_disjoint(&rc.combine_sum(&RangeConstraint::from_value(offset / coeff)))); - @output struct ReplaceAlgebraicConstraintBy(Expr, Expr); // Combine multiple variables that only occur in the same algebraic constraint. @@ -462,8 +419,18 @@ crepe! { for v in sys.single_occurrence_variables().cloned(), AlgebraicConstraint(e), ContainsVariable(e, v2), - (v == v2) - ; + (v == v2); + + struct LargestSingleOccurrenceVariablePairInExpr(Expr, Var, Var); + LargestSingleOccurrenceVariablePairInExpr(e, v1, v2) <- + S(sys), + SingleOccurrenceVariable(e, v1), + SingleOccurrenceVariable(e, v2), + (v1 < v2), + (sys + .single_occurrence_variables() + .filter(|v3| sys.referenced_variables(e).contains(v3)) + .all(|&v3| v3 == v1 || v3 == v2 || v3 < v1)); struct HasProductSummand(Expr, Expr, Expr); HasProductSummand(e, l, r) <- @@ -481,11 +448,18 @@ crepe! { Expression(l) <- HasProductSummand(_, l, _); Expression(r) <- HasProductSummand(_, _, r); - ReplaceAlgebraicConstraintBy(e, replacement) <- - S(sys), - SingleOccurrenceVariable(e, v1), - SingleOccurrenceVariable(e, v2), - (v1 < v2), + // FreeVariableCombinationCandidate(e, coeff1, v1, coeff2, v2, x1, x2) + // if e is the expression of an algebraic constraint and + // e = coeff1 * v1 * x1 + coeff2 * v2 * x2 + ... + // where v1 and v2 are different variables that only occur here and only once. + struct FreeVariableCombinationCandidate(Expr, F, Var, Expr, F, Var, Expr); + FreeVariableCombinationCandidate(e, coeff1, v1, x1, coeff2, v2, x2) <- + // SingleOccurrenceVariable(e, v1), + // SingleOccurrenceVariable(e, v2), + // TODO this way, we could miss optimization opportunities + // if this is not working out because of range constraints, + // but at least the replacement becomes deterministic. + LargestSingleOccurrenceVariablePairInExpr(e, v1, v2), AlgebraicConstraint(e), HasProductSummand(e, x1, v1_), AffineExpression(v1_, coeff1, v1, offset1), @@ -494,49 +468,64 @@ crepe! { (x2 != v1_), (x1 != v2_), AffineExpression(v2_, coeff2, v2, offset2), - (offset2.is_zero()), + (offset2.is_zero()); + + @output + struct PotentiallyReplaceAlgebraicConstraintBy(Expr, Expr); + + PotentiallyReplaceAlgebraicConstraintBy(e, replacement) <- + S(sys), + FreeVariableCombinationCandidate(e, coeff1, v1, x1, coeff2, v2, x2), // Here, we have e = coeff1 * v1 * x1 + coeff2 * v2 * x2 + ... RangeConstraintOnExpression(x1, rc1), RangeConstraintOnExpression(x2, rc2), - ({println!("XXX Considering combining {v1} and {v2} in constraint {}", sys.format_expr(e)); true}), - ({println!("XXX RC: {}: {}, {}: {}", sys.format_expr(x1), rc1, sys.format_expr(x2), rc2); true}), - // TODO Continue here. This is working, but we only have examples where - // the RC can also be negative. - (rc1.range().0.is_zero() && rc2.range().0.is_zero()), - (rc1.multiple(coeff1).range().1 < F::from(-1) / 2.into()), - (rc2.multiple(coeff2).range().1 < F::from(-1) / 2.into()), - let replacement = { - // TODO it could be that only subtracting sys.extract(v1_) * sys.extract(x1) works. - let e = sys.extract(e); - let x1 = sys.extract(x1); - let x2 = sys.extract(x2); - let r = e.clone() - x1.clone() * sys.extract(v1_) - x2.clone() * sys.extract(v2_); - println!("Replacement residual of {e}:\n{r}"); - let replacement = r + GroupedExpression::from_unknown_variable(v1) * (x1 * coeff1 + x2 * coeff2); - sys.insert_owned(replacement) - }; - - // } - // let Some((coeff2, rest)) = sys.try_extract_affine_var(e2, v2), - // let replacement_expr = { - // let db = sys.expressions.borrow(); - // let expr = &db[e]; - // let new_var = Var(1000000 + v1.0 + v2.0); // just a large number to avoid collisions - // let replaced = expr - // .substitute_by_unknown( - // &v1, - // &GroupedExpression::from_unknown_variable(new_var), - // ) - // .substitute_by_unknown( - // &v2, - // &GroupedExpression::from_unknown_variable(new_var), - // ); - // replaced * GroupedExpression::from_number(coeff1 + coeff2) - // }, - // let replacement = sys.insert_owned(replacement_expr); - - - + let Some(replacement) = (|| { + let x1_needs_squaring = rc1.range().0 != F::zero(); + let x2_needs_squaring = rc2.range().0 != F::zero(); + let rc1 = if x1_needs_squaring { + rc1.square() + } else { + rc1 + }; + let rc2 = if x2_needs_squaring { + rc2.square() + } else { + rc2 + }; + if !rc1.range().0.is_zero() || !rc2.range().0.is_zero() { + return None; + } + let sum_rc = rc1.multiple(coeff1).combine_sum(&rc2.multiple(coeff2)); + if !(sum_rc.range().0.is_zero() && sum_rc.range().1 < F::from(-1)) { + return None; + } + let e = sys.extract(e); + let x1 = sys.extract(x1); + let x1 = if x1_needs_squaring { + x1.clone() * x1 + } else { + x1 + }; + let x2 = sys.extract(x2); + let x2 = if x2_needs_squaring { + x2.clone() * x2 + } else { + x2 + }; + // TODO remove clone + let r = e.clone().into_summands().filter(|s|{ + if let GroupedExpressionComponent::Quadratic(l, r) = s { + let mut vars = l.referenced_unknown_variables().chain(r.referenced_unknown_variables()); + if vars.any(|v| v == &v1 || v == &v2) { + return false; + } + }; + true + }).map(GroupedExpression::from).sum::>(); + // TODO we actually need to insert a new variable at this point. + let replacement = r + GroupedExpression::from_unknown_variable(v1) * (x1 * coeff1 + x2 * coeff2); + Some(sys.insert_owned(replacement)) + })(); // TODO wait a second. We can craete range constraints on expressions for all // algebraic constraints. Then we just work on range constraints on expressions @@ -553,34 +542,34 @@ crepe! { Solvable(l, v, x1), Solvable(r, v, x1 + F::from(1)); - @output struct Assignment(Var, F); Assignment(var, v) <- AlgebraicConstraint(e), Solvable(e, var, v); - @output struct Equivalence(Var, Var); Equivalence(v1, v2) <- QuadraticEquivalence(v1, v2); // Do not do this because it is rather expensive. - // ReplaceAlgebraicConstraintBy(e, sys.substitute_by_known(e, v, val)) <- - // S(sys), - // AlgebraicConstraint(e), - // ContainsVariable(e, v), - // Assignment(v, val); - // ReplaceAlgebraicConstraintBy(e, sys.substitute_by_var(e, v, v2)) <- - // S(sys), - // AlgebraicConstraint(e), - // ContainsVariable(e, v), - // Equivalence(v, v2); + ReplaceAlgebraicConstraintBy(e, sys.substitute_by_known(e, v, val)) <- + S(sys), + AlgebraicConstraint(e), + ContainsVariable(e, v), + Assignment(v, val); + ReplaceAlgebraicConstraintBy(e, sys.substitute_by_var(e, v, v2)) <- + S(sys), + AlgebraicConstraint(e), + ContainsVariable(e, v), + Equivalence(v, v2); + // TODO this way, one constraint could be replaced by multiple + // alternative constraints. AlgebraicConstraint(e) <- ReplaceAlgebraicConstraintBy(_, e); - // // This constraint has been replaced by a different one (or is redundant). + // This constraint has been replaced by a different one (or is redundant). struct AlgebraicConstraintDeleted(Expr); AlgebraicConstraintDeleted(e) <- ReplaceAlgebraicConstraintBy(e, _); @@ -589,32 +578,34 @@ crepe! { AlgebraicConstraint(e), (sys.is_zero(e)); + @output + struct Change(); + Change() <- ReplaceAlgebraicConstraintBy(_, _); + Change() <- AlgebraicConstraintDeleted(_); + @output struct FinalAlgebraicConstraint(Expr); FinalAlgebraicConstraint(e) <- AlgebraicConstraint(e), !AlgebraicConstraintDeleted(e); } pub fn rule_based_optimization( - mut system: IndexedConstraintSystem, + system: IndexedConstraintSystem, range_constraints: impl RangeConstraintProvider, - bus_interaction_handler: impl BusInteractionHandler + Clone, + _bus_interaction_handler: impl BusInteractionHandler + Clone, ) -> IndexedConstraintSystem { if T::modulus().to_arbitrary_integer() != BabyBearField::modulus().to_arbitrary_integer() { return system; } - // println!("{system}"); - let start = std::time::Instant::now(); - let mut rt = Crepe::new(); let mut var_mapper = Default::default(); - let mut db = System::default(); + let mut db = ExpressionDB::default(); - let transformed_expressions = system + let algebraic_constraints = system .system() .algebraic_constraints .iter() .map(|c| transform_grouped_expression(&c.expression, &mut var_mapper)) - .map(|e| db.insert(&e)) + .map(|e| db.insert_owned(e)) .collect_vec(); let bus_interactions: Vec> = system .system() @@ -624,117 +615,119 @@ pub fn rule_based_optimization>(); + let sys = System::new( + db, + var_names, + single_occurrence_variables, + range_constraints_on_vars, + ); - let transform_end = std::time::Instant::now(); + let (algebraic_constraints, bus_interactions) = + apply_rules(algebraic_constraints, bus_interactions, &sys); - rt.extend( - transformed_expressions - .iter() - .copied() - .map(InitialAlgebraicConstraint), - ); - // rt.extend(bus_interactions.iter().map(BusInteractionConstraint)); - rt.extend(range_constraints_on_vars); - db.set_var_to_string_mapping( - var_mapper - .backward - .iter() - .map(|(var, v)| (*var, v.to_string())) - .collect(), - ); - db.set_single_occurrence_variables( - system - .referenced_unknown_variables() - .unique() - .filter_map(|var| { - let constr = system - .constraints_referencing_variables(std::iter::once(var)) - .exactly_one() - .ok()?; - constr - .referenced_unknown_variables() - .cloned() - .filter(|v| v == var) - .exactly_one() - .ok() - }) - .map(|v| var_mapper.forward(&v)) - .collect(), - ); - db.set_range_constraint_on_var(system.referenced_unknown_variables().unique().map(|v| { - let rc = range_constraints.get(v); - (var_mapper.forward(v), transform_range_constraint(&rc)) - })); - rt.extend(std::iter::once(S(&db))); - - let insert_end = std::time::Instant::now(); - - let (rcs, _, assignments, equivalences, .. /* , constrs*/) = rt.run(); - let run_end = std::time::Instant::now(); - // for RangeConstraintOnVar(var, rc) in &rcs { - // println!( - // "Rule-based range constraint: {} in {rc}", - // var_mapper.backward(&var), - // ); - // } - log::debug!("Found {} rule-based RCs", rcs.len()); - log::debug!("Found {} rule-based assignments", assignments.len()); - log::debug!("Found {} rule-based equivalences", equivalences.len()); - // log::debug!("Final algebraic constraints: {}", constrs.len()); - // for FinalAlgebraicConstraint(e) in &constrs { - // println!("{}", db.format_expr(*e)); - // } - for (var, value) in assignments + let algebraic_constraints = algebraic_constraints.into_iter().map(|e| { + let expr = sys.extract(e); + algebraic_constraint::AlgebraicConstraint::assert_zero( + untransform_grouped_expression::(&expr, &var_mapper), + ) + }); + let bus_interactions = bus_interactions .into_iter() - .map(|Assignment(var, value)| { - ( - var_mapper.backward(&var), - T::from(value.to_arbitrary_integer()), - ) + .map(|bus_inter| { + bus_inter + .fields() + .map(|&e| { + let expr = sys.extract(e); + untransform_grouped_expression::(&expr, &var_mapper) + }) + .collect::>>() }) - .sorted() - { - log::trace!("Rule-based assignment: {var} = {value}",); - system.substitute_by_known(var, &value); - } - for Equivalence(v1, v2) in &equivalences { - // println!("XXX Rule-based equivalence: {v1} == {v2}",); - let v1 = var_mapper.backward(v1).clone(); - let v2 = var_mapper.backward(v2).clone(); - let (v1, v2) = if v1 < v2 { (v1, v2) } else { (v2, v1) }; - system.substitute_by_unknown(&v2, &GroupedExpression::from_unknown_variable(v1.clone())); - } - system.retain_algebraic_constraints(|constraint| !constraint.is_redundant()); - system.retain_bus_interactions(|bus_interaction| !bus_interaction.multiplicity.is_zero()); - let substitution_end = std::time::Instant::now(); - - log::info!( - "Rule-based optimization timings:\n\ - Transform: {}\n\ - Insert: {}\n\ - Run: {}\n\ - Substitution: {}\n\ - Total: {}", - (transform_end - start).as_secs_f32(), - (insert_end - transform_end).as_secs_f32(), - (run_end - insert_end).as_secs_f32(), - (substitution_end - run_end).as_secs_f32(), - (substitution_end - start).as_secs_f32(), - ); + .collect_vec(); - system + ConstraintSystem { + algebraic_constraints: algebraic_constraints.collect(), + bus_interactions, + derived_variables: system.system().derived_variables.clone(), + } + .into() +} + +fn apply_rules( + mut algebraic_constraints: Vec, + bus_interactions: Vec>, + sys: &System, +) -> (Vec, Vec>) { + loop { + let mut progress = false; + let mut rt = Crepe::new(); + + rt.extend( + algebraic_constraints + .iter() + .copied() + .map(InitialAlgebraicConstraint), + ); + rt.extend(std::iter::once(S(sys))); + + let (replacements, change, mut final_constrs) = rt.run(); + progress |= !change.is_empty(); + + // We perform the constraint-by-constraint replacements instead of + // in the system because there can be multiple matches. + for PotentiallyReplaceAlgebraicConstraintBy(old, new) in &replacements { + if final_constrs.contains(&FinalAlgebraicConstraint(*old)) { + final_constrs.remove(&FinalAlgebraicConstraint(*old)); + final_constrs.insert(FinalAlgebraicConstraint(*new)); + progress = true; + } + } + // TODO this could all be non-deterministic. + algebraic_constraints = final_constrs + .iter() + .map(|FinalAlgebraicConstraint(e)| *e) + .collect(); + + if !progress { + break; + } + } + (algebraic_constraints, bus_interactions) } #[derive(Clone)] @@ -794,6 +787,28 @@ fn transform_grouped_expression( + expr: &GroupedExpression, + var_mapper: &VarMapper, +) -> GroupedExpression { + expr.clone() + .into_summands() + .map(|s| match s { + GroupedExpressionComponent::Quadratic(l, r) => { + untransform_grouped_expression(&l, var_mapper) + * untransform_grouped_expression(&r, var_mapper) + } + GroupedExpressionComponent::Linear(v, c) => { + GroupedExpression::from_unknown_variable(var_mapper.backward(&v).clone()) + * T::from(c.to_arbitrary_integer()) + } + GroupedExpressionComponent::Constant(c) => { + GroupedExpression::from_number(c.to_arbitrary_integer().into()) + } + }) + .sum() +} + fn transform_range_constraint( rc: &RangeConstraint, ) -> RangeConstraint { @@ -811,6 +826,7 @@ mod tests { use expect_test::expect; use powdr_constraint_solver::{ algebraic_constraint, constraint_system::DefaultBusInteractionHandler, + grouped_expression::NoRangeConstraints, }; use super::*; diff --git a/constraint-solver/src/algebraic_constraint/solve.rs b/constraint-solver/src/algebraic_constraint/solve.rs index 4b03d73b23..dd48c4d16a 100644 --- a/constraint-solver/src/algebraic_constraint/solve.rs +++ b/constraint-solver/src/algebraic_constraint/solve.rs @@ -393,7 +393,7 @@ mod tests { use crate::grouped_expression::NoRangeConstraints; use super::*; - use powdr_number::{FieldElement, GoldilocksField}; + use powdr_number::GoldilocksField; use pretty_assertions::assert_eq; @@ -407,14 +407,6 @@ mod tests { Qse::from_number(GoldilocksField::from(value)) } - impl RangeConstraintProvider - for HashMap<&'static str, RangeConstraint> - { - fn get(&self, var: &&'static str) -> RangeConstraint { - self.get(var).cloned().unwrap_or_default() - } - } - #[test] fn unsolvable() { let r = AlgebraicConstraint::assert_zero(&Qse::from_number(GoldilocksField::from(10))) diff --git a/constraint-solver/src/grouped_expression.rs b/constraint-solver/src/grouped_expression.rs index 957f3e0ca8..864064e1df 100644 --- a/constraint-solver/src/grouped_expression.rs +++ b/constraint-solver/src/grouped_expression.rs @@ -1,5 +1,5 @@ use std::{ - collections::{BTreeMap, HashSet}, + collections::{BTreeMap, HashMap, HashSet}, fmt::Display, hash::Hash, iter::{once, Sum}, @@ -55,6 +55,33 @@ pub enum GroupedExpressionComponent { Constant(T), } +impl From> for GroupedExpression +where + F: FieldElement, + T: RuntimeConstant, + V: Clone + Ord + Eq, +{ + fn from(s: GroupedExpressionComponent) -> Self { + match s { + GroupedExpressionComponent::Quadratic(l, r) => Self { + quadratic: vec![(l, r)], + linear: Default::default(), + constant: T::zero(), + }, + GroupedExpressionComponent::Linear(v, c) => Self { + quadratic: Default::default(), + linear: [(v, c)].into_iter().collect(), + constant: T::zero(), + }, + GroupedExpressionComponent::Constant(c) => Self { + quadratic: Default::default(), + linear: Default::default(), + constant: c, + }, + } + } +} + impl, V> GroupedExpression { pub fn from_number(k: F) -> Self { Self { @@ -300,8 +327,12 @@ impl GroupedExpression { self.quadratic .iter() .map(|(l, r)| { - l.range_constraint(range_constraints) - .combine_product(&r.range_constraint(range_constraints)) + if l == r { + l.range_constraint(range_constraints).square() + } else { + l.range_constraint(range_constraints) + .combine_product(&r.range_constraint(range_constraints)) + } }) .chain(self.linear.iter().map(|(var, coeff)| { range_constraints @@ -502,6 +533,14 @@ impl, T: FieldElement, V> RangeConstraintProvid } } +impl RangeConstraintProvider + for HashMap> +{ + fn get(&self, var: &V) -> RangeConstraint { + HashMap::get(self, var).cloned().unwrap_or_default() + } +} + #[derive(Clone, Copy)] pub struct NoRangeConstraints; impl RangeConstraintProvider for NoRangeConstraints { diff --git a/constraint-solver/src/range_constraint.rs b/constraint-solver/src/range_constraint.rs index 2966f91b69..501e7106f9 100644 --- a/constraint-solver/src/range_constraint.rs +++ b/constraint-solver/src/range_constraint.rs @@ -187,6 +187,22 @@ impl RangeConstraint { } } + /// If `Self` is a valid range constraint on an expression `e`, returns + /// a valid range constraint for `e * e`. + pub fn square(&self) -> Self { + let square_rc = if self.min > self.max { { + let max_abs = std::cmp::max(-self.min, self.max); + if max_abs.to_arbitrary_integer() * max_abs.to_arbitrary_integer() + < T::modulus().to_arbitrary_integer() + { + Self::from_range(T::zero(), max_abs * max_abs) + } else { + Default::default() + } + } } else { Default::default() }; + self.combine_product(self).conjunction(&square_rc) + } + /// Returns the conjunction of this constraint and the other. /// This operation is not lossless, but if `r1` and `r2` allow /// a value `x`, then `r1.conjunction(r2)` also allows `x`. diff --git a/openvm/src/lib.rs b/openvm/src/lib.rs index 011817dd17..93b39eed56 100644 --- a/openvm/src/lib.rs +++ b/openvm/src/lib.rs @@ -1737,7 +1737,7 @@ mod tests { AirMetrics { widths: AirWidths { preprocessed: 0, - main: 41, + main: 38, log_up: 56, }, constraints: 15, @@ -1765,7 +1765,7 @@ mod tests { AirMetrics { widths: AirWidths { preprocessed: 0, - main: 41, + main: 38, log_up: 56, }, constraints: 15, @@ -1787,7 +1787,7 @@ mod tests { }, after: AirWidths { preprocessed: 0, - main: 41, + main: 38, log_up: 56, }, } @@ -1813,7 +1813,7 @@ mod tests { AirMetrics { widths: AirWidths { preprocessed: 0, - main: 14263, + main: 14254, log_up: 22752, }, constraints: 4285, @@ -1841,7 +1841,7 @@ mod tests { AirMetrics { widths: AirWidths { preprocessed: 0, - main: 14235, + main: 14226, log_up: 22720, }, constraints: 4261, @@ -1863,7 +1863,7 @@ mod tests { }, after: AirWidths { preprocessed: 0, - main: 14235, + main: 14226, log_up: 22720, }, } @@ -1890,7 +1890,7 @@ mod tests { AirMetrics { widths: AirWidths { preprocessed: 0, - main: 17300, + main: 17297, log_up: 27896, }, constraints: 8834, @@ -1912,7 +1912,7 @@ mod tests { }, after: AirWidths { preprocessed: 0, - main: 17300, + main: 17297, log_up: 27896, }, } @@ -1987,7 +1987,7 @@ mod tests { AirMetrics { widths: AirWidths { preprocessed: 0, - main: 2025, + main: 2022, log_up: 3472, }, constraints: 187, @@ -2015,7 +2015,7 @@ mod tests { AirMetrics { widths: AirWidths { preprocessed: 0, - main: 2025, + main: 2022, log_up: 3472, }, constraints: 187, @@ -2043,7 +2043,7 @@ mod tests { AirMetrics { widths: AirWidths { preprocessed: 0, - main: 2025, + main: 2022, log_up: 3472, }, constraints: 187, @@ -2065,7 +2065,7 @@ mod tests { }, after: AirWidths { preprocessed: 0, - main: 2025, + main: 2022, log_up: 3472, }, } diff --git a/openvm/tests/apc_snapshots/complex/load_two_bytes_compare.txt b/openvm/tests/apc_snapshots/complex/load_two_bytes_compare.txt index b7b2a94581..e0c27e303b 100644 --- a/openvm/tests/apc_snapshots/complex/load_two_bytes_compare.txt +++ b/openvm/tests/apc_snapshots/complex/load_two_bytes_compare.txt @@ -4,11 +4,11 @@ Instructions: BNE 52 56 28 1 1 APC advantage: - - Main columns: 98 -> 53 (1.85x reduction) + - Main columns: 98 -> 52 (1.88x reduction) - Bus interactions: 47 -> 32 (1.47x reduction) - - Constraints: 47 -> 15 (3.13x reduction) + - Constraints: 47 -> 17 (2.76x reduction) -Symbolic machine using 53 unique main columns: +Symbolic machine using 52 unique main columns: from_state__timestamp_0 rs1_data__0_0 rs1_data__1_0 @@ -56,11 +56,10 @@ Symbolic machine using 53 unique main columns: prev_data__1_1 prev_data__2_1 prev_data__3_1 + a__0_2 + b__0_2 cmp_result_2 diff_inv_marker__0_2 - diff_inv_marker__1_2 - diff_inv_marker__2_2 - diff_inv_marker__3_2 is_valid // Bus 0 (EXECUTION_BRIDGE): @@ -78,8 +77,8 @@ mult=is_valid * 1, args=[1, 44, rs1_data__0_1, rs1_data__1_1, rs1_data__2_1, rs1 mult=is_valid * -1, args=[2, mem_ptr_limbs__0_1 + 65536 * mem_ptr_limbs__1_1 + opcode_loadb_flag0_1 - (2 * shift_most_sig_bit_1 + 1), shift_most_sig_bit_1 * shifted_read_data__2_1 + (1 - shift_most_sig_bit_1) * shifted_read_data__0_1, shift_most_sig_bit_1 * shifted_read_data__3_1 + (1 - shift_most_sig_bit_1) * shifted_read_data__1_1, shift_most_sig_bit_1 * shifted_read_data__0_1 + (1 - shift_most_sig_bit_1) * shifted_read_data__2_1, shift_most_sig_bit_1 * shifted_read_data__1_1 + (1 - shift_most_sig_bit_1) * shifted_read_data__3_1, read_data_aux__base__prev_timestamp_1] mult=is_valid * 1, args=[2, mem_ptr_limbs__0_1 + 65536 * mem_ptr_limbs__1_1 + opcode_loadb_flag0_1 - (2 * shift_most_sig_bit_1 + 1), shift_most_sig_bit_1 * shifted_read_data__2_1 + (1 - shift_most_sig_bit_1) * shifted_read_data__0_1, shift_most_sig_bit_1 * shifted_read_data__3_1 + (1 - shift_most_sig_bit_1) * shifted_read_data__1_1, shift_most_sig_bit_1 * shifted_read_data__0_1 + (1 - shift_most_sig_bit_1) * shifted_read_data__2_1, shift_most_sig_bit_1 * shifted_read_data__1_1 + (1 - shift_most_sig_bit_1) * shifted_read_data__3_1, from_state__timestamp_0 + 4] mult=is_valid * -1, args=[1, 56, prev_data__0_1, prev_data__1_1, prev_data__2_1, prev_data__3_1, write_base_aux__prev_timestamp_1] -mult=is_valid * 1, args=[1, 52, opcode_loadb_flag0_0 * shifted_read_data__0_0 + (1 - opcode_loadb_flag0_0) * shifted_read_data__1_0, 255 * data_most_sig_bit_0, 255 * data_most_sig_bit_0, 255 * data_most_sig_bit_0, from_state__timestamp_0 + 6] -mult=is_valid * 1, args=[1, 56, opcode_loadb_flag0_1 * shifted_read_data__0_1 + (1 - opcode_loadb_flag0_1) * shifted_read_data__1_1, 255 * data_most_sig_bit_1, 255 * data_most_sig_bit_1, 255 * data_most_sig_bit_1, from_state__timestamp_0 + 7] +mult=is_valid * 1, args=[1, 52, a__0_2, 255 * data_most_sig_bit_0, 255 * data_most_sig_bit_0, 255 * data_most_sig_bit_0, from_state__timestamp_0 + 6] +mult=is_valid * 1, args=[1, 56, b__0_2, 255 * data_most_sig_bit_1, 255 * data_most_sig_bit_1, 255 * data_most_sig_bit_1, from_state__timestamp_0 + 7] // Bus 3 (VARIABLE_RANGE_CHECKER): mult=is_valid * 1, args=[shifted_read_data__0_0 * opcode_loadb_flag0_0 + shifted_read_data__1_0 * (1 - opcode_loadb_flag0_0) - 128 * data_most_sig_bit_0, 7] @@ -113,7 +112,9 @@ shift_most_sig_bit_1 * (shift_most_sig_bit_1 - 1) = 0 (30720 * mem_ptr_limbs__0_1 - (30720 * rs1_data__0_1 + 7864320 * rs1_data__1_1)) * (30720 * mem_ptr_limbs__0_1 - (30720 * rs1_data__0_1 + 7864320 * rs1_data__1_1 + 1)) = 0 (943718400 * rs1_data__0_1 + 30720 * mem_ptr_limbs__1_1 - (120 * rs1_data__1_1 + 30720 * rs1_data__2_1 + 7864320 * rs1_data__3_1 + 943718400 * mem_ptr_limbs__0_1)) * (943718400 * rs1_data__0_1 + 30720 * mem_ptr_limbs__1_1 - (120 * rs1_data__1_1 + 30720 * rs1_data__2_1 + 7864320 * rs1_data__3_1 + 943718400 * mem_ptr_limbs__0_1 + 1)) = 0 cmp_result_2 * (cmp_result_2 - 1) = 0 -(1 - cmp_result_2) * ((opcode_loadb_flag0_1 - 1) * shifted_read_data__1_1 + opcode_loadb_flag0_0 * shifted_read_data__0_0 + (1 - opcode_loadb_flag0_0) * shifted_read_data__1_0 - opcode_loadb_flag0_1 * shifted_read_data__0_1) = 0 +(1 - cmp_result_2) * (a__0_2 - b__0_2) = 0 (1 - cmp_result_2) * (255 * data_most_sig_bit_0 - 255 * data_most_sig_bit_1) = 0 -((opcode_loadb_flag0_1 - 1) * shifted_read_data__1_1 + opcode_loadb_flag0_0 * shifted_read_data__0_0 + (1 - opcode_loadb_flag0_0) * shifted_read_data__1_0 - opcode_loadb_flag0_1 * shifted_read_data__0_1) * diff_inv_marker__0_2 + (255 * data_most_sig_bit_0 - 255 * data_most_sig_bit_1) * diff_inv_marker__1_2 + (255 * data_most_sig_bit_0 - 255 * data_most_sig_bit_1) * diff_inv_marker__2_2 + (255 * data_most_sig_bit_0 - 255 * data_most_sig_bit_1) * diff_inv_marker__3_2 - cmp_result_2 = 0 +opcode_loadb_flag0_0 * shifted_read_data__0_0 + (1 - opcode_loadb_flag0_0) * shifted_read_data__1_0 - a__0_2 = 0 +opcode_loadb_flag0_1 * shifted_read_data__0_1 + (1 - opcode_loadb_flag0_1) * shifted_read_data__1_1 - b__0_2 = 0 +diff_inv_marker__0_2 * ((a__0_2 - b__0_2) * (a__0_2 - b__0_2) + (255 * data_most_sig_bit_0 - 255 * data_most_sig_bit_1) * (255 * data_most_sig_bit_0 - 255 * data_most_sig_bit_1) + (255 * data_most_sig_bit_0 - 255 * data_most_sig_bit_1) * (255 * data_most_sig_bit_0 - 255 * data_most_sig_bit_1) + (255 * data_most_sig_bit_0 - 255 * data_most_sig_bit_1) * (255 * data_most_sig_bit_0 - 255 * data_most_sig_bit_1)) - cmp_result_2 = 0 is_valid * (is_valid - 1) = 0 \ No newline at end of file diff --git a/openvm/tests/apc_snapshots/complex/memcpy_block.txt b/openvm/tests/apc_snapshots/complex/memcpy_block.txt index cf1f110484..f7c84abd1b 100644 --- a/openvm/tests/apc_snapshots/complex/memcpy_block.txt +++ b/openvm/tests/apc_snapshots/complex/memcpy_block.txt @@ -108,7 +108,7 @@ diff_marker__0_2 * ((writes_aux__prev_data__0_2 - 1) * (2 * cmp_result_2 - 1) + (1 - (diff_marker__0_2 + diff_marker__1_2 + diff_marker__2_2 + diff_marker__3_2)) * cmp_result_2 = 0 cmp_result_4 * (cmp_result_4 - 1) = 0 (1 - cmp_result_4) * (cmp_result_1 + cmp_result_2 - cmp_result_1 * cmp_result_2) = 0 -(cmp_result_1 + cmp_result_2 - cmp_result_1 * cmp_result_2) * diff_inv_marker__0_4 - cmp_result_4 = 0 +diff_inv_marker__0_4 * (cmp_result_1 + cmp_result_2 - cmp_result_1 * cmp_result_2) - cmp_result_4 = 0 (1 - is_valid) * (diff_marker__0_1 + diff_marker__1_1 + diff_marker__2_1 + diff_marker__3_1) = 0 (1 - is_valid) * (diff_marker__0_2 + diff_marker__1_2 + diff_marker__2_2 + diff_marker__3_2) = 0 is_valid * (is_valid - 1) = 0 \ No newline at end of file diff --git a/openvm/tests/apc_snapshots/single_instructions/single_bne.txt b/openvm/tests/apc_snapshots/single_instructions/single_bne.txt index 9b9a117315..f0075ba2d8 100644 --- a/openvm/tests/apc_snapshots/single_instructions/single_bne.txt +++ b/openvm/tests/apc_snapshots/single_instructions/single_bne.txt @@ -2,11 +2,11 @@ Instructions: BNE 8 5 2 1 1 APC advantage: - - Main columns: 26 -> 19 (1.37x reduction) + - Main columns: 26 -> 16 (1.62x reduction) - Bus interactions: 11 -> 10 (1.10x reduction) - Constraints: 11 -> 7 (1.57x reduction) -Symbolic machine using 19 unique main columns: +Symbolic machine using 16 unique main columns: from_state__timestamp_0 reads_aux__0__base__prev_timestamp_0 reads_aux__0__base__timestamp_lt_aux__lower_decomp__0_0 @@ -22,9 +22,6 @@ Symbolic machine using 19 unique main columns: b__3_0 cmp_result_0 diff_inv_marker__0_0 - diff_inv_marker__1_0 - diff_inv_marker__2_0 - diff_inv_marker__3_0 is_valid // Bus 0 (EXECUTION_BRIDGE): @@ -44,10 +41,10 @@ mult=is_valid * 1, args=[reads_aux__1__base__timestamp_lt_aux__lower_decomp__0_0 mult=is_valid * 1, args=[15360 * reads_aux__1__base__prev_timestamp_0 + 15360 * reads_aux__1__base__timestamp_lt_aux__lower_decomp__0_0 - 15360 * from_state__timestamp_0, 12] // Algebraic constraints: -cmp_result_0 * (cmp_result_0 - 1) = 0 -(1 - cmp_result_0) * (a__0_0 - b__0_0) = 0 +diff_inv_marker__0_0 * ((a__0_0 - b__0_0) * (a__0_0 - b__0_0) + (a__1_0 - b__1_0) * (a__1_0 - b__1_0) + (a__2_0 - b__2_0) * (a__2_0 - b__2_0) + (a__3_0 - b__3_0) * (a__3_0 - b__3_0)) - cmp_result_0 = 0 (1 - cmp_result_0) * (a__1_0 - b__1_0) = 0 -(1 - cmp_result_0) * (a__2_0 - b__2_0) = 0 (1 - cmp_result_0) * (a__3_0 - b__3_0) = 0 -(a__0_0 - b__0_0) * diff_inv_marker__0_0 + (a__1_0 - b__1_0) * diff_inv_marker__1_0 + (a__2_0 - b__2_0) * diff_inv_marker__2_0 + (a__3_0 - b__3_0) * diff_inv_marker__3_0 - cmp_result_0 = 0 +cmp_result_0 * (cmp_result_0 - 1) = 0 +(1 - cmp_result_0) * (a__2_0 - b__2_0) = 0 +(1 - cmp_result_0) * (a__0_0 - b__0_0) = 0 is_valid * (is_valid - 1) = 0 \ No newline at end of file From 4c7c2cd757c66688c4da5e5460bdae49b9fc92e0 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 24 Nov 2025 17:21:19 +0000 Subject: [PATCH 32/60] Make deterministic and cleanup. --- autoprecompiles/src/rule_based_optimizer.rs | 235 ++++++++++-------- .../single_instructions/single_bne.txt | 8 +- 2 files changed, 130 insertions(+), 113 deletions(-) diff --git a/autoprecompiles/src/rule_based_optimizer.rs b/autoprecompiles/src/rule_based_optimizer.rs index c71da67c4c..c368d8380d 100644 --- a/autoprecompiles/src/rule_based_optimizer.rs +++ b/autoprecompiles/src/rule_based_optimizer.rs @@ -13,7 +13,7 @@ use std::{ use itertools::{EitherOrBoth, Itertools}; use powdr_constraint_solver::{ algebraic_constraint, - constraint_system::{BusInteraction, BusInteractionHandler, ConstraintSystem}, + constraint_system::{BusInteraction, BusInteractionHandler}, grouped_expression::{GroupedExpression, GroupedExpressionComponent, RangeConstraintProvider}, indexed_constraint_system::IndexedConstraintSystem, range_constraint::RangeConstraint, @@ -98,6 +98,10 @@ impl System { range_constraints_on_vars, } } + + fn expression_db(self) -> ExpressionDB { + self.expressions.into_inner() + } } impl PartialEq for System { @@ -314,6 +318,13 @@ impl RangeConstraintProvider for System { #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] struct Expr(usize); +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +enum Action { + SubstituteVariableByConstant(Var, F), + SubstituteVariableByVariable(Var, Var), + ReplaceAlgebraicConstraintBy(Expr, Expr), +} + crepe! { @input struct S<'a>(&'a System); @@ -470,7 +481,6 @@ crepe! { AffineExpression(v2_, coeff2, v2, offset2), (offset2.is_zero()); - @output struct PotentiallyReplaceAlgebraicConstraintBy(Expr, Expr); PotentiallyReplaceAlgebraicConstraintBy(e, replacement) <- @@ -569,27 +579,18 @@ crepe! { ReplaceAlgebraicConstraintBy(_, e); - // This constraint has been replaced by a different one (or is redundant). - struct AlgebraicConstraintDeleted(Expr); - AlgebraicConstraintDeleted(e) <- - ReplaceAlgebraicConstraintBy(e, _); - AlgebraicConstraintDeleted(e) <- - S(sys), - AlgebraicConstraint(e), - (sys.is_zero(e)); - - @output - struct Change(); - Change() <- ReplaceAlgebraicConstraintBy(_, _); - Change() <- AlgebraicConstraintDeleted(_); - @output - struct FinalAlgebraicConstraint(Expr); - FinalAlgebraicConstraint(e) <- AlgebraicConstraint(e), !AlgebraicConstraintDeleted(e); + struct ActionRule(Action); + ActionRule(Action::SubstituteVariableByConstant(v, val)) <- + Assignment(v, val); + ActionRule(Action::SubstituteVariableByVariable(v, v2)) <- + Equivalence(v, v2); + ActionRule(Action::ReplaceAlgebraicConstraintBy(old, new)) <- + PotentiallyReplaceAlgebraicConstraintBy(old, new); } pub fn rule_based_optimization( - system: IndexedConstraintSystem, + mut system: IndexedConstraintSystem, range_constraints: impl RangeConstraintProvider, _bus_interaction_handler: impl BusInteractionHandler + Clone, ) -> IndexedConstraintSystem { @@ -598,14 +599,91 @@ pub fn rule_based_optimization { + system.substitute_by_known( + var_mapper.backward(&var), + &untransform_field_element(&val), + ); + progress = true; + } + Action::SubstituteVariableByVariable(v1, v2) => { + system.substitute_by_unknown( + var_mapper.backward(&v1), + &GroupedExpression::from_unknown_variable(var_mapper.backward(&v2).clone()), + ); + progress = true; + } + Action::ReplaceAlgebraicConstraintBy(e1, e2) => { + let expr1 = untransform_grouped_expression(&sys.extract(e1), &var_mapper); + // TODO more efficient? + let mut found = false; + system.retain_algebraic_constraints(|c| { + if c.expression == expr1 { + found = true; + false + } else { + true + } + }); + if found { + let expr2 = untransform_grouped_expression(&sys.extract(e2), &var_mapper); + system.add_algebraic_constraints([ + algebraic_constraint::AlgebraicConstraint::assert_zero(expr2), + ]); + progress = true; + } else { + log::warn!( + "Was about to replace {expr1} but did not find it in the system." + ); + } + } + } + } + if !progress { + break; + } + db = Some(sys.expression_db()); + } + system +} + +// TODO use an expression cache so that we don't even +// have to transform the field elements. +fn transform_constraint_system( + system: &IndexedConstraintSystem, + range_constraints: impl RangeConstraintProvider, + var_mapper: &mut VarMapper, + mut expression_db: ExpressionDB, +) -> (Vec, Vec>, System) { let algebraic_constraints = system .system() .algebraic_constraints .iter() - .map(|c| transform_grouped_expression(&c.expression, &mut var_mapper)) - .map(|e| db.insert_owned(e)) + .map(|c| transform_grouped_expression(&c.expression, var_mapper)) + .map(|e| expression_db.insert_owned(e)) .collect_vec(); let bus_interactions: Vec> = system .system() @@ -614,8 +692,8 @@ pub fn rule_based_optimization>(); - let sys = System::new( - db, - var_names, - single_occurrence_variables, - range_constraints_on_vars, - ); - - let (algebraic_constraints, bus_interactions) = - apply_rules(algebraic_constraints, bus_interactions, &sys); - - let algebraic_constraints = algebraic_constraints.into_iter().map(|e| { - let expr = sys.extract(e); - algebraic_constraint::AlgebraicConstraint::assert_zero( - untransform_grouped_expression::(&expr, &var_mapper), - ) - }); - let bus_interactions = bus_interactions - .into_iter() - .map(|bus_inter| { - bus_inter - .fields() - .map(|&e| { - let expr = sys.extract(e); - untransform_grouped_expression::(&expr, &var_mapper) - }) - .collect::>>() - }) - .collect_vec(); - ConstraintSystem { - algebraic_constraints: algebraic_constraints.collect(), + ( + algebraic_constraints, bus_interactions, - derived_variables: system.system().derived_variables.clone(), - } - .into() -} - -fn apply_rules( - mut algebraic_constraints: Vec, - bus_interactions: Vec>, - sys: &System, -) -> (Vec, Vec>) { - loop { - let mut progress = false; - let mut rt = Crepe::new(); - - rt.extend( - algebraic_constraints - .iter() - .copied() - .map(InitialAlgebraicConstraint), - ); - rt.extend(std::iter::once(S(sys))); - - let (replacements, change, mut final_constrs) = rt.run(); - progress |= !change.is_empty(); - - // We perform the constraint-by-constraint replacements instead of - // in the system because there can be multiple matches. - for PotentiallyReplaceAlgebraicConstraintBy(old, new) in &replacements { - if final_constrs.contains(&FinalAlgebraicConstraint(*old)) { - final_constrs.remove(&FinalAlgebraicConstraint(*old)); - final_constrs.insert(FinalAlgebraicConstraint(*new)); - progress = true; - } - } - // TODO this could all be non-deterministic. - algebraic_constraints = final_constrs - .iter() - .map(|FinalAlgebraicConstraint(e)| *e) - .collect(); - - if !progress { - break; - } - } - (algebraic_constraints, bus_interactions) + System::new( + expression_db, + var_names, + single_occurrence_variables, + range_constraints_on_vars, + ), + ) } #[derive(Clone)] @@ -778,10 +790,10 @@ fn transform_grouped_expression { GroupedExpression::from_unknown_variable(var_mapper.forward(&v)) - * BabyBearField::from(c.to_arbitrary_integer()) + * transform_field_element(&c) } GroupedExpressionComponent::Constant(c) => { - GroupedExpression::from_number(c.to_arbitrary_integer().into()) + GroupedExpression::from_number(transform_field_element(&c)) } }) .sum() @@ -799,26 +811,31 @@ fn untransform_grouped_expression { - GroupedExpression::from_unknown_variable(var_mapper.backward(&v).clone()) - * T::from(c.to_arbitrary_integer()) + GroupedExpression::::from_unknown_variable(var_mapper.backward(&v).clone()) + * untransform_field_element::(&c) } GroupedExpressionComponent::Constant(c) => { - GroupedExpression::from_number(c.to_arbitrary_integer().into()) + GroupedExpression::from_number(untransform_field_element(&c)) } }) .sum() } +fn transform_field_element(fe: &T) -> BabyBearField { + BabyBearField::from(fe.to_arbitrary_integer()) +} + +fn untransform_field_element(fe: &BabyBearField) -> T { + T::from(fe.to_arbitrary_integer()) +} + fn transform_range_constraint( rc: &RangeConstraint, ) -> RangeConstraint { let (min, max) = rc.range(); let mask = *rc.mask(); - RangeConstraint::from_range( - BabyBearField::from(min.to_arbitrary_integer()), - BabyBearField::from(max.to_arbitrary_integer()), - ) - .conjunction(&RangeConstraint::from_mask(mask.try_into_u64().unwrap())) + RangeConstraint::from_range(transform_field_element(&min), transform_field_element(&max)) + .conjunction(&RangeConstraint::from_mask(mask.try_into_u64().unwrap())) } #[cfg(test)] diff --git a/openvm/tests/apc_snapshots/single_instructions/single_bne.txt b/openvm/tests/apc_snapshots/single_instructions/single_bne.txt index f0075ba2d8..c46391cf3c 100644 --- a/openvm/tests/apc_snapshots/single_instructions/single_bne.txt +++ b/openvm/tests/apc_snapshots/single_instructions/single_bne.txt @@ -41,10 +41,10 @@ mult=is_valid * 1, args=[reads_aux__1__base__timestamp_lt_aux__lower_decomp__0_0 mult=is_valid * 1, args=[15360 * reads_aux__1__base__prev_timestamp_0 + 15360 * reads_aux__1__base__timestamp_lt_aux__lower_decomp__0_0 - 15360 * from_state__timestamp_0, 12] // Algebraic constraints: -diff_inv_marker__0_0 * ((a__0_0 - b__0_0) * (a__0_0 - b__0_0) + (a__1_0 - b__1_0) * (a__1_0 - b__1_0) + (a__2_0 - b__2_0) * (a__2_0 - b__2_0) + (a__3_0 - b__3_0) * (a__3_0 - b__3_0)) - cmp_result_0 = 0 -(1 - cmp_result_0) * (a__1_0 - b__1_0) = 0 -(1 - cmp_result_0) * (a__3_0 - b__3_0) = 0 cmp_result_0 * (cmp_result_0 - 1) = 0 -(1 - cmp_result_0) * (a__2_0 - b__2_0) = 0 (1 - cmp_result_0) * (a__0_0 - b__0_0) = 0 +(1 - cmp_result_0) * (a__1_0 - b__1_0) = 0 +(1 - cmp_result_0) * (a__2_0 - b__2_0) = 0 +(1 - cmp_result_0) * (a__3_0 - b__3_0) = 0 +diff_inv_marker__0_0 * ((a__0_0 - b__0_0) * (a__0_0 - b__0_0) + (a__1_0 - b__1_0) * (a__1_0 - b__1_0) + (a__2_0 - b__2_0) * (a__2_0 - b__2_0) + (a__3_0 - b__3_0) * (a__3_0 - b__3_0)) - cmp_result_0 = 0 is_valid * (is_valid - 1) = 0 \ No newline at end of file From 306adb38815a72597d977552a462d77e69e77df9 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 24 Nov 2025 17:35:40 +0000 Subject: [PATCH 33/60] Add size limit and update expectations. --- autoprecompiles/src/rule_based_optimizer.rs | 12 ++++++++++++ openvm/src/lib.rs | 2 +- .../tests/apc_snapshots/pseudo_instructions/beqz.txt | 9 +++------ .../tests/apc_snapshots/pseudo_instructions/bnez.txt | 9 +++------ .../apc_snapshots/single_instructions/single_beq.txt | 9 +++------ .../single_instructions/single_loadhu.txt | 3 ++- 6 files changed, 24 insertions(+), 20 deletions(-) diff --git a/autoprecompiles/src/rule_based_optimizer.rs b/autoprecompiles/src/rule_based_optimizer.rs index c368d8380d..ea784fc474 100644 --- a/autoprecompiles/src/rule_based_optimizer.rs +++ b/autoprecompiles/src/rule_based_optimizer.rs @@ -25,6 +25,8 @@ use num_traits::{One, Zero}; use crepe::crepe; +const SIZE_LIMIT: usize = 400; + type F = BabyBearField; #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] @@ -534,6 +536,7 @@ crepe! { }).map(GroupedExpression::from).sum::>(); // TODO we actually need to insert a new variable at this point. let replacement = r + GroupedExpression::from_unknown_variable(v1) * (x1 * coeff1 + x2 * coeff2); + // v1 = -r / ((x1 * coeff1 + x2 * coeff2)) Some(sys.insert_owned(replacement)) })(); @@ -597,6 +600,14 @@ pub fn rule_based_optimization SIZE_LIMIT { + log::debug!( + "Skipping rule-based optimization because the system is too large ({} > {}).", + system.system().algebraic_constraints.len(), + SIZE_LIMIT + ); + return system; + } let mut var_mapper = Default::default(); let mut db = Some(ExpressionDB::default()); @@ -667,6 +678,7 @@ pub fn rule_based_optimization 15 (1.73x reduction) + - Main columns: 26 -> 12 (2.17x reduction) - Bus interactions: 11 -> 10 (1.10x reduction) - Constraints: 11 -> 7 (1.57x reduction) -Symbolic machine using 15 unique main columns: +Symbolic machine using 12 unique main columns: from_state__timestamp_0 reads_aux__0__base__prev_timestamp_0 reads_aux__0__base__timestamp_lt_aux__lower_decomp__0_0 @@ -18,9 +18,6 @@ Symbolic machine using 15 unique main columns: a__3_0 cmp_result_0 diff_inv_marker__0_0 - diff_inv_marker__1_0 - diff_inv_marker__2_0 - diff_inv_marker__3_0 is_valid // Bus 0 (EXECUTION_BRIDGE): @@ -45,5 +42,5 @@ cmp_result_0 * a__0_0 = 0 cmp_result_0 * a__1_0 = 0 cmp_result_0 * a__2_0 = 0 cmp_result_0 * a__3_0 = 0 -a__0_0 * diff_inv_marker__0_0 + a__1_0 * diff_inv_marker__1_0 + a__2_0 * diff_inv_marker__2_0 + a__3_0 * diff_inv_marker__3_0 + cmp_result_0 - 1 * is_valid = 0 +diff_inv_marker__0_0 * (a__0_0 + a__1_0 + a__2_0 + a__3_0) + cmp_result_0 - 1 * is_valid = 0 is_valid * (is_valid - 1) = 0 \ No newline at end of file diff --git a/openvm/tests/apc_snapshots/pseudo_instructions/bnez.txt b/openvm/tests/apc_snapshots/pseudo_instructions/bnez.txt index 786e3455a7..eea9d85d15 100644 --- a/openvm/tests/apc_snapshots/pseudo_instructions/bnez.txt +++ b/openvm/tests/apc_snapshots/pseudo_instructions/bnez.txt @@ -2,11 +2,11 @@ Instructions: BNE 5 0 8 1 1 APC advantage: - - Main columns: 26 -> 15 (1.73x reduction) + - Main columns: 26 -> 12 (2.17x reduction) - Bus interactions: 11 -> 10 (1.10x reduction) - Constraints: 11 -> 7 (1.57x reduction) -Symbolic machine using 15 unique main columns: +Symbolic machine using 12 unique main columns: from_state__timestamp_0 reads_aux__0__base__prev_timestamp_0 reads_aux__0__base__timestamp_lt_aux__lower_decomp__0_0 @@ -18,9 +18,6 @@ Symbolic machine using 15 unique main columns: a__3_0 cmp_result_0 diff_inv_marker__0_0 - diff_inv_marker__1_0 - diff_inv_marker__2_0 - diff_inv_marker__3_0 is_valid // Bus 0 (EXECUTION_BRIDGE): @@ -45,5 +42,5 @@ cmp_result_0 * (cmp_result_0 - 1) = 0 (1 - cmp_result_0) * a__1_0 = 0 (1 - cmp_result_0) * a__2_0 = 0 (1 - cmp_result_0) * a__3_0 = 0 -a__0_0 * diff_inv_marker__0_0 + a__1_0 * diff_inv_marker__1_0 + a__2_0 * diff_inv_marker__2_0 + a__3_0 * diff_inv_marker__3_0 - cmp_result_0 = 0 +diff_inv_marker__0_0 * (a__0_0 + a__1_0 + a__2_0 + a__3_0) - cmp_result_0 = 0 is_valid * (is_valid - 1) = 0 \ No newline at end of file diff --git a/openvm/tests/apc_snapshots/single_instructions/single_beq.txt b/openvm/tests/apc_snapshots/single_instructions/single_beq.txt index 277aca5da9..dc17cb086d 100644 --- a/openvm/tests/apc_snapshots/single_instructions/single_beq.txt +++ b/openvm/tests/apc_snapshots/single_instructions/single_beq.txt @@ -2,11 +2,11 @@ Instructions: BEQ 8 5 2 1 1 APC advantage: - - Main columns: 26 -> 19 (1.37x reduction) + - Main columns: 26 -> 16 (1.62x reduction) - Bus interactions: 11 -> 10 (1.10x reduction) - Constraints: 11 -> 7 (1.57x reduction) -Symbolic machine using 19 unique main columns: +Symbolic machine using 16 unique main columns: from_state__timestamp_0 reads_aux__0__base__prev_timestamp_0 reads_aux__0__base__timestamp_lt_aux__lower_decomp__0_0 @@ -22,9 +22,6 @@ Symbolic machine using 19 unique main columns: b__3_0 cmp_result_0 diff_inv_marker__0_0 - diff_inv_marker__1_0 - diff_inv_marker__2_0 - diff_inv_marker__3_0 is_valid // Bus 0 (EXECUTION_BRIDGE): @@ -49,5 +46,5 @@ cmp_result_0 * (a__0_0 - b__0_0) = 0 cmp_result_0 * (a__1_0 - b__1_0) = 0 cmp_result_0 * (a__2_0 - b__2_0) = 0 cmp_result_0 * (a__3_0 - b__3_0) = 0 -(a__0_0 - b__0_0) * diff_inv_marker__0_0 + (a__1_0 - b__1_0) * diff_inv_marker__1_0 + (a__2_0 - b__2_0) * diff_inv_marker__2_0 + (a__3_0 - b__3_0) * diff_inv_marker__3_0 + cmp_result_0 - 1 * is_valid = 0 +diff_inv_marker__0_0 * ((a__0_0 - b__0_0) * (a__0_0 - b__0_0) + (a__1_0 - b__1_0) * (a__1_0 - b__1_0) + (a__2_0 - b__2_0) * (a__2_0 - b__2_0) + (a__3_0 - b__3_0) * (a__3_0 - b__3_0)) + cmp_result_0 - 1 * is_valid = 0 is_valid * (is_valid - 1) = 0 \ No newline at end of file diff --git a/openvm/tests/apc_snapshots/single_instructions/single_loadhu.txt b/openvm/tests/apc_snapshots/single_instructions/single_loadhu.txt index 90a0c9618b..e028e91d03 100644 --- a/openvm/tests/apc_snapshots/single_instructions/single_loadhu.txt +++ b/openvm/tests/apc_snapshots/single_instructions/single_loadhu.txt @@ -4,7 +4,7 @@ Instructions: APC advantage: - Main columns: 41 -> 18 (2.28x reduction) - Bus interactions: 17 -> 12 (1.42x reduction) - - Constraints: 25 -> 8 (3.12x reduction) + - Constraints: 25 -> 9 (2.78x reduction) Symbolic machine using 18 unique main columns: from_state__timestamp_0 @@ -52,4 +52,5 @@ flags__2_0 * ((flags__2_0 - 1) * (flags__2_0 - 2)) = 0 (30720 * mem_ptr_limbs__0_0 - (30720 * rs1_data__0_0 + 7864320 * rs1_data__1_0 + 675840 * is_valid)) * (30720 * mem_ptr_limbs__0_0 - (30720 * rs1_data__0_0 + 7864320 * rs1_data__1_0 + 675841)) = 0 (943718400 * rs1_data__0_0 + 30720 * mem_ptr_limbs__1_0 + 629145590 * is_valid - (120 * rs1_data__1_0 + 30720 * rs1_data__2_0 + 7864320 * rs1_data__3_0 + 943718400 * mem_ptr_limbs__0_0)) * (943718400 * rs1_data__0_0 + 30720 * mem_ptr_limbs__1_0 + 629145589 - (120 * rs1_data__1_0 + 30720 * rs1_data__2_0 + 7864320 * rs1_data__3_0 + 943718400 * mem_ptr_limbs__0_0)) = 0 flags__1_0 * (flags__1_0 - 1) + flags__2_0 * (flags__2_0 - 1) + 5 * flags__1_0 * flags__2_0 - (flags__1_0 * (flags__1_0 + flags__2_0 - 2) + flags__2_0 * (flags__1_0 + flags__2_0 - 2) + 2 * is_valid) = 0 +flags__1_0 * flags__2_0 = 0 is_valid * (is_valid - 1) = 0 \ No newline at end of file From 80c10f8692fe9e1de07ddd8ead177c3308194a79 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 24 Nov 2025 17:38:57 +0000 Subject: [PATCH 34/60] Update expectations. --- autoprecompiles/src/rule_based_optimizer.rs | 2 +- openvm/tests/optimizer.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/autoprecompiles/src/rule_based_optimizer.rs b/autoprecompiles/src/rule_based_optimizer.rs index ea784fc474..be3ecb6ea0 100644 --- a/autoprecompiles/src/rule_based_optimizer.rs +++ b/autoprecompiles/src/rule_based_optimizer.rs @@ -25,7 +25,7 @@ use num_traits::{One, Zero}; use crepe::crepe; -const SIZE_LIMIT: usize = 400; +const SIZE_LIMIT: usize = 800; type F = BabyBearField; diff --git a/openvm/tests/optimizer.rs b/openvm/tests/optimizer.rs index 10e59c3136..f136e64bc0 100644 --- a/openvm/tests/optimizer.rs +++ b/openvm/tests/optimizer.rs @@ -50,7 +50,7 @@ fn test_optimize() { // This cbor file above has the `is_valid` column removed, this is why the number below // might be one less than in other tests. expect![[r#" - 1756 + 1753 "#]] .assert_debug_eq(&machine.main_columns().count()); expect![[r#" From 9c1569807be481f5e61bfbfecae3281c63a26a8f Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 25 Nov 2025 13:38:04 +0000 Subject: [PATCH 35/60] Give access to column allocator. --- autoprecompiles/src/lib.rs | 15 ++++++++++++--- autoprecompiles/src/optimizer.rs | 12 ++++++++++-- openvm/benches/optimizer_benchmark.rs | 4 +++- openvm/tests/optimizer.rs | 12 +++++++++--- 4 files changed, 34 insertions(+), 9 deletions(-) diff --git a/autoprecompiles/src/lib.rs b/autoprecompiles/src/lib.rs index e4e5a3b6d3..175849a5e5 100644 --- a/autoprecompiles/src/lib.rs +++ b/autoprecompiles/src/lib.rs @@ -393,7 +393,8 @@ impl Apc { } /// Allocates global poly_ids and keeps track of substitutions -struct ColumnAllocator { +#[derive(Clone)] +pub struct ColumnAllocator { /// For each original air, for each original column index, the associated poly_id in the APC air subs: Vec>, /// The next poly_id to issue @@ -401,7 +402,14 @@ struct ColumnAllocator { } impl ColumnAllocator { - fn issue_next_poly_id(&mut self) -> u64 { + pub fn from_max_poly_id_of_machine(machine: &SymbolicMachine) -> Self { + Self { + subs: Vec::new(), + next_poly_id: machine.main_columns().map(|c| c.id).max().unwrap_or(0) + 1, + } + } + + pub fn issue_next_poly_id(&mut self) -> u64 { let id = self.next_poly_id; self.next_poly_id += 1; id @@ -430,11 +438,12 @@ pub fn build( metrics::counter!("before_opt_interactions", &labels) .absolute(machine.unique_references().count() as u64); - let machine = optimizer::optimize::( + let (machine, column_allocator) = optimizer::optimize::( machine, vm_config.bus_interaction_handler, degree_bound, &vm_config.bus_map, + column_allocator, )?; // add guards to constraints that are not satisfied by zeroes diff --git a/autoprecompiles/src/optimizer.rs b/autoprecompiles/src/optimizer.rs index 1606736b8c..f5649ffdc2 100644 --- a/autoprecompiles/src/optimizer.rs +++ b/autoprecompiles/src/optimizer.rs @@ -16,6 +16,7 @@ use powdr_number::FieldElement; use crate::constraint_optimizer::trivial_simplifications; use crate::range_constraint_optimizer::optimize_range_constraints; +use crate::ColumnAllocator; use crate::{ adapter::Adapter, constraint_optimizer::optimize_constraints, @@ -26,12 +27,16 @@ use crate::{ BusMap, BusType, DegreeBound, SymbolicBusInteraction, SymbolicMachine, }; +/// Optimizes a given symbolic machine and returns an equivalent, but "simpler" one. +/// All constraints in the returned machine will respect the given degree bound. +/// New variables may be introduced in the process. pub fn optimize( mut machine: SymbolicMachine, bus_interaction_handler: A::BusInteractionHandler, degree_bound: DegreeBound, bus_map: &BusMap, -) -> Result, crate::constraint_optimizer::Error> { + column_allocator: ColumnAllocator, +) -> Result<(SymbolicMachine, ColumnAllocator), crate::constraint_optimizer::Error> { let mut stats_logger = StatsLogger::start(&machine); if let Some(exec_bus_id) = bus_map.get_bus_id(&BusType::ExecutionBridge) { @@ -112,7 +117,10 @@ pub fn optimize( == GroupedExpression::from_number(A::PowdrField::from(pc_lookup_bus_id))), "Expected all PC lookups to be removed." ); - Ok(constraint_system_to_symbolic_machine(constraint_system)) + Ok(( + constraint_system_to_symbolic_machine(constraint_system), + column_allocator, + )) } pub fn optimize_exec_bus( diff --git a/openvm/benches/optimizer_benchmark.rs b/openvm/benches/optimizer_benchmark.rs index 727662ad63..a6833291de 100644 --- a/openvm/benches/optimizer_benchmark.rs +++ b/openvm/benches/optimizer_benchmark.rs @@ -1,5 +1,5 @@ use criterion::{black_box, criterion_group, criterion_main, Criterion}; -use powdr_autoprecompiles::{optimizer::optimize, SymbolicMachine}; +use powdr_autoprecompiles::{optimizer::optimize, ColumnAllocator, SymbolicMachine}; use powdr_number::BabyBearField; use powdr_openvm::{ bus_interaction_handler::OpenVmBusInteractionHandler, bus_map::default_openvm_bus_map, @@ -14,6 +14,7 @@ fn optimize_keccak_benchmark(c: &mut Criterion) { let file = std::fs::File::open("tests/keccak_apc_pre_opt.cbor").unwrap(); let reader = std::io::BufReader::new(file); let machine: SymbolicMachine = serde_cbor::from_reader(reader).unwrap(); + let column_allocator = ColumnAllocator::from_max_poly_id_of_machine(&machine); group.bench_function("optimize", |b| { b.iter_batched( @@ -24,6 +25,7 @@ fn optimize_keccak_benchmark(c: &mut Criterion) { OpenVmBusInteractionHandler::default(), DEFAULT_DEGREE_BOUND, &default_openvm_bus_map(), + column_allocator.clone(), ) .unwrap() }, diff --git a/openvm/tests/optimizer.rs b/openvm/tests/optimizer.rs index 10e59c3136..3a0cf4948b 100644 --- a/openvm/tests/optimizer.rs +++ b/openvm/tests/optimizer.rs @@ -1,6 +1,6 @@ use expect_test::expect; use powdr_autoprecompiles::optimizer::optimize; -use powdr_autoprecompiles::SymbolicMachine; +use powdr_autoprecompiles::{ColumnAllocator, SymbolicMachine}; use powdr_number::BabyBearField; use powdr_openvm::{ bus_interaction_handler::OpenVmBusInteractionHandler, bus_map::default_openvm_bus_map, @@ -39,13 +39,16 @@ fn test_optimize() { let machine: SymbolicMachine = serde_cbor::from_reader(reader).unwrap(); assert!(machine.derived_columns.is_empty()); + let column_allocator = ColumnAllocator::from_max_poly_id_of_machine(&machine); let machine = optimize::( machine, OpenVmBusInteractionHandler::default(), DEFAULT_DEGREE_BOUND, &default_openvm_bus_map(), + column_allocator, ) - .unwrap(); + .unwrap() + .0; // This cbor file above has the `is_valid` column removed, this is why the number below // might be one less than in other tests. @@ -69,14 +72,17 @@ fn test_sha256() { let reader = flate2::read::GzDecoder::new(file); let machine: SymbolicMachine = serde_cbor::from_reader(reader).unwrap(); assert!(machine.derived_columns.is_empty()); + let column_allocator = ColumnAllocator::from_max_poly_id_of_machine(&machine); let machine = optimize::( machine, OpenVmBusInteractionHandler::default(), DEFAULT_DEGREE_BOUND, &default_openvm_bus_map(), + column_allocator, ) - .unwrap(); + .unwrap() + .0; // This cbor file above has the `is_valid` column removed, this is why the number below // might be one less than in other tests. From 44dbb4014d9758ba7c9ddeba00393197d163e2d8 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 25 Nov 2025 14:42:25 +0000 Subject: [PATCH 36/60] Remove clone. --- autoprecompiles/src/lib.rs | 1 - openvm/benches/optimizer_benchmark.rs | 13 ++++++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/autoprecompiles/src/lib.rs b/autoprecompiles/src/lib.rs index 175849a5e5..7d6ccee94c 100644 --- a/autoprecompiles/src/lib.rs +++ b/autoprecompiles/src/lib.rs @@ -393,7 +393,6 @@ impl Apc { } /// Allocates global poly_ids and keeps track of substitutions -#[derive(Clone)] pub struct ColumnAllocator { /// For each original air, for each original column index, the associated poly_id in the APC air subs: Vec>, diff --git a/openvm/benches/optimizer_benchmark.rs b/openvm/benches/optimizer_benchmark.rs index a6833291de..efe5ec824c 100644 --- a/openvm/benches/optimizer_benchmark.rs +++ b/openvm/benches/optimizer_benchmark.rs @@ -14,18 +14,21 @@ fn optimize_keccak_benchmark(c: &mut Criterion) { let file = std::fs::File::open("tests/keccak_apc_pre_opt.cbor").unwrap(); let reader = std::io::BufReader::new(file); let machine: SymbolicMachine = serde_cbor::from_reader(reader).unwrap(); - let column_allocator = ColumnAllocator::from_max_poly_id_of_machine(&machine); - group.bench_function("optimize", |b| { b.iter_batched( - || machine.clone(), - |machine| { + || { + ( + machine.clone(), + ColumnAllocator::from_max_poly_id_of_machine(&machine), + ) + }, + |(machine, column_allocator)| { optimize::( black_box(machine), OpenVmBusInteractionHandler::default(), DEFAULT_DEGREE_BOUND, &default_openvm_bus_map(), - column_allocator.clone(), + column_allocator, ) .unwrap() }, From 90e5163e26259409fa33e193b52182e10df034c9 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 25 Nov 2025 14:34:30 +0000 Subject: [PATCH 37/60] Replace computation method "InverseOrZero" by "QuotientOrZero". --- autoprecompiles/src/optimizer.rs | 14 +++++++----- .../src/symbolic_machine_generator.rs | 5 +++-- constraint-solver/src/constraint_system.rs | 22 ++++++++++++------- .../src/indexed_constraint_system.rs | 22 ++++++++++--------- .../trace_generator/cpu/mod.rs | 11 ++++++---- .../trace_generator/cuda/mod.rs | 6 +++-- 6 files changed, 48 insertions(+), 32 deletions(-) diff --git a/autoprecompiles/src/optimizer.rs b/autoprecompiles/src/optimizer.rs index 1606736b8c..29113d447d 100644 --- a/autoprecompiles/src/optimizer.rs +++ b/autoprecompiles/src/optimizer.rs @@ -209,9 +209,10 @@ fn symbolic_machine_to_constraint_system( .map(|(v, method)| { let method = match method { ComputationMethod::Constant(c) => ComputationMethod::Constant(*c), - ComputationMethod::InverseOrZero(c) => { - ComputationMethod::InverseOrZero(algebraic_to_grouped_expression(c)) - } + ComputationMethod::QuotientOrZero(e1, e2) => ComputationMethod::QuotientOrZero( + algebraic_to_grouped_expression(e1), + algebraic_to_grouped_expression(e2), + ), }; DerivedVariable { variable: v.clone(), @@ -242,9 +243,10 @@ fn constraint_system_to_symbolic_machine( .map(|derived_var| { let method = match derived_var.computation_method { ComputationMethod::Constant(c) => ComputationMethod::Constant(c), - ComputationMethod::InverseOrZero(c) => { - ComputationMethod::InverseOrZero(grouped_expression_to_algebraic(c)) - } + ComputationMethod::QuotientOrZero(e1, e2) => ComputationMethod::QuotientOrZero( + grouped_expression_to_algebraic(e1), + grouped_expression_to_algebraic(e2), + ), }; (derived_var.variable, method) }) diff --git a/autoprecompiles/src/symbolic_machine_generator.rs b/autoprecompiles/src/symbolic_machine_generator.rs index a0c10500ac..c3fe988858 100644 --- a/autoprecompiles/src/symbolic_machine_generator.rs +++ b/autoprecompiles/src/symbolic_machine_generator.rs @@ -35,8 +35,9 @@ pub fn convert_machine_field_type( ComputationMethod::Constant(c) => { ComputationMethod::Constant(convert_field_element(c)) } - ComputationMethod::InverseOrZero(e) => ComputationMethod::InverseOrZero( - convert_expression(e, convert_field_element), + ComputationMethod::QuotientOrZero(e1, e2) => ComputationMethod::QuotientOrZero( + convert_expression(e1, convert_field_element), + convert_expression(e2, convert_field_element), ), }; (v, method) diff --git a/constraint-solver/src/constraint_system.rs b/constraint-solver/src/constraint_system.rs index 3c3a76ebbe..dc4580836e 100644 --- a/constraint-solver/src/constraint_system.rs +++ b/constraint-solver/src/constraint_system.rs @@ -94,15 +94,16 @@ pub struct DerivedVariable { pub enum ComputationMethod { /// A constant value. Constant(T), - /// The field inverse of an expression if it exists or zero otherwise. - InverseOrZero(E), + /// The quotiont (using inversion in the field) of the first argument + /// by the second argument, or zero if the latter is zero. + QuotientOrZero(E, E), } impl Display for ComputationMethod { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { ComputationMethod::Constant(c) => write!(f, "{c}"), - ComputationMethod::InverseOrZero(e) => write!(f, "InverseOrZero({e})"), + ComputationMethod::QuotientOrZero(e1, e2) => write!(f, "QuotientOrZero({e1}, {e2})"), } } } @@ -112,7 +113,10 @@ impl ComputationMethod> { pub fn referenced_unknown_variables(&self) -> Box + '_> { match self { ComputationMethod::Constant(_) => Box::new(std::iter::empty()), - ComputationMethod::InverseOrZero(e) => e.referenced_unknown_variables(), + ComputationMethod::QuotientOrZero(e1, e2) => Box::new( + e1.referenced_unknown_variables() + .chain(e2.referenced_unknown_variables()), + ), } } } @@ -125,8 +129,9 @@ impl, V: Ord + Clone + Eq> pub fn substitute_by_known(&mut self, variable: &V, substitution: &T) { match self { ComputationMethod::Constant(_) => {} - ComputationMethod::InverseOrZero(e) => { - e.substitute_by_known(variable, substitution); + ComputationMethod::QuotientOrZero(e1, e2) => { + e1.substitute_by_known(variable, substitution); + e2.substitute_by_known(variable, substitution); } } } @@ -138,8 +143,9 @@ impl, V: Ord + Clone + Eq> pub fn substitute_by_unknown(&mut self, variable: &V, substitution: &GroupedExpression) { match self { ComputationMethod::Constant(_) => {} - ComputationMethod::InverseOrZero(e) => { - e.substitute_by_unknown(variable, substitution); + ComputationMethod::QuotientOrZero(e1, e2) => { + e1.substitute_by_unknown(variable, substitution); + e2.substitute_by_unknown(variable, substitution); } } } diff --git a/constraint-solver/src/indexed_constraint_system.rs b/constraint-solver/src/indexed_constraint_system.rs index 45c260ad41..abb3808987 100644 --- a/constraint-solver/src/indexed_constraint_system.rs +++ b/constraint-solver/src/indexed_constraint_system.rs @@ -829,30 +829,32 @@ mod tests { derived_variables: vec![ DerivedVariable { variable: "d1", - computation_method: ComputationMethod::InverseOrZero( - GroupedExpression::from_unknown_variable("x"), + computation_method: ComputationMethod::QuotientOrZero( + GroupedExpression::from_unknown_variable("x1"), + GroupedExpression::from_unknown_variable("x2"), ), }, DerivedVariable { variable: "d2", - computation_method: ComputationMethod::InverseOrZero( - GroupedExpression::from_unknown_variable("y"), + computation_method: ComputationMethod::QuotientOrZero( + GroupedExpression::from_unknown_variable("y1"), + GroupedExpression::from_unknown_variable("y2"), ), }, ], } .into(); - // We first substitute `y` by an expression that contains `x` such that when we - // substitute `x` in the next step, `d2` has to be updated again. + // We first substitute `y2` by an expression that contains `x1` such that when we + // substitute `x1` in the next step, `d2` has to be updated again. system.substitute_by_unknown( - &"y", - &(GroupedExpression::from_unknown_variable("x") + &"y2", + &(GroupedExpression::from_unknown_variable("x1") + GroupedExpression::from_number(7.into())), ); - system.substitute_by_known(&"x", &1.into()); + system.substitute_by_known(&"x1", &1.into()); assert_eq!( format!("{system}"), - "d1 := InverseOrZero(1)\nd2 := InverseOrZero(8)" + "d1 := QuotientOrZero(1, x2)\nd2 := QuotientOrZero(y1, 8)" ); } } diff --git a/openvm/src/powdr_extension/trace_generator/cpu/mod.rs b/openvm/src/powdr_extension/trace_generator/cpu/mod.rs index 33806582ed..94d9ef6d3e 100644 --- a/openvm/src/powdr_extension/trace_generator/cpu/mod.rs +++ b/openvm/src/powdr_extension/trace_generator/cpu/mod.rs @@ -183,16 +183,19 @@ impl PowdrTraceGeneratorCpu { let col_index = apc_poly_id_to_index[&column.id]; row_slice[col_index] = match computation_method { ComputationMethod::Constant(c) => *c, - ComputationMethod::InverseOrZero(expr) => { + ComputationMethod::QuotientOrZero(e1, e2) => { use powdr_number::ExpressionConvertible; - let expr_val = expr.to_expression(&|n| *n, &|column_ref| { + let divisor_val = e2.to_expression(&|n| *n, &|column_ref| { row_slice[apc_poly_id_to_index[&column_ref.id]] }); - if expr_val.is_zero() { + if divisor_val.is_zero() { BabyBear::ZERO } else { - expr_val.inverse() + divisor_val.inverse() + * e1.to_expression(&|n| *n, &|column_ref| { + row_slice[apc_poly_id_to_index[&column_ref.id]] + }) } } }; diff --git a/openvm/src/powdr_extension/trace_generator/cuda/mod.rs b/openvm/src/powdr_extension/trace_generator/cuda/mod.rs index 53543b9e3e..e5adb49399 100644 --- a/openvm/src/powdr_extension/trace_generator/cuda/mod.rs +++ b/openvm/src/powdr_extension/trace_generator/cuda/mod.rs @@ -120,10 +120,12 @@ fn compile_derived_to_gpu( bytecode.push(OpCode::PushConst as u32); bytecode.push(c.as_canonical_u32()); } - ComputationMethod::InverseOrZero(expr) => { + ComputationMethod::QuotientOrZero(e1, e2) => { // Encode inner expression, then apply InvOrZero - emit_expr(&mut bytecode, expr, apc_poly_id_to_index, apc_height); + emit_expr(&mut bytecode, e2, apc_poly_id_to_index, apc_height); bytecode.push(OpCode::InvOrZero as u32); + emit_expr(&mut bytecode, e1, apc_poly_id_to_index, apc_height); + bytecode.push(OpCode::Mul as u32); } } let len = (bytecode.len() as u32) - off; From 533948e228084d85c1228e5213efe67624845492 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 26 Nov 2025 11:47:56 +0000 Subject: [PATCH 38/60] Update comment. --- openvm/src/powdr_extension/trace_generator/cuda/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openvm/src/powdr_extension/trace_generator/cuda/mod.rs b/openvm/src/powdr_extension/trace_generator/cuda/mod.rs index e5adb49399..4fe2c99a78 100644 --- a/openvm/src/powdr_extension/trace_generator/cuda/mod.rs +++ b/openvm/src/powdr_extension/trace_generator/cuda/mod.rs @@ -121,7 +121,7 @@ fn compile_derived_to_gpu( bytecode.push(c.as_canonical_u32()); } ComputationMethod::QuotientOrZero(e1, e2) => { - // Encode inner expression, then apply InvOrZero + // Invert denominator (or use zero), then multiply with numerator. emit_expr(&mut bytecode, e2, apc_poly_id_to_index, apc_height); bytecode.push(OpCode::InvOrZero as u32); emit_expr(&mut bytecode, e1, apc_poly_id_to_index, apc_height); From a72e4f9e61a35d488986a76127d7fe5cdab9f8b6 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 26 Nov 2025 16:51:07 +0000 Subject: [PATCH 39/60] Trying to make it work. --- autoprecompiles/src/constraint_optimizer.rs | 10 +- autoprecompiles/src/optimizer.rs | 12 +- autoprecompiles/src/rule_based_optimizer.rs | 271 ++++++++++++------ .../src/indexed_constraint_system.rs | 18 ++ openvm/src/lib.rs | 20 +- .../complex/load_two_bytes_compare.txt | 4 +- .../apc_snapshots/complex/memcpy_block.txt | 4 +- .../complex/unaligned_memcpy.txt | 4 +- .../pseudo_instructions/beqz.txt | 4 +- .../pseudo_instructions/bnez.txt | 4 +- .../single_instructions/single_beq.txt | 4 +- .../single_instructions/single_bne.txt | 4 +- 12 files changed, 247 insertions(+), 112 deletions(-) diff --git a/autoprecompiles/src/constraint_optimizer.rs b/autoprecompiles/src/constraint_optimizer.rs index abef421528..209c09ff5b 100644 --- a/autoprecompiles/src/constraint_optimizer.rs +++ b/autoprecompiles/src/constraint_optimizer.rs @@ -25,6 +25,7 @@ use crate::{ range_constraint_optimizer::RangeConstraintHandler, rule_based_optimizer::rule_based_optimization, stats_logger::StatsLogger, + ColumnAllocator, }; #[derive(Debug)] @@ -58,12 +59,17 @@ pub fn optimize_constraints< stats_logger: &mut StatsLogger, memory_bus_id: Option, degree_bound: DegreeBound, + new_var: &mut impl FnMut(&str) -> V, ) -> Result, Error> { // Index the constraint system for the first time let constraint_system = IndexedConstraintSystem::from(constraint_system); - let constraint_system = - rule_based_optimization(constraint_system, &*solver, bus_interaction_handler.clone()); + let constraint_system = rule_based_optimization( + constraint_system, + &*solver, + bus_interaction_handler.clone(), + new_var, + ); stats_logger.log("rule-based optimization", &constraint_system); let constraint_system = solver_based_optimization(constraint_system, solver)?; diff --git a/autoprecompiles/src/optimizer.rs b/autoprecompiles/src/optimizer.rs index 5f1e7e77e3..dc2e93250b 100644 --- a/autoprecompiles/src/optimizer.rs +++ b/autoprecompiles/src/optimizer.rs @@ -35,7 +35,7 @@ pub fn optimize( bus_interaction_handler: A::BusInteractionHandler, degree_bound: DegreeBound, bus_map: &BusMap, - column_allocator: ColumnAllocator, + mut column_allocator: ColumnAllocator, ) -> Result<(SymbolicMachine, ColumnAllocator), crate::constraint_optimizer::Error> { let mut stats_logger = StatsLogger::start(&machine); @@ -44,6 +44,15 @@ pub fn optimize( stats_logger.log("exec bus optimization", &machine); } + let mut new_var = |name: &str| { + let id = column_allocator.issue_next_poly_id(); + AlgebraicReference { + // TODO is it a problem that we do not check the name to be unique? + name: format!("{name}_{id}").into(), + id, + } + }; + let mut constraint_system = symbolic_machine_to_constraint_system(machine); let mut solver = new_solver(constraint_system.clone(), bus_interaction_handler.clone()); @@ -56,6 +65,7 @@ pub fn optimize( &mut stats_logger, bus_map.get_bus_id(&BusType::Memory), degree_bound, + &mut new_var, )? .clone(); if stats == stats_logger::Stats::from(&constraint_system) { diff --git a/autoprecompiles/src/rule_based_optimizer.rs b/autoprecompiles/src/rule_based_optimizer.rs index be3ecb6ea0..a6983e9bc8 100644 --- a/autoprecompiles/src/rule_based_optimizer.rs +++ b/autoprecompiles/src/rule_based_optimizer.rs @@ -8,12 +8,15 @@ use std::{ fmt::Display, hash::Hash, ops::Index, + rc::Rc, }; use itertools::{EitherOrBoth, Itertools}; use powdr_constraint_solver::{ algebraic_constraint, - constraint_system::{BusInteraction, BusInteractionHandler}, + constraint_system::{ + BusInteraction, BusInteractionHandler, ComputationMethod, ConstraintSystem, DerivedVariable, + }, grouped_expression::{GroupedExpression, GroupedExpressionComponent, RangeConstraintProvider}, indexed_constraint_system::IndexedConstraintSystem, range_constraint::RangeConstraint, @@ -76,6 +79,8 @@ impl ExpressionDB { } } +// TODO rename this "Environment" + struct System { expressions: RefCell, var_to_string: HashMap, @@ -84,6 +89,7 @@ struct System { /// (also only once in the constraint they occur in). single_occurrence_variables: HashSet, range_constraints_on_vars: HashMap>, + new_var_generator: NewVarGenerator, } impl System { @@ -92,17 +98,19 @@ impl System { var_to_string: HashMap, single_occurrence_variables: HashSet, range_constraints_on_vars: HashMap>, + new_var_generator: NewVarGenerator, ) -> Self { Self { expressions: RefCell::new(expressions), var_to_string, single_occurrence_variables, range_constraints_on_vars, + new_var_generator, } } - fn expression_db(self) -> ExpressionDB { - self.expressions.into_inner() + fn terminate(self) -> (ExpressionDB, NewVarGenerator) { + (self.expressions.into_inner(), self.new_var_generator) } } @@ -150,6 +158,13 @@ impl System { self.expressions.borrow()[expr].clone() } + pub fn new_var( + &self, + prefix: &str, + method: ComputationMethod>, + ) -> Var { + self.new_var_generator.generate(prefix, method) + } pub fn referenced_variables(&self, expr: Expr) -> BTreeSet { let db = self.expressions.borrow(); db[expr].referenced_unknown_variables().cloned().collect() @@ -308,6 +323,37 @@ impl System { } } +struct NewVarGenerator { + counter: Rc>, + requests: Rc>>, + computation_methods: Rc>>>>, +} + +impl NewVarGenerator { + fn new(initial_counter: u32) -> Self { + Self { + counter: Rc::new(RefCell::new(initial_counter)), + requests: Rc::new(RefCell::new(HashMap::new())), + computation_methods: Rc::new(RefCell::new(HashMap::new())), + } + } + + fn generate( + &self, + prefix: &str, + computation_method: ComputationMethod>, + ) -> Var { + let mut counter = self.counter.borrow_mut(); + let var = Var(*counter); + self.requests.borrow_mut().insert(var, prefix.to_string()); + self.computation_methods + .borrow_mut() + .insert(var, computation_method); + *counter += 1; + var + } +} + impl RangeConstraintProvider for System { fn get(&self, var: &Var) -> RangeConstraint { self.range_constraints_on_vars @@ -421,11 +467,6 @@ crepe! { // // For the general case, where e.g. `X` can be negative, we replace it by `X * X`, // if that value is still small enough. - - // TODO here we replace V1 by V2, but we should actually replace it by - // a new variable. - // TODO make this iterate nicely. - struct SingleOccurrenceVariable(Expr, Var); SingleOccurrenceVariable(e, v) <- S(sys), @@ -524,8 +565,7 @@ crepe! { } else { x2 }; - // TODO remove clone - let r = e.clone().into_summands().filter(|s|{ + let r = e.into_summands().filter(|s|{ if let GroupedExpressionComponent::Quadratic(l, r) = s { let mut vars = l.referenced_unknown_variables().chain(r.referenced_unknown_variables()); if vars.any(|v| v == &v1 || v == &v2) { @@ -534,9 +574,9 @@ crepe! { }; true }).map(GroupedExpression::from).sum::>(); - // TODO we actually need to insert a new variable at this point. - let replacement = r + GroupedExpression::from_unknown_variable(v1) * (x1 * coeff1 + x2 * coeff2); - // v1 = -r / ((x1 * coeff1 + x2 * coeff2)) + let factor = x1 * coeff1 + x2 * coeff2; + let combined_var = sys.new_var("free_var", ComputationMethod::QuotientOrZero(-r.clone(), factor.clone())); + let replacement = r + GroupedExpression::from_unknown_variable(combined_var) * factor; Some(sys.insert_owned(replacement)) })(); @@ -596,6 +636,7 @@ pub fn rule_based_optimization, range_constraints: impl RangeConstraintProvider, _bus_interaction_handler: impl BusInteractionHandler + Clone, + new_var_outer: &mut impl FnMut(&str) -> V, ) -> IndexedConstraintSystem { if T::modulus().to_arbitrary_integer() != BabyBearField::modulus().to_arbitrary_integer() { return system; @@ -609,16 +650,35 @@ pub fn rule_based_optimization>(); + let mut db = Some(ExpressionDB::default()); loop { - let (algebraic_constraints, _bus_interactions, sys) = transform_constraint_system( - &system, - &range_constraints, - &mut var_mapper, + let (algebraic_constraints, _bus_interactions) = + transform_constraint_system(&system, &var_mapper, db.as_mut().unwrap()); + + let sys = System::new( db.take().unwrap(), + var_mapper.all_names(), + system + .single_occurrence_variables() + .map(|v| var_mapper.forward(&v)) + .collect(), + system + .referenced_unknown_variables() + .map(|v| { + let rc = transform_range_constraint(&range_constraints.get(v)); + (var_mapper.forward(v), rc) + }) + .collect(), + // TODO or we just clone the var mapper? + NewVarGenerator::new(var_mapper.next_free_id()), ); + let mut rt = Crepe::new(); rt.extend( algebraic_constraints @@ -628,9 +688,32 @@ pub fn rule_based_optimization { @@ -648,7 +731,8 @@ pub fn rule_based_optimization { - let expr1 = untransform_grouped_expression(&sys.extract(e1), &var_mapper); + let expr1 = + untransform_grouped_expression(&db.as_ref().unwrap()[e1], &var_mapper); // TODO more efficient? let mut found = false; system.retain_algebraic_constraints(|c| { @@ -660,7 +744,8 @@ pub fn rule_based_optimization Var>) {} + // TODO use an expression cache so that we don't even // have to transform the field elements. fn transform_constraint_system( system: &IndexedConstraintSystem, - range_constraints: impl RangeConstraintProvider, - var_mapper: &mut VarMapper, - mut expression_db: ExpressionDB, -) -> (Vec, Vec>, System) { + var_mapper: &VarMapper, + expression_db: &mut ExpressionDB, +) -> (Vec, Vec>) { let algebraic_constraints = system .system() .algebraic_constraints @@ -709,89 +794,75 @@ fn transform_constraint_system>(); - - ( - algebraic_constraints, - bus_interactions, - System::new( - expression_db, - var_names, - single_occurrence_variables, - range_constraints_on_vars, - ), - ) + (algebraic_constraints, bus_interactions) } #[derive(Clone)] struct VarMapper { forward: HashMap, backward: HashMap, - next_id: u32, } -impl Default for VarMapper { - fn default() -> Self { - Self { - forward: HashMap::new(), - backward: HashMap::new(), - next_id: 0, - } +impl FromIterator for VarMapper +where + V: Hash + Eq + Clone + Display, +{ + fn from_iter>(iter: T) -> Self { + let forward: HashMap = iter + .into_iter() + .unique() + .enumerate() + .map(|(i, v)| (v, Var(i as u32))) + .collect(); + let backward = forward + .iter() + .map(|(outer, inner)| (*inner, outer.clone())) + .collect(); + VarMapper { forward, backward } } } impl VarMapper { - fn forward(&mut self, v: &V) -> Var { - if let Some(var) = self.forward.get(v) { + fn forward_or_insert(&mut self, v: V) -> Var { + if let Some(var) = self.forward.get(&v) { *var } else { - let var = Var(self.next_id); + let var = Var(self.forward.len() as u32); self.forward.insert(v.clone(), var); - self.backward.insert(var, v.clone()); - self.next_id += 1; + self.backward.insert(var, v); var } } + pub fn insert_existing(&mut self, v: V, var: Var) { + assert!(self.forward.insert(v.clone(), var).is_none()); + assert!(self.backward.insert(var, v).is_none()); + } + + // TODO avoid using this (as pub) + pub fn next_free_id(&self) -> u32 { + self.forward.len() as u32 + } + + fn forward(&self, v: &V) -> Var { + *self.forward.get(v).unwrap() + } + fn backward(&self, var: &Var) -> &V { self.backward.get(var).unwrap() } + + fn all_names(&self) -> HashMap { + self.backward + .iter() + .map(|(var, v)| (*var, v.to_string())) + .collect() + } } fn transform_grouped_expression( expr: &GroupedExpression, - var_mapper: &mut VarMapper, + var_mapper: &VarMapper, ) -> GroupedExpression { expr.clone() .into_summands() @@ -833,6 +904,21 @@ fn untransform_grouped_expression( + method: &ComputationMethod>, + var_mapper: &VarMapper, +) -> ComputationMethod> { + match method { + ComputationMethod::Constant(c) => ComputationMethod::Constant(untransform_field_element(c)), + ComputationMethod::QuotientOrZero(numerator, denominator) => { + ComputationMethod::QuotientOrZero( + untransform_grouped_expression(numerator, var_mapper), + untransform_grouped_expression(denominator, var_mapper), + ) + } + } +} + fn transform_field_element(fe: &T) -> BabyBearField { BabyBearField::from(fe.to_arbitrary_integer()) } @@ -874,6 +960,15 @@ mod tests { GroupedExpression::from_number(BabyBearField::from(value)) } + fn new_var() -> impl FnMut(&str) -> String { + let mut counter = 0; + move |prefix: &str| { + let name = format!("{}_{}", prefix, counter); + counter += 1; + name + } + } + fn handle_variable_range_checker( payload: &[RangeConstraint], ) -> Vec> { @@ -951,6 +1046,7 @@ mod tests { system, NoRangeConstraints, DefaultBusInteractionHandler::default(), + &mut new_var(), ); assert_eq!(optimized_system.system().algebraic_constraints.len(), 0); } @@ -967,6 +1063,7 @@ mod tests { system, NoRangeConstraints, DefaultBusInteractionHandler::default(), + &mut new_var(), ); expect!["(y) * (y - 1) - 3 = 0"].assert_eq(&optimized_system.to_string()); } @@ -1006,8 +1103,12 @@ mod tests { payload: vec![c(-503316480) * v("mem_ptr_limbs__0_2"), c(14)], }, ]); - let optimized_system = - rule_based_optimization(system, NoRangeConstraints, TestBusInteractionHandler); + let optimized_system = rule_based_optimization( + system, + NoRangeConstraints, + TestBusInteractionHandler, + &mut new_var(), + ); // Note that in the system below, mem_ptr_limbs__0_2 has been eliminated expect![[r#" (30720 * mem_ptr_limbs__0_1 - 30720 * rs1_data__0_1 - 7864320 * rs1_data__1_1 - 737280) * (30720 * mem_ptr_limbs__0_1 - 30720 * rs1_data__0_1 - 7864320 * rs1_data__1_1 - 737281) = 0 diff --git a/constraint-solver/src/indexed_constraint_system.rs b/constraint-solver/src/indexed_constraint_system.rs index abb3808987..536bc131fb 100644 --- a/constraint-solver/src/indexed_constraint_system.rs +++ b/constraint-solver/src/indexed_constraint_system.rs @@ -198,6 +198,24 @@ impl IndexedConstraintSystem { self.constraint_system.referenced_unknown_variables() } + /// Returns a list of variables that occur exactly once in the system. + pub fn single_occurrence_variables<'a>(&'a self) -> impl Iterator + 'a { + self.variable_occurrences.iter().filter_map(|(v, items)| { + let item = items + .iter() + .filter(|item| !item.is_derived_variable()) + .exactly_one() + .ok()?; + item.try_to_constraint_ref(&self.constraint_system) + .unwrap() + .referenced_unknown_variables() + .filter(|var| *var == v) + .exactly_one() + .is_ok() + .then(|| v) + }) + } + /// Removes all constraints that do not fulfill the predicate. pub fn retain_algebraic_constraints( &mut self, diff --git a/openvm/src/lib.rs b/openvm/src/lib.rs index 9681dfc82e..96ef351c99 100644 --- a/openvm/src/lib.rs +++ b/openvm/src/lib.rs @@ -1813,7 +1813,7 @@ mod tests { AirMetrics { widths: AirWidths { preprocessed: 0, - main: 14254, + main: 14257, log_up: 22752, }, constraints: 4285, @@ -1987,7 +1987,7 @@ mod tests { AirMetrics { widths: AirWidths { preprocessed: 0, - main: 2025, + main: 2022, log_up: 3472, }, constraints: 187, @@ -2094,11 +2094,11 @@ mod tests { AirMetrics { widths: AirWidths { preprocessed: 0, - main: 3246, - log_up: 5264, + main: 3242, + log_up: 5268, }, - constraints: 598, - bus_interactions: 2562, + constraints: 594, + bus_interactions: 2564, } "#]], powdr_expected_machine_count: expect![[r#" @@ -2111,13 +2111,13 @@ mod tests { AirWidthsDiff { before: AirWidths { preprocessed: 0, - main: 32370, - log_up: 41644, + main: 32376, + log_up: 41660, }, after: AirWidths { preprocessed: 0, - main: 3246, - log_up: 5264, + main: 3242, + log_up: 5268, }, } "#]]), diff --git a/openvm/tests/apc_snapshots/complex/load_two_bytes_compare.txt b/openvm/tests/apc_snapshots/complex/load_two_bytes_compare.txt index e0c27e303b..675b1bbf6b 100644 --- a/openvm/tests/apc_snapshots/complex/load_two_bytes_compare.txt +++ b/openvm/tests/apc_snapshots/complex/load_two_bytes_compare.txt @@ -59,7 +59,7 @@ Symbolic machine using 52 unique main columns: a__0_2 b__0_2 cmp_result_2 - diff_inv_marker__0_2 + free_var_103 is_valid // Bus 0 (EXECUTION_BRIDGE): @@ -116,5 +116,5 @@ cmp_result_2 * (cmp_result_2 - 1) = 0 (1 - cmp_result_2) * (255 * data_most_sig_bit_0 - 255 * data_most_sig_bit_1) = 0 opcode_loadb_flag0_0 * shifted_read_data__0_0 + (1 - opcode_loadb_flag0_0) * shifted_read_data__1_0 - a__0_2 = 0 opcode_loadb_flag0_1 * shifted_read_data__0_1 + (1 - opcode_loadb_flag0_1) * shifted_read_data__1_1 - b__0_2 = 0 -diff_inv_marker__0_2 * ((a__0_2 - b__0_2) * (a__0_2 - b__0_2) + (255 * data_most_sig_bit_0 - 255 * data_most_sig_bit_1) * (255 * data_most_sig_bit_0 - 255 * data_most_sig_bit_1) + (255 * data_most_sig_bit_0 - 255 * data_most_sig_bit_1) * (255 * data_most_sig_bit_0 - 255 * data_most_sig_bit_1) + (255 * data_most_sig_bit_0 - 255 * data_most_sig_bit_1) * (255 * data_most_sig_bit_0 - 255 * data_most_sig_bit_1)) - cmp_result_2 = 0 +free_var_103 * ((a__0_2 - b__0_2) * (a__0_2 - b__0_2) + (255 * data_most_sig_bit_0 - 255 * data_most_sig_bit_1) * (255 * data_most_sig_bit_0 - 255 * data_most_sig_bit_1) + (255 * data_most_sig_bit_0 - 255 * data_most_sig_bit_1) * (255 * data_most_sig_bit_0 - 255 * data_most_sig_bit_1) + (255 * data_most_sig_bit_0 - 255 * data_most_sig_bit_1) * (255 * data_most_sig_bit_0 - 255 * data_most_sig_bit_1)) - cmp_result_2 = 0 is_valid * (is_valid - 1) = 0 \ No newline at end of file diff --git a/openvm/tests/apc_snapshots/complex/memcpy_block.txt b/openvm/tests/apc_snapshots/complex/memcpy_block.txt index f7c84abd1b..6bbff134a9 100644 --- a/openvm/tests/apc_snapshots/complex/memcpy_block.txt +++ b/openvm/tests/apc_snapshots/complex/memcpy_block.txt @@ -46,7 +46,7 @@ Symbolic machine using 37 unique main columns: reads_aux__1__base__prev_timestamp_4 reads_aux__1__base__timestamp_lt_aux__lower_decomp__0_4 cmp_result_4 - diff_inv_marker__0_4 + free_var_177 is_valid // Bus 0 (EXECUTION_BRIDGE): @@ -108,7 +108,7 @@ diff_marker__0_2 * ((writes_aux__prev_data__0_2 - 1) * (2 * cmp_result_2 - 1) + (1 - (diff_marker__0_2 + diff_marker__1_2 + diff_marker__2_2 + diff_marker__3_2)) * cmp_result_2 = 0 cmp_result_4 * (cmp_result_4 - 1) = 0 (1 - cmp_result_4) * (cmp_result_1 + cmp_result_2 - cmp_result_1 * cmp_result_2) = 0 -diff_inv_marker__0_4 * (cmp_result_1 + cmp_result_2 - cmp_result_1 * cmp_result_2) - cmp_result_4 = 0 +free_var_177 * (cmp_result_1 + cmp_result_2 - cmp_result_1 * cmp_result_2) - cmp_result_4 = 0 (1 - is_valid) * (diff_marker__0_1 + diff_marker__1_1 + diff_marker__2_1 + diff_marker__3_1) = 0 (1 - is_valid) * (diff_marker__0_2 + diff_marker__1_2 + diff_marker__2_2 + diff_marker__3_2) = 0 is_valid * (is_valid - 1) = 0 \ No newline at end of file diff --git a/openvm/tests/apc_snapshots/complex/unaligned_memcpy.txt b/openvm/tests/apc_snapshots/complex/unaligned_memcpy.txt index c0295a0265..6b3cdbf80d 100644 --- a/openvm/tests/apc_snapshots/complex/unaligned_memcpy.txt +++ b/openvm/tests/apc_snapshots/complex/unaligned_memcpy.txt @@ -122,7 +122,7 @@ Symbolic machine using 105 unique main columns: a__2_9 a__3_9 cmp_result_12 - diff_inv_marker__0_12 + free_var_470 is_valid // Bus 0 (EXECUTION_BRIDGE): @@ -255,9 +255,9 @@ diff_marker__0_7 * (diff_val_7 - a__0_4 * (2 * cmp_result_7 - 1)) = 0 (943718400 * b__0_5 + 120 * a__1_9 + 30720 * a__2_9 + 7864320 * a__3_9 + 943718400 * is_valid - (120 * b__1_5 + 30720 * b__2_5 + 7864320 * b__3_5 + 943718400 * a__0_9)) * (943718400 * b__0_5 + 120 * a__1_9 + 30720 * a__2_9 + 7864320 * a__3_9 + 943718399 - (120 * b__1_5 + 30720 * b__2_5 + 7864320 * b__3_5 + 943718400 * a__0_9)) = 0 cmp_result_12 * (cmp_result_12 - 1) = 0 (1 - cmp_result_12) * (cmp_result_6 * cmp_result_7) = 0 -cmp_result_6 * cmp_result_7 * diff_inv_marker__0_12 - cmp_result_12 = 0 flags__2_3 * (flags__2_3 - 1) - (flags__0_3 * (flags__0_3 + flags__1_3 + flags__2_3 + flags__3_3 - 2) + 2 * flags__1_3 * (flags__0_3 + flags__1_3 + flags__2_3 + flags__3_3 - 2) + 3 * flags__2_3 * (flags__0_3 + flags__1_3 + flags__2_3 + flags__3_3 - 2)) = 0 opcode_loadb_flag0_0 * shifted_read_data__0_0 + (1 - opcode_loadb_flag0_0) * shifted_read_data__1_0 - read_data__0_3 = 0 +free_var_470 * (cmp_result_6 * cmp_result_7) - cmp_result_12 = 0 (1 - is_valid) * (diff_marker__0_6 + diff_marker__1_6 + diff_marker__2_6 + diff_marker__3_6) = 0 (1 - is_valid) * (diff_marker__0_7 + diff_marker__1_7 + diff_marker__2_7 + diff_marker__3_7) = 0 is_valid * (is_valid - 1) = 0 \ No newline at end of file diff --git a/openvm/tests/apc_snapshots/pseudo_instructions/beqz.txt b/openvm/tests/apc_snapshots/pseudo_instructions/beqz.txt index e5cf59191b..4ac37e0708 100644 --- a/openvm/tests/apc_snapshots/pseudo_instructions/beqz.txt +++ b/openvm/tests/apc_snapshots/pseudo_instructions/beqz.txt @@ -17,7 +17,7 @@ Symbolic machine using 12 unique main columns: a__2_0 a__3_0 cmp_result_0 - diff_inv_marker__0_0 + free_var_31 is_valid // Bus 0 (EXECUTION_BRIDGE): @@ -42,5 +42,5 @@ cmp_result_0 * a__0_0 = 0 cmp_result_0 * a__1_0 = 0 cmp_result_0 * a__2_0 = 0 cmp_result_0 * a__3_0 = 0 -diff_inv_marker__0_0 * (a__0_0 + a__1_0 + a__2_0 + a__3_0) + cmp_result_0 - 1 * is_valid = 0 +free_var_31 * (a__0_0 + a__1_0 + a__2_0 + a__3_0) + cmp_result_0 - 1 * is_valid = 0 is_valid * (is_valid - 1) = 0 \ No newline at end of file diff --git a/openvm/tests/apc_snapshots/pseudo_instructions/bnez.txt b/openvm/tests/apc_snapshots/pseudo_instructions/bnez.txt index eea9d85d15..cfa031036e 100644 --- a/openvm/tests/apc_snapshots/pseudo_instructions/bnez.txt +++ b/openvm/tests/apc_snapshots/pseudo_instructions/bnez.txt @@ -17,7 +17,7 @@ Symbolic machine using 12 unique main columns: a__2_0 a__3_0 cmp_result_0 - diff_inv_marker__0_0 + free_var_31 is_valid // Bus 0 (EXECUTION_BRIDGE): @@ -42,5 +42,5 @@ cmp_result_0 * (cmp_result_0 - 1) = 0 (1 - cmp_result_0) * a__1_0 = 0 (1 - cmp_result_0) * a__2_0 = 0 (1 - cmp_result_0) * a__3_0 = 0 -diff_inv_marker__0_0 * (a__0_0 + a__1_0 + a__2_0 + a__3_0) - cmp_result_0 = 0 +free_var_31 * (a__0_0 + a__1_0 + a__2_0 + a__3_0) - cmp_result_0 = 0 is_valid * (is_valid - 1) = 0 \ No newline at end of file diff --git a/openvm/tests/apc_snapshots/single_instructions/single_beq.txt b/openvm/tests/apc_snapshots/single_instructions/single_beq.txt index dc17cb086d..f329a628a8 100644 --- a/openvm/tests/apc_snapshots/single_instructions/single_beq.txt +++ b/openvm/tests/apc_snapshots/single_instructions/single_beq.txt @@ -21,7 +21,7 @@ Symbolic machine using 16 unique main columns: b__2_0 b__3_0 cmp_result_0 - diff_inv_marker__0_0 + free_var_31 is_valid // Bus 0 (EXECUTION_BRIDGE): @@ -46,5 +46,5 @@ cmp_result_0 * (a__0_0 - b__0_0) = 0 cmp_result_0 * (a__1_0 - b__1_0) = 0 cmp_result_0 * (a__2_0 - b__2_0) = 0 cmp_result_0 * (a__3_0 - b__3_0) = 0 -diff_inv_marker__0_0 * ((a__0_0 - b__0_0) * (a__0_0 - b__0_0) + (a__1_0 - b__1_0) * (a__1_0 - b__1_0) + (a__2_0 - b__2_0) * (a__2_0 - b__2_0) + (a__3_0 - b__3_0) * (a__3_0 - b__3_0)) + cmp_result_0 - 1 * is_valid = 0 +free_var_31 * ((a__0_0 - b__0_0) * (a__0_0 - b__0_0) + (a__1_0 - b__1_0) * (a__1_0 - b__1_0) + (a__2_0 - b__2_0) * (a__2_0 - b__2_0) + (a__3_0 - b__3_0) * (a__3_0 - b__3_0)) + cmp_result_0 - 1 * is_valid = 0 is_valid * (is_valid - 1) = 0 \ No newline at end of file diff --git a/openvm/tests/apc_snapshots/single_instructions/single_bne.txt b/openvm/tests/apc_snapshots/single_instructions/single_bne.txt index c46391cf3c..babcac2e1e 100644 --- a/openvm/tests/apc_snapshots/single_instructions/single_bne.txt +++ b/openvm/tests/apc_snapshots/single_instructions/single_bne.txt @@ -21,7 +21,7 @@ Symbolic machine using 16 unique main columns: b__2_0 b__3_0 cmp_result_0 - diff_inv_marker__0_0 + free_var_31 is_valid // Bus 0 (EXECUTION_BRIDGE): @@ -46,5 +46,5 @@ cmp_result_0 * (cmp_result_0 - 1) = 0 (1 - cmp_result_0) * (a__1_0 - b__1_0) = 0 (1 - cmp_result_0) * (a__2_0 - b__2_0) = 0 (1 - cmp_result_0) * (a__3_0 - b__3_0) = 0 -diff_inv_marker__0_0 * ((a__0_0 - b__0_0) * (a__0_0 - b__0_0) + (a__1_0 - b__1_0) * (a__1_0 - b__1_0) + (a__2_0 - b__2_0) * (a__2_0 - b__2_0) + (a__3_0 - b__3_0) * (a__3_0 - b__3_0)) - cmp_result_0 = 0 +free_var_31 * ((a__0_0 - b__0_0) * (a__0_0 - b__0_0) + (a__1_0 - b__1_0) * (a__1_0 - b__1_0) + (a__2_0 - b__2_0) * (a__2_0 - b__2_0) + (a__3_0 - b__3_0) * (a__3_0 - b__3_0)) - cmp_result_0 = 0 is_valid * (is_valid - 1) = 0 \ No newline at end of file From b44a4b039f806731ee8bc38f5e407d99df3ac57e Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 26 Nov 2025 17:04:47 +0000 Subject: [PATCH 40/60] Fix clippy. --- autoprecompiles/src/constraint_optimizer.rs | 1 - autoprecompiles/src/rule_based_optimizer.rs | 72 +++++++------------ .../src/indexed_constraint_system.rs | 2 +- constraint-solver/src/range_constraint.rs | 8 ++- 4 files changed, 34 insertions(+), 49 deletions(-) diff --git a/autoprecompiles/src/constraint_optimizer.rs b/autoprecompiles/src/constraint_optimizer.rs index 209c09ff5b..e418671207 100644 --- a/autoprecompiles/src/constraint_optimizer.rs +++ b/autoprecompiles/src/constraint_optimizer.rs @@ -25,7 +25,6 @@ use crate::{ range_constraint_optimizer::RangeConstraintHandler, rule_based_optimizer::rule_based_optimization, stats_logger::StatsLogger, - ColumnAllocator, }; #[derive(Debug)] diff --git a/autoprecompiles/src/rule_based_optimizer.rs b/autoprecompiles/src/rule_based_optimizer.rs index a6983e9bc8..568c41cdcd 100644 --- a/autoprecompiles/src/rule_based_optimizer.rs +++ b/autoprecompiles/src/rule_based_optimizer.rs @@ -8,7 +8,6 @@ use std::{ fmt::Display, hash::Hash, ops::Index, - rc::Rc, }; use itertools::{EitherOrBoth, Itertools}; @@ -89,7 +88,7 @@ struct System { /// (also only once in the constraint they occur in). single_occurrence_variables: HashSet, range_constraints_on_vars: HashMap>, - new_var_generator: NewVarGenerator, + new_var_generator: RefCell, } impl System { @@ -105,12 +104,15 @@ impl System { var_to_string, single_occurrence_variables, range_constraints_on_vars, - new_var_generator, + new_var_generator: RefCell::new(new_var_generator), } } fn terminate(self) -> (ExpressionDB, NewVarGenerator) { - (self.expressions.into_inner(), self.new_var_generator) + ( + self.expressions.into_inner(), + self.new_var_generator.into_inner(), + ) } } @@ -163,7 +165,7 @@ impl System { prefix: &str, method: ComputationMethod>, ) -> Var { - self.new_var_generator.generate(prefix, method) + self.new_var_generator.borrow_mut().generate(prefix, method) } pub fn referenced_variables(&self, expr: Expr) -> BTreeSet { let db = self.expressions.borrow(); @@ -324,32 +326,29 @@ impl System { } struct NewVarGenerator { - counter: Rc>, - requests: Rc>>, - computation_methods: Rc>>>>, + counter: u32, + requests: HashMap, + computation_methods: HashMap>>, } impl NewVarGenerator { fn new(initial_counter: u32) -> Self { Self { - counter: Rc::new(RefCell::new(initial_counter)), - requests: Rc::new(RefCell::new(HashMap::new())), - computation_methods: Rc::new(RefCell::new(HashMap::new())), + counter: initial_counter, + requests: Default::default(), + computation_methods: Default::default(), } } fn generate( - &self, + &mut self, prefix: &str, computation_method: ComputationMethod>, ) -> Var { - let mut counter = self.counter.borrow_mut(); - let var = Var(*counter); - self.requests.borrow_mut().insert(var, prefix.to_string()); - self.computation_methods - .borrow_mut() - .insert(var, computation_method); - *counter += 1; + let var = Var(self.counter); + self.requests.insert(var, prefix.to_string()); + self.computation_methods.insert(var, computation_method); + self.counter += 1; var } } @@ -666,7 +665,7 @@ pub fn rule_based_optimization Var>) {} - // TODO use an expression cache so that we don't even // have to transform the field elements. fn transform_constraint_system( @@ -823,17 +816,6 @@ where } impl VarMapper { - fn forward_or_insert(&mut self, v: V) -> Var { - if let Some(var) = self.forward.get(&v) { - *var - } else { - let var = Var(self.forward.len() as u32); - self.forward.insert(v.clone(), var); - self.backward.insert(var, v); - var - } - } - pub fn insert_existing(&mut self, v: V, var: Var) { assert!(self.forward.insert(v.clone(), var).is_none()); assert!(self.backward.insert(var, v).is_none()); @@ -963,7 +945,7 @@ mod tests { fn new_var() -> impl FnMut(&str) -> String { let mut counter = 0; move |prefix: &str| { - let name = format!("{}_{}", prefix, counter); + let name = format!("{prefix}_{counter}"); counter += 1; name } diff --git a/constraint-solver/src/indexed_constraint_system.rs b/constraint-solver/src/indexed_constraint_system.rs index 536bc131fb..c02d2b36b3 100644 --- a/constraint-solver/src/indexed_constraint_system.rs +++ b/constraint-solver/src/indexed_constraint_system.rs @@ -212,7 +212,7 @@ impl IndexedConstraintSystem { .filter(|var| *var == v) .exactly_one() .is_ok() - .then(|| v) + .then_some(v) }) } diff --git a/constraint-solver/src/range_constraint.rs b/constraint-solver/src/range_constraint.rs index 501e7106f9..bcc014eb66 100644 --- a/constraint-solver/src/range_constraint.rs +++ b/constraint-solver/src/range_constraint.rs @@ -190,7 +190,8 @@ impl RangeConstraint { /// If `Self` is a valid range constraint on an expression `e`, returns /// a valid range constraint for `e * e`. pub fn square(&self) -> Self { - let square_rc = if self.min > self.max { { + let square_rc = if self.min > self.max { + { let max_abs = std::cmp::max(-self.min, self.max); if max_abs.to_arbitrary_integer() * max_abs.to_arbitrary_integer() < T::modulus().to_arbitrary_integer() @@ -199,7 +200,10 @@ impl RangeConstraint { } else { Default::default() } - } } else { Default::default() }; + } + } else { + Default::default() + }; self.combine_product(self).conjunction(&square_rc) } From fc42bfab0e345c40ce4b1106647cfbccf1352287 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 27 Nov 2025 00:21:53 +0000 Subject: [PATCH 41/60] Move rules to a later point and update expectations. --- autoprecompiles/src/constraint_optimizer.rs | 16 ++++++++-------- autoprecompiles/src/rule_based_optimizer.rs | 4 ++-- .../complex/load_two_bytes_compare.txt | 2 +- .../complex/load_two_bytes_compare_unsigned.txt | 4 ++-- .../apc_snapshots/complex/unaligned_memcpy.txt | 2 +- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/autoprecompiles/src/constraint_optimizer.rs b/autoprecompiles/src/constraint_optimizer.rs index e418671207..931a446870 100644 --- a/autoprecompiles/src/constraint_optimizer.rs +++ b/autoprecompiles/src/constraint_optimizer.rs @@ -63,14 +63,6 @@ pub fn optimize_constraints< // Index the constraint system for the first time let constraint_system = IndexedConstraintSystem::from(constraint_system); - let constraint_system = rule_based_optimization( - constraint_system, - &*solver, - bus_interaction_handler.clone(), - new_var, - ); - stats_logger.log("rule-based optimization", &constraint_system); - let constraint_system = solver_based_optimization(constraint_system, solver)?; stats_logger.log("solver-based optimization", &constraint_system); @@ -91,6 +83,14 @@ pub fn optimize_constraints< stats_logger, ); + let constraint_system = rule_based_optimization( + constraint_system, + &*solver, + bus_interaction_handler.clone(), + new_var, + ); + stats_logger.log("rule-based optimization", &constraint_system); + // At this point, we throw away the index and only keep the constraint system, since the rest of the optimisations are defined on the system alone let constraint_system: ConstraintSystem = constraint_system.into(); diff --git a/autoprecompiles/src/rule_based_optimizer.rs b/autoprecompiles/src/rule_based_optimizer.rs index 568c41cdcd..b87dc94956 100644 --- a/autoprecompiles/src/rule_based_optimizer.rs +++ b/autoprecompiles/src/rule_based_optimizer.rs @@ -1094,10 +1094,10 @@ mod tests { // Note that in the system below, mem_ptr_limbs__0_2 has been eliminated expect![[r#" (30720 * mem_ptr_limbs__0_1 - 30720 * rs1_data__0_1 - 7864320 * rs1_data__1_1 - 737280) * (30720 * mem_ptr_limbs__0_1 - 30720 * rs1_data__0_1 - 7864320 * rs1_data__1_1 - 737281) = 0 - (30720 * mem_ptr_limbs__0_1 - 30720 * rs1_data__0_1 - 7864320 * rs1_data__1_1 - 737280) * (30720 * mem_ptr_limbs__0_1 - 30720 * rs1_data__0_1 - 7864320 * rs1_data__1_1 - 737281) = 0 + (30720 * mem_ptr_limbs__0_2 - 30720 * rs1_data__0_1 - 7864320 * rs1_data__1_1 - 737280) * (30720 * mem_ptr_limbs__0_2 - 30720 * rs1_data__0_1 - 7864320 * rs1_data__1_1 - 737281) = 0 BusInteraction { bus_id: 3, multiplicity: 1, payload: rs1_data__0_1, 8 } BusInteraction { bus_id: 3, multiplicity: 1, payload: rs1_data__1_1, 8 } BusInteraction { bus_id: 3, multiplicity: 1, payload: -(503316480 * mem_ptr_limbs__0_1), 14 } - BusInteraction { bus_id: 3, multiplicity: 1, payload: -(503316480 * mem_ptr_limbs__0_1), 14 }"#]].assert_eq(&optimized_system.to_string()); + BusInteraction { bus_id: 3, multiplicity: 1, payload: -(503316480 * mem_ptr_limbs__0_2), 14 }"#]].assert_eq(&optimized_system.to_string()); } } diff --git a/openvm/tests/apc_snapshots/complex/load_two_bytes_compare.txt b/openvm/tests/apc_snapshots/complex/load_two_bytes_compare.txt index 675b1bbf6b..794552590e 100644 --- a/openvm/tests/apc_snapshots/complex/load_two_bytes_compare.txt +++ b/openvm/tests/apc_snapshots/complex/load_two_bytes_compare.txt @@ -114,7 +114,7 @@ shift_most_sig_bit_1 * (shift_most_sig_bit_1 - 1) = 0 cmp_result_2 * (cmp_result_2 - 1) = 0 (1 - cmp_result_2) * (a__0_2 - b__0_2) = 0 (1 - cmp_result_2) * (255 * data_most_sig_bit_0 - 255 * data_most_sig_bit_1) = 0 +free_var_103 * ((a__0_2 - b__0_2) * (a__0_2 - b__0_2) + (255 * data_most_sig_bit_0 - 255 * data_most_sig_bit_1) * (255 * data_most_sig_bit_0 - 255 * data_most_sig_bit_1) + (255 * data_most_sig_bit_0 - 255 * data_most_sig_bit_1) * (255 * data_most_sig_bit_0 - 255 * data_most_sig_bit_1) + (255 * data_most_sig_bit_0 - 255 * data_most_sig_bit_1) * (255 * data_most_sig_bit_0 - 255 * data_most_sig_bit_1)) - cmp_result_2 = 0 opcode_loadb_flag0_0 * shifted_read_data__0_0 + (1 - opcode_loadb_flag0_0) * shifted_read_data__1_0 - a__0_2 = 0 opcode_loadb_flag0_1 * shifted_read_data__0_1 + (1 - opcode_loadb_flag0_1) * shifted_read_data__1_1 - b__0_2 = 0 -free_var_103 * ((a__0_2 - b__0_2) * (a__0_2 - b__0_2) + (255 * data_most_sig_bit_0 - 255 * data_most_sig_bit_1) * (255 * data_most_sig_bit_0 - 255 * data_most_sig_bit_1) + (255 * data_most_sig_bit_0 - 255 * data_most_sig_bit_1) * (255 * data_most_sig_bit_0 - 255 * data_most_sig_bit_1) + (255 * data_most_sig_bit_0 - 255 * data_most_sig_bit_1) * (255 * data_most_sig_bit_0 - 255 * data_most_sig_bit_1)) - cmp_result_2 = 0 is_valid * (is_valid - 1) = 0 \ No newline at end of file diff --git a/openvm/tests/apc_snapshots/complex/load_two_bytes_compare_unsigned.txt b/openvm/tests/apc_snapshots/complex/load_two_bytes_compare_unsigned.txt index 5a5b974705..803edc1689 100644 --- a/openvm/tests/apc_snapshots/complex/load_two_bytes_compare_unsigned.txt +++ b/openvm/tests/apc_snapshots/complex/load_two_bytes_compare_unsigned.txt @@ -61,7 +61,7 @@ Symbolic machine using 54 unique main columns: prev_data__3_1 write_data__0_1 cmp_result_2 - diff_inv_marker__0_2 + free_var_113 is_valid // Bus 0 (EXECUTION_BRIDGE): @@ -129,7 +129,7 @@ flags__3_1 * ((flags__3_1 - 1) * (flags__3_1 - 2)) = 0 flags__1_1 * (flags__1_1 - 1) + flags__2_1 * (flags__2_1 - 1) + 4 * flags__0_1 * flags__1_1 + 4 * flags__0_1 * flags__2_1 + 5 * flags__0_1 * flags__3_1 + 5 * flags__1_1 * flags__2_1 + 5 * flags__1_1 * flags__3_1 + 5 * flags__2_1 * flags__3_1 - (1006632960 * flags__3_1 * (flags__3_1 - 1) + flags__0_1 * (flags__0_1 + flags__1_1 + flags__2_1 + flags__3_1 - 2) + flags__1_1 * (flags__0_1 + flags__1_1 + flags__2_1 + flags__3_1 - 2) + flags__2_1 * (flags__0_1 + flags__1_1 + flags__2_1 + flags__3_1 - 2) + 3 * flags__3_1 * (flags__0_1 + flags__1_1 + flags__2_1 + flags__3_1 - 2) + 1 * is_valid) = 0 cmp_result_2 * (cmp_result_2 - 1) = 0 (1 - cmp_result_2) * (write_data__0_0 - write_data__0_1) = 0 -(write_data__0_0 - write_data__0_1) * diff_inv_marker__0_2 - cmp_result_2 = 0 flags__1_0 * flags__2_0 + 2 * flags__0_0 * flags__2_0 + 2 * flags__1_0 * flags__3_0 + 3 * flags__2_0 * flags__3_0 = 0 flags__1_1 * flags__2_1 + 2 * flags__0_1 * flags__2_1 + 2 * flags__1_1 * flags__3_1 + 3 * flags__2_1 * flags__3_1 = 0 +free_var_113 * ((write_data__0_0 - write_data__0_1) * (write_data__0_0 - write_data__0_1)) - cmp_result_2 = 0 is_valid * (is_valid - 1) = 0 \ No newline at end of file diff --git a/openvm/tests/apc_snapshots/complex/unaligned_memcpy.txt b/openvm/tests/apc_snapshots/complex/unaligned_memcpy.txt index 6b3cdbf80d..ab4b030135 100644 --- a/openvm/tests/apc_snapshots/complex/unaligned_memcpy.txt +++ b/openvm/tests/apc_snapshots/complex/unaligned_memcpy.txt @@ -256,8 +256,8 @@ diff_marker__0_7 * (diff_val_7 - a__0_4 * (2 * cmp_result_7 - 1)) = 0 cmp_result_12 * (cmp_result_12 - 1) = 0 (1 - cmp_result_12) * (cmp_result_6 * cmp_result_7) = 0 flags__2_3 * (flags__2_3 - 1) - (flags__0_3 * (flags__0_3 + flags__1_3 + flags__2_3 + flags__3_3 - 2) + 2 * flags__1_3 * (flags__0_3 + flags__1_3 + flags__2_3 + flags__3_3 - 2) + 3 * flags__2_3 * (flags__0_3 + flags__1_3 + flags__2_3 + flags__3_3 - 2)) = 0 -opcode_loadb_flag0_0 * shifted_read_data__0_0 + (1 - opcode_loadb_flag0_0) * shifted_read_data__1_0 - read_data__0_3 = 0 free_var_470 * (cmp_result_6 * cmp_result_7) - cmp_result_12 = 0 +opcode_loadb_flag0_0 * shifted_read_data__0_0 + (1 - opcode_loadb_flag0_0) * shifted_read_data__1_0 - read_data__0_3 = 0 (1 - is_valid) * (diff_marker__0_6 + diff_marker__1_6 + diff_marker__2_6 + diff_marker__3_6) = 0 (1 - is_valid) * (diff_marker__0_7 + diff_marker__1_7 + diff_marker__2_7 + diff_marker__3_7) = 0 is_valid * (is_valid - 1) = 0 \ No newline at end of file From 4bebcb67f4e981b782832b481f4b88f14a36bec2 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 27 Nov 2025 00:43:16 +0000 Subject: [PATCH 42/60] Use generic crepe. --- autoprecompiles/Cargo.toml | 2 +- autoprecompiles/src/rule_based_optimizer.rs | 213 ++++++++------------ 2 files changed, 81 insertions(+), 134 deletions(-) diff --git a/autoprecompiles/Cargo.toml b/autoprecompiles/Cargo.toml index 89f0e79fe4..002109d947 100644 --- a/autoprecompiles/Cargo.toml +++ b/autoprecompiles/Cargo.toml @@ -23,7 +23,7 @@ rayon = "1.10.0" strum = { version = "0.27.0", features = ["derive"] } metrics = "0.23.0" -crepe = "0.1.8" +crepe = { git = "https://github.com/chriseth/crepe", branch = "generics", version = "0.1.8" } [dev-dependencies] expect-test = "1.5.1" diff --git a/autoprecompiles/src/rule_based_optimizer.rs b/autoprecompiles/src/rule_based_optimizer.rs index b87dc94956..cac839993b 100644 --- a/autoprecompiles/src/rule_based_optimizer.rs +++ b/autoprecompiles/src/rule_based_optimizer.rs @@ -23,14 +23,12 @@ use powdr_constraint_solver::{ }; use powdr_number::{BabyBearField, FieldElement, LargeInt}; -use num_traits::{One, Zero}; +use num_traits::Zero; use crepe::crepe; const SIZE_LIMIT: usize = 800; -type F = BabyBearField; - #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] struct Var(u32); @@ -40,28 +38,30 @@ impl Display for Var { } } +// TODO could be generic over Expr + #[derive(Default)] -struct ExpressionDB { - expressions: Vec>, - reverse: HashMap, usize>, +struct ExpressionDB { + expressions: Vec>, + reverse: HashMap, usize>, } -impl Index for ExpressionDB { - type Output = GroupedExpression; +impl Index for ExpressionDB { + type Output = GroupedExpression; fn index(&self, index: Expr) -> &Self::Output { &self.expressions[index.0] } } -impl ExpressionDB { - fn insert_owned_new(&mut self, expr: GroupedExpression) -> Expr { +impl ExpressionDB { + fn insert_owned_new(&mut self, expr: GroupedExpression) -> Expr { self.expressions.push(expr.clone()); let id = self.expressions.len() - 1; self.reverse.insert(expr, id); Expr(id) } - pub fn insert(&mut self, expr: &GroupedExpression) -> Expr { + pub fn insert(&mut self, expr: &GroupedExpression) -> Expr { if let Some(&id) = self.reverse.get(expr) { Expr(id) } else { @@ -69,7 +69,7 @@ impl ExpressionDB { } } - pub fn insert_owned(&mut self, expr: GroupedExpression) -> Expr { + pub fn insert_owned(&mut self, expr: GroupedExpression) -> Expr { if let Some(&id) = self.reverse.get(&expr) { Expr(id) } else { @@ -80,24 +80,24 @@ impl ExpressionDB { // TODO rename this "Environment" -struct System { - expressions: RefCell, +struct System { + expressions: RefCell>, var_to_string: HashMap, /// Variables that only occurr once in the system /// (also only once in the constraint they occur in). single_occurrence_variables: HashSet, - range_constraints_on_vars: HashMap>, - new_var_generator: RefCell, + range_constraints_on_vars: HashMap>, + new_var_generator: RefCell>, } -impl System { +impl System { fn new( - expressions: ExpressionDB, + expressions: ExpressionDB, var_to_string: HashMap, single_occurrence_variables: HashSet, - range_constraints_on_vars: HashMap>, - new_var_generator: NewVarGenerator, + range_constraints_on_vars: HashMap>, + new_var_generator: NewVarGenerator, ) -> Self { Self { expressions: RefCell::new(expressions), @@ -108,7 +108,7 @@ impl System { } } - fn terminate(self) -> (ExpressionDB, NewVarGenerator) { + fn terminate(self) -> (ExpressionDB, NewVarGenerator) { ( self.expressions.into_inner(), self.new_var_generator.into_inner(), @@ -116,54 +116,54 @@ impl System { } } -impl PartialEq for System { +impl PartialEq for System { fn eq(&self, _other: &Self) -> bool { // TODO change this as soon as we have different systems true } } -impl Eq for System {} +impl Eq for System {} -impl PartialOrd for System { +impl PartialOrd for System { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } -impl Ord for System { +impl Ord for System { fn cmp(&self, _other: &Self) -> std::cmp::Ordering { // TODO change this as soon as we have different systems std::cmp::Ordering::Equal } } -impl Hash for System { +impl Hash for System { fn hash(&self, state: &mut H) { // TODO change this as soon as we have different systems 0.hash(state); } } -impl System { - pub fn insert(&self, expr: &GroupedExpression) -> Expr { +impl System { + pub fn insert(&self, expr: &GroupedExpression) -> Expr { self.expressions.borrow_mut().insert(expr) } - pub fn insert_owned(&self, expr: GroupedExpression) -> Expr { + pub fn insert_owned(&self, expr: GroupedExpression) -> Expr { self.expressions.borrow_mut().insert_owned(expr) } /// Extract an Expr into a free GroupedExpression. /// This is expensive since it clones the expression. - pub fn extract(&self, expr: Expr) -> GroupedExpression { + pub fn extract(&self, expr: Expr) -> GroupedExpression { self.expressions.borrow()[expr].clone() } pub fn new_var( &self, prefix: &str, - method: ComputationMethod>, + method: ComputationMethod>, ) -> Var { self.new_var_generator.borrow_mut().generate(prefix, method) } @@ -182,7 +182,7 @@ impl System { expr.is_affine().then(|| expr.linear_components().count()) } - pub fn try_to_affine(&self, expr: Expr) -> Option<(F, Var, F)> { + pub fn try_to_affine(&self, expr: Expr) -> Option<(T, Var, T)> { let db = self.expressions.borrow(); let expr = &db[expr]; if !expr.is_affine() { @@ -196,14 +196,14 @@ impl System { &self, expr: Expr, args: Args, - f: impl Fn(&GroupedExpression, Args) -> Ret, + f: impl Fn(&GroupedExpression, Args) -> Ret, ) -> Ret { let db = self.expressions.borrow(); let expr = &db[expr]; f(expr, args) } - pub fn range_constraint_on_expr(&self, expr: Expr) -> RangeConstraint { + pub fn range_constraint_on_expr(&self, expr: Expr) -> RangeConstraint { let db = self.expressions.borrow(); db[expr].range_constraint(self) } @@ -227,7 +227,7 @@ impl System { } /// Returns Some(C) if `a - b = C' and both are affine. - pub fn constant_difference(&self, a: Expr, b: Expr) -> Option { + pub fn constant_difference(&self, a: Expr, b: Expr) -> Option { let db = self.expressions.borrow(); let a = &db[a]; let b = &db[b]; @@ -243,7 +243,7 @@ impl System { /// such that `b` is obtained from `a` when replacing `v1` by `v2` and /// `coeff` is the coefficient of `v1` in `a` (and also of `v2` in `b`) /// also `a` and `b` have at least two variables each. - pub fn differ_in_exactly_one_variable(&self, a_id: Expr, b_id: Expr) -> Option<(Var, Var, F)> { + pub fn differ_in_exactly_one_variable(&self, a_id: Expr, b_id: Expr) -> Option<(Var, Var, T)> { let db = self.expressions.borrow(); let a = &db[a_id]; let b = &db[b_id]; @@ -283,7 +283,7 @@ impl System { } #[allow(dead_code)] - pub fn substitute_by_known(&self, e: Expr, var: Var, value: F) -> Expr { + pub fn substitute_by_known(&self, e: Expr, var: Var, value: T) -> Expr { let expr = { let db = self.expressions.borrow(); let mut expr = db[e].clone(); @@ -325,13 +325,13 @@ impl System { } } -struct NewVarGenerator { +struct NewVarGenerator { counter: u32, requests: HashMap, - computation_methods: HashMap>>, + computation_methods: HashMap>>, } -impl NewVarGenerator { +impl NewVarGenerator { fn new(initial_counter: u32) -> Self { Self { counter: initial_counter, @@ -343,7 +343,7 @@ impl NewVarGenerator { fn generate( &mut self, prefix: &str, - computation_method: ComputationMethod>, + computation_method: ComputationMethod>, ) -> Var { let var = Var(self.counter); self.requests.insert(var, prefix.to_string()); @@ -353,8 +353,8 @@ impl NewVarGenerator { } } -impl RangeConstraintProvider for System { - fn get(&self, var: &Var) -> RangeConstraint { +impl RangeConstraintProvider for System { + fn get(&self, var: &Var) -> RangeConstraint { self.range_constraints_on_vars .get(var) .cloned() @@ -366,15 +366,15 @@ impl RangeConstraintProvider for System { struct Expr(usize); #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -enum Action { - SubstituteVariableByConstant(Var, F), +enum Action { + SubstituteVariableByConstant(Var, T), SubstituteVariableByVariable(Var, Var), ReplaceAlgebraicConstraintBy(Expr, Expr), } crepe! { @input - struct S<'a>(&'a System); + struct S<'a, T: FieldElement>(&'a System); @input struct InitialAlgebraicConstraint(Expr); @@ -385,7 +385,7 @@ crepe! { // @input // struct BusInteractionConstraint<'a>(&'a BusInteraction>); - struct RangeConstraintOnExpression(Expr, RangeConstraint); + struct RangeConstraintOnExpression(Expr, RangeConstraint); struct Expression(Expr); Expression(e) <- AlgebraicConstraint(e); @@ -403,21 +403,21 @@ crepe! { S(sys), for v in sys.referenced_variables(e); - struct AffineExpression(Expr, F, Var, F); + struct AffineExpression(Expr, T, Var, T); AffineExpression(e, coeff, var, offset) <- Expression(e), S(sys), let Some((coeff, var, offset)) = sys.try_to_affine(e); - struct RangeConstraintOnVar(Var, RangeConstraint); + struct RangeConstraintOnVar(Var, RangeConstraint); RangeConstraintOnVar(v, rc) <- S(sys), ContainsVariable(_, v), let rc = sys.get(&v); // RC(coeff * var + offset) = rc <=> // coeff * RC(var) + offset = rc <=> // RC(var) = (rc - offset) / coeff - RangeConstraintOnVar(v, rc.combine_sum(&RangeConstraint::from_value(-offset)).multiple(F::one() / coeff)) <- + RangeConstraintOnVar(v, rc.combine_sum(&RangeConstraint::from_value(-offset)).multiple(T::one() / coeff)) <- RangeConstraintOnExpression(e, rc), AffineExpression(e, coeff, v, offset), - (coeff != F::zero()); + (coeff != T::zero()); RangeConstraintOnVar(v, v_rc1.conjunction(&v_rc2)) <- RangeConstraintOnVar(v, v_rc1), @@ -431,7 +431,7 @@ crepe! { Product(e, r, l) <- Product(e, l, r); // (E, expr, offset) <-> E = (expr) * (expr + offset) is a constraint - struct QuadraticEquivalenceCandidate(Expr, Expr, F); + struct QuadraticEquivalenceCandidate(Expr, Expr, T); QuadraticEquivalenceCandidate(e, r, offset) <- AlgebraicConstraint(e), S(sys), @@ -505,7 +505,7 @@ crepe! { // if e is the expression of an algebraic constraint and // e = coeff1 * v1 * x1 + coeff2 * v2 * x2 + ... // where v1 and v2 are different variables that only occur here and only once. - struct FreeVariableCombinationCandidate(Expr, F, Var, Expr, F, Var, Expr); + struct FreeVariableCombinationCandidate(Expr, T, Var, Expr, T, Var, Expr); FreeVariableCombinationCandidate(e, coeff1, v1, x1, coeff2, v2, x2) <- // SingleOccurrenceVariable(e, v1), // SingleOccurrenceVariable(e, v2), @@ -532,8 +532,8 @@ crepe! { RangeConstraintOnExpression(x1, rc1), RangeConstraintOnExpression(x2, rc2), let Some(replacement) = (|| { - let x1_needs_squaring = rc1.range().0 != F::zero(); - let x2_needs_squaring = rc2.range().0 != F::zero(); + let x1_needs_squaring = rc1.range().0 != T::zero(); + let x2_needs_squaring = rc2.range().0 != T::zero(); let rc1 = if x1_needs_squaring { rc1.square() } else { @@ -548,7 +548,7 @@ crepe! { return None; } let sum_rc = rc1.multiple(coeff1).combine_sum(&rc2.multiple(coeff2)); - if !(sum_rc.range().0.is_zero() && sum_rc.range().1 < F::from(-1)) { + if !(sum_rc.range().0.is_zero() && sum_rc.range().1 < T::from(-1)) { return None; } let e = sys.extract(e); @@ -572,7 +572,7 @@ crepe! { } }; true - }).map(GroupedExpression::from).sum::>(); + }).map(GroupedExpression::from).sum::>(); let factor = x1 * coeff1 + x2 * coeff2; let combined_var = sys.new_var("free_var", ComputationMethod::QuotientOrZero(-r.clone(), factor.clone())); let replacement = r + GroupedExpression::from_unknown_variable(combined_var) * factor; @@ -583,18 +583,18 @@ crepe! { // algebraic constraints. Then we just work on range constraints on expressions // instead of algebraic constraints. Might be more difficult with the scaling, though. - struct Solvable(Expr, Var, F); + struct Solvable(Expr, Var, T); Solvable(e, var, -offset / coeff) <- AffineExpression(e, coeff, var, offset); // Boolean range constraint - RangeConstraintOnVar(v, RangeConstraint::from_range(x1, x1 + F::from(1))) <- + RangeConstraintOnVar(v, RangeConstraint::from_range(x1, x1 + T::from(1))) <- AlgebraicConstraint(e), Product(e, l, r), Solvable(l, v, x1), - Solvable(r, v, x1 + F::from(1)); + Solvable(r, v, x1 + T::from(1)); - struct Assignment(Var, F); + struct Assignment(Var, T); Assignment(var, v) <- AlgebraicConstraint(e), Solvable(e, var, v); @@ -622,7 +622,7 @@ crepe! { @output - struct ActionRule(Action); + struct ActionRule(Action); ActionRule(Action::SubstituteVariableByConstant(v, val)) <- Assignment(v, val); ActionRule(Action::SubstituteVariableByVariable(v, v2)) <- @@ -654,13 +654,13 @@ pub fn rule_based_optimization>(); - let mut db = Some(ExpressionDB::default()); + let mut db = Some(ExpressionDB::::default()); loop { let (algebraic_constraints, _bus_interactions) = transform_constraint_system(&system, &var_mapper, db.as_mut().unwrap()); - let sys = System::new( + let sys = System::::new( db.take().unwrap(), var_mapper.all_names(), system @@ -669,10 +669,7 @@ pub fn rule_based_optimization { - system.substitute_by_known( - var_mapper.backward(&var), - &untransform_field_element(&val), - ); + system.substitute_by_known(var_mapper.backward(&var), &val); progress = true; } Action::SubstituteVariableByVariable(v1, v2) => { @@ -766,7 +760,7 @@ pub fn rule_based_optimization( system: &IndexedConstraintSystem, var_mapper: &VarMapper, - expression_db: &mut ExpressionDB, + expression_db: &mut ExpressionDB, ) -> (Vec, Vec>) { let algebraic_constraints = system .system() @@ -845,53 +839,23 @@ impl VarMapper { fn transform_grouped_expression( expr: &GroupedExpression, var_mapper: &VarMapper, -) -> GroupedExpression { - expr.clone() - .into_summands() - .map(|s| match s { - GroupedExpressionComponent::Quadratic(l, r) => { - transform_grouped_expression(&l, var_mapper) - * transform_grouped_expression(&r, var_mapper) - } - GroupedExpressionComponent::Linear(v, c) => { - GroupedExpression::from_unknown_variable(var_mapper.forward(&v)) - * transform_field_element(&c) - } - GroupedExpressionComponent::Constant(c) => { - GroupedExpression::from_number(transform_field_element(&c)) - } - }) - .sum() +) -> GroupedExpression { + expr.transform_var_type(&mut |v| var_mapper.forward(v)) } fn untransform_grouped_expression( - expr: &GroupedExpression, + expr: &GroupedExpression, var_mapper: &VarMapper, ) -> GroupedExpression { - expr.clone() - .into_summands() - .map(|s| match s { - GroupedExpressionComponent::Quadratic(l, r) => { - untransform_grouped_expression(&l, var_mapper) - * untransform_grouped_expression(&r, var_mapper) - } - GroupedExpressionComponent::Linear(v, c) => { - GroupedExpression::::from_unknown_variable(var_mapper.backward(&v).clone()) - * untransform_field_element::(&c) - } - GroupedExpressionComponent::Constant(c) => { - GroupedExpression::from_number(untransform_field_element(&c)) - } - }) - .sum() + expr.transform_var_type(&mut |v| var_mapper.backward(v).clone()) } fn untransform_computation_method( - method: &ComputationMethod>, + method: &ComputationMethod>, var_mapper: &VarMapper, ) -> ComputationMethod> { match method { - ComputationMethod::Constant(c) => ComputationMethod::Constant(untransform_field_element(c)), + ComputationMethod::Constant(c) => ComputationMethod::Constant(*c), ComputationMethod::QuotientOrZero(numerator, denominator) => { ComputationMethod::QuotientOrZero( untransform_grouped_expression(numerator, var_mapper), @@ -901,23 +865,6 @@ fn untransform_computation_method(fe: &T) -> BabyBearField { - BabyBearField::from(fe.to_arbitrary_integer()) -} - -fn untransform_field_element(fe: &BabyBearField) -> T { - T::from(fe.to_arbitrary_integer()) -} - -fn transform_range_constraint( - rc: &RangeConstraint, -) -> RangeConstraint { - let (min, max) = rc.range(); - let mask = *rc.mask(); - RangeConstraint::from_range(transform_field_element(&min), transform_field_element(&max)) - .conjunction(&RangeConstraint::from_mask(mask.try_into_u64().unwrap())) -} - #[cfg(test)] mod tests { use expect_test::expect; @@ -975,9 +922,9 @@ mod tests { } } - fn try_handle_bus_interaction( - bus_interaction: &BusInteraction>, - ) -> Option>> { + fn try_handle_bus_interaction( + bus_interaction: &BusInteraction>, + ) -> Option>> { let mult = bus_interaction.multiplicity.try_to_single_value()?; if mult == Zero::zero() { return None; @@ -1000,11 +947,11 @@ mod tests { #[derive(Clone)] struct TestBusInteractionHandler; - impl BusInteractionHandler for TestBusInteractionHandler { + impl BusInteractionHandler for TestBusInteractionHandler { fn handle_bus_interaction( &self, - bus_interaction: BusInteraction>, - ) -> BusInteraction> { + bus_interaction: BusInteraction>, + ) -> BusInteraction> { try_handle_bus_interaction(&bus_interaction).unwrap_or(bus_interaction) } } @@ -1038,7 +985,7 @@ mod tests { let mut system = IndexedConstraintSystem::default(); let x = v("x"); system.add_algebraic_constraints([ - assert_zero(x * F::from(7) - c(21)), + assert_zero(x * BabyBearField::from(7) - c(21)), assert_zero(v("y") * (v("y") - c(1)) - v("x")), ]); let optimized_system = rule_based_optimization( From 4fdfde47d3ed4f9335ecae08cdd1ee5871088a92 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 27 Nov 2025 00:47:08 +0000 Subject: [PATCH 43/60] Simplify generic parameter of ExpressionDB. --- autoprecompiles/src/rule_based_optimizer.rs | 38 ++++++++++++--------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/autoprecompiles/src/rule_based_optimizer.rs b/autoprecompiles/src/rule_based_optimizer.rs index cac839993b..4ad3639c93 100644 --- a/autoprecompiles/src/rule_based_optimizer.rs +++ b/autoprecompiles/src/rule_based_optimizer.rs @@ -38,30 +38,36 @@ impl Display for Var { } } -// TODO could be generic over Expr +struct ExpressionDB { + expressions: Vec, + reverse: HashMap, +} -#[derive(Default)] -struct ExpressionDB { - expressions: Vec>, - reverse: HashMap, usize>, +impl Default for ExpressionDB { + fn default() -> Self { + Self { + expressions: Vec::new(), + reverse: HashMap::new(), + } + } } -impl Index for ExpressionDB { - type Output = GroupedExpression; +impl Index for ExpressionDB { + type Output = E; fn index(&self, index: Expr) -> &Self::Output { &self.expressions[index.0] } } -impl ExpressionDB { - fn insert_owned_new(&mut self, expr: GroupedExpression) -> Expr { +impl ExpressionDB { + fn insert_owned_new(&mut self, expr: E) -> Expr { self.expressions.push(expr.clone()); let id = self.expressions.len() - 1; self.reverse.insert(expr, id); Expr(id) } - pub fn insert(&mut self, expr: &GroupedExpression) -> Expr { + pub fn insert(&mut self, expr: &E) -> Expr { if let Some(&id) = self.reverse.get(expr) { Expr(id) } else { @@ -69,7 +75,7 @@ impl ExpressionDB { } } - pub fn insert_owned(&mut self, expr: GroupedExpression) -> Expr { + pub fn insert_owned(&mut self, expr: E) -> Expr { if let Some(&id) = self.reverse.get(&expr) { Expr(id) } else { @@ -81,7 +87,7 @@ impl ExpressionDB { // TODO rename this "Environment" struct System { - expressions: RefCell>, + expressions: RefCell>>, var_to_string: HashMap, /// Variables that only occurr once in the system @@ -93,7 +99,7 @@ struct System { impl System { fn new( - expressions: ExpressionDB, + expressions: ExpressionDB>, var_to_string: HashMap, single_occurrence_variables: HashSet, range_constraints_on_vars: HashMap>, @@ -108,7 +114,7 @@ impl System { } } - fn terminate(self) -> (ExpressionDB, NewVarGenerator) { + fn terminate(self) -> (ExpressionDB>, NewVarGenerator) { ( self.expressions.into_inner(), self.new_var_generator.into_inner(), @@ -654,7 +660,7 @@ pub fn rule_based_optimization>(); - let mut db = Some(ExpressionDB::::default()); + let mut db = Some(ExpressionDB::>::default()); loop { let (algebraic_constraints, _bus_interactions) = @@ -760,7 +766,7 @@ pub fn rule_based_optimization( system: &IndexedConstraintSystem, var_mapper: &VarMapper, - expression_db: &mut ExpressionDB, + expression_db: &mut ExpressionDB>, ) -> (Vec, Vec>) { let algebraic_constraints = system .system() From dc32b16ad95494f03b8833fa5df7da0addcfabbc Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 27 Nov 2025 00:50:43 +0000 Subject: [PATCH 44/60] Rename system to environment. --- autoprecompiles/src/rule_based_optimizer.rs | 100 ++++++++++---------- 1 file changed, 49 insertions(+), 51 deletions(-) diff --git a/autoprecompiles/src/rule_based_optimizer.rs b/autoprecompiles/src/rule_based_optimizer.rs index 4ad3639c93..c52294b5c5 100644 --- a/autoprecompiles/src/rule_based_optimizer.rs +++ b/autoprecompiles/src/rule_based_optimizer.rs @@ -84,9 +84,7 @@ impl ExpressionDB { } } -// TODO rename this "Environment" - -struct System { +struct Environment { expressions: RefCell>>, var_to_string: HashMap, @@ -97,7 +95,7 @@ struct System { new_var_generator: RefCell>, } -impl System { +impl Environment { fn new( expressions: ExpressionDB>, var_to_string: HashMap, @@ -122,36 +120,36 @@ impl System { } } -impl PartialEq for System { +impl PartialEq for Environment { fn eq(&self, _other: &Self) -> bool { - // TODO change this as soon as we have different systems + // TODO change this as soon as we have different environments true } } -impl Eq for System {} +impl Eq for Environment {} -impl PartialOrd for System { +impl PartialOrd for Environment { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } -impl Ord for System { +impl Ord for Environment { fn cmp(&self, _other: &Self) -> std::cmp::Ordering { - // TODO change this as soon as we have different systems + // TODO change this as soon as we have different environments std::cmp::Ordering::Equal } } -impl Hash for System { +impl Hash for Environment { fn hash(&self, state: &mut H) { - // TODO change this as soon as we have different systems + // TODO change this as soon as we have different environments 0.hash(state); } } -impl System { +impl Environment { pub fn insert(&self, expr: &GroupedExpression) -> Expr { self.expressions.borrow_mut().insert(expr) } @@ -359,7 +357,7 @@ impl NewVarGenerator { } } -impl RangeConstraintProvider for System { +impl RangeConstraintProvider for Environment { fn get(&self, var: &Var) -> RangeConstraint { self.range_constraints_on_vars .get(var) @@ -380,7 +378,7 @@ enum Action { crepe! { @input - struct S<'a, T: FieldElement>(&'a System); + struct Env<'a, T: FieldElement>(&'a Environment); @input struct InitialAlgebraicConstraint(Expr); @@ -398,25 +396,25 @@ crepe! { Expression(e) <- RangeConstraintOnExpression(e, _); RangeConstraintOnExpression(e, rc) <- - S(sys), + Env(env), Expression(e), - let rc = sys.range_constraint_on_expr(e); + let rc = env.range_constraint_on_expr(e); struct ContainsVariable(Expr, Var); ContainsVariable(e, v) <- Expression(e), - S(sys), - for v in sys.referenced_variables(e); + Env(env), + for v in env.referenced_variables(e); struct AffineExpression(Expr, T, Var, T); AffineExpression(e, coeff, var, offset) <- Expression(e), - S(sys), - let Some((coeff, var, offset)) = sys.try_to_affine(e); + Env(env), + let Some((coeff, var, offset)) = env.try_to_affine(e); struct RangeConstraintOnVar(Var, RangeConstraint); - RangeConstraintOnVar(v, rc) <- S(sys), ContainsVariable(_, v), let rc = sys.get(&v); + RangeConstraintOnVar(v, rc) <- Env(env), ContainsVariable(_, v), let rc = env.get(&v); // RC(coeff * var + offset) = rc <=> // coeff * RC(var) + offset = rc <=> // RC(var) = (rc - offset) / coeff @@ -432,25 +430,25 @@ crepe! { struct Product(Expr, Expr, Expr); Product(e, l, r) <- Expression(e), - S(sys), - let Some((l, r)) = sys.try_as_single_product(e); + Env(env), + let Some((l, r)) = env.try_as_single_product(e); Product(e, r, l) <- Product(e, l, r); // (E, expr, offset) <-> E = (expr) * (expr + offset) is a constraint struct QuadraticEquivalenceCandidate(Expr, Expr, T); QuadraticEquivalenceCandidate(e, r, offset) <- AlgebraicConstraint(e), - S(sys), + Env(env), Product(e, l, r), - ({sys.affine_var_count(l).unwrap_or(0) > 1 && sys.affine_var_count(r).unwrap_or(0) > 1}), - let Some(offset) = sys.constant_difference(l, r); + ({env.affine_var_count(l).unwrap_or(0) > 1 && env.affine_var_count(r).unwrap_or(0) > 1}), + let Some(offset) = env.constant_difference(l, r); struct QuadraticEquivalence(Var, Var); QuadraticEquivalence(v1, v2) <- QuadraticEquivalenceCandidate(_, expr1, offset), QuadraticEquivalenceCandidate(_, expr2, offset), - S(sys), - let Some((v1, v2, coeff)) = sys.differ_in_exactly_one_variable(expr1, expr2), + Env(env), + let Some((v1, v2, coeff)) = env.differ_in_exactly_one_variable(expr1, expr2), RangeConstraintOnVar(v1, rc), RangeConstraintOnVar(v2, rc), (rc.is_disjoint(&rc.combine_sum(&RangeConstraint::from_value(offset / coeff)))); @@ -474,31 +472,31 @@ crepe! { // if that value is still small enough. struct SingleOccurrenceVariable(Expr, Var); SingleOccurrenceVariable(e, v) <- - S(sys), - for v in sys.single_occurrence_variables().cloned(), + Env(env), + for v in env.single_occurrence_variables().cloned(), AlgebraicConstraint(e), ContainsVariable(e, v2), (v == v2); struct LargestSingleOccurrenceVariablePairInExpr(Expr, Var, Var); LargestSingleOccurrenceVariablePairInExpr(e, v1, v2) <- - S(sys), + Env(env), SingleOccurrenceVariable(e, v1), SingleOccurrenceVariable(e, v2), (v1 < v2), - (sys + (env .single_occurrence_variables() - .filter(|v3| sys.referenced_variables(e).contains(v3)) + .filter(|v3| env.referenced_variables(e).contains(v3)) .all(|&v3| v3 == v1 || v3 == v2 || v3 < v1)); struct HasProductSummand(Expr, Expr, Expr); HasProductSummand(e, l, r) <- - S(sys), + Env(env), Expression(e), - (!sys.on_expr(e, (), |e, _| e.is_affine())), - for (l, r) in sys.extract(e).into_summands().filter_map(|s| { + (!env.on_expr(e, (), |e, _| e.is_affine())), + for (l, r) in env.extract(e).into_summands().filter_map(|s| { if let GroupedExpressionComponent::Quadratic(l, r) = s { - Some((sys.insert_owned(l), sys.insert_owned(r))) + Some((env.insert_owned(l), env.insert_owned(r))) } else { None } @@ -532,7 +530,7 @@ crepe! { struct PotentiallyReplaceAlgebraicConstraintBy(Expr, Expr); PotentiallyReplaceAlgebraicConstraintBy(e, replacement) <- - S(sys), + Env(env), FreeVariableCombinationCandidate(e, coeff1, v1, x1, coeff2, v2, x2), // Here, we have e = coeff1 * v1 * x1 + coeff2 * v2 * x2 + ... RangeConstraintOnExpression(x1, rc1), @@ -557,14 +555,14 @@ crepe! { if !(sum_rc.range().0.is_zero() && sum_rc.range().1 < T::from(-1)) { return None; } - let e = sys.extract(e); - let x1 = sys.extract(x1); + let e = env.extract(e); + let x1 = env.extract(x1); let x1 = if x1_needs_squaring { x1.clone() * x1 } else { x1 }; - let x2 = sys.extract(x2); + let x2 = env.extract(x2); let x2 = if x2_needs_squaring { x2.clone() * x2 } else { @@ -580,9 +578,9 @@ crepe! { true }).map(GroupedExpression::from).sum::>(); let factor = x1 * coeff1 + x2 * coeff2; - let combined_var = sys.new_var("free_var", ComputationMethod::QuotientOrZero(-r.clone(), factor.clone())); + let combined_var = env.new_var("free_var", ComputationMethod::QuotientOrZero(-r.clone(), factor.clone())); let replacement = r + GroupedExpression::from_unknown_variable(combined_var) * factor; - Some(sys.insert_owned(replacement)) + Some(env.insert_owned(replacement)) })(); // TODO wait a second. We can craete range constraints on expressions for all @@ -610,13 +608,13 @@ crepe! { // Do not do this because it is rather expensive. - ReplaceAlgebraicConstraintBy(e, sys.substitute_by_known(e, v, val)) <- - S(sys), + ReplaceAlgebraicConstraintBy(e, env.substitute_by_known(e, v, val)) <- + Env(env), AlgebraicConstraint(e), ContainsVariable(e, v), Assignment(v, val); - ReplaceAlgebraicConstraintBy(e, sys.substitute_by_var(e, v, v2)) <- - S(sys), + ReplaceAlgebraicConstraintBy(e, env.substitute_by_var(e, v, v2)) <- + Env(env), AlgebraicConstraint(e), ContainsVariable(e, v), Equivalence(v, v2); @@ -666,7 +664,7 @@ pub fn rule_based_optimization::new( + let env = Environment::::new( db.take().unwrap(), var_mapper.all_names(), system @@ -688,10 +686,10 @@ pub fn rule_based_optimization Date: Thu, 27 Nov 2025 02:49:40 +0000 Subject: [PATCH 45/60] Update expectations. --- openvm/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openvm/src/lib.rs b/openvm/src/lib.rs index 96ef351c99..8f0f44f6d2 100644 --- a/openvm/src/lib.rs +++ b/openvm/src/lib.rs @@ -1841,7 +1841,7 @@ mod tests { AirMetrics { widths: AirWidths { preprocessed: 0, - main: 14226, + main: 14229, log_up: 22720, }, constraints: 4261, @@ -1863,7 +1863,7 @@ mod tests { }, after: AirWidths { preprocessed: 0, - main: 14226, + main: 14229, log_up: 22720, }, } From aed745ebaa10828c97a3c601158064b23dd87022 Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 28 Nov 2025 11:25:19 +0000 Subject: [PATCH 46/60] Combine non-negative factors. --- autoprecompiles/src/rule_based_optimizer.rs | 52 +++++++++++++++++++ openvm/src/lib.rs | 14 ++--- .../apc_snapshots/complex/memcpy_block.txt | 6 +-- .../complex/unaligned_memcpy.txt | 8 ++- .../pseudo_instructions/beqz.txt | 7 +-- .../pseudo_instructions/bnez.txt | 7 +-- .../pseudo_instructions/snez.txt | 37 ++++++++----- .../single_instructions/single_sra.txt | 9 ++-- 8 files changed, 94 insertions(+), 46 deletions(-) diff --git a/autoprecompiles/src/rule_based_optimizer.rs b/autoprecompiles/src/rule_based_optimizer.rs index c52294b5c5..d1c36ba7c0 100644 --- a/autoprecompiles/src/rule_based_optimizer.rs +++ b/autoprecompiles/src/rule_based_optimizer.rs @@ -374,6 +374,7 @@ enum Action { SubstituteVariableByConstant(Var, T), SubstituteVariableByVariable(Var, Var), ReplaceAlgebraicConstraintBy(Expr, Expr), + ReplacePairOfAlgebraicConstraintsBy(Expr, Expr, Expr), } crepe! { @@ -583,6 +584,23 @@ crepe! { Some(env.insert_owned(replacement)) })(); + // If we have x * a = 0 and x * b = 0 and (a = 0 and b = 0) is equivalent to (a + b = 0), + // replace those two by x * (a + b) = 0. + struct PotentiallyReplacePairOfAlgebraicConstraintsBy(Expr, Expr, Expr); + PotentiallyReplacePairOfAlgebraicConstraintsBy(e1, e2, replacement) <- + Env(env), + AlgebraicConstraint(e1), + AlgebraicConstraint(e2), + Product(e1, x, a), + Product(e2, x, b), + (e1 < e2), + RangeConstraintOnExpression(a, rc_a), + RangeConstraintOnExpression(b, rc_b), + (rc_a.range().0 == T::zero() + && rc_b.range().0 == T::zero() && rc_a.combine_sum(&rc_b).range().1 < T::from(-1)), + let replacement = env.insert_owned(env.extract(x) * (env.extract(a) + env.extract(b))); + + // TODO wait a second. We can craete range constraints on expressions for all // algebraic constraints. Then we just work on range constraints on expressions // instead of algebraic constraints. Might be more difficult with the scaling, though. @@ -633,6 +651,8 @@ crepe! { Equivalence(v, v2); ActionRule(Action::ReplaceAlgebraicConstraintBy(old, new)) <- PotentiallyReplaceAlgebraicConstraintBy(old, new); + ActionRule(Action::ReplacePairOfAlgebraicConstraintsBy(e1, e2, replacement)) <- + PotentiallyReplacePairOfAlgebraicConstraintsBy(e1, e2, replacement); } pub fn rule_based_optimization( @@ -749,6 +769,38 @@ pub fn rule_based_optimization { + let expr1 = + untransform_grouped_expression(&db.as_ref().unwrap()[e1], &var_mapper); + let expr2 = + untransform_grouped_expression(&db.as_ref().unwrap()[e2], &var_mapper); + let mut found1 = false; + let mut found2 = false; + for c in system.algebraic_constraints() { + if c.expression == expr1 { + found1 = true; + } else if c.expression == expr2 { + found2 = true; + } + } + if found1 && found2 { + system.retain_algebraic_constraints(|c| { + c.expression != expr1 && c.expression != expr2 + }); + let replacement = untransform_grouped_expression( + &db.as_ref().unwrap()[replacement], + &var_mapper, + ); + system.add_algebraic_constraints([ + algebraic_constraint::AlgebraicConstraint::assert_zero(replacement), + ]); + progress = true; + } else { + log::warn!( + "Was about to replace {expr1} and {expr2} but did not find them in the system." + ); + } + } } } if !progress { diff --git a/openvm/src/lib.rs b/openvm/src/lib.rs index 8f0f44f6d2..2a984f3b6d 100644 --- a/openvm/src/lib.rs +++ b/openvm/src/lib.rs @@ -1740,7 +1740,7 @@ mod tests { main: 38, log_up: 56, }, - constraints: 15, + constraints: 12, bus_interactions: 26, } "#]], @@ -1768,7 +1768,7 @@ mod tests { main: 38, log_up: 56, }, - constraints: 15, + constraints: 12, bus_interactions: 26, } "#]], @@ -1816,7 +1816,7 @@ mod tests { main: 14257, log_up: 22752, }, - constraints: 4285, + constraints: 4279, bus_interactions: 11143, } "#]], @@ -1844,7 +1844,7 @@ mod tests { main: 14229, log_up: 22720, }, - constraints: 4261, + constraints: 4255, bus_interactions: 11133, } "#]], @@ -1893,7 +1893,7 @@ mod tests { main: 17297, log_up: 27896, }, - constraints: 8834, + constraints: 8830, bus_interactions: 11925, } "#]], @@ -1942,7 +1942,7 @@ mod tests { main: 19928, log_up: 30924, }, - constraints: 11103, + constraints: 11099, bus_interactions: 13442, } "#]], @@ -2097,7 +2097,7 @@ mod tests { main: 3242, log_up: 5268, }, - constraints: 594, + constraints: 592, bus_interactions: 2564, } "#]], diff --git a/openvm/tests/apc_snapshots/complex/memcpy_block.txt b/openvm/tests/apc_snapshots/complex/memcpy_block.txt index 6bbff134a9..ce061f467d 100644 --- a/openvm/tests/apc_snapshots/complex/memcpy_block.txt +++ b/openvm/tests/apc_snapshots/complex/memcpy_block.txt @@ -8,7 +8,7 @@ Instructions: APC advantage: - Main columns: 172 -> 37 (4.65x reduction) - Bus interactions: 87 -> 21 (4.14x reduction) - - Constraints: 111 -> 33 (3.36x reduction) + - Constraints: 111 -> 31 (3.58x reduction) Symbolic machine using 37 unique main columns: from_state__timestamp_0 @@ -81,11 +81,8 @@ mult=diff_marker__0_2 + diff_marker__1_2 + diff_marker__2_2 + diff_marker__3_2, // Algebraic constraints: cmp_result_1 * (cmp_result_1 - 1) = 0 diff_marker__3_1 * (diff_marker__3_1 - 1) = 0 -diff_marker__3_1 * diff_val_1 = 0 diff_marker__2_1 * (diff_marker__2_1 - 1) = 0 -diff_marker__2_1 * diff_val_1 = 0 diff_marker__1_1 * (diff_marker__1_1 - 1) = 0 -diff_marker__1_1 * diff_val_1 = 0 diff_marker__0_1 * (diff_marker__0_1 - 1) = 0 (1 * is_valid - (diff_marker__0_1 + diff_marker__1_1 + diff_marker__2_1 + diff_marker__3_1)) * ((1 - a__0_0) * (2 * cmp_result_1 - 1)) = 0 diff_marker__0_1 * ((a__0_0 - 1) * (2 * cmp_result_1 - 1) + diff_val_1) = 0 @@ -109,6 +106,7 @@ diff_marker__0_2 * ((writes_aux__prev_data__0_2 - 1) * (2 * cmp_result_2 - 1) + cmp_result_4 * (cmp_result_4 - 1) = 0 (1 - cmp_result_4) * (cmp_result_1 + cmp_result_2 - cmp_result_1 * cmp_result_2) = 0 free_var_177 * (cmp_result_1 + cmp_result_2 - cmp_result_1 * cmp_result_2) - cmp_result_4 = 0 +diff_val_1 * (diff_marker__1_1 + diff_marker__2_1 + diff_marker__3_1) = 0 (1 - is_valid) * (diff_marker__0_1 + diff_marker__1_1 + diff_marker__2_1 + diff_marker__3_1) = 0 (1 - is_valid) * (diff_marker__0_2 + diff_marker__1_2 + diff_marker__2_2 + diff_marker__3_2) = 0 is_valid * (is_valid - 1) = 0 \ No newline at end of file diff --git a/openvm/tests/apc_snapshots/complex/unaligned_memcpy.txt b/openvm/tests/apc_snapshots/complex/unaligned_memcpy.txt index ab4b030135..88d02fc553 100644 --- a/openvm/tests/apc_snapshots/complex/unaligned_memcpy.txt +++ b/openvm/tests/apc_snapshots/complex/unaligned_memcpy.txt @@ -16,7 +16,7 @@ Instructions: APC advantage: - Main columns: 465 -> 105 (4.43x reduction) - Bus interactions: 242 -> 58 (4.17x reduction) - - Constraints: 286 -> 69 (4.14x reduction) + - Constraints: 286 -> 67 (4.27x reduction) Symbolic machine using 105 unique main columns: from_state__timestamp_0 @@ -224,11 +224,8 @@ flags__1_3 * (flags__1_3 - 1) + flags__2_3 * (flags__2_3 - 1) + 4 * flags__0_3 * (943718400 * writes_aux__prev_data__0_4 + 120 * a__1_4 + 30720 * a__2_4 + 7864320 * a__3_4 - (120 * writes_aux__prev_data__1_4 + 30720 * writes_aux__prev_data__2_4 + 7864320 * writes_aux__prev_data__3_4 + 943718400 * a__0_4 + 943718399 * is_valid)) * (943718400 * writes_aux__prev_data__0_4 + 120 * a__1_4 + 30720 * a__2_4 + 7864320 * a__3_4 - (120 * writes_aux__prev_data__1_4 + 30720 * writes_aux__prev_data__2_4 + 7864320 * writes_aux__prev_data__3_4 + 943718400 * a__0_4 + 943718400)) = 0 cmp_result_6 * (cmp_result_6 - 1) = 0 diff_marker__3_6 * (diff_marker__3_6 - 1) = 0 -diff_marker__3_6 * diff_val_6 = 0 diff_marker__2_6 * (diff_marker__2_6 - 1) = 0 -diff_marker__2_6 * diff_val_6 = 0 diff_marker__1_6 * (diff_marker__1_6 - 1) = 0 -diff_marker__1_6 * diff_val_6 = 0 diff_marker__0_6 * (diff_marker__0_6 - 1) = 0 (1 - (diff_marker__0_6 + diff_marker__1_6 + diff_marker__2_6 + diff_marker__3_6)) * (a__0_5 * (2 * cmp_result_6 - 1)) = 0 diff_marker__0_6 * (diff_val_6 - a__0_5 * (2 * cmp_result_6 - 1)) = 0 @@ -254,10 +251,11 @@ diff_marker__0_7 * (diff_val_7 - a__0_4 * (2 * cmp_result_7 - 1)) = 0 (120 * a__0_9 + 30720 * a__1_9 + 7864320 * a__2_9 - (120 * b__0_5 + 30720 * b__1_5 + 7864320 * b__2_5 + 120 * is_valid)) * (120 * a__0_9 + 30720 * a__1_9 + 7864320 * a__2_9 - (120 * b__0_5 + 30720 * b__1_5 + 7864320 * b__2_5 + 121)) = 0 (943718400 * b__0_5 + 120 * a__1_9 + 30720 * a__2_9 + 7864320 * a__3_9 + 943718400 * is_valid - (120 * b__1_5 + 30720 * b__2_5 + 7864320 * b__3_5 + 943718400 * a__0_9)) * (943718400 * b__0_5 + 120 * a__1_9 + 30720 * a__2_9 + 7864320 * a__3_9 + 943718399 - (120 * b__1_5 + 30720 * b__2_5 + 7864320 * b__3_5 + 943718400 * a__0_9)) = 0 cmp_result_12 * (cmp_result_12 - 1) = 0 -(1 - cmp_result_12) * (cmp_result_6 * cmp_result_7) = 0 flags__2_3 * (flags__2_3 - 1) - (flags__0_3 * (flags__0_3 + flags__1_3 + flags__2_3 + flags__3_3 - 2) + 2 * flags__1_3 * (flags__0_3 + flags__1_3 + flags__2_3 + flags__3_3 - 2) + 3 * flags__2_3 * (flags__0_3 + flags__1_3 + flags__2_3 + flags__3_3 - 2)) = 0 +(1 - cmp_result_12) * (cmp_result_6 * cmp_result_7) = 0 free_var_470 * (cmp_result_6 * cmp_result_7) - cmp_result_12 = 0 opcode_loadb_flag0_0 * shifted_read_data__0_0 + (1 - opcode_loadb_flag0_0) * shifted_read_data__1_0 - read_data__0_3 = 0 +diff_val_6 * (diff_marker__1_6 + diff_marker__2_6 + diff_marker__3_6) = 0 (1 - is_valid) * (diff_marker__0_6 + diff_marker__1_6 + diff_marker__2_6 + diff_marker__3_6) = 0 (1 - is_valid) * (diff_marker__0_7 + diff_marker__1_7 + diff_marker__2_7 + diff_marker__3_7) = 0 is_valid * (is_valid - 1) = 0 \ No newline at end of file diff --git a/openvm/tests/apc_snapshots/pseudo_instructions/beqz.txt b/openvm/tests/apc_snapshots/pseudo_instructions/beqz.txt index 4ac37e0708..4a4695ac59 100644 --- a/openvm/tests/apc_snapshots/pseudo_instructions/beqz.txt +++ b/openvm/tests/apc_snapshots/pseudo_instructions/beqz.txt @@ -4,7 +4,7 @@ Instructions: APC advantage: - Main columns: 26 -> 12 (2.17x reduction) - Bus interactions: 11 -> 10 (1.10x reduction) - - Constraints: 11 -> 7 (1.57x reduction) + - Constraints: 11 -> 4 (2.75x reduction) Symbolic machine using 12 unique main columns: from_state__timestamp_0 @@ -38,9 +38,6 @@ mult=is_valid * 1, args=[15360 * reads_aux__1__base__prev_timestamp_0 + 15360 * // Algebraic constraints: cmp_result_0 * (cmp_result_0 - 1) = 0 -cmp_result_0 * a__0_0 = 0 -cmp_result_0 * a__1_0 = 0 -cmp_result_0 * a__2_0 = 0 -cmp_result_0 * a__3_0 = 0 +cmp_result_0 * (a__0_0 + a__1_0 + a__2_0 + a__3_0) = 0 free_var_31 * (a__0_0 + a__1_0 + a__2_0 + a__3_0) + cmp_result_0 - 1 * is_valid = 0 is_valid * (is_valid - 1) = 0 \ No newline at end of file diff --git a/openvm/tests/apc_snapshots/pseudo_instructions/bnez.txt b/openvm/tests/apc_snapshots/pseudo_instructions/bnez.txt index cfa031036e..d67b478a08 100644 --- a/openvm/tests/apc_snapshots/pseudo_instructions/bnez.txt +++ b/openvm/tests/apc_snapshots/pseudo_instructions/bnez.txt @@ -4,7 +4,7 @@ Instructions: APC advantage: - Main columns: 26 -> 12 (2.17x reduction) - Bus interactions: 11 -> 10 (1.10x reduction) - - Constraints: 11 -> 7 (1.57x reduction) + - Constraints: 11 -> 4 (2.75x reduction) Symbolic machine using 12 unique main columns: from_state__timestamp_0 @@ -38,9 +38,6 @@ mult=is_valid * 1, args=[15360 * reads_aux__1__base__prev_timestamp_0 + 15360 * // Algebraic constraints: cmp_result_0 * (cmp_result_0 - 1) = 0 -(1 - cmp_result_0) * a__0_0 = 0 -(1 - cmp_result_0) * a__1_0 = 0 -(1 - cmp_result_0) * a__2_0 = 0 -(1 - cmp_result_0) * a__3_0 = 0 +(1 - cmp_result_0) * (a__0_0 + a__1_0 + a__2_0 + a__3_0) = 0 free_var_31 * (a__0_0 + a__1_0 + a__2_0 + a__3_0) - cmp_result_0 = 0 is_valid * (is_valid - 1) = 0 \ No newline at end of file diff --git a/openvm/tests/apc_snapshots/pseudo_instructions/snez.txt b/openvm/tests/apc_snapshots/pseudo_instructions/snez.txt index e88451810a..e87a75fad2 100644 --- a/openvm/tests/apc_snapshots/pseudo_instructions/snez.txt +++ b/openvm/tests/apc_snapshots/pseudo_instructions/snez.txt @@ -54,20 +54,29 @@ mult=is_valid * 1, args=[15360 * writes_aux__base__prev_timestamp_0 + 15360 * wr mult=diff_marker__0_0 + diff_marker__1_0 + diff_marker__2_0 + diff_marker__3_0, args=[diff_val_0 - 1, 0, 0, 0] // Algebraic constraints: -cmp_result_0 * (cmp_result_0 - 1) = 0 -diff_marker__3_0 * (diff_marker__3_0 - 1) = 0 -(1 - diff_marker__3_0) * (c__3_0 * (2 * cmp_result_0 - 1)) = 0 -diff_marker__3_0 * (diff_val_0 - c__3_0 * (2 * cmp_result_0 - 1)) = 0 -diff_marker__2_0 * (diff_marker__2_0 - 1) = 0 -(1 - (diff_marker__2_0 + diff_marker__3_0)) * (c__2_0 * (2 * cmp_result_0 - 1)) = 0 -diff_marker__2_0 * (diff_val_0 - c__2_0 * (2 * cmp_result_0 - 1)) = 0 -diff_marker__1_0 * (diff_marker__1_0 - 1) = 0 -(1 - (diff_marker__1_0 + diff_marker__2_0 + diff_marker__3_0)) * (c__1_0 * (2 * cmp_result_0 - 1)) = 0 -diff_marker__1_0 * (diff_val_0 - c__1_0 * (2 * cmp_result_0 - 1)) = 0 -diff_marker__0_0 * (diff_marker__0_0 - 1) = 0 (1 - (diff_marker__0_0 + diff_marker__1_0 + diff_marker__2_0 + diff_marker__3_0)) * (c__0_0 * (2 * cmp_result_0 - 1)) = 0 +(1 - (diff_marker__1_0 + diff_marker__2_0 + diff_marker__3_0)) * (c__1_0 * (2 * cmp_result_0 - 1)) = 0 +(1 - (diff_marker__2_0 + diff_marker__3_0)) * (c__2_0 * (2 * cmp_result_0 - 1)) = 0 +(1 - diff_marker__3_0) * (c__3_0 * (2 * cmp_result_0 - 1)) = 0 +cmp_result_0 * (cmp_result_0 - 1) = 0 diff_marker__0_0 * (diff_val_0 - c__0_0 * (2 * cmp_result_0 - 1)) = 0 -(diff_marker__0_0 + diff_marker__1_0 + diff_marker__2_0 + diff_marker__3_0) * (diff_marker__0_0 + diff_marker__1_0 + diff_marker__2_0 + diff_marker__3_0 - 1) = 0 +diff_marker__1_0 * (diff_val_0 - c__1_0 * (2 * cmp_result_0 - 1)) = 0 +diff_marker__2_0 * (diff_val_0 - c__2_0 * (2 * cmp_result_0 - 1)) = 0 +diff_marker__3_0 * (diff_val_0 - c__3_0 * (2 * cmp_result_0 - 1)) = 0 + + (1 - (diff_marker__0_0 + diff_marker__1_0 + diff_marker__2_0 + diff_marker__3_0)) * cmp_result_0 = 0 -(1 - is_valid) * (diff_marker__0_0 + diff_marker__1_0 + diff_marker__2_0 + diff_marker__3_0) = 0 -is_valid * (is_valid - 1) = 0 \ No newline at end of file + + +(diff_marker__0_0 + diff_marker__1_0 + diff_marker__2_0 + diff_marker__3_0) * +(diff_marker__0_0 + diff_marker__1_0 + diff_marker__2_0 + diff_marker__3_0 - 1) = 0 +and all of the summands are binary as well + +diff_marker__3_0 = 0 => c__3_0 = 0 \/ cmp_result_0 = 1/2 (invalid +diff_marker__3_0 = 0 => c__3_0 = 0 +c__3_0 = 0 => (diff_marker__3_0 = 0 \/ diff_val_0 = 0) + +c__3_0 != 0 => diff_marker__3_0 = 1 +diff_marke__3_0 = 1 => diff_val_0 = c__3_0 * (2 * cmp_result_0 - 1) + +c__3_0 = 0 => diff_marker__3_0 = 1 \ No newline at end of file diff --git a/openvm/tests/apc_snapshots/single_instructions/single_sra.txt b/openvm/tests/apc_snapshots/single_instructions/single_sra.txt index 0ed2ebb4c2..acff293b1e 100644 --- a/openvm/tests/apc_snapshots/single_instructions/single_sra.txt +++ b/openvm/tests/apc_snapshots/single_instructions/single_sra.txt @@ -4,7 +4,7 @@ Instructions: APC advantage: - Main columns: 53 -> 40 (1.32x reduction) - Bus interactions: 24 -> 22 (1.09x reduction) - - Constraints: 76 -> 38 (2.00x reduction) + - Constraints: 76 -> 35 (2.17x reduction) Symbolic machine using 40 unique main columns: from_state__timestamp_0 @@ -104,16 +104,13 @@ limb_shift_marker__1_0 * (limb_shift_marker__1_0 - 1) = 0 limb_shift_marker__1_0 * (a__0_0 * bit_multiplier_right_0 + bit_shift_carry__1_0 - (b__1_0 + 256 * bit_shift_carry__2_0)) = 0 limb_shift_marker__1_0 * (a__1_0 * bit_multiplier_right_0 + bit_shift_carry__2_0 - (b__2_0 + 256 * bit_shift_carry__3_0)) = 0 limb_shift_marker__1_0 * (a__2_0 * bit_multiplier_right_0 + bit_shift_carry__3_0 - (256 * b_sign_0 * (bit_multiplier_right_0 - 1) + b__3_0)) = 0 -limb_shift_marker__1_0 * (a__3_0 - 255 * b_sign_0) = 0 limb_shift_marker__2_0 * (limb_shift_marker__2_0 - 1) = 0 limb_shift_marker__2_0 * (a__0_0 * bit_multiplier_right_0 + bit_shift_carry__2_0 - (b__2_0 + 256 * bit_shift_carry__3_0)) = 0 limb_shift_marker__2_0 * (a__1_0 * bit_multiplier_right_0 + bit_shift_carry__3_0 - (256 * b_sign_0 * (bit_multiplier_right_0 - 1) + b__3_0)) = 0 -limb_shift_marker__2_0 * (a__2_0 - 255 * b_sign_0) = 0 -limb_shift_marker__2_0 * (a__3_0 - 255 * b_sign_0) = 0 (1 - (limb_shift_marker__0_0 + limb_shift_marker__1_0 + limb_shift_marker__2_0)) * (limb_shift_marker__0_0 + limb_shift_marker__1_0 + limb_shift_marker__2_0) = 0 (1 - (limb_shift_marker__0_0 + limb_shift_marker__1_0 + limb_shift_marker__2_0)) * (a__0_0 * bit_multiplier_right_0 + bit_shift_carry__3_0 - (256 * b_sign_0 * (bit_multiplier_right_0 - 1) + b__3_0)) = 0 (1 - (limb_shift_marker__0_0 + limb_shift_marker__1_0 + limb_shift_marker__2_0)) * (a__1_0 - 255 * b_sign_0) = 0 -(1 - (limb_shift_marker__0_0 + limb_shift_marker__1_0 + limb_shift_marker__2_0)) * (a__2_0 - 255 * b_sign_0) = 0 -(1 - (limb_shift_marker__0_0 + limb_shift_marker__1_0 + limb_shift_marker__2_0)) * (a__3_0 - 255 * b_sign_0) = 0 b_sign_0 * (b_sign_0 - 1) = 0 +(a__2_0 - 255 * b_sign_0) * (1 - (limb_shift_marker__0_0 + limb_shift_marker__1_0)) = 0 +(a__3_0 - 255 * b_sign_0) * (1 - limb_shift_marker__0_0) = 0 is_valid * (is_valid - 1) = 0 \ No newline at end of file From bc3e6b7553dfbd44cc5e19e8755f2c6323ce0a4c Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 28 Nov 2025 11:28:30 +0000 Subject: [PATCH 47/60] Add comment. --- autoprecompiles/src/rule_based_optimizer.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/autoprecompiles/src/rule_based_optimizer.rs b/autoprecompiles/src/rule_based_optimizer.rs index d1c36ba7c0..7f4b9466aa 100644 --- a/autoprecompiles/src/rule_based_optimizer.rs +++ b/autoprecompiles/src/rule_based_optimizer.rs @@ -476,6 +476,7 @@ crepe! { Env(env), for v in env.single_occurrence_variables().cloned(), AlgebraicConstraint(e), + // We somehow cannot use "v" directly here. ContainsVariable(e, v2), (v == v2); From 8e3aef9affc01a23a7286eeebcab07e930b20e62 Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 28 Nov 2025 13:16:26 +0000 Subject: [PATCH 48/60] Update expectations. --- .../pseudo_instructions/snez.txt | 37 +++++++------------ 1 file changed, 14 insertions(+), 23 deletions(-) diff --git a/openvm/tests/apc_snapshots/pseudo_instructions/snez.txt b/openvm/tests/apc_snapshots/pseudo_instructions/snez.txt index e87a75fad2..e88451810a 100644 --- a/openvm/tests/apc_snapshots/pseudo_instructions/snez.txt +++ b/openvm/tests/apc_snapshots/pseudo_instructions/snez.txt @@ -54,29 +54,20 @@ mult=is_valid * 1, args=[15360 * writes_aux__base__prev_timestamp_0 + 15360 * wr mult=diff_marker__0_0 + diff_marker__1_0 + diff_marker__2_0 + diff_marker__3_0, args=[diff_val_0 - 1, 0, 0, 0] // Algebraic constraints: -(1 - (diff_marker__0_0 + diff_marker__1_0 + diff_marker__2_0 + diff_marker__3_0)) * (c__0_0 * (2 * cmp_result_0 - 1)) = 0 -(1 - (diff_marker__1_0 + diff_marker__2_0 + diff_marker__3_0)) * (c__1_0 * (2 * cmp_result_0 - 1)) = 0 -(1 - (diff_marker__2_0 + diff_marker__3_0)) * (c__2_0 * (2 * cmp_result_0 - 1)) = 0 -(1 - diff_marker__3_0) * (c__3_0 * (2 * cmp_result_0 - 1)) = 0 cmp_result_0 * (cmp_result_0 - 1) = 0 -diff_marker__0_0 * (diff_val_0 - c__0_0 * (2 * cmp_result_0 - 1)) = 0 -diff_marker__1_0 * (diff_val_0 - c__1_0 * (2 * cmp_result_0 - 1)) = 0 -diff_marker__2_0 * (diff_val_0 - c__2_0 * (2 * cmp_result_0 - 1)) = 0 +diff_marker__3_0 * (diff_marker__3_0 - 1) = 0 +(1 - diff_marker__3_0) * (c__3_0 * (2 * cmp_result_0 - 1)) = 0 diff_marker__3_0 * (diff_val_0 - c__3_0 * (2 * cmp_result_0 - 1)) = 0 - - +diff_marker__2_0 * (diff_marker__2_0 - 1) = 0 +(1 - (diff_marker__2_0 + diff_marker__3_0)) * (c__2_0 * (2 * cmp_result_0 - 1)) = 0 +diff_marker__2_0 * (diff_val_0 - c__2_0 * (2 * cmp_result_0 - 1)) = 0 +diff_marker__1_0 * (diff_marker__1_0 - 1) = 0 +(1 - (diff_marker__1_0 + diff_marker__2_0 + diff_marker__3_0)) * (c__1_0 * (2 * cmp_result_0 - 1)) = 0 +diff_marker__1_0 * (diff_val_0 - c__1_0 * (2 * cmp_result_0 - 1)) = 0 +diff_marker__0_0 * (diff_marker__0_0 - 1) = 0 +(1 - (diff_marker__0_0 + diff_marker__1_0 + diff_marker__2_0 + diff_marker__3_0)) * (c__0_0 * (2 * cmp_result_0 - 1)) = 0 +diff_marker__0_0 * (diff_val_0 - c__0_0 * (2 * cmp_result_0 - 1)) = 0 +(diff_marker__0_0 + diff_marker__1_0 + diff_marker__2_0 + diff_marker__3_0) * (diff_marker__0_0 + diff_marker__1_0 + diff_marker__2_0 + diff_marker__3_0 - 1) = 0 (1 - (diff_marker__0_0 + diff_marker__1_0 + diff_marker__2_0 + diff_marker__3_0)) * cmp_result_0 = 0 - - -(diff_marker__0_0 + diff_marker__1_0 + diff_marker__2_0 + diff_marker__3_0) * -(diff_marker__0_0 + diff_marker__1_0 + diff_marker__2_0 + diff_marker__3_0 - 1) = 0 -and all of the summands are binary as well - -diff_marker__3_0 = 0 => c__3_0 = 0 \/ cmp_result_0 = 1/2 (invalid -diff_marker__3_0 = 0 => c__3_0 = 0 -c__3_0 = 0 => (diff_marker__3_0 = 0 \/ diff_val_0 = 0) - -c__3_0 != 0 => diff_marker__3_0 = 1 -diff_marke__3_0 = 1 => diff_val_0 = c__3_0 * (2 * cmp_result_0 - 1) - -c__3_0 = 0 => diff_marker__3_0 = 1 \ No newline at end of file +(1 - is_valid) * (diff_marker__0_0 + diff_marker__1_0 + diff_marker__2_0 + diff_marker__3_0) = 0 +is_valid * (is_valid - 1) = 0 \ No newline at end of file From 391e9839a66d798cb7c540ad2859c5a5b74098e4 Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 28 Nov 2025 13:57:12 +0000 Subject: [PATCH 49/60] Unify expression and variable db. --- autoprecompiles/src/rule_based_optimizer.rs | 246 +++++++++++--------- 1 file changed, 137 insertions(+), 109 deletions(-) diff --git a/autoprecompiles/src/rule_based_optimizer.rs b/autoprecompiles/src/rule_based_optimizer.rs index 7f4b9466aa..e299e47465 100644 --- a/autoprecompiles/src/rule_based_optimizer.rs +++ b/autoprecompiles/src/rule_based_optimizer.rs @@ -32,60 +32,122 @@ const SIZE_LIMIT: usize = 800; #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] struct Var(u32); +// TODO auto-derive +impl From for Var { + fn from(value: usize) -> Self { + Var(value as u32) + } +} + +// TODO auto-derive +impl From for usize { + fn from(value: Var) -> Self { + value.0 as usize + } +} + impl Display for Var { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "v_{}", self.0) } } -struct ExpressionDB { - expressions: Vec, - reverse: HashMap, +/// A database of items that are assigned consecutive identifiers +/// and which can translate back and forth between identifiers +/// and items. +struct ItemDB { + items: Vec, + reverse: HashMap, + _phantom: std::marker::PhantomData, } -impl Default for ExpressionDB { +impl Default for ItemDB { fn default() -> Self { Self { - expressions: Vec::new(), + items: Vec::new(), reverse: HashMap::new(), + _phantom: std::marker::PhantomData, + } + } +} + +impl FromIterator for ItemDB +where + Item: Clone + Hash + Eq, +{ + fn from_iter>(iter: T) -> Self { + let items = iter.into_iter().collect::>(); + let reverse = items + .iter() + .cloned() + .enumerate() + .map(|(i, v)| (v, i)) + .collect(); + Self { + items, + reverse, + _phantom: std::marker::PhantomData, } } } -impl Index for ExpressionDB { - type Output = E; - fn index(&self, index: Expr) -> &Self::Output { - &self.expressions[index.0] +impl Index for ItemDB +where + Ident: Into, +{ + type Output = Item; + fn index(&self, index: Ident) -> &Self::Output { + &self.items[index.into()] } } -impl ExpressionDB { - fn insert_owned_new(&mut self, expr: E) -> Expr { - self.expressions.push(expr.clone()); - let id = self.expressions.len() - 1; - self.reverse.insert(expr, id); - Expr(id) +impl ItemDB +where + Item: Clone + Hash + Eq, + Ident: From + Copy, +{ + fn insert_owned_new(&mut self, item: Item) -> Ident { + let id = self.items.len(); + self.items.push(item.clone()); + self.reverse.insert(item, id); + Ident::from(id) } - pub fn insert(&mut self, expr: &E) -> Expr { - if let Some(&id) = self.reverse.get(expr) { - Expr(id) + pub fn insert(&mut self, item: &Item) -> Ident { + if let Some(&id) = self.reverse.get(item) { + Ident::from(id) } else { - self.insert_owned_new(expr.clone()) + self.insert_owned_new(item.clone()) } } - pub fn insert_owned(&mut self, expr: E) -> Expr { - if let Some(&id) = self.reverse.get(&expr) { - Expr(id) + pub fn insert_owned(&mut self, item: Item) -> Ident { + if let Some(&id) = self.reverse.get(&item) { + Ident::from(id) } else { - self.insert_owned_new(expr) + self.insert_owned_new(item) } } + + pub fn id(&self, item: &Item) -> Ident { + self.reverse.get(item).map(|&id| Ident::from(id)).unwrap() + } + + pub fn iter(&self) -> impl Iterator { + self.items + .iter() + .enumerate() + .map(|(i, item)| (Ident::from(i), item)) + } + + // TODO avoid using this (as pub) + pub fn next_free_id(&self) -> usize { + self.items.len() as usize + } } struct Environment { - expressions: RefCell>>, + expressions: RefCell, Expr>>, var_to_string: HashMap, /// Variables that only occurr once in the system @@ -97,7 +159,7 @@ struct Environment { impl Environment { fn new( - expressions: ExpressionDB>, + expressions: ItemDB, Expr>, var_to_string: HashMap, single_occurrence_variables: HashSet, range_constraints_on_vars: HashMap>, @@ -112,7 +174,7 @@ impl Environment { } } - fn terminate(self) -> (ExpressionDB>, NewVarGenerator) { + fn terminate(self) -> (ItemDB, Expr>, NewVarGenerator) { ( self.expressions.into_inner(), self.new_var_generator.into_inner(), @@ -330,13 +392,13 @@ impl Environment { } struct NewVarGenerator { - counter: u32, - requests: HashMap, + counter: usize, + requests: Vec<(Var, String)>, computation_methods: HashMap>>, } impl NewVarGenerator { - fn new(initial_counter: u32) -> Self { + fn new(initial_counter: usize) -> Self { Self { counter: initial_counter, requests: Default::default(), @@ -349,8 +411,8 @@ impl NewVarGenerator { prefix: &str, computation_method: ComputationMethod>, ) -> Var { - let var = Var(self.counter); - self.requests.insert(var, prefix.to_string()); + let var = Var::from(self.counter); + self.requests.push((var, prefix.to_string())); self.computation_methods.insert(var, computation_method); self.counter += 1; var @@ -369,6 +431,19 @@ impl RangeConstraintProvider for Environment { #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] struct Expr(usize); +// TODO auto-derive +impl From for Expr { + fn from(value: usize) -> Self { + Expr(value) + } +} + +impl From for usize { + fn from(value: Expr) -> Self { + value.0 + } +} + #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] enum Action { SubstituteVariableByConstant(Var, T), @@ -677,24 +752,27 @@ pub fn rule_based_optimization>(); + .collect::>(); - let mut db = Some(ExpressionDB::>::default()); + let mut expr_db = Some(ItemDB::, Expr>::default()); loop { let (algebraic_constraints, _bus_interactions) = - transform_constraint_system(&system, &var_mapper, db.as_mut().unwrap()); + transform_constraint_system(&system, &var_mapper, expr_db.as_mut().unwrap()); let env = Environment::::new( - db.take().unwrap(), - var_mapper.all_names(), + expr_db.take().unwrap(), + var_mapper + .iter() + .map(|(id, var)| (id, var.to_string())) + .collect(), system .single_occurrence_variables() - .map(|v| var_mapper.forward(v)) + .map(|v| var_mapper.id(v)) .collect(), system .referenced_unknown_variables() - .map(|v| (var_mapper.forward(v), range_constraints.get(v))) + .map(|v| (var_mapper.id(v), range_constraints.get(v))) .collect(), // TODO or we just clone the var mapper? NewVarGenerator::new(var_mapper.next_free_id()), @@ -710,13 +788,13 @@ pub fn rule_based_optimization { - system.substitute_by_known(var_mapper.backward(&var), &val); + system.substitute_by_known(&var_mapper[var], &val); progress = true; } Action::SubstituteVariableByVariable(v1, v2) => { system.substitute_by_unknown( - var_mapper.backward(&v1), - &GroupedExpression::from_unknown_variable(var_mapper.backward(&v2).clone()), + &var_mapper[v1], + &GroupedExpression::from_unknown_variable(var_mapper[v2].clone()), ); progress = true; } Action::ReplaceAlgebraicConstraintBy(e1, e2) => { let expr1 = - untransform_grouped_expression(&db.as_ref().unwrap()[e1], &var_mapper); + untransform_grouped_expression(&expr_db.as_ref().unwrap()[e1], &var_mapper); // TODO more efficient? let mut found = false; system.retain_algebraic_constraints(|c| { @@ -758,8 +836,10 @@ pub fn rule_based_optimization { let expr1 = - untransform_grouped_expression(&db.as_ref().unwrap()[e1], &var_mapper); + untransform_grouped_expression(&expr_db.as_ref().unwrap()[e1], &var_mapper); let expr2 = - untransform_grouped_expression(&db.as_ref().unwrap()[e2], &var_mapper); + untransform_grouped_expression(&expr_db.as_ref().unwrap()[e2], &var_mapper); let mut found1 = false; let mut found2 = false; for c in system.algebraic_constraints() { @@ -789,7 +869,7 @@ pub fn rule_based_optimization( system: &IndexedConstraintSystem, - var_mapper: &VarMapper, - expression_db: &mut ExpressionDB>, + var_mapper: &ItemDB, + expression_db: &mut ItemDB, Expr>, ) -> (Vec, Vec>) { let algebraic_constraints = system .system() @@ -841,75 +921,23 @@ fn transform_constraint_system { - forward: HashMap, - backward: HashMap, -} - -impl FromIterator for VarMapper -where - V: Hash + Eq + Clone + Display, -{ - fn from_iter>(iter: T) -> Self { - let forward: HashMap = iter - .into_iter() - .unique() - .enumerate() - .map(|(i, v)| (v, Var(i as u32))) - .collect(); - let backward = forward - .iter() - .map(|(outer, inner)| (*inner, outer.clone())) - .collect(); - VarMapper { forward, backward } - } -} - -impl VarMapper { - pub fn insert_existing(&mut self, v: V, var: Var) { - assert!(self.forward.insert(v.clone(), var).is_none()); - assert!(self.backward.insert(var, v).is_none()); - } - - // TODO avoid using this (as pub) - pub fn next_free_id(&self) -> u32 { - self.forward.len() as u32 - } - - fn forward(&self, v: &V) -> Var { - *self.forward.get(v).unwrap() - } - - fn backward(&self, var: &Var) -> &V { - self.backward.get(var).unwrap() - } - - fn all_names(&self) -> HashMap { - self.backward - .iter() - .map(|(var, v)| (*var, v.to_string())) - .collect() - } -} - fn transform_grouped_expression( expr: &GroupedExpression, - var_mapper: &VarMapper, + var_mapper: &ItemDB, ) -> GroupedExpression { - expr.transform_var_type(&mut |v| var_mapper.forward(v)) + expr.transform_var_type(&mut |v| var_mapper.id(v)) } fn untransform_grouped_expression( expr: &GroupedExpression, - var_mapper: &VarMapper, + var_mapper: &ItemDB, ) -> GroupedExpression { - expr.transform_var_type(&mut |v| var_mapper.backward(v).clone()) + expr.transform_var_type(&mut |v| var_mapper[*v].clone()) } fn untransform_computation_method( method: &ComputationMethod>, - var_mapper: &VarMapper, + var_mapper: &ItemDB, ) -> ComputationMethod> { match method { ComputationMethod::Constant(c) => ComputationMethod::Constant(*c), From 81a4ead0bf6d9e2b371669cd503c24cb873d1a5b Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 28 Nov 2025 13:57:53 +0000 Subject: [PATCH 50/60] remove accidentally added file. --- .../conflicting_constraints_in_bus_interaction.cobr | 12 ------------ 1 file changed, 12 deletions(-) delete mode 100644 openvm/tests/conflicting_constraints_in_bus_interaction.cobr diff --git a/openvm/tests/conflicting_constraints_in_bus_interaction.cobr b/openvm/tests/conflicting_constraints_in_bus_interaction.cobr deleted file mode 100644 index 6eaa0ae0e5..0000000000 --- a/openvm/tests/conflicting_constraints_in_bus_interaction.cobr +++ /dev/null @@ -1,12 +0,0 @@ -¢kconstraints˜¡dexpr¡oBinaryOperation£dleft¡iReference£dnameqopcode_sll_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnameqopcode_sll_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡fNumber¡dexpr¡oBinaryOperation£dleft¡iReference£dnameqopcode_srl_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnameqopcode_srl_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡fNumber¡dexpr¡oBinaryOperation£dleft¡iReference£dnameqopcode_sra_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnameqopcode_sra_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡fNumber¡dexpr¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_sll_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_srl_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sra_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_sll_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_srl_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sra_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡fNumber¡dexpr¡oBinaryOperation£dleft¡iReference£dnameubit_shift_marker__0_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnameubit_shift_marker__0_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡fNumber¡dexpr¡oBinaryOperation£dleft¡iReference£dnameubit_shift_marker__0_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnameubit_multiplier_left_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡iReference£dnameqopcode_sll_flag_0gpoly_id¢bideptypeiCommitteddnextô¡dexpr¡oBinaryOperation£dleft¡iReference£dnameubit_shift_marker__0_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnamevbit_multiplier_right_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡iReference£dnameqopcode_srl_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sra_flag_0gpoly_id¢bideptypeiCommitteddnextô¡dexpr¡oBinaryOperation£dleft¡iReference£dnameubit_shift_marker__1_0gpoly_id¢bid eptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnameubit_shift_marker__1_0gpoly_id¢bid eptypeiCommitteddnextôbopcSuberight¡fNumber¡dexpr¡oBinaryOperation£dleft¡iReference£dnameubit_shift_marker__1_0gpoly_id¢bid eptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnameubit_multiplier_left_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡fNumberbopcMuleright¡iReference£dnameqopcode_sll_flag_0gpoly_id¢bideptypeiCommitteddnextô¡dexpr¡oBinaryOperation£dleft¡iReference£dnameubit_shift_marker__1_0gpoly_id¢bid eptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnamevbit_multiplier_right_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡fNumberbopcMuleright¡oBinaryOperation£dleft¡iReference£dnameqopcode_srl_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sra_flag_0gpoly_id¢bideptypeiCommitteddnextô¡dexpr¡oBinaryOperation£dleft¡iReference£dnameubit_shift_marker__2_0gpoly_id¢bid -eptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnameubit_shift_marker__2_0gpoly_id¢bid -eptypeiCommitteddnextôbopcSuberight¡fNumber¡dexpr¡oBinaryOperation£dleft¡iReference£dnameubit_shift_marker__2_0gpoly_id¢bid -eptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnameubit_multiplier_left_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡fNumberbopcMuleright¡iReference£dnameqopcode_sll_flag_0gpoly_id¢bideptypeiCommitteddnextô¡dexpr¡oBinaryOperation£dleft¡iReference£dnameubit_shift_marker__2_0gpoly_id¢bid -eptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnamevbit_multiplier_right_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡fNumberbopcMuleright¡oBinaryOperation£dleft¡iReference£dnameqopcode_srl_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sra_flag_0gpoly_id¢bideptypeiCommitteddnextô¡dexpr¡oBinaryOperation£dleft¡iReference£dnameubit_shift_marker__3_0gpoly_id¢bid eptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnameubit_shift_marker__3_0gpoly_id¢bid eptypeiCommitteddnextôbopcSuberight¡fNumber¡dexpr¡oBinaryOperation£dleft¡iReference£dnameubit_shift_marker__3_0gpoly_id¢bid eptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnameubit_multiplier_left_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡fNumberbopcMuleright¡iReference£dnameqopcode_sll_flag_0gpoly_id¢bideptypeiCommitteddnextô¡dexpr¡oBinaryOperation£dleft¡iReference£dnameubit_shift_marker__3_0gpoly_id¢bid eptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnamevbit_multiplier_right_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡fNumberbopcMuleright¡oBinaryOperation£dleft¡iReference£dnameqopcode_srl_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sra_flag_0gpoly_id¢bideptypeiCommitteddnextô¡dexpr¡oBinaryOperation£dleft¡iReference£dnameubit_shift_marker__4_0gpoly_id¢bid eptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnameubit_shift_marker__4_0gpoly_id¢bid eptypeiCommitteddnextôbopcSuberight¡fNumber¡dexpr¡oBinaryOperation£dleft¡iReference£dnameubit_shift_marker__4_0gpoly_id¢bid eptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnameubit_multiplier_left_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡fNumberbopcMuleright¡iReference£dnameqopcode_sll_flag_0gpoly_id¢bideptypeiCommitteddnextô¡dexpr¡oBinaryOperation£dleft¡iReference£dnameubit_shift_marker__4_0gpoly_id¢bid eptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnamevbit_multiplier_right_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡fNumberbopcMuleright¡oBinaryOperation£dleft¡iReference£dnameqopcode_srl_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sra_flag_0gpoly_id¢bideptypeiCommitteddnextô¡dexpr¡oBinaryOperation£dleft¡iReference£dnameubit_shift_marker__5_0gpoly_id¢bid eptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnameubit_shift_marker__5_0gpoly_id¢bid eptypeiCommitteddnextôbopcSuberight¡fNumber¡dexpr¡oBinaryOperation£dleft¡iReference£dnameubit_shift_marker__5_0gpoly_id¢bid eptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnameubit_multiplier_left_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡fNumber bopcMuleright¡iReference£dnameqopcode_sll_flag_0gpoly_id¢bideptypeiCommitteddnextô¡dexpr¡oBinaryOperation£dleft¡iReference£dnameubit_shift_marker__5_0gpoly_id¢bid eptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnamevbit_multiplier_right_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡fNumber bopcMuleright¡oBinaryOperation£dleft¡iReference£dnameqopcode_srl_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sra_flag_0gpoly_id¢bideptypeiCommitteddnextô¡dexpr¡oBinaryOperation£dleft¡iReference£dnameubit_shift_marker__6_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnameubit_shift_marker__6_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡fNumber¡dexpr¡oBinaryOperation£dleft¡iReference£dnameubit_shift_marker__6_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnameubit_multiplier_left_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡fNumber@bopcMuleright¡iReference£dnameqopcode_sll_flag_0gpoly_id¢bideptypeiCommitteddnextô¡dexpr¡oBinaryOperation£dleft¡iReference£dnameubit_shift_marker__6_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnamevbit_multiplier_right_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡fNumber@bopcMuleright¡oBinaryOperation£dleft¡iReference£dnameqopcode_srl_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sra_flag_0gpoly_id¢bideptypeiCommitteddnextô¡dexpr¡oBinaryOperation£dleft¡iReference£dnameubit_shift_marker__7_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnameubit_shift_marker__7_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡fNumber¡dexpr¡oBinaryOperation£dleft¡iReference£dnameubit_shift_marker__7_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnameubit_multiplier_left_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡fNumber€bopcMuleright¡iReference£dnameqopcode_sll_flag_0gpoly_id¢bideptypeiCommitteddnextô¡dexpr¡oBinaryOperation£dleft¡iReference£dnameubit_shift_marker__7_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnamevbit_multiplier_right_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡fNumber€bopcMuleright¡oBinaryOperation£dleft¡iReference£dnameqopcode_srl_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sra_flag_0gpoly_id¢bideptypeiCommitteddnextô¡dexpr¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_sll_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_srl_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sra_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameubit_shift_marker__0_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnameubit_shift_marker__1_0gpoly_id¢bid eptypeiCommitteddnextôbopcAdderight¡iReference£dnameubit_shift_marker__2_0gpoly_id¢bid -eptypeiCommitteddnextôbopcAdderight¡iReference£dnameubit_shift_marker__3_0gpoly_id¢bid eptypeiCommitteddnextôbopcAdderight¡iReference£dnameubit_shift_marker__4_0gpoly_id¢bid eptypeiCommitteddnextôbopcAdderight¡iReference£dnameubit_shift_marker__5_0gpoly_id¢bid eptypeiCommitteddnextôbopcAdderight¡iReference£dnameubit_shift_marker__6_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnameubit_shift_marker__7_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡fNumber¡dexpr¡oBinaryOperation£dleft¡iReference£dnamevlimb_shift_marker__0_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnamevlimb_shift_marker__0_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡fNumber¡dexpr¡oBinaryOperation£dleft¡iReference£dnamevlimb_shift_marker__0_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnamefa__0_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnameqopcode_sll_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnamefb__0_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnameubit_multiplier_left_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡fNumberbopcMuleright¡iReference£dnametbit_shift_carry__0_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnameqopcode_sll_flag_0gpoly_id¢bideptypeiCommitteddnextô¡dexpr¡oBinaryOperation£dleft¡iReference£dnamevlimb_shift_marker__0_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnamefa__0_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnamevbit_multiplier_right_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnametbit_shift_carry__1_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnameqopcode_srl_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sra_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡fNumberbopcAdderight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_srl_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sra_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnamefb__0_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡iReference£dnametbit_shift_carry__0_0gpoly_id¢bideptypeiCommitteddnextô¡dexpr¡oBinaryOperation£dleft¡iReference£dnamevlimb_shift_marker__0_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnamefa__1_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnameqopcode_sll_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnametbit_shift_carry__0_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnameqopcode_sll_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡oBinaryOperation£dleft¡iReference£dnamefb__1_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnameubit_multiplier_left_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡fNumberbopcMuleright¡iReference£dnametbit_shift_carry__1_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnameqopcode_sll_flag_0gpoly_id¢bideptypeiCommitteddnextô¡dexpr¡oBinaryOperation£dleft¡iReference£dnamevlimb_shift_marker__0_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnamefa__1_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnamevbit_multiplier_right_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnametbit_shift_carry__2_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnameqopcode_srl_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sra_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡fNumberbopcAdderight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_srl_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sra_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnamefb__1_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡iReference£dnametbit_shift_carry__1_0gpoly_id¢bideptypeiCommitteddnextô¡dexpr¡oBinaryOperation£dleft¡iReference£dnamevlimb_shift_marker__0_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnamefa__2_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnameqopcode_sll_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnametbit_shift_carry__1_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnameqopcode_sll_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡oBinaryOperation£dleft¡iReference£dnamefb__2_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnameubit_multiplier_left_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡fNumberbopcMuleright¡iReference£dnametbit_shift_carry__2_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnameqopcode_sll_flag_0gpoly_id¢bideptypeiCommitteddnextô¡dexpr¡oBinaryOperation£dleft¡iReference£dnamevlimb_shift_marker__0_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnamefa__2_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnamevbit_multiplier_right_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnametbit_shift_carry__3_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnameqopcode_srl_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sra_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡fNumberbopcAdderight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_srl_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sra_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnamefb__2_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡iReference£dnametbit_shift_carry__2_0gpoly_id¢bideptypeiCommitteddnextô¡dexpr¡oBinaryOperation£dleft¡iReference£dnamevlimb_shift_marker__0_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnamefa__3_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnameqopcode_sll_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnametbit_shift_carry__2_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnameqopcode_sll_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡oBinaryOperation£dleft¡iReference£dnamefb__3_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnameubit_multiplier_left_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡fNumberbopcMuleright¡iReference£dnametbit_shift_carry__3_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnameqopcode_sll_flag_0gpoly_id¢bideptypeiCommitteddnextô¡dexpr¡oBinaryOperation£dleft¡iReference£dnamevlimb_shift_marker__0_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnamefa__3_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnamevbit_multiplier_right_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnamehb_sign_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnamevbit_multiplier_right_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡fNumberbopcMuleright¡fNumberbopcAdderight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_srl_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sra_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnamefb__3_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡iReference£dnametbit_shift_carry__3_0gpoly_id¢bideptypeiCommitteddnextô¡dexpr¡oBinaryOperation£dleft¡iReference£dnamevlimb_shift_marker__1_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnamevlimb_shift_marker__1_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡fNumber¡dexpr¡oBinaryOperation£dleft¡iReference£dnamevlimb_shift_marker__1_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnamefa__0_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnameqopcode_sll_flag_0gpoly_id¢bideptypeiCommitteddnextô¡dexpr¡oBinaryOperation£dleft¡iReference£dnamevlimb_shift_marker__1_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnamefa__0_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnamevbit_multiplier_right_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnametbit_shift_carry__2_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnameqopcode_srl_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sra_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡fNumberbopcAdderight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_srl_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sra_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnamefb__1_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡iReference£dnametbit_shift_carry__1_0gpoly_id¢bideptypeiCommitteddnextô¡dexpr¡oBinaryOperation£dleft¡iReference£dnamevlimb_shift_marker__1_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnamefa__1_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnameqopcode_sll_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnamefb__0_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnameubit_multiplier_left_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡fNumberbopcMuleright¡iReference£dnametbit_shift_carry__0_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnameqopcode_sll_flag_0gpoly_id¢bideptypeiCommitteddnextô¡dexpr¡oBinaryOperation£dleft¡iReference£dnamevlimb_shift_marker__1_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnamefa__1_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnamevbit_multiplier_right_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnametbit_shift_carry__3_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnameqopcode_srl_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sra_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡fNumberbopcAdderight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_srl_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sra_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnamefb__2_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡iReference£dnametbit_shift_carry__2_0gpoly_id¢bideptypeiCommitteddnextô¡dexpr¡oBinaryOperation£dleft¡iReference£dnamevlimb_shift_marker__1_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnamefa__2_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnameqopcode_sll_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnametbit_shift_carry__0_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnameqopcode_sll_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡oBinaryOperation£dleft¡iReference£dnamefb__1_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnameubit_multiplier_left_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡fNumberbopcMuleright¡iReference£dnametbit_shift_carry__1_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnameqopcode_sll_flag_0gpoly_id¢bideptypeiCommitteddnextô¡dexpr¡oBinaryOperation£dleft¡iReference£dnamevlimb_shift_marker__1_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnamefa__2_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnamevbit_multiplier_right_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnamehb_sign_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnamevbit_multiplier_right_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡fNumberbopcMuleright¡fNumberbopcAdderight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_srl_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sra_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnamefb__3_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡iReference£dnametbit_shift_carry__3_0gpoly_id¢bideptypeiCommitteddnextô¡dexpr¡oBinaryOperation£dleft¡iReference£dnamevlimb_shift_marker__1_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnamefa__3_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnameqopcode_sll_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnametbit_shift_carry__1_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnameqopcode_sll_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡oBinaryOperation£dleft¡iReference£dnamefb__2_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnameubit_multiplier_left_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡fNumberbopcMuleright¡iReference£dnametbit_shift_carry__2_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnameqopcode_sll_flag_0gpoly_id¢bideptypeiCommitteddnextô¡dexpr¡oBinaryOperation£dleft¡iReference£dnamevlimb_shift_marker__1_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnamefa__3_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnameqopcode_srl_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sra_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡iReference£dnamehb_sign_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡fNumberÿ¡dexpr¡oBinaryOperation£dleft¡iReference£dnamevlimb_shift_marker__2_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnamevlimb_shift_marker__2_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡fNumber¡dexpr¡oBinaryOperation£dleft¡iReference£dnamevlimb_shift_marker__2_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnamefa__0_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnameqopcode_sll_flag_0gpoly_id¢bideptypeiCommitteddnextô¡dexpr¡oBinaryOperation£dleft¡iReference£dnamevlimb_shift_marker__2_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnamefa__0_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnamevbit_multiplier_right_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnametbit_shift_carry__3_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnameqopcode_srl_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sra_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡fNumberbopcAdderight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_srl_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sra_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnamefb__2_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡iReference£dnametbit_shift_carry__2_0gpoly_id¢bideptypeiCommitteddnextô¡dexpr¡oBinaryOperation£dleft¡iReference£dnamevlimb_shift_marker__2_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnamefa__1_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnameqopcode_sll_flag_0gpoly_id¢bideptypeiCommitteddnextô¡dexpr¡oBinaryOperation£dleft¡iReference£dnamevlimb_shift_marker__2_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnamefa__1_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnamevbit_multiplier_right_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnamehb_sign_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnamevbit_multiplier_right_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡fNumberbopcMuleright¡fNumberbopcAdderight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_srl_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sra_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnamefb__3_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡iReference£dnametbit_shift_carry__3_0gpoly_id¢bideptypeiCommitteddnextô¡dexpr¡oBinaryOperation£dleft¡iReference£dnamevlimb_shift_marker__2_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnamefa__2_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnameqopcode_sll_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnamefb__0_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnameubit_multiplier_left_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡fNumberbopcMuleright¡iReference£dnametbit_shift_carry__0_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnameqopcode_sll_flag_0gpoly_id¢bideptypeiCommitteddnextô¡dexpr¡oBinaryOperation£dleft¡iReference£dnamevlimb_shift_marker__2_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnamefa__2_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnameqopcode_srl_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sra_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡iReference£dnamehb_sign_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡fNumberÿ¡dexpr¡oBinaryOperation£dleft¡iReference£dnamevlimb_shift_marker__2_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnamefa__3_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnameqopcode_sll_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnametbit_shift_carry__0_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnameqopcode_sll_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡oBinaryOperation£dleft¡iReference£dnamefb__1_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnameubit_multiplier_left_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡fNumberbopcMuleright¡iReference£dnametbit_shift_carry__1_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnameqopcode_sll_flag_0gpoly_id¢bideptypeiCommitteddnextô¡dexpr¡oBinaryOperation£dleft¡iReference£dnamevlimb_shift_marker__2_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnamefa__3_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnameqopcode_srl_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sra_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡iReference£dnamehb_sign_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡fNumberÿ¡dexpr¡oBinaryOperation£dleft¡iReference£dnamevlimb_shift_marker__3_0gpoly_id¢bid eptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnamevlimb_shift_marker__3_0gpoly_id¢bid eptypeiCommitteddnextôbopcSuberight¡fNumber¡dexpr¡oBinaryOperation£dleft¡iReference£dnamevlimb_shift_marker__3_0gpoly_id¢bid eptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnamefa__0_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnameqopcode_sll_flag_0gpoly_id¢bideptypeiCommitteddnextô¡dexpr¡oBinaryOperation£dleft¡iReference£dnamevlimb_shift_marker__3_0gpoly_id¢bid eptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnamefa__0_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnamevbit_multiplier_right_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnamehb_sign_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnamevbit_multiplier_right_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡fNumberbopcMuleright¡fNumberbopcAdderight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_srl_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sra_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnamefb__3_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡iReference£dnametbit_shift_carry__3_0gpoly_id¢bideptypeiCommitteddnextô¡dexpr¡oBinaryOperation£dleft¡iReference£dnamevlimb_shift_marker__3_0gpoly_id¢bid eptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnamefa__1_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnameqopcode_sll_flag_0gpoly_id¢bideptypeiCommitteddnextô¡dexpr¡oBinaryOperation£dleft¡iReference£dnamevlimb_shift_marker__3_0gpoly_id¢bid eptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnamefa__1_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnameqopcode_srl_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sra_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡iReference£dnamehb_sign_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡fNumberÿ¡dexpr¡oBinaryOperation£dleft¡iReference£dnamevlimb_shift_marker__3_0gpoly_id¢bid eptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnamefa__2_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnameqopcode_sll_flag_0gpoly_id¢bideptypeiCommitteddnextô¡dexpr¡oBinaryOperation£dleft¡iReference£dnamevlimb_shift_marker__3_0gpoly_id¢bid eptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnamefa__2_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnameqopcode_srl_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sra_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡iReference£dnamehb_sign_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡fNumberÿ¡dexpr¡oBinaryOperation£dleft¡iReference£dnamevlimb_shift_marker__3_0gpoly_id¢bid eptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnamefa__3_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnameqopcode_sll_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnamefb__0_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnameubit_multiplier_left_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡fNumberbopcMuleright¡iReference£dnametbit_shift_carry__0_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnameqopcode_sll_flag_0gpoly_id¢bideptypeiCommitteddnextô¡dexpr¡oBinaryOperation£dleft¡iReference£dnamevlimb_shift_marker__3_0gpoly_id¢bid eptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnamefa__3_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnameqopcode_srl_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sra_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡iReference£dnamehb_sign_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡fNumberÿ¡dexpr¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_sll_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_srl_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sra_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnamevlimb_shift_marker__0_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnamevlimb_shift_marker__1_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnamevlimb_shift_marker__2_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnamevlimb_shift_marker__3_0gpoly_id¢bid eptypeiCommitteddnextôbopcSuberight¡fNumber¡dexpr¡oBinaryOperation£dleft¡iReference£dnamehb_sign_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnamehb_sign_0gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡fNumber¡dexpr¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡fNumberbopcSuberight¡iReference£dnameqopcode_sra_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡iReference£dnamehb_sign_0gpoly_id¢bideptypeiCommitteddnextô¡dexpr¡fNumber¡dexpr¡oBinaryOperation£dleft¡fNumberbopcSuberight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnamefc__0_0gpoly_id¢bid#eptypeiCommitteddnextôbopcAdderight¡oBinaryOperation£dleft¡iReference£dnamefc__1_0gpoly_id¢bid$eptypeiCommitteddnextôbopcMuleright¡fNumberbopcAdderight¡oBinaryOperation£dleft¡iReference£dnamefc__2_0gpoly_id¢bid%eptypeiCommitteddnextôbopcMuleright¡fNumber¡dexpr¡oBinaryOperation£dleft¡iReference£dnamefc__2_0gpoly_id¢bid%eptypeiCommitteddnextôbopcSuberight¡iReference£dnamefc__3_0gpoly_id¢bid&eptypeiCommitteddnextô¡dexpr¡oBinaryOperation£dleft¡iReference£dnamefc__2_0gpoly_id¢bid%eptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡fNumberÿbopcSuberight¡iReference£dnamefc__2_0gpoly_id¢bid%eptypeiCommitteddnextô¡dexpr¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_sll_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_srl_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sra_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnamewfrom_state__timestamp_0gpoly_id¢bid'eptypeiCommitteddnextôbopcSuberight¡iReference£dnamex$reads_aux__0__base__prev_timestamp_0gpoly_id¢bid(eptypeiCommitteddnextôbopcSuberight¡fNumberbopcSuberight¡oBinaryOperation£dleft¡iReference£dnamex7reads_aux__0__base__timestamp_lt_aux__lower_decomp__0_0gpoly_id¢bid)eptypeiCommitteddnextôbopcAdderight¡oBinaryOperation£dleft¡iReference£dnamex7reads_aux__0__base__timestamp_lt_aux__lower_decomp__1_0gpoly_id¢bid*eptypeiCommitteddnextôbopcMuleright¡fNumber¡dexpr¡fNumber¡dexpr¡fNumber¡dexpr¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_sll_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_srl_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sra_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnamewfrom_state__timestamp_0gpoly_id¢bid'eptypeiCommitteddnextôbopcAdderight¡fNumberbopcSuberight¡iReference£dnamex"writes_aux__base__prev_timestamp_0gpoly_id¢bid.eptypeiCommitteddnextôbopcSuberight¡fNumberbopcSuberight¡oBinaryOperation£dleft¡iReference£dnamex5writes_aux__base__timestamp_lt_aux__lower_decomp__0_0gpoly_id¢bid/eptypeiCommitteddnextôbopcAdderight¡oBinaryOperation£dleft¡iReference£dnamex5writes_aux__base__timestamp_lt_aux__lower_decomp__1_0gpoly_id¢bid0eptypeiCommitteddnextôbopcMuleright¡fNumber¡dexpr¡oBinaryOperation£dleft¡nUnaryOperation¢bopeMinusdexpr¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_sll_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_srl_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sra_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡fNumber¡dexpr¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡fNumberbopcAdderight¡oBinaryOperation£dleft¡iReference£dnameqopcode_srl_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡oBinaryOperation£dleft¡iReference£dnameqopcode_sra_flag_0gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡fNumberbopcSuberight¡fNumber¡dexpr¡oBinaryOperation£dleft¡iReference£dnameqopcode_slt_flag_1gpoly_id¢bid8eptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnameqopcode_slt_flag_1gpoly_id¢bid8eptypeiCommitteddnextôbopcSuberight¡fNumber¡dexpr¡oBinaryOperation£dleft¡iReference£dnameropcode_sltu_flag_1gpoly_id¢bid9eptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnameropcode_sltu_flag_1gpoly_id¢bid9eptypeiCommitteddnextôbopcSuberight¡fNumber¡dexpr¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_slt_flag_1gpoly_id¢bid8eptypeiCommitteddnextôbopcAdderight¡iReference£dnameropcode_sltu_flag_1gpoly_id¢bid9eptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_slt_flag_1gpoly_id¢bid8eptypeiCommitteddnextôbopcAdderight¡iReference£dnameropcode_sltu_flag_1gpoly_id¢bid9eptypeiCommitteddnextôbopcSuberight¡fNumber¡dexpr¡oBinaryOperation£dleft¡iReference£dnamelcmp_result_1gpoly_id¢bid:eptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnamelcmp_result_1gpoly_id¢bid:eptypeiCommitteddnextôbopcSuberight¡fNumber¡dexpr¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnamefb__3_1gpoly_id¢bid;eptypeiCommitteddnextôbopcSuberight¡iReference£dnameib_msb_f_1gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡fNumberbopcSuberight¡oBinaryOperation£dleft¡iReference£dnamefc__3_1gpoly_id¢bid=eptypeiCommitteddnextôbopcSuberight¡iReference£dnameic_msb_f_1gpoly_id¢bid>eptypeiCommitteddnextô¡dexpr¡oBinaryOperation£dleft¡iReference£dnamepdiff_marker__3_1gpoly_id¢bid?eptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡iReference£dnamepdiff_marker__3_1gpoly_id¢bid?eptypeiCommitteddnextôbopcSuberight¡fNumber¡dexpr¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡fNumberbopcSuberight¡iReference£dnamepdiff_marker__3_1gpoly_id¢bid?eptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameic_msb_f_1gpoly_id¢bid>eptypeiCommitteddnextôbopcSuberight¡iReference£dnameib_msb_f_1gpoly_id¢bideptypeiCommitteddnextôbopcSuberight¡iReference£dnameib_msb_f_1gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡oBinaryOperation£dleft¡fNumber€bopcMuleright¡iReference£dnameqopcode_slt_flag_1gpoly_id¢bid8eptypeiCommitteddnextô¡fNumber¡fNumbernoriginal_index¥dkinddSendbiddmult¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnamepdiff_marker__3_1gpoly_id¢bid?eptypeiCommitteddnextôbopcAdderight¡iReference£dnamepdiff_marker__2_1gpoly_id¢bidAeptypeiCommitteddnextôbopcAdderight¡iReference£dnamepdiff_marker__1_1gpoly_id¢bidDeptypeiCommitteddnextôbopcAdderight¡iReference£dnamepdiff_marker__0_1gpoly_id¢bidGeptypeiCommitteddnextôdargs„¡oBinaryOperation£dleft¡iReference£dnamejdiff_val_1gpoly_id¢bid@eptypeiCommitteddnextôbopcSuberight¡fNumber¡fNumber¡fNumber¡fNumbernoriginal_index¥dkinddSendbiddmult¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_slt_flag_1gpoly_id¢bid8eptypeiCommitteddnextôbopcAdderight¡iReference£dnameropcode_sltu_flag_1gpoly_id¢bid9eptypeiCommitteddnextôbopcSuberight¡fNumberdargs„¡iReference£dnamefc__0_1gpoly_id¢bidHeptypeiCommitteddnextô¡iReference£dnamefc__1_1gpoly_id¢bidEeptypeiCommitteddnextô¡fNumber¡fNumbernoriginal_index¥dkinddSendbiddmult¡oBinaryOperation£dleft¡iReference£dnameqopcode_slt_flag_1gpoly_id¢bid8eptypeiCommitteddnextôbopcAdderight¡iReference£dnameropcode_sltu_flag_1gpoly_id¢bid9eptypeiCommitteddnextôdargs‚¡iReference£dnamex7reads_aux__0__base__timestamp_lt_aux__lower_decomp__0_1gpoly_id¢bidNeptypeiCommitteddnextô¡fNumbernoriginal_index¥dkinddSendbiddmult¡oBinaryOperation£dleft¡iReference£dnameqopcode_slt_flag_1gpoly_id¢bid8eptypeiCommitteddnextôbopcAdderight¡iReference£dnameropcode_sltu_flag_1gpoly_id¢bid9eptypeiCommitteddnextôdargs‚¡iReference£dnamex7reads_aux__0__base__timestamp_lt_aux__lower_decomp__1_1gpoly_id¢bidOeptypeiCommitteddnextô¡fNumber noriginal_index¥dkinddSendbiddmult¡oBinaryOperation£dleft¡fNumberxbopcMuleright¡oBinaryOperation£dleft¡iReference£dnameqopcode_slt_flag_1gpoly_id¢bid8eptypeiCommitteddnextôbopcAdderight¡iReference£dnameropcode_sltu_flag_1gpoly_id¢bid9eptypeiCommitteddnextôdargs‡¡fNumber¡fNumber¡iReference£dnamefb__0_1gpoly_id¢bidIeptypeiCommitteddnextô¡iReference£dnamefb__1_1gpoly_id¢bidFeptypeiCommitteddnextô¡iReference£dnamefb__2_1gpoly_id¢bidCeptypeiCommitteddnextô¡iReference£dnamefb__3_1gpoly_id¢bid;eptypeiCommitteddnextô¡iReference£dnamex$reads_aux__0__base__prev_timestamp_1gpoly_id¢bidMeptypeiCommitteddnextônoriginal_index¥dkinddSendbiddmult¡oBinaryOperation£dleft¡iReference£dnameqopcode_slt_flag_1gpoly_id¢bid8eptypeiCommitteddnextôbopcAdderight¡iReference£dnameropcode_sltu_flag_1gpoly_id¢bid9eptypeiCommitteddnextôdargs‡¡fNumber¡fNumber¡iReference£dnamefb__0_1gpoly_id¢bidIeptypeiCommitteddnextô¡iReference£dnamefb__1_1gpoly_id¢bidFeptypeiCommitteddnextô¡iReference£dnamefb__2_1gpoly_id¢bidCeptypeiCommitteddnextô¡iReference£dnamefb__3_1gpoly_id¢bid;eptypeiCommitteddnextô¡iReference£dnamewfrom_state__timestamp_1gpoly_id¢bidLeptypeiCommitteddnextônoriginal_index¥dkinddSendbiddmult¡fNumberdargs‚¡iReference£dnamex7reads_aux__1__base__timestamp_lt_aux__lower_decomp__0_1gpoly_id¢bidQeptypeiCommitteddnextô¡fNumbernoriginal_index¥dkinddSendbiddmult¡fNumberdargs‚¡iReference£dnamex7reads_aux__1__base__timestamp_lt_aux__lower_decomp__1_1gpoly_id¢bidReptypeiCommitteddnextô¡fNumber noriginal_index ¥dkinddSendbiddmult¡fNumberxdargs‡¡fNumber¡fNumber(¡iReference£dnamefc__0_1gpoly_id¢bidHeptypeiCommitteddnextô¡iReference£dnamefc__1_1gpoly_id¢bidEeptypeiCommitteddnextô¡iReference£dnamefc__2_1gpoly_id¢bidBeptypeiCommitteddnextô¡iReference£dnamefc__3_1gpoly_id¢bid=eptypeiCommitteddnextô¡iReference£dnamex$reads_aux__1__base__prev_timestamp_1gpoly_id¢bidPeptypeiCommitteddnextônoriginal_index!¥dkinddSendbiddmult¡fNumberdargs‡¡fNumber¡fNumber(¡iReference£dnamefc__0_1gpoly_id¢bidHeptypeiCommitteddnextô¡iReference£dnamefc__1_1gpoly_id¢bidEeptypeiCommitteddnextô¡iReference£dnamefc__2_1gpoly_id¢bidBeptypeiCommitteddnextô¡iReference£dnamefc__3_1gpoly_id¢bid=eptypeiCommitteddnextô¡oBinaryOperation£dleft¡iReference£dnamewfrom_state__timestamp_1gpoly_id¢bidLeptypeiCommitteddnextôbopcAdderight¡fNumbernoriginal_index"¥dkinddSendbiddmult¡oBinaryOperation£dleft¡iReference£dnameqopcode_slt_flag_1gpoly_id¢bid8eptypeiCommitteddnextôbopcAdderight¡iReference£dnameropcode_sltu_flag_1gpoly_id¢bid9eptypeiCommitteddnextôdargs‚¡iReference£dnamex5writes_aux__base__timestamp_lt_aux__lower_decomp__0_1gpoly_id¢bidTeptypeiCommitteddnextô¡fNumbernoriginal_index#¥dkinddSendbiddmult¡oBinaryOperation£dleft¡iReference£dnameqopcode_slt_flag_1gpoly_id¢bid8eptypeiCommitteddnextôbopcAdderight¡iReference£dnameropcode_sltu_flag_1gpoly_id¢bid9eptypeiCommitteddnextôdargs‚¡iReference£dnamex5writes_aux__base__timestamp_lt_aux__lower_decomp__1_1gpoly_id¢bidUeptypeiCommitteddnextô¡fNumber noriginal_index$¥dkinddSendbiddmult¡oBinaryOperation£dleft¡fNumberxbopcMuleright¡oBinaryOperation£dleft¡iReference£dnameqopcode_slt_flag_1gpoly_id¢bid8eptypeiCommitteddnextôbopcAdderight¡iReference£dnameropcode_sltu_flag_1gpoly_id¢bid9eptypeiCommitteddnextôdargs‡¡fNumber¡fNumber(¡iReference£dnamexwrites_aux__prev_data__0_1gpoly_id¢bidXeptypeiCommitteddnextô¡iReference£dnamexwrites_aux__prev_data__1_1gpoly_id¢bidYeptypeiCommitteddnextô¡iReference£dnamexwrites_aux__prev_data__2_1gpoly_id¢bidZeptypeiCommitteddnextô¡iReference£dnamexwrites_aux__prev_data__3_1gpoly_id¢bid[eptypeiCommitteddnextô¡iReference£dnamex"writes_aux__base__prev_timestamp_1gpoly_id¢bidSeptypeiCommitteddnextônoriginal_index%¥dkinddSendbiddmult¡oBinaryOperation£dleft¡iReference£dnameqopcode_slt_flag_1gpoly_id¢bid8eptypeiCommitteddnextôbopcAdderight¡iReference£dnameropcode_sltu_flag_1gpoly_id¢bid9eptypeiCommitteddnextôdargs‡¡fNumber¡fNumber(¡iReference£dnamelcmp_result_1gpoly_id¢bid:eptypeiCommitteddnextô¡fNumber¡fNumber¡fNumber¡oBinaryOperation£dleft¡iReference£dnamewfrom_state__timestamp_1gpoly_id¢bidLeptypeiCommitteddnextôbopcAdderight¡fNumbernoriginal_index&¥dkinddSendbiddmult¡oBinaryOperation£dleft¡iReference£dnameqopcode_slt_flag_1gpoly_id¢bid8eptypeiCommitteddnextôbopcAdderight¡iReference£dnameropcode_sltu_flag_1gpoly_id¢bid9eptypeiCommitteddnextôdargs‰¡iReference£dnamepfrom_state__pc_1gpoly_id¢bid\eptypeiCommitteddnextô¡oBinaryOperation£dleft¡iReference£dnameropcode_sltu_flag_1gpoly_id¢bid9eptypeiCommitteddnextôbopcAdderight¡fNumber¡fNumber(¡fNumber¡fNumber(¡fNumber¡fNumber¡fNumber¡fNumbernoriginal_index'¥dkinddSendbiddmult¡nUnaryOperation¢bopeMinusdexpr¡oBinaryOperation£dleft¡iReference£dnameqopcode_slt_flag_1gpoly_id¢bid8eptypeiCommitteddnextôbopcAdderight¡iReference£dnameropcode_sltu_flag_1gpoly_id¢bid9eptypeiCommitteddnextôdargs‚¡iReference£dnamepfrom_state__pc_1gpoly_id¢bid\eptypeiCommitteddnextô¡iReference£dnamewfrom_state__timestamp_1gpoly_id¢bidLeptypeiCommitteddnextônoriginal_index(¥dkinddSendbiddmult¡oBinaryOperation£dleft¡iReference£dnameqopcode_slt_flag_1gpoly_id¢bid8eptypeiCommitteddnextôbopcAdderight¡iReference£dnameropcode_sltu_flag_1gpoly_id¢bid9eptypeiCommitteddnextôdargs‚¡oBinaryOperation£dleft¡iReference£dnamepfrom_state__pc_1gpoly_id¢bid\eptypeiCommitteddnextôbopcAdderight¡fNumber¡oBinaryOperation£dleft¡iReference£dnamewfrom_state__timestamp_1gpoly_id¢bidLeptypeiCommitteddnextôbopcAdderight¡fNumbernoriginal_index)¥dkinddSendbiddmult¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_add_flag_2gpoly_id¢bid]eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sub_flag_2gpoly_id¢bid^eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_xor_flag_2gpoly_id¢bid_eptypeiCommitteddnextôbopcAdderight¡iReference£dnamepopcode_or_flag_2gpoly_id¢bid`eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_and_flag_2gpoly_id¢bidaeptypeiCommitteddnextôdargs„¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡fNumberbopcSuberight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_xor_flag_2gpoly_id¢bid_eptypeiCommitteddnextôbopcAdderight¡iReference£dnamepopcode_or_flag_2gpoly_id¢bid`eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_and_flag_2gpoly_id¢bidaeptypeiCommitteddnextôbopcMuleright¡iReference£dnamefa__0_2gpoly_id¢biddeptypeiCommitteddnextôbopcAdderight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_xor_flag_2gpoly_id¢bid_eptypeiCommitteddnextôbopcAdderight¡iReference£dnamepopcode_or_flag_2gpoly_id¢bid`eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_and_flag_2gpoly_id¢bidaeptypeiCommitteddnextôbopcMuleright¡iReference£dnamefb__0_2gpoly_id¢bidbeptypeiCommitteddnextô¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡fNumberbopcSuberight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_xor_flag_2gpoly_id¢bid_eptypeiCommitteddnextôbopcAdderight¡iReference£dnamepopcode_or_flag_2gpoly_id¢bid`eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_and_flag_2gpoly_id¢bidaeptypeiCommitteddnextôbopcMuleright¡iReference£dnamefa__0_2gpoly_id¢biddeptypeiCommitteddnextôbopcAdderight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_xor_flag_2gpoly_id¢bid_eptypeiCommitteddnextôbopcAdderight¡iReference£dnamepopcode_or_flag_2gpoly_id¢bid`eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_and_flag_2gpoly_id¢bidaeptypeiCommitteddnextôbopcMuleright¡iReference£dnamefc__0_2gpoly_id¢bidceptypeiCommitteddnextô¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_xor_flag_2gpoly_id¢bid_eptypeiCommitteddnextôbopcMuleright¡iReference£dnamefa__0_2gpoly_id¢biddeptypeiCommitteddnextôbopcAdderight¡oBinaryOperation£dleft¡iReference£dnamepopcode_or_flag_2gpoly_id¢bid`eptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡fNumberbopcMuleright¡iReference£dnamefa__0_2gpoly_id¢biddeptypeiCommitteddnextôbopcSuberight¡iReference£dnamefb__0_2gpoly_id¢bidbeptypeiCommitteddnextôbopcSuberight¡iReference£dnamefc__0_2gpoly_id¢bidceptypeiCommitteddnextôbopcAdderight¡oBinaryOperation£dleft¡iReference£dnameqopcode_and_flag_2gpoly_id¢bidaeptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnamefb__0_2gpoly_id¢bidbeptypeiCommitteddnextôbopcAdderight¡iReference£dnamefc__0_2gpoly_id¢bidceptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡fNumberbopcMuleright¡iReference£dnamefa__0_2gpoly_id¢biddeptypeiCommitteddnextô¡fNumbernoriginal_index*¥dkinddSendbiddmult¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_add_flag_2gpoly_id¢bid]eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sub_flag_2gpoly_id¢bid^eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_xor_flag_2gpoly_id¢bid_eptypeiCommitteddnextôbopcAdderight¡iReference£dnamepopcode_or_flag_2gpoly_id¢bid`eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_and_flag_2gpoly_id¢bidaeptypeiCommitteddnextôdargs„¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡fNumberbopcSuberight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_xor_flag_2gpoly_id¢bid_eptypeiCommitteddnextôbopcAdderight¡iReference£dnamepopcode_or_flag_2gpoly_id¢bid`eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_and_flag_2gpoly_id¢bidaeptypeiCommitteddnextôbopcMuleright¡iReference£dnamefa__1_2gpoly_id¢bidgeptypeiCommitteddnextôbopcAdderight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_xor_flag_2gpoly_id¢bid_eptypeiCommitteddnextôbopcAdderight¡iReference£dnamepopcode_or_flag_2gpoly_id¢bid`eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_and_flag_2gpoly_id¢bidaeptypeiCommitteddnextôbopcMuleright¡iReference£dnamefb__1_2gpoly_id¢bideeptypeiCommitteddnextô¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡fNumberbopcSuberight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_xor_flag_2gpoly_id¢bid_eptypeiCommitteddnextôbopcAdderight¡iReference£dnamepopcode_or_flag_2gpoly_id¢bid`eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_and_flag_2gpoly_id¢bidaeptypeiCommitteddnextôbopcMuleright¡iReference£dnamefa__1_2gpoly_id¢bidgeptypeiCommitteddnextôbopcAdderight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_xor_flag_2gpoly_id¢bid_eptypeiCommitteddnextôbopcAdderight¡iReference£dnamepopcode_or_flag_2gpoly_id¢bid`eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_and_flag_2gpoly_id¢bidaeptypeiCommitteddnextôbopcMuleright¡iReference£dnamefc__1_2gpoly_id¢bidfeptypeiCommitteddnextô¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_xor_flag_2gpoly_id¢bid_eptypeiCommitteddnextôbopcMuleright¡iReference£dnamefa__1_2gpoly_id¢bidgeptypeiCommitteddnextôbopcAdderight¡oBinaryOperation£dleft¡iReference£dnamepopcode_or_flag_2gpoly_id¢bid`eptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡fNumberbopcMuleright¡iReference£dnamefa__1_2gpoly_id¢bidgeptypeiCommitteddnextôbopcSuberight¡iReference£dnamefb__1_2gpoly_id¢bideeptypeiCommitteddnextôbopcSuberight¡iReference£dnamefc__1_2gpoly_id¢bidfeptypeiCommitteddnextôbopcAdderight¡oBinaryOperation£dleft¡iReference£dnameqopcode_and_flag_2gpoly_id¢bidaeptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnamefb__1_2gpoly_id¢bideeptypeiCommitteddnextôbopcAdderight¡iReference£dnamefc__1_2gpoly_id¢bidfeptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡fNumberbopcMuleright¡iReference£dnamefa__1_2gpoly_id¢bidgeptypeiCommitteddnextô¡fNumbernoriginal_index+¥dkinddSendbiddmult¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_add_flag_2gpoly_id¢bid]eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sub_flag_2gpoly_id¢bid^eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_xor_flag_2gpoly_id¢bid_eptypeiCommitteddnextôbopcAdderight¡iReference£dnamepopcode_or_flag_2gpoly_id¢bid`eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_and_flag_2gpoly_id¢bidaeptypeiCommitteddnextôdargs„¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡fNumberbopcSuberight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_xor_flag_2gpoly_id¢bid_eptypeiCommitteddnextôbopcAdderight¡iReference£dnamepopcode_or_flag_2gpoly_id¢bid`eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_and_flag_2gpoly_id¢bidaeptypeiCommitteddnextôbopcMuleright¡iReference£dnamefa__2_2gpoly_id¢bidjeptypeiCommitteddnextôbopcAdderight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_xor_flag_2gpoly_id¢bid_eptypeiCommitteddnextôbopcAdderight¡iReference£dnamepopcode_or_flag_2gpoly_id¢bid`eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_and_flag_2gpoly_id¢bidaeptypeiCommitteddnextôbopcMuleright¡iReference£dnamefb__2_2gpoly_id¢bidheptypeiCommitteddnextô¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡fNumberbopcSuberight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_xor_flag_2gpoly_id¢bid_eptypeiCommitteddnextôbopcAdderight¡iReference£dnamepopcode_or_flag_2gpoly_id¢bid`eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_and_flag_2gpoly_id¢bidaeptypeiCommitteddnextôbopcMuleright¡iReference£dnamefa__2_2gpoly_id¢bidjeptypeiCommitteddnextôbopcAdderight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_xor_flag_2gpoly_id¢bid_eptypeiCommitteddnextôbopcAdderight¡iReference£dnamepopcode_or_flag_2gpoly_id¢bid`eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_and_flag_2gpoly_id¢bidaeptypeiCommitteddnextôbopcMuleright¡iReference£dnamefc__2_2gpoly_id¢bidieptypeiCommitteddnextô¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_xor_flag_2gpoly_id¢bid_eptypeiCommitteddnextôbopcMuleright¡iReference£dnamefa__2_2gpoly_id¢bidjeptypeiCommitteddnextôbopcAdderight¡oBinaryOperation£dleft¡iReference£dnamepopcode_or_flag_2gpoly_id¢bid`eptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡fNumberbopcMuleright¡iReference£dnamefa__2_2gpoly_id¢bidjeptypeiCommitteddnextôbopcSuberight¡iReference£dnamefb__2_2gpoly_id¢bidheptypeiCommitteddnextôbopcSuberight¡iReference£dnamefc__2_2gpoly_id¢bidieptypeiCommitteddnextôbopcAdderight¡oBinaryOperation£dleft¡iReference£dnameqopcode_and_flag_2gpoly_id¢bidaeptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnamefb__2_2gpoly_id¢bidheptypeiCommitteddnextôbopcAdderight¡iReference£dnamefc__2_2gpoly_id¢bidieptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡fNumberbopcMuleright¡iReference£dnamefa__2_2gpoly_id¢bidjeptypeiCommitteddnextô¡fNumbernoriginal_index,¥dkinddSendbiddmult¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_add_flag_2gpoly_id¢bid]eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sub_flag_2gpoly_id¢bid^eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_xor_flag_2gpoly_id¢bid_eptypeiCommitteddnextôbopcAdderight¡iReference£dnamepopcode_or_flag_2gpoly_id¢bid`eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_and_flag_2gpoly_id¢bidaeptypeiCommitteddnextôdargs„¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡fNumberbopcSuberight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_xor_flag_2gpoly_id¢bid_eptypeiCommitteddnextôbopcAdderight¡iReference£dnamepopcode_or_flag_2gpoly_id¢bid`eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_and_flag_2gpoly_id¢bidaeptypeiCommitteddnextôbopcMuleright¡iReference£dnamefa__3_2gpoly_id¢bidmeptypeiCommitteddnextôbopcAdderight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_xor_flag_2gpoly_id¢bid_eptypeiCommitteddnextôbopcAdderight¡iReference£dnamepopcode_or_flag_2gpoly_id¢bid`eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_and_flag_2gpoly_id¢bidaeptypeiCommitteddnextôbopcMuleright¡iReference£dnamefb__3_2gpoly_id¢bidkeptypeiCommitteddnextô¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡fNumberbopcSuberight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_xor_flag_2gpoly_id¢bid_eptypeiCommitteddnextôbopcAdderight¡iReference£dnamepopcode_or_flag_2gpoly_id¢bid`eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_and_flag_2gpoly_id¢bidaeptypeiCommitteddnextôbopcMuleright¡iReference£dnamefa__3_2gpoly_id¢bidmeptypeiCommitteddnextôbopcAdderight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_xor_flag_2gpoly_id¢bid_eptypeiCommitteddnextôbopcAdderight¡iReference£dnamepopcode_or_flag_2gpoly_id¢bid`eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_and_flag_2gpoly_id¢bidaeptypeiCommitteddnextôbopcMuleright¡iReference£dnamefc__3_2gpoly_id¢bidleptypeiCommitteddnextô¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_xor_flag_2gpoly_id¢bid_eptypeiCommitteddnextôbopcMuleright¡iReference£dnamefa__3_2gpoly_id¢bidmeptypeiCommitteddnextôbopcAdderight¡oBinaryOperation£dleft¡iReference£dnamepopcode_or_flag_2gpoly_id¢bid`eptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡fNumberbopcMuleright¡iReference£dnamefa__3_2gpoly_id¢bidmeptypeiCommitteddnextôbopcSuberight¡iReference£dnamefb__3_2gpoly_id¢bidkeptypeiCommitteddnextôbopcSuberight¡iReference£dnamefc__3_2gpoly_id¢bidleptypeiCommitteddnextôbopcAdderight¡oBinaryOperation£dleft¡iReference£dnameqopcode_and_flag_2gpoly_id¢bidaeptypeiCommitteddnextôbopcMuleright¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnamefb__3_2gpoly_id¢bidkeptypeiCommitteddnextôbopcAdderight¡iReference£dnamefc__3_2gpoly_id¢bidleptypeiCommitteddnextôbopcSuberight¡oBinaryOperation£dleft¡fNumberbopcMuleright¡iReference£dnamefa__3_2gpoly_id¢bidmeptypeiCommitteddnextô¡fNumbernoriginal_index-¥dkinddSendbiddmult¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_add_flag_2gpoly_id¢bid]eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sub_flag_2gpoly_id¢bid^eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_xor_flag_2gpoly_id¢bid_eptypeiCommitteddnextôbopcAdderight¡iReference£dnamepopcode_or_flag_2gpoly_id¢bid`eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_and_flag_2gpoly_id¢bidaeptypeiCommitteddnextôdargs„¡iReference£dnamefc__0_2gpoly_id¢bidceptypeiCommitteddnextô¡iReference£dnamefc__1_2gpoly_id¢bidfeptypeiCommitteddnextô¡fNumber¡fNumbernoriginal_index.¥dkinddSendbiddmult¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_add_flag_2gpoly_id¢bid]eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sub_flag_2gpoly_id¢bid^eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_xor_flag_2gpoly_id¢bid_eptypeiCommitteddnextôbopcAdderight¡iReference£dnamepopcode_or_flag_2gpoly_id¢bid`eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_and_flag_2gpoly_id¢bidaeptypeiCommitteddnextôdargs‚¡iReference£dnamex7reads_aux__0__base__timestamp_lt_aux__lower_decomp__0_2gpoly_id¢bidreptypeiCommitteddnextô¡fNumbernoriginal_index/¥dkinddSendbiddmult¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_add_flag_2gpoly_id¢bid]eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sub_flag_2gpoly_id¢bid^eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_xor_flag_2gpoly_id¢bid_eptypeiCommitteddnextôbopcAdderight¡iReference£dnamepopcode_or_flag_2gpoly_id¢bid`eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_and_flag_2gpoly_id¢bidaeptypeiCommitteddnextôdargs‚¡iReference£dnamex7reads_aux__0__base__timestamp_lt_aux__lower_decomp__1_2gpoly_id¢bidseptypeiCommitteddnextô¡fNumber noriginal_index0¥dkinddSendbiddmult¡oBinaryOperation£dleft¡fNumberxbopcMuleright¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_add_flag_2gpoly_id¢bid]eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sub_flag_2gpoly_id¢bid^eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_xor_flag_2gpoly_id¢bid_eptypeiCommitteddnextôbopcAdderight¡iReference£dnamepopcode_or_flag_2gpoly_id¢bid`eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_and_flag_2gpoly_id¢bidaeptypeiCommitteddnextôdargs‡¡fNumber¡fNumber(¡iReference£dnamefb__0_2gpoly_id¢bidbeptypeiCommitteddnextô¡iReference£dnamefb__1_2gpoly_id¢bideeptypeiCommitteddnextô¡iReference£dnamefb__2_2gpoly_id¢bidheptypeiCommitteddnextô¡iReference£dnamefb__3_2gpoly_id¢bidkeptypeiCommitteddnextô¡iReference£dnamex$reads_aux__0__base__prev_timestamp_2gpoly_id¢bidqeptypeiCommitteddnextônoriginal_index1¥dkinddSendbiddmult¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_add_flag_2gpoly_id¢bid]eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sub_flag_2gpoly_id¢bid^eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_xor_flag_2gpoly_id¢bid_eptypeiCommitteddnextôbopcAdderight¡iReference£dnamepopcode_or_flag_2gpoly_id¢bid`eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_and_flag_2gpoly_id¢bidaeptypeiCommitteddnextôdargs‡¡fNumber¡fNumber(¡iReference£dnamefb__0_2gpoly_id¢bidbeptypeiCommitteddnextô¡iReference£dnamefb__1_2gpoly_id¢bideeptypeiCommitteddnextô¡iReference£dnamefb__2_2gpoly_id¢bidheptypeiCommitteddnextô¡iReference£dnamefb__3_2gpoly_id¢bidkeptypeiCommitteddnextô¡iReference£dnamewfrom_state__timestamp_2gpoly_id¢bidpeptypeiCommitteddnextônoriginal_index2¥dkinddSendbiddmult¡fNumberdargs‚¡iReference£dnamex7reads_aux__1__base__timestamp_lt_aux__lower_decomp__0_2gpoly_id¢bidueptypeiCommitteddnextô¡fNumbernoriginal_index3¥dkinddSendbiddmult¡fNumberdargs‚¡iReference£dnamex7reads_aux__1__base__timestamp_lt_aux__lower_decomp__1_2gpoly_id¢bidveptypeiCommitteddnextô¡fNumber noriginal_index4¥dkinddSendbiddmult¡fNumberdargs‡¡fNumber¡fNumber¡iReference£dnamefc__0_2gpoly_id¢bidceptypeiCommitteddnextô¡iReference£dnamefc__1_2gpoly_id¢bidfeptypeiCommitteddnextô¡iReference£dnamefc__2_2gpoly_id¢bidieptypeiCommitteddnextô¡iReference£dnamefc__3_2gpoly_id¢bidleptypeiCommitteddnextô¡iReference£dnamex$reads_aux__1__base__prev_timestamp_2gpoly_id¢bidteptypeiCommitteddnextônoriginal_index5¥dkinddSendbiddmult¡fNumberdargs‡¡fNumber¡fNumber¡iReference£dnamefc__0_2gpoly_id¢bidceptypeiCommitteddnextô¡iReference£dnamefc__1_2gpoly_id¢bidfeptypeiCommitteddnextô¡iReference£dnamefc__2_2gpoly_id¢bidieptypeiCommitteddnextô¡iReference£dnamefc__3_2gpoly_id¢bidleptypeiCommitteddnextô¡oBinaryOperation£dleft¡iReference£dnamewfrom_state__timestamp_2gpoly_id¢bidpeptypeiCommitteddnextôbopcAdderight¡fNumbernoriginal_index6¥dkinddSendbiddmult¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_add_flag_2gpoly_id¢bid]eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sub_flag_2gpoly_id¢bid^eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_xor_flag_2gpoly_id¢bid_eptypeiCommitteddnextôbopcAdderight¡iReference£dnamepopcode_or_flag_2gpoly_id¢bid`eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_and_flag_2gpoly_id¢bidaeptypeiCommitteddnextôdargs‚¡iReference£dnamex5writes_aux__base__timestamp_lt_aux__lower_decomp__0_2gpoly_id¢bidxeptypeiCommitteddnextô¡fNumbernoriginal_index7¥dkinddSendbiddmult¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_add_flag_2gpoly_id¢bid]eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sub_flag_2gpoly_id¢bid^eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_xor_flag_2gpoly_id¢bid_eptypeiCommitteddnextôbopcAdderight¡iReference£dnamepopcode_or_flag_2gpoly_id¢bid`eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_and_flag_2gpoly_id¢bidaeptypeiCommitteddnextôdargs‚¡iReference£dnamex5writes_aux__base__timestamp_lt_aux__lower_decomp__1_2gpoly_id¢bidyeptypeiCommitteddnextô¡fNumber noriginal_index8¥dkinddSendbiddmult¡oBinaryOperation£dleft¡fNumberxbopcMuleright¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_add_flag_2gpoly_id¢bid]eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sub_flag_2gpoly_id¢bid^eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_xor_flag_2gpoly_id¢bid_eptypeiCommitteddnextôbopcAdderight¡iReference£dnamepopcode_or_flag_2gpoly_id¢bid`eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_and_flag_2gpoly_id¢bidaeptypeiCommitteddnextôdargs‡¡fNumber¡fNumber(¡iReference£dnamexwrites_aux__prev_data__0_2gpoly_id¢bid|eptypeiCommitteddnextô¡iReference£dnamexwrites_aux__prev_data__1_2gpoly_id¢bid}eptypeiCommitteddnextô¡iReference£dnamexwrites_aux__prev_data__2_2gpoly_id¢bid~eptypeiCommitteddnextô¡iReference£dnamexwrites_aux__prev_data__3_2gpoly_id¢bideptypeiCommitteddnextô¡iReference£dnamex"writes_aux__base__prev_timestamp_2gpoly_id¢bidweptypeiCommitteddnextônoriginal_index9¥dkinddSendbiddmult¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_add_flag_2gpoly_id¢bid]eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sub_flag_2gpoly_id¢bid^eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_xor_flag_2gpoly_id¢bid_eptypeiCommitteddnextôbopcAdderight¡iReference£dnamepopcode_or_flag_2gpoly_id¢bid`eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_and_flag_2gpoly_id¢bidaeptypeiCommitteddnextôdargs‡¡fNumber¡fNumber(¡iReference£dnamefa__0_2gpoly_id¢biddeptypeiCommitteddnextô¡iReference£dnamefa__1_2gpoly_id¢bidgeptypeiCommitteddnextô¡iReference£dnamefa__2_2gpoly_id¢bidjeptypeiCommitteddnextô¡iReference£dnamefa__3_2gpoly_id¢bidmeptypeiCommitteddnextô¡oBinaryOperation£dleft¡iReference£dnamewfrom_state__timestamp_2gpoly_id¢bidpeptypeiCommitteddnextôbopcAdderight¡fNumbernoriginal_index:¥dkinddSendbiddmult¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_add_flag_2gpoly_id¢bid]eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sub_flag_2gpoly_id¢bid^eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_xor_flag_2gpoly_id¢bid_eptypeiCommitteddnextôbopcAdderight¡iReference£dnamepopcode_or_flag_2gpoly_id¢bid`eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_and_flag_2gpoly_id¢bidaeptypeiCommitteddnextôdargs‰¡iReference£dnamepfrom_state__pc_2gpoly_id¢bid€eptypeiCommitteddnextô¡oBinaryOperation£dleft¡fNumberbopcAdderight¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_sub_flag_2gpoly_id¢bid^eptypeiCommitteddnextôbopcAdderight¡oBinaryOperation£dleft¡iReference£dnameqopcode_xor_flag_2gpoly_id¢bid_eptypeiCommitteddnextôbopcMuleright¡fNumberbopcAdderight¡oBinaryOperation£dleft¡iReference£dnamepopcode_or_flag_2gpoly_id¢bid`eptypeiCommitteddnextôbopcMuleright¡fNumberbopcAdderight¡oBinaryOperation£dleft¡iReference£dnameqopcode_and_flag_2gpoly_id¢bidaeptypeiCommitteddnextôbopcMuleright¡fNumber¡fNumber(¡fNumber(¡fNumber¡fNumber¡fNumber¡fNumber¡fNumbernoriginal_index;¥dkinddSendbiddmult¡nUnaryOperation¢bopeMinusdexpr¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_add_flag_2gpoly_id¢bid]eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sub_flag_2gpoly_id¢bid^eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_xor_flag_2gpoly_id¢bid_eptypeiCommitteddnextôbopcAdderight¡iReference£dnamepopcode_or_flag_2gpoly_id¢bid`eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_and_flag_2gpoly_id¢bidaeptypeiCommitteddnextôdargs‚¡iReference£dnamepfrom_state__pc_2gpoly_id¢bid€eptypeiCommitteddnextô¡iReference£dnamewfrom_state__timestamp_2gpoly_id¢bidpeptypeiCommitteddnextônoriginal_index<¥dkinddSendbiddmult¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnameqopcode_add_flag_2gpoly_id¢bid]eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_sub_flag_2gpoly_id¢bid^eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_xor_flag_2gpoly_id¢bid_eptypeiCommitteddnextôbopcAdderight¡iReference£dnamepopcode_or_flag_2gpoly_id¢bid`eptypeiCommitteddnextôbopcAdderight¡iReference£dnameqopcode_and_flag_2gpoly_id¢bidaeptypeiCommitteddnextôdargs‚¡oBinaryOperation£dleft¡iReference£dnamepfrom_state__pc_2gpoly_id¢bid€eptypeiCommitteddnextôbopcAdderight¡fNumber¡oBinaryOperation£dleft¡iReference£dnamewfrom_state__timestamp_2gpoly_id¢bidpeptypeiCommitteddnextôbopcAdderight¡fNumbernoriginal_index=¥dkinddSendbiddmult¡oBinaryOperation£dleft¡iReference£dnamehis_lui_3gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnamehis_jal_3gpoly_id¢bid‚eptypeiCommitteddnextôdargs„¡iReference£dnamelrd_data__0_3gpoly_id¢bidƒeptypeiCommitteddnextô¡iReference£dnamelrd_data__1_3gpoly_id¢bid„eptypeiCommitteddnextô¡fNumber¡fNumbernoriginal_index>¥dkinddSendbiddmult¡oBinaryOperation£dleft¡iReference£dnamehis_lui_3gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnamehis_jal_3gpoly_id¢bid‚eptypeiCommitteddnextôdargs„¡iReference£dnamelrd_data__2_3gpoly_id¢bid…eptypeiCommitteddnextô¡iReference£dnamelrd_data__3_3gpoly_id¢bid†eptypeiCommitteddnextô¡fNumber¡fNumbernoriginal_index?¥dkinddSendbiddmult¡iReference£dnamehis_jal_3gpoly_id¢bid‚eptypeiCommitteddnextôdargs„¡iReference£dnamelrd_data__3_3gpoly_id¢bid†eptypeiCommitteddnextô¡fNumberÀ¡oBinaryOperation£dleft¡iReference£dnamelrd_data__3_3gpoly_id¢bid†eptypeiCommitteddnextôbopcAdderight¡fNumberÀ¡fNumbernoriginal_index@¥dkinddSendbiddmult¡fNumberdargs‚¡iReference£dnamex=inner__rd_aux_cols__base__timestamp_lt_aux__lower_decomp__0_3gpoly_id¢bidŒeptypeiCommitteddnextô¡fNumbernoriginal_indexA¥dkinddSendbiddmult¡fNumberdargs‚¡iReference£dnamex=inner__rd_aux_cols__base__timestamp_lt_aux__lower_decomp__1_3gpoly_id¢bideptypeiCommitteddnextô¡fNumber noriginal_indexB¥dkinddSendbiddmult¡fNumberdargs‡¡fNumber¡fNumber¡iReference£dnamex"inner__rd_aux_cols__prev_data__0_3gpoly_id¢bideptypeiCommitteddnextô¡iReference£dnamex"inner__rd_aux_cols__prev_data__1_3gpoly_id¢bideptypeiCommitteddnextô¡iReference£dnamex"inner__rd_aux_cols__prev_data__2_3gpoly_id¢bid‘eptypeiCommitteddnextô¡iReference£dnamex"inner__rd_aux_cols__prev_data__3_3gpoly_id¢bid’eptypeiCommitteddnextô¡iReference£dnamex*inner__rd_aux_cols__base__prev_timestamp_3gpoly_id¢bid‹eptypeiCommitteddnextônoriginal_indexC¥dkinddSendbiddmult¡fNumberdargs‡¡fNumber¡fNumber¡iReference£dnamelrd_data__0_3gpoly_id¢bidƒeptypeiCommitteddnextô¡iReference£dnamelrd_data__1_3gpoly_id¢bid„eptypeiCommitteddnextô¡iReference£dnamelrd_data__2_3gpoly_id¢bid…eptypeiCommitteddnextô¡iReference£dnamelrd_data__3_3gpoly_id¢bid†eptypeiCommitteddnextô¡iReference£dnamexinner__from_state__timestamp_3gpoly_id¢bidŠeptypeiCommitteddnextônoriginal_indexD¥dkinddSendbiddmult¡oBinaryOperation£dleft¡iReference£dnamehis_lui_3gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnamehis_jal_3gpoly_id¢bid‚eptypeiCommitteddnextôdargs‰¡iReference£dnamewinner__from_state__pc_3gpoly_id¢bidˆeptypeiCommitteddnextô¡oBinaryOperation£dleft¡fNumber0bopcAdderight¡iReference£dnamehis_lui_3gpoly_id¢bideptypeiCommitteddnextô¡fNumber¡fNumber¡fNumberwÿýñ¡fNumber¡fNumber¡fNumber¡fNumbernoriginal_indexE¥dkinddSendbiddmult¡nUnaryOperation¢bopeMinusdexpr¡oBinaryOperation£dleft¡iReference£dnamehis_lui_3gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnamehis_jal_3gpoly_id¢bid‚eptypeiCommitteddnextôdargs‚¡iReference£dnamewinner__from_state__pc_3gpoly_id¢bidˆeptypeiCommitteddnextô¡iReference£dnamexinner__from_state__timestamp_3gpoly_id¢bidŠeptypeiCommitteddnextônoriginal_indexF¥dkinddSendbiddmult¡oBinaryOperation£dleft¡iReference£dnamehis_lui_3gpoly_id¢bideptypeiCommitteddnextôbopcAdderight¡iReference£dnamehis_jal_3gpoly_id¢bid‚eptypeiCommitteddnextôdargs‚¡oBinaryOperation£dleft¡oBinaryOperation£dleft¡iReference£dnamewinner__from_state__pc_3gpoly_id¢bidˆeptypeiCommitteddnextôbopcAdderight¡oBinaryOperation£dleft¡iReference£dnamehis_lui_3gpoly_id¢bideptypeiCommitteddnextôbopcMuleright¡fNumberbopcAdderight¡oBinaryOperation£dleft¡iReference£dnamehis_jal_3gpoly_id¢bid‚eptypeiCommitteddnextôbopcMuleright¡fNumberwÿýñ¡oBinaryOperation£dleft¡iReference£dnamexinner__from_state__timestamp_3gpoly_id¢bidŠeptypeiCommitteddnextôbopcAdderight¡fNumbernoriginal_indexG \ No newline at end of file From 81d4f18c307fa048298799433f5d877e303f67ec Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 28 Nov 2025 14:49:49 +0000 Subject: [PATCH 51/60] Clippy. --- autoprecompiles/src/rule_based_optimizer.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/autoprecompiles/src/rule_based_optimizer.rs b/autoprecompiles/src/rule_based_optimizer.rs index e299e47465..89f79c48d8 100644 --- a/autoprecompiles/src/rule_based_optimizer.rs +++ b/autoprecompiles/src/rule_based_optimizer.rs @@ -142,7 +142,7 @@ where // TODO avoid using this (as pub) pub fn next_free_id(&self) -> usize { - self.items.len() as usize + self.items.len() } } From ac056c3cb8dc060f2dd75c50717620a8bbc04164 Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 28 Nov 2025 17:10:50 +0000 Subject: [PATCH 52/60] Cleanup --- autoprecompiles/Cargo.toml | 1 + autoprecompiles/src/rule_based_optimizer.rs | 46 ++++----------------- 2 files changed, 8 insertions(+), 39 deletions(-) diff --git a/autoprecompiles/Cargo.toml b/autoprecompiles/Cargo.toml index 002109d947..3439624d2c 100644 --- a/autoprecompiles/Cargo.toml +++ b/autoprecompiles/Cargo.toml @@ -24,6 +24,7 @@ strum = { version = "0.27.0", features = ["derive"] } metrics = "0.23.0" crepe = { git = "https://github.com/chriseth/crepe", branch = "generics", version = "0.1.8" } +derive_more = "0.99.17" [dev-dependencies] expect-test = "1.5.1" diff --git a/autoprecompiles/src/rule_based_optimizer.rs b/autoprecompiles/src/rule_based_optimizer.rs index 89f79c48d8..dc0401ebad 100644 --- a/autoprecompiles/src/rule_based_optimizer.rs +++ b/autoprecompiles/src/rule_based_optimizer.rs @@ -1,6 +1,6 @@ #![allow(clippy::iter_over_hash_type)] // This is about a warning about interior mutability for the key -// `S`. We need it and it is probably fine. +// `Env`. We need it and it is probably fine. #![allow(clippy::mutable_key_type)] use std::{ cell::RefCell, @@ -23,28 +23,16 @@ use powdr_constraint_solver::{ }; use powdr_number::{BabyBearField, FieldElement, LargeInt}; +use derive_more::{From, Into}; +#[allow(unused_imports)] use num_traits::Zero; use crepe::crepe; const SIZE_LIMIT: usize = 800; -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -struct Var(u32); - -// TODO auto-derive -impl From for Var { - fn from(value: usize) -> Self { - Var(value as u32) - } -} - -// TODO auto-derive -impl From for usize { - fn from(value: Var) -> Self { - value.0 as usize - } -} +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, From, Into)] +struct Var(usize); impl Display for Var { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -184,7 +172,6 @@ impl Environment { impl PartialEq for Environment { fn eq(&self, _other: &Self) -> bool { - // TODO change this as soon as we have different environments true } } @@ -199,14 +186,12 @@ impl PartialOrd for Environment { impl Ord for Environment { fn cmp(&self, _other: &Self) -> std::cmp::Ordering { - // TODO change this as soon as we have different environments std::cmp::Ordering::Equal } } impl Hash for Environment { fn hash(&self, state: &mut H) { - // TODO change this as soon as we have different environments 0.hash(state); } } @@ -233,6 +218,7 @@ impl Environment { ) -> Var { self.new_var_generator.borrow_mut().generate(prefix, method) } + pub fn referenced_variables(&self, expr: Expr) -> BTreeSet { let db = self.expressions.borrow(); db[expr].referenced_unknown_variables().cloned().collect() @@ -274,11 +260,6 @@ impl Environment { db[expr].range_constraint(self) } - #[allow(dead_code)] - pub fn is_zero(&self, expr: Expr) -> bool { - self.expressions.borrow()[expr].is_zero() - } - // TODO potentially make this a more generic "matches structure" function pub fn try_as_single_product(&self, expr: Expr) -> Option<(Expr, Expr)> { let (l, r) = { @@ -428,22 +409,9 @@ impl RangeConstraintProvider for Environment { } } -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, From, Into)] struct Expr(usize); -// TODO auto-derive -impl From for Expr { - fn from(value: usize) -> Self { - Expr(value) - } -} - -impl From for usize { - fn from(value: Expr) -> Self { - value.0 - } -} - #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] enum Action { SubstituteVariableByConstant(Var, T), From 805010d1be883f61b8ba081aaad1f603779a3073 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 1 Dec 2025 12:03:17 +0000 Subject: [PATCH 53/60] Simplify. --- autoprecompiles/src/rule_based_optimizer.rs | 37 +++++++-------------- 1 file changed, 12 insertions(+), 25 deletions(-) diff --git a/autoprecompiles/src/rule_based_optimizer.rs b/autoprecompiles/src/rule_based_optimizer.rs index dc0401ebad..93fdb3f9b6 100644 --- a/autoprecompiles/src/rule_based_optimizer.rs +++ b/autoprecompiles/src/rule_based_optimizer.rs @@ -581,18 +581,17 @@ crepe! { RangeConstraintOnExpression(x1, rc1), RangeConstraintOnExpression(x2, rc2), let Some(replacement) = (|| { - let x1_needs_squaring = rc1.range().0 != T::zero(); - let x2_needs_squaring = rc2.range().0 != T::zero(); - let rc1 = if x1_needs_squaring { - rc1.square() - } else { - rc1 - }; - let rc2 = if x2_needs_squaring { - rc2.square() - } else { - rc2 + // If the expression is not known to be non-negative, we square it. + let square_if_needed = |expr: Expr, rc: RangeConstraint| { + let expr = env.extract(expr); + if rc.range().0 == T::zero() { + (expr, rc) + } else { + (expr.clone() * expr, rc.square()) + } }; + let (x1, rc1) = square_if_needed(x1, rc1); + let (x2, rc2) = square_if_needed(x2, rc2); if !rc1.range().0.is_zero() || !rc2.range().0.is_zero() { return None; } @@ -600,20 +599,8 @@ crepe! { if !(sum_rc.range().0.is_zero() && sum_rc.range().1 < T::from(-1)) { return None; } - let e = env.extract(e); - let x1 = env.extract(x1); - let x1 = if x1_needs_squaring { - x1.clone() * x1 - } else { - x1 - }; - let x2 = env.extract(x2); - let x2 = if x2_needs_squaring { - x2.clone() * x2 - } else { - x2 - }; - let r = e.into_summands().filter(|s|{ + // Remove the summands with v1 and v2 from the expression. + let r = env.extract(e).into_summands().filter(|s|{ if let GroupedExpressionComponent::Quadratic(l, r) = s { let mut vars = l.referenced_unknown_variables().chain(r.referenced_unknown_variables()); if vars.any(|v| v == &v1 || v == &v2) { From fc24975377a5627004ebf825b7df4184ee20bb52 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 1 Dec 2025 16:20:34 +0000 Subject: [PATCH 54/60] Allow quadratic substitution below degree bound. --- autoprecompiles/src/constraint_optimizer.rs | 3 +++ autoprecompiles/src/optimizer.rs | 14 ++++++++--- autoprecompiles/src/rule_based_optimizer.rs | 27 +++++++++++++++------ openvm/src/lib.rs | 16 ++++++------ 4 files changed, 40 insertions(+), 20 deletions(-) diff --git a/autoprecompiles/src/constraint_optimizer.rs b/autoprecompiles/src/constraint_optimizer.rs index 931a446870..1f04a4a27b 100644 --- a/autoprecompiles/src/constraint_optimizer.rs +++ b/autoprecompiles/src/constraint_optimizer.rs @@ -88,6 +88,9 @@ pub fn optimize_constraints< &*solver, bus_interaction_handler.clone(), new_var, + // No degree bound given, i.e. only perform replacements that + // do not increase the degree. + None, ); stats_logger.log("rule-based optimization", &constraint_system); diff --git a/autoprecompiles/src/optimizer.rs b/autoprecompiles/src/optimizer.rs index dc2e93250b..2e0274aba1 100644 --- a/autoprecompiles/src/optimizer.rs +++ b/autoprecompiles/src/optimizer.rs @@ -16,6 +16,7 @@ use powdr_number::FieldElement; use crate::constraint_optimizer::trivial_simplifications; use crate::range_constraint_optimizer::optimize_range_constraints; +use crate::rule_based_optimizer::rule_based_optimization; use crate::ColumnAllocator; use crate::{ adapter::Adapter, @@ -76,15 +77,20 @@ pub fn optimize( let constraint_system = inliner::replace_constrained_witness_columns( constraint_system.into(), inline_everything_below_degree_bound(degree_bound), - ) - .system() - .clone(); + ); stats_logger.log("inlining", &constraint_system); + let constraint_system = rule_based_optimization( + constraint_system, + &solver, + bus_interaction_handler.clone(), + &mut new_var, + Some(degree_bound), + ); // Note that the rest of the optimization does not benefit from optimizing range constraints, // so we only do it once at the end. let constraint_system = optimize_range_constraints( - constraint_system, + constraint_system.into(), bus_interaction_handler.clone(), degree_bound, ); diff --git a/autoprecompiles/src/rule_based_optimizer.rs b/autoprecompiles/src/rule_based_optimizer.rs index 93fdb3f9b6..acb00cbe4c 100644 --- a/autoprecompiles/src/rule_based_optimizer.rs +++ b/autoprecompiles/src/rule_based_optimizer.rs @@ -18,10 +18,11 @@ use powdr_constraint_solver::{ }, grouped_expression::{GroupedExpression, GroupedExpressionComponent, RangeConstraintProvider}, indexed_constraint_system::IndexedConstraintSystem, + inliner::DegreeBound, range_constraint::RangeConstraint, runtime_constant::VarTransformable, }; -use powdr_number::{BabyBearField, FieldElement, LargeInt}; +use powdr_number::FieldElement; use derive_more::{From, Into}; #[allow(unused_imports)] @@ -67,9 +68,8 @@ where let items = iter.into_iter().collect::>(); let reverse = items .iter() - .cloned() .enumerate() - .map(|(i, v)| (v, i)) + .map(|(i, v)| (v.clone(), i)) .collect(); Self { items, @@ -691,10 +691,8 @@ pub fn rule_based_optimization, _bus_interaction_handler: impl BusInteractionHandler + Clone, new_var_outer: &mut impl FnMut(&str) -> V, + degree_bound: Option, ) -> IndexedConstraintSystem { - if T::modulus().to_arbitrary_integer() != BabyBearField::modulus().to_arbitrary_integer() { - return system; - } if system.system().algebraic_constraints.len() > SIZE_LIMIT { log::debug!( "Skipping rule-based optimization because the system is too large ({} > {}).", @@ -810,6 +808,17 @@ pub fn rule_based_optimization expr1.degree() + && (degree_bound.is_none() + || expr2.degree() > degree_bound.unwrap().identities) + { + log::debug!( + "Skipping replacement of {expr1} by {expr2} due to degree constraints." + ); + continue; + } let mut found1 = false; let mut found2 = false; for c in system.algebraic_constraints() { @@ -847,8 +856,6 @@ pub fn rule_based_optimization( system: &IndexedConstraintSystem, var_mapper: &ItemDB, @@ -912,6 +919,7 @@ mod tests { algebraic_constraint, constraint_system::DefaultBusInteractionHandler, grouped_expression::NoRangeConstraints, }; + use powdr_number::{BabyBearField, LargeInt}; use super::*; @@ -1016,6 +1024,7 @@ mod tests { NoRangeConstraints, DefaultBusInteractionHandler::default(), &mut new_var(), + None, ); assert_eq!(optimized_system.system().algebraic_constraints.len(), 0); } @@ -1033,6 +1042,7 @@ mod tests { NoRangeConstraints, DefaultBusInteractionHandler::default(), &mut new_var(), + None, ); expect!["(y) * (y - 1) - 3 = 0"].assert_eq(&optimized_system.to_string()); } @@ -1077,6 +1087,7 @@ mod tests { NoRangeConstraints, TestBusInteractionHandler, &mut new_var(), + None, ); // Note that in the system below, mem_ptr_limbs__0_2 has been eliminated expect![[r#" diff --git a/openvm/src/lib.rs b/openvm/src/lib.rs index 2a984f3b6d..075cad1ca9 100644 --- a/openvm/src/lib.rs +++ b/openvm/src/lib.rs @@ -2094,11 +2094,11 @@ mod tests { AirMetrics { widths: AirWidths { preprocessed: 0, - main: 3242, - log_up: 5268, + main: 3246, + log_up: 5264, }, - constraints: 592, - bus_interactions: 2564, + constraints: 596, + bus_interactions: 2562, } "#]], powdr_expected_machine_count: expect![[r#" @@ -2111,13 +2111,13 @@ mod tests { AirWidthsDiff { before: AirWidths { preprocessed: 0, - main: 32376, - log_up: 41660, + main: 32370, + log_up: 41644, }, after: AirWidths { preprocessed: 0, - main: 3242, - log_up: 5268, + main: 3246, + log_up: 5264, }, } "#]]), From 707366db3b41783acf17c6d08a8b40c705fd7f1c Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 1 Dec 2025 16:35:46 +0000 Subject: [PATCH 55/60] Implement degree bound properly and update expectations. --- autoprecompiles/src/rule_based_optimizer.rs | 29 +++++++++---------- .../complex/load_two_bytes_compare.txt | 21 ++++++-------- .../load_two_bytes_compare_unsigned.txt | 4 +-- .../single_instructions/single_beq.txt | 4 +-- .../single_instructions/single_bne.txt | 4 +-- 5 files changed, 29 insertions(+), 33 deletions(-) diff --git a/autoprecompiles/src/rule_based_optimizer.rs b/autoprecompiles/src/rule_based_optimizer.rs index acb00cbe4c..398c364120 100644 --- a/autoprecompiles/src/rule_based_optimizer.rs +++ b/autoprecompiles/src/rule_based_optimizer.rs @@ -778,6 +778,19 @@ pub fn rule_based_optimization { let expr1 = untransform_grouped_expression(&expr_db.as_ref().unwrap()[e1], &var_mapper); + let expr2 = + untransform_grouped_expression(&expr_db.as_ref().unwrap()[e2], &var_mapper); + // If the degree does not increase, we do it in any case. If the degree increases, we + // only do it if a degree bound is given and it stays within the bound. + if expr2.degree() > expr1.degree() + && (degree_bound.is_none() + || expr2.degree() > degree_bound.unwrap().identities) + { + log::debug!( + "Skipping replacement of {expr1} by {expr2} due to degree constraints." + ); + continue; + } // TODO more efficient? let mut found = false; system.retain_algebraic_constraints(|c| { @@ -789,10 +802,6 @@ pub fn rule_based_optimization expr1.degree() - && (degree_bound.is_none() - || expr2.degree() > degree_bound.unwrap().identities) - { - log::debug!( - "Skipping replacement of {expr1} by {expr2} due to degree constraints." - ); - continue; - } let mut found1 = false; let mut found2 = false; for c in system.algebraic_constraints() { @@ -836,6 +834,7 @@ pub fn rule_based_optimization 52 (1.88x reduction) + - Main columns: 98 -> 51 (1.92x reduction) - Bus interactions: 47 -> 32 (1.47x reduction) - - Constraints: 47 -> 17 (2.76x reduction) + - Constraints: 47 -> 15 (3.13x reduction) -Symbolic machine using 52 unique main columns: +Symbolic machine using 51 unique main columns: from_state__timestamp_0 rs1_data__0_0 rs1_data__1_0 @@ -56,10 +56,9 @@ Symbolic machine using 52 unique main columns: prev_data__1_1 prev_data__2_1 prev_data__3_1 - a__0_2 - b__0_2 cmp_result_2 - free_var_103 + diff_inv_marker__0_2 + free_var_105 is_valid // Bus 0 (EXECUTION_BRIDGE): @@ -77,8 +76,8 @@ mult=is_valid * 1, args=[1, 44, rs1_data__0_1, rs1_data__1_1, rs1_data__2_1, rs1 mult=is_valid * -1, args=[2, mem_ptr_limbs__0_1 + 65536 * mem_ptr_limbs__1_1 + opcode_loadb_flag0_1 - (2 * shift_most_sig_bit_1 + 1), shift_most_sig_bit_1 * shifted_read_data__2_1 + (1 - shift_most_sig_bit_1) * shifted_read_data__0_1, shift_most_sig_bit_1 * shifted_read_data__3_1 + (1 - shift_most_sig_bit_1) * shifted_read_data__1_1, shift_most_sig_bit_1 * shifted_read_data__0_1 + (1 - shift_most_sig_bit_1) * shifted_read_data__2_1, shift_most_sig_bit_1 * shifted_read_data__1_1 + (1 - shift_most_sig_bit_1) * shifted_read_data__3_1, read_data_aux__base__prev_timestamp_1] mult=is_valid * 1, args=[2, mem_ptr_limbs__0_1 + 65536 * mem_ptr_limbs__1_1 + opcode_loadb_flag0_1 - (2 * shift_most_sig_bit_1 + 1), shift_most_sig_bit_1 * shifted_read_data__2_1 + (1 - shift_most_sig_bit_1) * shifted_read_data__0_1, shift_most_sig_bit_1 * shifted_read_data__3_1 + (1 - shift_most_sig_bit_1) * shifted_read_data__1_1, shift_most_sig_bit_1 * shifted_read_data__0_1 + (1 - shift_most_sig_bit_1) * shifted_read_data__2_1, shift_most_sig_bit_1 * shifted_read_data__1_1 + (1 - shift_most_sig_bit_1) * shifted_read_data__3_1, from_state__timestamp_0 + 4] mult=is_valid * -1, args=[1, 56, prev_data__0_1, prev_data__1_1, prev_data__2_1, prev_data__3_1, write_base_aux__prev_timestamp_1] -mult=is_valid * 1, args=[1, 52, a__0_2, 255 * data_most_sig_bit_0, 255 * data_most_sig_bit_0, 255 * data_most_sig_bit_0, from_state__timestamp_0 + 6] -mult=is_valid * 1, args=[1, 56, b__0_2, 255 * data_most_sig_bit_1, 255 * data_most_sig_bit_1, 255 * data_most_sig_bit_1, from_state__timestamp_0 + 7] +mult=is_valid * 1, args=[1, 52, opcode_loadb_flag0_0 * shifted_read_data__0_0 + (1 - opcode_loadb_flag0_0) * shifted_read_data__1_0, 255 * data_most_sig_bit_0, 255 * data_most_sig_bit_0, 255 * data_most_sig_bit_0, from_state__timestamp_0 + 6] +mult=is_valid * 1, args=[1, 56, opcode_loadb_flag0_1 * shifted_read_data__0_1 + (1 - opcode_loadb_flag0_1) * shifted_read_data__1_1, 255 * data_most_sig_bit_1, 255 * data_most_sig_bit_1, 255 * data_most_sig_bit_1, from_state__timestamp_0 + 7] // Bus 3 (VARIABLE_RANGE_CHECKER): mult=is_valid * 1, args=[shifted_read_data__0_0 * opcode_loadb_flag0_0 + shifted_read_data__1_0 * (1 - opcode_loadb_flag0_0) - 128 * data_most_sig_bit_0, 7] @@ -112,9 +111,7 @@ shift_most_sig_bit_1 * (shift_most_sig_bit_1 - 1) = 0 (30720 * mem_ptr_limbs__0_1 - (30720 * rs1_data__0_1 + 7864320 * rs1_data__1_1)) * (30720 * mem_ptr_limbs__0_1 - (30720 * rs1_data__0_1 + 7864320 * rs1_data__1_1 + 1)) = 0 (943718400 * rs1_data__0_1 + 30720 * mem_ptr_limbs__1_1 - (120 * rs1_data__1_1 + 30720 * rs1_data__2_1 + 7864320 * rs1_data__3_1 + 943718400 * mem_ptr_limbs__0_1)) * (943718400 * rs1_data__0_1 + 30720 * mem_ptr_limbs__1_1 - (120 * rs1_data__1_1 + 30720 * rs1_data__2_1 + 7864320 * rs1_data__3_1 + 943718400 * mem_ptr_limbs__0_1 + 1)) = 0 cmp_result_2 * (cmp_result_2 - 1) = 0 -(1 - cmp_result_2) * (a__0_2 - b__0_2) = 0 +(1 - cmp_result_2) * ((opcode_loadb_flag0_1 - 1) * shifted_read_data__1_1 + opcode_loadb_flag0_0 * shifted_read_data__0_0 + (1 - opcode_loadb_flag0_0) * shifted_read_data__1_0 - opcode_loadb_flag0_1 * shifted_read_data__0_1) = 0 (1 - cmp_result_2) * (255 * data_most_sig_bit_0 - 255 * data_most_sig_bit_1) = 0 -free_var_103 * ((a__0_2 - b__0_2) * (a__0_2 - b__0_2) + (255 * data_most_sig_bit_0 - 255 * data_most_sig_bit_1) * (255 * data_most_sig_bit_0 - 255 * data_most_sig_bit_1) + (255 * data_most_sig_bit_0 - 255 * data_most_sig_bit_1) * (255 * data_most_sig_bit_0 - 255 * data_most_sig_bit_1) + (255 * data_most_sig_bit_0 - 255 * data_most_sig_bit_1) * (255 * data_most_sig_bit_0 - 255 * data_most_sig_bit_1)) - cmp_result_2 = 0 -opcode_loadb_flag0_0 * shifted_read_data__0_0 + (1 - opcode_loadb_flag0_0) * shifted_read_data__1_0 - a__0_2 = 0 -opcode_loadb_flag0_1 * shifted_read_data__0_1 + (1 - opcode_loadb_flag0_1) * shifted_read_data__1_1 - b__0_2 = 0 +((opcode_loadb_flag0_1 - 1) * shifted_read_data__1_1 + opcode_loadb_flag0_0 * shifted_read_data__0_0 + (1 - opcode_loadb_flag0_0) * shifted_read_data__1_0 - opcode_loadb_flag0_1 * shifted_read_data__0_1) * diff_inv_marker__0_2 + free_var_105 * ((255 * data_most_sig_bit_0 - 255 * data_most_sig_bit_1) * (255 * data_most_sig_bit_0 - 255 * data_most_sig_bit_1) + (255 * data_most_sig_bit_0 - 255 * data_most_sig_bit_1) * (255 * data_most_sig_bit_0 - 255 * data_most_sig_bit_1) + (255 * data_most_sig_bit_0 - 255 * data_most_sig_bit_1) * (255 * data_most_sig_bit_0 - 255 * data_most_sig_bit_1)) - cmp_result_2 = 0 is_valid * (is_valid - 1) = 0 \ No newline at end of file diff --git a/openvm/tests/apc_snapshots/complex/load_two_bytes_compare_unsigned.txt b/openvm/tests/apc_snapshots/complex/load_two_bytes_compare_unsigned.txt index 803edc1689..5a5b974705 100644 --- a/openvm/tests/apc_snapshots/complex/load_two_bytes_compare_unsigned.txt +++ b/openvm/tests/apc_snapshots/complex/load_two_bytes_compare_unsigned.txt @@ -61,7 +61,7 @@ Symbolic machine using 54 unique main columns: prev_data__3_1 write_data__0_1 cmp_result_2 - free_var_113 + diff_inv_marker__0_2 is_valid // Bus 0 (EXECUTION_BRIDGE): @@ -129,7 +129,7 @@ flags__3_1 * ((flags__3_1 - 1) * (flags__3_1 - 2)) = 0 flags__1_1 * (flags__1_1 - 1) + flags__2_1 * (flags__2_1 - 1) + 4 * flags__0_1 * flags__1_1 + 4 * flags__0_1 * flags__2_1 + 5 * flags__0_1 * flags__3_1 + 5 * flags__1_1 * flags__2_1 + 5 * flags__1_1 * flags__3_1 + 5 * flags__2_1 * flags__3_1 - (1006632960 * flags__3_1 * (flags__3_1 - 1) + flags__0_1 * (flags__0_1 + flags__1_1 + flags__2_1 + flags__3_1 - 2) + flags__1_1 * (flags__0_1 + flags__1_1 + flags__2_1 + flags__3_1 - 2) + flags__2_1 * (flags__0_1 + flags__1_1 + flags__2_1 + flags__3_1 - 2) + 3 * flags__3_1 * (flags__0_1 + flags__1_1 + flags__2_1 + flags__3_1 - 2) + 1 * is_valid) = 0 cmp_result_2 * (cmp_result_2 - 1) = 0 (1 - cmp_result_2) * (write_data__0_0 - write_data__0_1) = 0 +(write_data__0_0 - write_data__0_1) * diff_inv_marker__0_2 - cmp_result_2 = 0 flags__1_0 * flags__2_0 + 2 * flags__0_0 * flags__2_0 + 2 * flags__1_0 * flags__3_0 + 3 * flags__2_0 * flags__3_0 = 0 flags__1_1 * flags__2_1 + 2 * flags__0_1 * flags__2_1 + 2 * flags__1_1 * flags__3_1 + 3 * flags__2_1 * flags__3_1 = 0 -free_var_113 * ((write_data__0_0 - write_data__0_1) * (write_data__0_0 - write_data__0_1)) - cmp_result_2 = 0 is_valid * (is_valid - 1) = 0 \ No newline at end of file diff --git a/openvm/tests/apc_snapshots/single_instructions/single_beq.txt b/openvm/tests/apc_snapshots/single_instructions/single_beq.txt index f329a628a8..716f3dc16c 100644 --- a/openvm/tests/apc_snapshots/single_instructions/single_beq.txt +++ b/openvm/tests/apc_snapshots/single_instructions/single_beq.txt @@ -21,7 +21,7 @@ Symbolic machine using 16 unique main columns: b__2_0 b__3_0 cmp_result_0 - free_var_31 + free_var_33 is_valid // Bus 0 (EXECUTION_BRIDGE): @@ -46,5 +46,5 @@ cmp_result_0 * (a__0_0 - b__0_0) = 0 cmp_result_0 * (a__1_0 - b__1_0) = 0 cmp_result_0 * (a__2_0 - b__2_0) = 0 cmp_result_0 * (a__3_0 - b__3_0) = 0 -free_var_31 * ((a__0_0 - b__0_0) * (a__0_0 - b__0_0) + (a__1_0 - b__1_0) * (a__1_0 - b__1_0) + (a__2_0 - b__2_0) * (a__2_0 - b__2_0) + (a__3_0 - b__3_0) * (a__3_0 - b__3_0)) + cmp_result_0 - 1 * is_valid = 0 +free_var_33 * ((a__0_0 - b__0_0) * (a__0_0 - b__0_0) + (a__1_0 - b__1_0) * (a__1_0 - b__1_0) + (a__2_0 - b__2_0) * (a__2_0 - b__2_0) + (a__3_0 - b__3_0) * (a__3_0 - b__3_0)) + cmp_result_0 - 1 * is_valid = 0 is_valid * (is_valid - 1) = 0 \ No newline at end of file diff --git a/openvm/tests/apc_snapshots/single_instructions/single_bne.txt b/openvm/tests/apc_snapshots/single_instructions/single_bne.txt index babcac2e1e..a65969adb3 100644 --- a/openvm/tests/apc_snapshots/single_instructions/single_bne.txt +++ b/openvm/tests/apc_snapshots/single_instructions/single_bne.txt @@ -21,7 +21,7 @@ Symbolic machine using 16 unique main columns: b__2_0 b__3_0 cmp_result_0 - free_var_31 + free_var_33 is_valid // Bus 0 (EXECUTION_BRIDGE): @@ -46,5 +46,5 @@ cmp_result_0 * (cmp_result_0 - 1) = 0 (1 - cmp_result_0) * (a__1_0 - b__1_0) = 0 (1 - cmp_result_0) * (a__2_0 - b__2_0) = 0 (1 - cmp_result_0) * (a__3_0 - b__3_0) = 0 -free_var_31 * ((a__0_0 - b__0_0) * (a__0_0 - b__0_0) + (a__1_0 - b__1_0) * (a__1_0 - b__1_0) + (a__2_0 - b__2_0) * (a__2_0 - b__2_0) + (a__3_0 - b__3_0) * (a__3_0 - b__3_0)) - cmp_result_0 = 0 +free_var_33 * ((a__0_0 - b__0_0) * (a__0_0 - b__0_0) + (a__1_0 - b__1_0) * (a__1_0 - b__1_0) + (a__2_0 - b__2_0) * (a__2_0 - b__2_0) + (a__3_0 - b__3_0) * (a__3_0 - b__3_0)) - cmp_result_0 = 0 is_valid * (is_valid - 1) = 0 \ No newline at end of file From b63bc22a38d3743a0275c4fbcd8ea975b44b1dfd Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 1 Dec 2025 16:44:15 +0000 Subject: [PATCH 56/60] Update expectations. --- openvm/src/lib.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/openvm/src/lib.rs b/openvm/src/lib.rs index 075cad1ca9..2a984f3b6d 100644 --- a/openvm/src/lib.rs +++ b/openvm/src/lib.rs @@ -2094,11 +2094,11 @@ mod tests { AirMetrics { widths: AirWidths { preprocessed: 0, - main: 3246, - log_up: 5264, + main: 3242, + log_up: 5268, }, - constraints: 596, - bus_interactions: 2562, + constraints: 592, + bus_interactions: 2564, } "#]], powdr_expected_machine_count: expect![[r#" @@ -2111,13 +2111,13 @@ mod tests { AirWidthsDiff { before: AirWidths { preprocessed: 0, - main: 32370, - log_up: 41644, + main: 32376, + log_up: 41660, }, after: AirWidths { preprocessed: 0, - main: 3246, - log_up: 5264, + main: 3242, + log_up: 5268, }, } "#]]), From a5c1ac33476dad814f43e9f65609c5ef29368a9e Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 1 Dec 2025 21:45:35 +0000 Subject: [PATCH 57/60] Inform solver about equivalences and disable equivalence search in the solver. --- autoprecompiles/src/constraint_optimizer.rs | 3 +- autoprecompiles/src/optimizer.rs | 2 +- autoprecompiles/src/rule_based_optimizer.rs | 30 +++++++++++---- constraint-solver/src/solver/base.rs | 18 ++++----- .../src/solver/quadratic_equivalences.rs | 9 ++++- openvm/src/lib.rs | 38 +++++++++---------- openvm/tests/optimizer.rs | 6 +-- 7 files changed, 64 insertions(+), 42 deletions(-) diff --git a/autoprecompiles/src/constraint_optimizer.rs b/autoprecompiles/src/constraint_optimizer.rs index 1f04a4a27b..bb0516baea 100644 --- a/autoprecompiles/src/constraint_optimizer.rs +++ b/autoprecompiles/src/constraint_optimizer.rs @@ -83,7 +83,7 @@ pub fn optimize_constraints< stats_logger, ); - let constraint_system = rule_based_optimization( + let (constraint_system, derived_constraints) = rule_based_optimization( constraint_system, &*solver, bus_interaction_handler.clone(), @@ -92,6 +92,7 @@ pub fn optimize_constraints< // do not increase the degree. None, ); + solver.add_algebraic_constraints(derived_constraints); stats_logger.log("rule-based optimization", &constraint_system); // At this point, we throw away the index and only keep the constraint system, since the rest of the optimisations are defined on the system alone diff --git a/autoprecompiles/src/optimizer.rs b/autoprecompiles/src/optimizer.rs index 2e0274aba1..554006233b 100644 --- a/autoprecompiles/src/optimizer.rs +++ b/autoprecompiles/src/optimizer.rs @@ -80,7 +80,7 @@ pub fn optimize( ); stats_logger.log("inlining", &constraint_system); - let constraint_system = rule_based_optimization( + let (constraint_system, _) = rule_based_optimization( constraint_system, &solver, bus_interaction_handler.clone(), diff --git a/autoprecompiles/src/rule_based_optimizer.rs b/autoprecompiles/src/rule_based_optimizer.rs index 398c364120..128464ef41 100644 --- a/autoprecompiles/src/rule_based_optimizer.rs +++ b/autoprecompiles/src/rule_based_optimizer.rs @@ -30,7 +30,7 @@ use num_traits::Zero; use crepe::crepe; -const SIZE_LIMIT: usize = 800; +const SIZE_LIMIT: usize = 1600; #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, From, Into)] struct Var(usize); @@ -692,16 +692,20 @@ pub fn rule_based_optimization + Clone, new_var_outer: &mut impl FnMut(&str) -> V, degree_bound: Option, -) -> IndexedConstraintSystem { +) -> ( + IndexedConstraintSystem, + Vec>>, +) { if system.system().algebraic_constraints.len() > SIZE_LIMIT { log::debug!( "Skipping rule-based optimization because the system is too large ({} > {}).", system.system().algebraic_constraints.len(), SIZE_LIMIT ); - return system; + return (system, vec![]); } + let mut additional_algebraic_constraints = vec![]; let mut var_mapper = system .referenced_unknown_variables() .cloned() @@ -769,6 +773,18 @@ pub fn rule_based_optimization { + let (v1, v2) = if var_mapper[v1] < var_mapper[v2] { + (v1, v2) + } else { + (v2, v1) + }; + // We need to notify the solver of the equivalence. + additional_algebraic_constraints.push( + algebraic_constraint::AlgebraicConstraint::assert_zero( + GroupedExpression::from_unknown_variable(var_mapper[v1].clone()) + - GroupedExpression::from_unknown_variable(var_mapper[v2].clone()), + ), + ); system.substitute_by_unknown( &var_mapper[v1], &GroupedExpression::from_unknown_variable(var_mapper[v2].clone()), @@ -852,7 +868,7 @@ pub fn rule_based_optimization( @@ -1025,7 +1041,7 @@ mod tests { &mut new_var(), None, ); - assert_eq!(optimized_system.system().algebraic_constraints.len(), 0); + assert_eq!(optimized_system.0.system().algebraic_constraints.len(), 0); } #[test] @@ -1043,7 +1059,7 @@ mod tests { &mut new_var(), None, ); - expect!["(y) * (y - 1) - 3 = 0"].assert_eq(&optimized_system.to_string()); + expect!["(y) * (y - 1) - 3 = 0"].assert_eq(&optimized_system.0.to_string()); } #[test] @@ -1095,6 +1111,6 @@ mod tests { BusInteraction { bus_id: 3, multiplicity: 1, payload: rs1_data__0_1, 8 } BusInteraction { bus_id: 3, multiplicity: 1, payload: rs1_data__1_1, 8 } BusInteraction { bus_id: 3, multiplicity: 1, payload: -(503316480 * mem_ptr_limbs__0_1), 14 } - BusInteraction { bus_id: 3, multiplicity: 1, payload: -(503316480 * mem_ptr_limbs__0_2), 14 }"#]].assert_eq(&optimized_system.to_string()); + BusInteraction { bus_id: 3, multiplicity: 1, payload: -(503316480 * mem_ptr_limbs__0_2), 14 }"#]].assert_eq(&optimized_system.0.to_string()); } } diff --git a/constraint-solver/src/solver/base.rs b/constraint-solver/src/solver/base.rs index b5e2231d89..5593a4ec31 100644 --- a/constraint-solver/src/solver/base.rs +++ b/constraint-solver/src/solver/base.rs @@ -374,15 +374,15 @@ where /// Tries to find equivalent variables using quadratic constraints. fn try_solve_quadratic_equivalences(&mut self) -> bool { - // false - let equivalences = quadratic_equivalences::find_quadratic_equalities( - self.constraint_system.system().algebraic_constraints(), - &*self, - ); - for (x, y) in &equivalences { - self.apply_assignment(y, &GroupedExpression::from_unknown_variable(x.clone())); - } - !equivalences.is_empty() + false + // let equivalences = quadratic_equivalences::find_quadratic_equalities( + // self.constraint_system.system().algebraic_constraints(), + // &*self, + // ); + // for (x, y) in &equivalences { + // self.apply_assignment(y, &GroupedExpression::from_unknown_variable(x.clone())); + // } + // !equivalences.is_empty() } /// Find groups of variables with a small set of possible assignments. diff --git a/constraint-solver/src/solver/quadratic_equivalences.rs b/constraint-solver/src/solver/quadratic_equivalences.rs index 81942214ee..d8f032f2a3 100644 --- a/constraint-solver/src/solver/quadratic_equivalences.rs +++ b/constraint-solver/src/solver/quadratic_equivalences.rs @@ -22,11 +22,16 @@ pub fn find_quadratic_equalities= 2) .collect::>(); - candidates + let equiv = candidates .iter() .tuple_combinations() .flat_map(|(c1, c2)| process_quadratic_equality_candidate_pair(c1, c2, &range_constraints)) - .collect() + .collect_vec(); + for (e1, e2) in &equiv { + let (e1, e2) = if e1 < e2 { (e1, e2) } else { (e2, e1) }; + println!("{e1} == {e2} (EQS)"); + } + vec![] } /// If we have two constraints of the form diff --git a/openvm/src/lib.rs b/openvm/src/lib.rs index 2a984f3b6d..e9eea46505 100644 --- a/openvm/src/lib.rs +++ b/openvm/src/lib.rs @@ -1813,11 +1813,11 @@ mod tests { AirMetrics { widths: AirWidths { preprocessed: 0, - main: 14257, - log_up: 22752, + main: 16961, + log_up: 26908, }, - constraints: 4279, - bus_interactions: 11143, + constraints: 5009, + bus_interactions: 13221, } "#]], powdr_expected_machine_count: expect![[r#" @@ -1841,11 +1841,11 @@ mod tests { AirMetrics { widths: AirWidths { preprocessed: 0, - main: 14229, - log_up: 22720, + main: 16933, + log_up: 26876, }, - constraints: 4255, - bus_interactions: 11133, + constraints: 4985, + bus_interactions: 13211, } "#]], powdr_expected_machine_count: expect![[r#" @@ -1863,8 +1863,8 @@ mod tests { }, after: AirWidths { preprocessed: 0, - main: 14229, - log_up: 22720, + main: 16933, + log_up: 26876, }, } "#]]), @@ -2094,15 +2094,15 @@ mod tests { AirMetrics { widths: AirWidths { preprocessed: 0, - main: 3242, - log_up: 5268, + main: 3291, + log_up: 5204, }, - constraints: 592, - bus_interactions: 2564, + constraints: 381, + bus_interactions: 2571, } "#]], powdr_expected_machine_count: expect![[r#" - 22 + 8 "#]], non_powdr_expected_sum: NON_POWDR_EXPECTED_SUM, non_powdr_expected_machine_count: NON_POWDR_EXPECTED_MACHINE_COUNT, @@ -2111,13 +2111,13 @@ mod tests { AirWidthsDiff { before: AirWidths { preprocessed: 0, - main: 32376, - log_up: 41660, + main: 29106, + log_up: 37212, }, after: AirWidths { preprocessed: 0, - main: 3242, - log_up: 5268, + main: 3291, + log_up: 5204, }, } "#]]), diff --git a/openvm/tests/optimizer.rs b/openvm/tests/optimizer.rs index 3175ff78c4..a089d9391d 100644 --- a/openvm/tests/optimizer.rs +++ b/openvm/tests/optimizer.rs @@ -87,15 +87,15 @@ fn test_sha256() { // This cbor file above has the `is_valid` column removed, this is why the number below // might be one less than in other tests. expect![[r#" - 12394 + 13288 "#]] .assert_debug_eq(&machine.main_columns().count()); expect![[r#" - 9753 + 10483 "#]] .assert_debug_eq(&machine.bus_interactions.len()); expect![[r#" - 3746 + 4148 "#]] .assert_debug_eq(&machine.constraints.len()); } From 1a993235e16d7558e09e4fca82a6cd60c994b8ff Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 2 Dec 2025 11:51:34 +0000 Subject: [PATCH 58/60] Optimize rules. --- autoprecompiles/src/constraint_optimizer.rs | 12 ++++++ autoprecompiles/src/rule_based_optimizer.rs | 41 ++++++++++++--------- constraint-solver/src/grouped_expression.rs | 5 ++- 3 files changed, 39 insertions(+), 19 deletions(-) diff --git a/autoprecompiles/src/constraint_optimizer.rs b/autoprecompiles/src/constraint_optimizer.rs index bb0516baea..706b95f18c 100644 --- a/autoprecompiles/src/constraint_optimizer.rs +++ b/autoprecompiles/src/constraint_optimizer.rs @@ -63,6 +63,18 @@ pub fn optimize_constraints< // Index the constraint system for the first time let constraint_system = IndexedConstraintSystem::from(constraint_system); + stats_logger.log("indexing", &constraint_system); + let (constraint_system, derived_constraints) = rule_based_optimization( + constraint_system, + &*solver, + bus_interaction_handler.clone(), + new_var, + // No degree bound given, i.e. only perform replacements that + // do not increase the degree. + None, + ); + stats_logger.log("rule-based optimization", &constraint_system); + let constraint_system = solver_based_optimization(constraint_system, solver)?; stats_logger.log("solver-based optimization", &constraint_system); diff --git a/autoprecompiles/src/rule_based_optimizer.rs b/autoprecompiles/src/rule_based_optimizer.rs index 128464ef41..a6f6a1d637 100644 --- a/autoprecompiles/src/rule_based_optimizer.rs +++ b/autoprecompiles/src/rule_based_optimizer.rs @@ -30,7 +30,7 @@ use num_traits::Zero; use crepe::crepe; -const SIZE_LIMIT: usize = 1600; +const SIZE_LIMIT: usize = 100600; #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, From, Into)] struct Var(usize); @@ -231,7 +231,7 @@ impl Environment { pub fn affine_var_count(&self, expr: Expr) -> Option { let db = self.expressions.borrow(); let expr = &db[expr]; - expr.is_affine().then(|| expr.linear_components().count()) + expr.is_affine().then(|| expr.linear_components().len()) } pub fn try_to_affine(&self, expr: Expr) -> Option<(T, Var, T)> { @@ -296,14 +296,12 @@ impl Environment { let b = &db[b_id]; if !a.is_affine() || !b.is_affine() - || a.referenced_unknown_variables().count() != b.referenced_unknown_variables().count() - || a.referenced_unknown_variables().count() < 2 + || a.constant_offset() != b.constant_offset() + || a.linear_components().len() != b.linear_components().len() + || a.linear_components().len() < 2 { return None; } - if a.constant_offset() != b.constant_offset() { - return None; - } let mut joined = a .linear_components() // Join the sorted iterators into another sorted list, @@ -489,9 +487,10 @@ crepe! { struct QuadraticEquivalence(Var, Var); QuadraticEquivalence(v1, v2) <- + Env(env), QuadraticEquivalenceCandidate(_, expr1, offset), QuadraticEquivalenceCandidate(_, expr2, offset), - Env(env), + (expr1 < expr2), let Some((v1, v2, coeff)) = env.differ_in_exactly_one_variable(expr1, expr2), RangeConstraintOnVar(v1, rc), RangeConstraintOnVar(v2, rc), @@ -514,14 +513,17 @@ crepe! { // // For the general case, where e.g. `X` can be negative, we replace it by `X * X`, // if that value is still small enough. + struct SOV(Var); + SOV(v) <- + Env(env), + for v in env.single_occurrence_variables().cloned(); struct SingleOccurrenceVariable(Expr, Var); SingleOccurrenceVariable(e, v) <- Env(env), - for v in env.single_occurrence_variables().cloned(), - AlgebraicConstraint(e), + SOV(v), // We somehow cannot use "v" directly here. - ContainsVariable(e, v2), - (v == v2); + ContainsVariable(e, v), + AlgebraicConstraint(e); struct LargestSingleOccurrenceVariablePairInExpr(Expr, Var, Var); LargestSingleOccurrenceVariablePairInExpr(e, v1, v2) <- @@ -615,15 +617,18 @@ crepe! { Some(env.insert_owned(replacement)) })(); + struct ProductConstraint(Expr, Expr, Expr); + ProductConstraint(e, l, r) <- + AlgebraicConstraint(e), + Product(e, l, r); + // If we have x * a = 0 and x * b = 0 and (a = 0 and b = 0) is equivalent to (a + b = 0), // replace those two by x * (a + b) = 0. struct PotentiallyReplacePairOfAlgebraicConstraintsBy(Expr, Expr, Expr); PotentiallyReplacePairOfAlgebraicConstraintsBy(e1, e2, replacement) <- Env(env), - AlgebraicConstraint(e1), - AlgebraicConstraint(e2), - Product(e1, x, a), - Product(e2, x, b), + ProductConstraint(e1, x, a), + ProductConstraint(e2, x, b), (e1 < e2), RangeConstraintOnExpression(a, rc_a), RangeConstraintOnExpression(b, rc_b), @@ -642,8 +647,8 @@ crepe! { // Boolean range constraint RangeConstraintOnVar(v, RangeConstraint::from_range(x1, x1 + T::from(1))) <- - AlgebraicConstraint(e), - Product(e, l, r), + ProductConstraint(_, l, r), + (l < r), Solvable(l, v, x1), Solvable(r, v, x1 + T::from(1)); diff --git a/constraint-solver/src/grouped_expression.rs b/constraint-solver/src/grouped_expression.rs index 864064e1df..474e87aa6a 100644 --- a/constraint-solver/src/grouped_expression.rs +++ b/constraint-solver/src/grouped_expression.rs @@ -237,7 +237,10 @@ impl GroupedExpression { /// Otherwise, the variables returned here might also appear inside the higher order terms /// and this the dependency on these variables might be more complicated than just a /// runtime constant factor. - pub fn linear_components(&self) -> impl DoubleEndedIterator + Clone { + pub fn linear_components( + &self, + ) -> impl DoubleEndedIterator + ExactSizeIterator + Clone + { self.linear.iter() } From eda63eb929076b722345808f8f6d84e7b928b01b Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 2 Dec 2025 13:01:31 +0000 Subject: [PATCH 59/60] Optimize rules. --- autoprecompiles/Cargo.toml | 2 +- autoprecompiles/src/optimizer.rs | 18 ++++++++++++++++-- autoprecompiles/src/rule_based_optimizer.rs | 16 +++++++++++----- 3 files changed, 28 insertions(+), 8 deletions(-) diff --git a/autoprecompiles/Cargo.toml b/autoprecompiles/Cargo.toml index 3439624d2c..b1539ddc38 100644 --- a/autoprecompiles/Cargo.toml +++ b/autoprecompiles/Cargo.toml @@ -23,7 +23,7 @@ rayon = "1.10.0" strum = { version = "0.27.0", features = ["derive"] } metrics = "0.23.0" -crepe = { git = "https://github.com/chriseth/crepe", branch = "generics", version = "0.1.8" } +crepe = { git = "https://github.com/chriseth/crepe", branch = "profiling", version = "0.1.8" } derive_more = "0.99.17" [dev-dependencies] diff --git a/autoprecompiles/src/optimizer.rs b/autoprecompiles/src/optimizer.rs index 554006233b..7358d60a43 100644 --- a/autoprecompiles/src/optimizer.rs +++ b/autoprecompiles/src/optimizer.rs @@ -4,8 +4,9 @@ use std::{collections::BTreeMap, fmt::Display}; use itertools::Itertools; use powdr_constraint_solver::constraint_system::{ - AlgebraicConstraint, ComputationMethod, DerivedVariable, + self, AlgebraicConstraint, ComputationMethod, DerivedVariable, }; +use powdr_constraint_solver::grouped_expression::NoRangeConstraints; use powdr_constraint_solver::inliner::{self, inline_everything_below_degree_bound}; use powdr_constraint_solver::solver::new_solver; use powdr_constraint_solver::{ @@ -54,7 +55,20 @@ pub fn optimize( } }; - let mut constraint_system = symbolic_machine_to_constraint_system(machine); + let constraint_system = symbolic_machine_to_constraint_system(machine); + stats_logger.log("system construction", &constraint_system); + + let (constraint_system, derived_constraints) = rule_based_optimization( + constraint_system.into(), + NoRangeConstraints, + bus_interaction_handler.clone(), + &mut new_var, + // No degree bound given, i.e. only perform replacements that + // do not increase the degree. + None, + ); + stats_logger.log("rule-based optimization", &constraint_system); + let mut constraint_system = constraint_system.system().clone(); let mut solver = new_solver(constraint_system.clone(), bus_interaction_handler.clone()); loop { diff --git a/autoprecompiles/src/rule_based_optimizer.rs b/autoprecompiles/src/rule_based_optimizer.rs index a6f6a1d637..1ff3ec5428 100644 --- a/autoprecompiles/src/rule_based_optimizer.rs +++ b/autoprecompiles/src/rule_based_optimizer.rs @@ -485,16 +485,21 @@ crepe! { ({env.affine_var_count(l).unwrap_or(0) > 1 && env.affine_var_count(r).unwrap_or(0) > 1}), let Some(offset) = env.constant_difference(l, r); - struct QuadraticEquivalence(Var, Var); - QuadraticEquivalence(v1, v2) <- + struct QuadraticEquivalenceCandidatePair(Expr, Expr, T, Var, Var); + QuadraticEquivalenceCandidatePair(expr1, expr2, offset / coeff, v1, v2) <- Env(env), QuadraticEquivalenceCandidate(_, expr1, offset), QuadraticEquivalenceCandidate(_, expr2, offset), (expr1 < expr2), - let Some((v1, v2, coeff)) = env.differ_in_exactly_one_variable(expr1, expr2), + let Some((v1, v2, coeff)) = env.differ_in_exactly_one_variable(expr1, expr2); + // what exactly is re-executed for an update? + + struct QuadraticEquivalence(Var, Var); + QuadraticEquivalence(v1, v2) <- + QuadraticEquivalenceCandidatePair(_, _, offset, v1, v2), RangeConstraintOnVar(v1, rc), RangeConstraintOnVar(v2, rc), - (rc.is_disjoint(&rc.combine_sum(&RangeConstraint::from_value(offset / coeff)))); + (rc.is_disjoint(&rc.combine_sum(&RangeConstraint::from_value(offset)))); struct ReplaceAlgebraicConstraintBy(Expr, Expr); @@ -749,7 +754,8 @@ pub fn rule_based_optimization Date: Tue, 2 Dec 2025 16:58:55 +0000 Subject: [PATCH 60/60] Range constraints from bus interactions. --- autoprecompiles/src/constraint_optimizer.rs | 1 + autoprecompiles/src/rule_based_optimizer.rs | 62 +++++++++++++++++++-- 2 files changed, 57 insertions(+), 6 deletions(-) diff --git a/autoprecompiles/src/constraint_optimizer.rs b/autoprecompiles/src/constraint_optimizer.rs index 706b95f18c..a1217aa088 100644 --- a/autoprecompiles/src/constraint_optimizer.rs +++ b/autoprecompiles/src/constraint_optimizer.rs @@ -74,6 +74,7 @@ pub fn optimize_constraints< None, ); stats_logger.log("rule-based optimization", &constraint_system); + println!("XXXX algebraic constraints:\n{constraint_system}",); let constraint_system = solver_based_optimization(constraint_system, solver)?; stats_logger.log("solver-based optimization", &constraint_system); diff --git a/autoprecompiles/src/rule_based_optimizer.rs b/autoprecompiles/src/rule_based_optimizer.rs index 1ff3ec5428..63c9e4d24f 100644 --- a/autoprecompiles/src/rule_based_optimizer.rs +++ b/autoprecompiles/src/rule_based_optimizer.rs @@ -425,11 +425,14 @@ crepe! { @input struct InitialAlgebraicConstraint(Expr); + @input + struct InitialRangeConstraintOnExpression(Expr, RangeConstraint); + struct AlgebraicConstraint(Expr); AlgebraicConstraint(e) <- InitialAlgebraicConstraint(e); // @input - // struct BusInteractionConstraint<'a>(&'a BusInteraction>); + // struct BusInteractionConstraint<'a>(&'a BusInteraction); struct RangeConstraintOnExpression(Expr, RangeConstraint); @@ -437,12 +440,14 @@ crepe! { Expression(e) <- AlgebraicConstraint(e); Expression(e) <- RangeConstraintOnExpression(e, _); + RangeConstraintOnExpression(e, rc) <- + InitialRangeConstraintOnExpression(e, rc); + RangeConstraintOnExpression(e, rc) <- Env(env), Expression(e), let rc = env.range_constraint_on_expr(e); - struct ContainsVariable(Expr, Var); ContainsVariable(e, v) <- Expression(e), @@ -665,7 +670,24 @@ crepe! { struct Equivalence(Var, Var); Equivalence(v1, v2) <- QuadraticEquivalence(v1, v2); - // Do not do this because it is rather expensive. + Equivalence(v1, v2) <- + Env(env), + AlgebraicConstraint(e), + let Some((v1, v2)) = env.on_expr(e, (), |e, _| { + if !e.is_affine() || e.constant_offset() != &T::zero() { + return None; + } + let ((v1, c1), (v2, c2)) = e.linear_components().collect_tuple()?; + let (v1, c1, v2, c2) = if v1 < v2 { + (v1, c1, v2, c2) + } else { + (v2, c2, v1, c1) + }; + // We have `c1 * v1 + c2 * v2 = 0`, which is equivalent to + // `v1 = -c2 / c1 * v2` + (-*c2 / *c1).is_one().then_some((*v1, *v2)) + }); + ReplaceAlgebraicConstraintBy(e, env.substitute_by_known(e, v, val)) <- Env(env), @@ -699,7 +721,7 @@ crepe! { pub fn rule_based_optimization( mut system: IndexedConstraintSystem, range_constraints: impl RangeConstraintProvider, - _bus_interaction_handler: impl BusInteractionHandler + Clone, + bus_interaction_handler: impl BusInteractionHandler + Clone, new_var_outer: &mut impl FnMut(&str) -> V, degree_bound: Option, ) -> ( @@ -724,8 +746,35 @@ pub fn rule_based_optimization, Expr>::default()); loop { - let (algebraic_constraints, _bus_interactions) = + let (algebraic_constraints, bus_interactions) = transform_constraint_system(&system, &var_mapper, expr_db.as_mut().unwrap()); + // TODO it would be better to handle that inside the rule system, + // but it is difficult because of the vector and the combinatorial + // explosion of the range constraints. + let rcs = system + .bus_interactions() + .iter() + .zip(bus_interactions) + .flat_map(|(bus_inter, bus_inter_transformed)| { + let bi_rc = bus_inter + .fields() + .map(|e| e.range_constraint(&range_constraints)) + .collect(); + let updated_rcs = bus_interaction_handler + .handle_bus_interaction(bi_rc) + .fields() + .cloned() + .collect_vec(); + bus_inter_transformed + .fields() + .cloned() + .zip(updated_rcs) + .collect_vec() + }) + .map(|(e, rc)| InitialRangeConstraintOnExpression(e, rc)) + .collect_vec(); + let mut rt = Crepe::new(); + rt.extend(rcs.into_iter()); let env = Environment::::new( expr_db.take().unwrap(), @@ -745,7 +794,6 @@ pub fn rule_based_optimization