Booleanize changed constraints. (#3208)

The boolean extractor currently handles newly incoming algebraic
constraints. Substitutions that are performed during solving can lead to
constraints being boolean-extractable where we were not able to do
boolean extraction before. This PR tries to perform boolean extraction
on all constraints that potentially changed because of a substitution.

---------

Co-authored-by: schaeff <thibaut@powdrlabs.com>
This commit is contained in:
chriseth
2025-08-21 17:01:45 +02:00
committed by GitHub
parent 3422658efd
commit 1ecfbf5484
2 changed files with 32 additions and 2 deletions

View File

@@ -317,6 +317,9 @@ impl<T: RuntimeConstant + Substitutable<V>, V: Clone + Hash + Ord + Eq>
///
/// Note this does NOT work properly if the variable is used inside a
/// known SymbolicExpression.
///
/// It does not delete the occurrence of `variable` so that it can be used to check
/// which constraints it used to occur in.
pub fn substitute_by_unknown(&mut self, variable: &V, substitution: &GroupedExpression<T, V>) {
let items = self
.variable_occurrences
@@ -464,6 +467,9 @@ where
/// Substitutes a variable with a known value in the whole system.
/// This function also updates the queue accordingly.
///
/// It does not delete the occurrence of `variable` so that it can be used to check
/// which constraints it used to occur in.
pub fn substitute_by_unknown(&mut self, variable: &V, substitution: &GroupedExpression<T, V>) {
self.constraint_system
.substitute_by_unknown(variable, substitution);

View File

@@ -17,6 +17,7 @@ use crate::utils::possible_concrete_values;
use std::collections::{BTreeSet, HashMap, HashSet};
use std::fmt::Display;
use std::hash::Hash;
use std::iter::once;
/// Given a list of constraints, tries to derive as many variable assignments as possible.
///
@@ -333,6 +334,7 @@ where
+ Hash
+ ExpressionConvertible<T::FieldType, V>
+ Substitutable<V>,
VD: VarDispenser<V>,
{
fn loop_until_no_progress(&mut self) -> Result<(), Error> {
loop {
@@ -524,10 +526,32 @@ where
fn apply_assignment(&mut self, variable: &V, expr: &GroupedExpression<T, V>) -> bool {
log::debug!("({variable} := {expr})");
self.constraint_system.substitute_by_unknown(variable, expr);
let mut vars_to_boolean_constrain = vec![];
let new_constraints = self
.constraint_system
.system()
.constraints_referencing_variables(once(variable.clone()))
.filter_map(|constr| match constr {
ConstraintRef::AlgebraicConstraint(c) => Some(c),
ConstraintRef::BusInteraction(_) => None,
})
.flat_map(|constr| {
let (constr, new_var) = self
.boolean_extractor
.try_extract_boolean(constr, &mut || self.var_dispenser.next_boolean())?;
vars_to_boolean_constrain.extend(new_var);
Some(constr)
})
.collect_vec();
for v in vars_to_boolean_constrain {
self.add_range_constraint(&v, RangeConstraint::from_mask(1));
}
self.add_algebraic_constraints(new_constraints);
self.assignments_to_return
.push((variable.clone(), expr.clone()));
// TODO we could check if the variable already has an assignment,
// but usually it should not be in the system once it has been assigned.
true
}
}