mod abs;
mod add;
mod alldiff;
mod allequal;
pub mod between;
mod bool_logic;
pub mod cardinality;
pub mod conditional;
mod count;
mod div;
mod element;
mod eq;
mod leq;
mod linear;
mod max;
mod min;
mod modulo;
mod mul;
mod neq;
mod noop;
mod reification;
mod sum;
mod table;
use std::ops::{Index, IndexMut};
use std::rc::Rc;
use crate::{
variables::VarId,
variables::views::{Context, View, ViewExt},
};
type PropagatorBox = Box<dyn Prune>;
type SharedPropagator = Rc<PropagatorBox>;
pub trait Prune: core::fmt::Debug + std::any::Any {
fn prune(&self, ctx: &mut Context) -> Option<()>;
}
pub trait Propagate: Prune + 'static {
fn list_trigger_vars(&self) -> impl Iterator<Item = VarId>;
}
#[doc(hidden)]
#[derive(Clone, Debug, Default)]
pub struct Propagators {
state: Vec<SharedPropagator>,
dependencies: Vec<Vec<PropId>>,
propagation_count: usize,
node_count: usize,
constraint_registry: crate::optimization::constraint_metadata::ConstraintRegistry,
}
#[doc(hidden)]
impl Propagators {
pub fn on_new_var(&mut self) {
self.dependencies.push(Vec::new());
}
pub fn get_prop_ids_iter(&self) -> impl Iterator<Item = PropId> {
(0..self.state.len()).map(PropId)
}
pub fn get_state(&self, p: PropId) -> &SharedPropagator {
&self.state[p.0]
}
pub fn on_bound_change(&self, v: VarId) -> impl Iterator<Item = PropId> + '_ {
self.dependencies[v].iter().copied()
}
pub fn get_propagation_count(&self) -> usize {
self.propagation_count
}
pub fn increment_propagation_count(&mut self) {
self.propagation_count += 1;
}
pub fn get_node_count(&self) -> usize {
self.node_count
}
pub fn increment_node_count(&mut self) {
self.node_count += 1;
}
pub fn count(&self) -> usize {
self.state.len()
}
pub fn get_constraint_registry(
&self,
) -> &crate::optimization::constraint_metadata::ConstraintRegistry {
&self.constraint_registry
}
pub fn get_constraint_registry_mut(
&mut self,
) -> &mut crate::optimization::constraint_metadata::ConstraintRegistry {
&mut self.constraint_registry
}
pub fn extract_linear_system(&self) -> crate::lpsolver::LinearConstraintSystem {
use crate::lpsolver::{LinearConstraint, LinearConstraintSystem};
use crate::variables::VarId;
use std::collections::HashMap;
let mut system = LinearConstraintSystem::new();
let mut derived_vars: HashMap<VarId, (Vec<(VarId, f64)>, f64)> = HashMap::new();
for prop_rc in &self.state {
let prop_box: &Box<dyn Prune> = prop_rc.as_ref();
let prop_ref: &dyn Prune = prop_box.as_ref();
if let Some(eq) = (prop_ref as &dyn std::any::Any).downcast_ref::<linear::FloatLinEq>()
{
let constraint = LinearConstraint::equality(
eq.coefficients().to_vec(),
eq.variables().to_vec(),
eq.constant(),
);
system.add_constraint(constraint);
} else if let Some(le) =
(prop_ref as &dyn std::any::Any).downcast_ref::<linear::FloatLinLe>()
{
let constraint = LinearConstraint::less_or_equal(
le.coefficients().to_vec(),
le.variables().to_vec(),
le.constant(),
);
system.add_constraint(constraint);
} else if let Some(add) =
(prop_ref as &dyn std::any::Any).downcast_ref::<add::Add<VarId, VarId>>()
{
let x = *add.x();
let y = *add.y();
let s = add.s();
derived_vars.insert(s, (vec![(x, 1.0), (y, 1.0)], 0.0));
let constraint =
LinearConstraint::equality(vec![1.0, 1.0, -1.0], vec![x, y, s], 0.0);
system.add_constraint(constraint);
}
}
for prop_rc in &self.state {
let prop_box: &Box<dyn Prune> = prop_rc.as_ref();
let prop_ref: &dyn Prune = prop_box.as_ref();
if let Some(leq) = (prop_ref as &dyn std::any::Any)
.downcast_ref::<leq::LessThanOrEquals<VarId, VarId>>()
{
let x_var = *leq.x();
let y_var = *leq.y();
let constraint =
LinearConstraint::less_or_equal(vec![1.0, -1.0], vec![x_var, y_var], 0.0);
system.add_constraint(constraint);
}
}
system
}
pub fn optimize_alldiff_order(&mut self, vars: &crate::variables::Vars) {
use crate::optimization::constraint_metadata::ConstraintType;
let alldiff_constraint_ids = self
.constraint_registry
.get_constraints_by_type(&ConstraintType::AllDifferent);
if alldiff_constraint_ids.len() <= 1 {
return; }
let variable_connectivity = self.calculate_variable_connectivity_map();
let mut alldiff_priorities: Vec<(usize, (f64, f64, f64))> = Vec::new();
for constraint_id in alldiff_constraint_ids {
if let Some(metadata) = self.constraint_registry.get_constraint(constraint_id) {
let constraint_vars = match &metadata.data {
crate::optimization::constraint_metadata::ConstraintData::NAry { operands } => {
operands
.iter()
.filter_map(|op| match op {
crate::optimization::constraint_metadata::ViewInfo::Variable {
var_id,
} => Some(*var_id),
_ => None,
})
.collect::<Vec<_>>()
}
_ => continue, };
let tightness_score = self.calculate_domain_tightness(&constraint_vars, vars);
let connectivity_score =
self.calculate_variable_connectivity(&constraint_vars, &variable_connectivity);
let saturation_score = self.calculate_constraint_saturation(&constraint_vars, vars);
let prop_index = constraint_id.0;
if prop_index < self.state.len() {
alldiff_priorities.push((
prop_index,
(tightness_score, connectivity_score, saturation_score),
));
}
}
}
if alldiff_priorities.is_empty() {
return; }
alldiff_priorities.sort_by(|a, b| {
let tightness_cmp =
a.1.0
.partial_cmp(&b.1.0)
.unwrap_or(std::cmp::Ordering::Equal);
if tightness_cmp != std::cmp::Ordering::Equal {
return tightness_cmp;
}
let saturation_cmp =
b.1.2
.partial_cmp(&a.1.2)
.unwrap_or(std::cmp::Ordering::Equal);
if saturation_cmp != std::cmp::Ordering::Equal {
return saturation_cmp;
}
b.1.1
.partial_cmp(&a.1.1)
.unwrap_or(std::cmp::Ordering::Equal)
});
let mut needs_reordering = false;
for (i, &(prop_idx, _)) in alldiff_priorities.iter().enumerate() {
if i < alldiff_priorities.len() - 1 {
let next_prop_idx = alldiff_priorities[i + 1].0;
if prop_idx > next_prop_idx {
needs_reordering = true;
break;
}
}
}
if !needs_reordering {
return; }
let original_state = self.state.clone();
let original_dependencies = self.dependencies.clone();
let alldiff_indices: std::collections::HashSet<usize> =
alldiff_priorities.iter().map(|&(idx, _)| idx).collect();
self.state.clear();
let mut index_mapping = vec![0; original_state.len()];
let mut new_idx = 0;
for &(old_idx, _) in &alldiff_priorities {
if old_idx < original_state.len() {
self.state.push(original_state[old_idx].clone());
index_mapping[old_idx] = new_idx;
new_idx += 1;
}
}
for (old_idx, constraint) in original_state.iter().enumerate() {
if !alldiff_indices.contains(&old_idx) {
self.state.push(constraint.clone());
index_mapping[old_idx] = new_idx;
new_idx += 1;
}
}
self.dependencies = vec![Vec::new(); original_dependencies.len()];
for (var_id, deps) in original_dependencies.into_iter().enumerate() {
for old_prop_id in deps {
if old_prop_id.0 < index_mapping.len() {
let new_prop_id = PropId(index_mapping[old_prop_id.0]);
self.dependencies[var_id].push(new_prop_id);
}
}
}
}
fn calculate_domain_tightness(
&self,
constraint_vars: &[VarId],
vars: &crate::variables::Vars,
) -> f64 {
if constraint_vars.is_empty() {
return f64::INFINITY; }
let total_domain_size: f64 = constraint_vars
.iter()
.map(|&var_id| self.calculate_variable_domain_size(var_id, vars))
.sum();
total_domain_size / (constraint_vars.len() as f64)
}
fn calculate_variable_domain_size(&self, var_id: VarId, vars: &crate::variables::Vars) -> f64 {
match &vars[var_id] {
crate::variables::Var::VarI(sparse_set) => sparse_set.size() as f64,
crate::variables::Var::VarF(interval) => {
let range = interval.max - interval.min;
if range <= 0.0 {
return 1.0; }
let estimated_steps = (range / interval.step).ceil();
estimated_steps.max(1.0) }
}
}
fn calculate_variable_connectivity_map(&self) -> std::collections::HashMap<VarId, usize> {
let mut connectivity_map = std::collections::HashMap::new();
use crate::optimization::constraint_metadata::ConstraintType;
let constraint_types = [
ConstraintType::AllDifferent,
ConstraintType::Addition,
ConstraintType::Multiplication,
ConstraintType::Modulo,
ConstraintType::Division,
ConstraintType::AbsoluteValue,
ConstraintType::Minimum,
ConstraintType::Maximum,
ConstraintType::Sum,
ConstraintType::Equals,
ConstraintType::NotEquals,
ConstraintType::LessThanOrEquals,
ConstraintType::LessThan,
ConstraintType::GreaterThanOrEquals,
ConstraintType::GreaterThan,
ConstraintType::BooleanAnd,
ConstraintType::BooleanOr,
ConstraintType::BooleanNot,
];
for constraint_type in &constraint_types {
for constraint_id in self
.constraint_registry
.get_constraints_by_type(constraint_type)
{
if let Some(metadata) = self.constraint_registry.get_constraint(constraint_id) {
for var_id in &metadata.variables {
*connectivity_map.entry(*var_id).or_insert(0) += 1;
}
}
}
}
connectivity_map
}
fn calculate_variable_connectivity(
&self,
constraint_vars: &[VarId],
connectivity_map: &std::collections::HashMap<VarId, usize>,
) -> f64 {
if constraint_vars.is_empty() {
return 0.0; }
let total_connectivity: usize = constraint_vars
.iter()
.map(|var_id| connectivity_map.get(var_id).copied().unwrap_or(0))
.sum();
total_connectivity as f64 / constraint_vars.len() as f64
}
fn calculate_constraint_saturation(
&self,
constraint_vars: &[VarId],
vars: &crate::variables::Vars,
) -> f64 {
if constraint_vars.is_empty() {
return 0.0; }
let fixed_variables = constraint_vars
.iter()
.filter(|&&var_id| {
match &vars[var_id] {
crate::variables::Var::VarI(sparse_set) => sparse_set.size() == 1,
crate::variables::Var::VarF(interval) => {
let range = interval.max - interval.min;
range <= interval.step
}
}
})
.count();
fixed_variables as f64 / constraint_vars.len() as f64
}
pub fn add(&mut self, x: impl View, y: impl View, s: VarId) -> PropId {
use crate::optimization::constraint_metadata::{ConstraintData, ConstraintType, ViewInfo};
let x_info = self.analyze_view(&x);
let y_info = self.analyze_view(&y);
let s_info = ViewInfo::Variable { var_id: s };
let variables: Vec<_> = x
.get_underlying_var()
.into_iter()
.chain(y.get_underlying_var())
.chain(std::iter::once(s))
.collect();
let metadata = ConstraintData::NAry {
operands: vec![x_info, y_info, s_info],
};
self.push_new_prop_with_metadata(
self::add::Add::new(x, y, s),
ConstraintType::Addition,
variables,
metadata,
)
}
pub fn sub(&mut self, x: impl View, y: impl View, s: VarId) -> PropId {
use crate::variables::Val;
self.add(x, y.times_neg(Val::ValI(-1)), s)
}
pub fn mul(&mut self, x: impl View, y: impl View, s: VarId) -> PropId {
use crate::optimization::constraint_metadata::{
ConstraintData, ConstraintType, ConstraintValue, ViewInfo,
};
fn analyze_view_for_constants<T: View>(view: &T) -> ViewInfo {
if let Some(var_id) = view.get_underlying_var() {
ViewInfo::Variable { var_id }
} else {
let min_val = view.min_raw(&crate::variables::Vars::default());
let max_val = view.max_raw(&crate::variables::Vars::default());
if min_val == max_val {
let const_val = match min_val {
crate::variables::Val::ValI(i) => ConstraintValue::Integer(i),
crate::variables::Val::ValF(f) => ConstraintValue::Float(f),
};
ViewInfo::Constant { value: const_val }
} else {
ViewInfo::Complex
}
}
}
let x_info = analyze_view_for_constants(&x);
let y_info = analyze_view_for_constants(&y);
let s_info = ViewInfo::Variable { var_id: s };
let variables: Vec<_> = x
.get_underlying_var()
.into_iter()
.chain(y.get_underlying_var())
.chain(std::iter::once(s))
.collect();
let metadata = ConstraintData::NAry {
operands: vec![x_info, y_info, s_info],
};
self.push_new_prop_with_metadata(
self::mul::Mul::new(x, y, s),
ConstraintType::Multiplication,
variables,
metadata,
)
}
pub fn modulo(&mut self, x: impl View, y: impl View, s: VarId) -> PropId {
use crate::optimization::constraint_metadata::{ConstraintData, ConstraintType, ViewInfo};
let x_info = self.analyze_view(&x);
let y_info = self.analyze_view(&y);
let s_info = ViewInfo::Variable { var_id: s };
let variables: Vec<_> = x
.get_underlying_var()
.into_iter()
.chain(y.get_underlying_var())
.chain(std::iter::once(s))
.collect();
let metadata = ConstraintData::NAry {
operands: vec![x_info, y_info, s_info],
};
self.push_new_prop_with_metadata(
self::modulo::Modulo::new(x, y, s),
ConstraintType::Modulo,
variables,
metadata,
)
}
pub fn abs(&mut self, x: impl View, s: VarId) -> PropId {
use crate::optimization::constraint_metadata::{ConstraintData, ConstraintType, ViewInfo};
let x_info = self.analyze_view(&x);
let s_info = ViewInfo::Variable { var_id: s };
let variables: Vec<_> = x
.get_underlying_var()
.into_iter()
.chain(std::iter::once(s))
.collect();
let metadata = ConstraintData::NAry {
operands: vec![x_info, s_info],
};
self.push_new_prop_with_metadata(
self::abs::Abs::new(x, s),
ConstraintType::AbsoluteValue,
variables,
metadata,
)
}
pub fn min(&mut self, vars: Vec<VarId>, result: VarId) -> PropId {
use crate::optimization::constraint_metadata::{ConstraintData, ConstraintType, ViewInfo};
let operands: Vec<ViewInfo> = vars
.iter()
.map(|&var| ViewInfo::Variable { var_id: var })
.chain(std::iter::once(ViewInfo::Variable { var_id: result }))
.collect();
let variables: Vec<_> = vars
.iter()
.cloned()
.chain(std::iter::once(result))
.collect();
let metadata = ConstraintData::NAry { operands };
self.push_new_prop_with_metadata(
self::min::Min::new(vars, result),
ConstraintType::Minimum,
variables,
metadata,
)
}
pub fn max(&mut self, vars: Vec<VarId>, result: VarId) -> PropId {
use crate::optimization::constraint_metadata::{ConstraintData, ConstraintType, ViewInfo};
let operands: Vec<ViewInfo> = vars
.iter()
.map(|&var| ViewInfo::Variable { var_id: var })
.chain(std::iter::once(ViewInfo::Variable { var_id: result }))
.collect();
let variables: Vec<_> = vars
.iter()
.cloned()
.chain(std::iter::once(result))
.collect();
let metadata = ConstraintData::NAry { operands };
self.push_new_prop_with_metadata(
self::max::Max::new(vars, result),
ConstraintType::Maximum,
variables,
metadata,
)
}
pub fn div(&mut self, x: impl View, y: impl View, s: VarId) -> PropId {
use crate::optimization::constraint_metadata::{ConstraintData, ConstraintType, ViewInfo};
let x_info = self.analyze_view(&x);
let y_info = self.analyze_view(&y);
let s_info = ViewInfo::Variable { var_id: s };
let variables: Vec<_> = x
.get_underlying_var()
.into_iter()
.chain(y.get_underlying_var())
.chain(std::iter::once(s))
.collect();
let metadata = ConstraintData::NAry {
operands: vec![x_info, y_info, s_info],
};
self.push_new_prop_with_metadata(
self::div::Div::new(x, y, s),
ConstraintType::Division,
variables,
metadata,
)
}
pub fn sum(&mut self, xs: Vec<impl View>, s: VarId) -> PropId {
use crate::optimization::constraint_metadata::{ConstraintData, ConstraintType, ViewInfo};
let mut operands = Vec::new();
let mut variables = Vec::new();
for x in &xs {
operands.push(self.analyze_view(x));
if let Some(var_id) = x.get_underlying_var() {
variables.push(var_id);
}
}
operands.push(ViewInfo::Variable { var_id: s });
variables.push(s);
let metadata = ConstraintData::NAry { operands };
self.push_new_prop_with_metadata(
self::sum::Sum::new(xs, s),
ConstraintType::Sum,
variables,
metadata,
)
}
pub fn equals(&mut self, x: impl View, y: impl View) -> PropId {
use crate::optimization::constraint_metadata::{ConstraintData, ConstraintType};
let x_info = self.analyze_view(&x);
let y_info = self.analyze_view(&y);
let variables: Vec<_> = x
.get_underlying_var()
.into_iter()
.chain(y.get_underlying_var())
.collect();
let metadata = ConstraintData::Binary {
left: x_info,
right: y_info,
};
self.push_new_prop_with_metadata(
self::eq::Eq::new(x, y),
ConstraintType::Equals,
variables,
metadata,
)
}
pub fn not_equals(&mut self, x: impl View, y: impl View) -> PropId {
use crate::optimization::constraint_metadata::{ConstraintData, ConstraintType};
let x_info = self.analyze_view(&x);
let y_info = self.analyze_view(&y);
let variables: Vec<_> = x
.get_underlying_var()
.into_iter()
.chain(y.get_underlying_var())
.collect();
let metadata = ConstraintData::Binary {
left: x_info,
right: y_info,
};
self.push_new_prop_with_metadata(
self::neq::NotEquals::new(x, y),
ConstraintType::NotEquals,
variables,
metadata,
)
}
pub fn less_than_or_equals(&mut self, x: impl View, y: impl View) -> PropId {
self.less_than_or_equals_with_metadata(x, y)
}
pub fn less_than(&mut self, x: impl View, y: impl View) -> PropId {
self.less_than_with_metadata(x, y)
}
pub fn greater_than_or_equals(&mut self, x: impl View, y: impl View) -> PropId {
use crate::optimization::constraint_metadata::{ConstraintData, ConstraintType};
let x_info = self.analyze_view(&x);
let y_info = self.analyze_view(&y);
let variables: Vec<_> = x
.get_underlying_var()
.into_iter()
.chain(y.get_underlying_var())
.collect();
let metadata = ConstraintData::Binary {
left: x_info,
right: y_info,
};
self.push_new_prop_with_metadata(
self::leq::LessThanOrEquals::new(y, x), ConstraintType::GreaterThanOrEquals,
variables,
metadata,
)
}
pub fn greater_than(&mut self, x: impl View, y: impl View) -> PropId {
use crate::optimization::constraint_metadata::{ConstraintData, ConstraintType};
let x_info = self.analyze_view(&x);
let y_info = self.analyze_view(&y);
let metadata = ConstraintData::Binary {
left: x_info,
right: y_info,
};
let variables: Vec<_> = x
.get_underlying_var()
.into_iter()
.chain(y.get_underlying_var())
.collect();
self.push_new_prop_with_metadata(
self::leq::LessThanOrEquals::new(y.next(), x), ConstraintType::GreaterThan,
variables,
metadata,
)
}
pub fn all_different(&mut self, vars: Vec<VarId>) -> PropId {
use crate::optimization::constraint_metadata::{ConstraintData, ConstraintType, ViewInfo};
let operands: Vec<_> = vars
.iter()
.map(|&var_id| ViewInfo::Variable { var_id })
.collect();
let metadata = ConstraintData::NAry { operands };
self.push_new_prop_with_metadata(
self::alldiff::AllDiff::new(vars.clone()),
ConstraintType::AllDifferent,
vars,
metadata,
)
}
pub fn all_equal(&mut self, vars: Vec<VarId>) -> PropId {
use crate::optimization::constraint_metadata::{ConstraintData, ConstraintType, ViewInfo};
let operands: Vec<_> = vars
.iter()
.map(|&var_id| ViewInfo::Variable { var_id })
.collect();
let metadata = ConstraintData::NAry { operands };
self.push_new_prop_with_metadata(
self::allequal::AllEqual::new(vars.clone()),
ConstraintType::AllEqual,
vars,
metadata,
)
}
pub fn count_constraint(
&mut self,
vars: Vec<VarId>,
target_var: impl crate::variables::View,
count_var: VarId,
) -> PropId {
use crate::optimization::constraint_metadata::{ConstraintData, ConstraintType, ViewInfo};
let target_info = self.analyze_view(&target_var);
let count_instance = count::Count::new(vars.clone(), target_var, count_var);
let mut operands: Vec<ViewInfo> = vars
.iter()
.map(|&var_id| ViewInfo::Variable { var_id })
.collect();
operands.push(target_info);
operands.push(ViewInfo::Variable { var_id: count_var });
let metadata = ConstraintData::NAry { operands };
let mut all_vars = vars.clone();
all_vars.extend(target_var.get_underlying_var());
all_vars.push(count_var);
self.push_new_prop_with_metadata(count_instance, ConstraintType::Count, all_vars, metadata)
}
pub fn element(&mut self, array: Vec<VarId>, index: VarId, value: VarId) -> PropId {
use crate::optimization::constraint_metadata::{ConstraintData, ConstraintType, ViewInfo};
let mut operands: Vec<ViewInfo> = array
.iter()
.map(|&var_id| ViewInfo::Variable { var_id })
.collect();
operands.push(ViewInfo::Variable { var_id: index });
operands.push(ViewInfo::Variable { var_id: value });
let metadata = ConstraintData::NAry { operands };
let trigger_vars = {
let mut vars = array.clone();
vars.push(index);
vars.push(value);
vars
};
self.push_new_prop_with_metadata(
self::element::Element::new(array, index, value),
ConstraintType::Element,
trigger_vars,
metadata,
)
}
pub fn between_constraint(&mut self, lower: VarId, middle: VarId, upper: VarId) -> PropId {
use crate::optimization::constraint_metadata::{ConstraintData, ConstraintType, ViewInfo};
let variables = vec![lower, middle, upper];
let operands = vec![
ViewInfo::Variable { var_id: lower },
ViewInfo::Variable { var_id: middle },
ViewInfo::Variable { var_id: upper },
];
let metadata = ConstraintData::NAry { operands };
self.push_new_prop_with_metadata(
self::between::BetweenConstraint::new(lower, middle, upper),
ConstraintType::Between,
variables,
metadata,
)
}
pub fn at_least_constraint(
&mut self,
vars: Vec<VarId>,
target_value: i32,
count: i32,
) -> PropId {
use crate::optimization::constraint_metadata::{ConstraintData, ConstraintType, ViewInfo};
let operands: Vec<ViewInfo> = vars
.iter()
.map(|&var_id| ViewInfo::Variable { var_id })
.collect();
let metadata = ConstraintData::NAry { operands };
self.push_new_prop_with_metadata(
self::cardinality::CardinalityConstraint::at_least(vars.clone(), target_value, count),
ConstraintType::AtLeast,
vars,
metadata,
)
}
pub fn at_most_constraint(
&mut self,
vars: Vec<VarId>,
target_value: i32,
count: i32,
) -> PropId {
use crate::optimization::constraint_metadata::{ConstraintData, ConstraintType, ViewInfo};
let operands: Vec<ViewInfo> = vars
.iter()
.map(|&var_id| ViewInfo::Variable { var_id })
.collect();
let metadata = ConstraintData::NAry { operands };
self.push_new_prop_with_metadata(
self::cardinality::CardinalityConstraint::at_most(vars.clone(), target_value, count),
ConstraintType::AtMost,
vars,
metadata,
)
}
pub fn exactly_constraint(
&mut self,
vars: Vec<VarId>,
target_value: i32,
count: i32,
) -> PropId {
use crate::optimization::constraint_metadata::{ConstraintData, ConstraintType, ViewInfo};
let operands: Vec<ViewInfo> = vars
.iter()
.map(|&var_id| ViewInfo::Variable { var_id })
.collect();
let metadata = ConstraintData::NAry { operands };
self.push_new_prop_with_metadata(
self::cardinality::CardinalityConstraint::exactly(vars.clone(), target_value, count),
ConstraintType::Exactly,
vars,
metadata,
)
}
pub fn if_then_else_constraint(
&mut self,
condition: self::conditional::Condition,
then_constraint: self::conditional::SimpleConstraint,
else_constraint: Option<self::conditional::SimpleConstraint>,
) -> PropId {
use crate::optimization::constraint_metadata::{ConstraintData, ConstraintType, ViewInfo};
let constraint = self::conditional::IfThenElseConstraint::new(
condition,
then_constraint,
else_constraint,
);
let variables = constraint.variables();
let operands: Vec<ViewInfo> = variables
.iter()
.map(|&var_id| ViewInfo::Variable { var_id })
.collect();
let metadata = ConstraintData::NAry { operands };
self.push_new_prop_with_metadata(
constraint,
ConstraintType::IfThenElse,
variables,
metadata,
)
}
pub fn table_constraint(
&mut self,
vars: Vec<VarId>,
tuples: Vec<Vec<crate::variables::Val>>,
) -> PropId {
use crate::optimization::constraint_metadata::{ConstraintData, ConstraintType, ViewInfo};
let operands: Vec<ViewInfo> = vars
.iter()
.map(|&var_id| ViewInfo::Variable { var_id })
.collect();
let metadata = ConstraintData::NAry { operands };
self.push_new_prop_with_metadata(
self::table::Table::new(vars.clone(), tuples),
ConstraintType::Table,
vars,
metadata,
)
}
pub fn noop(&mut self) -> PropId {
self.push_new_prop(self::noop::NoOp::new())
}
fn push_new_prop(&mut self, state: impl Propagate) -> PropId {
let p = PropId(self.state.len());
for v in state.list_trigger_vars() {
while self.dependencies.len() <= v.to_index() {
self.dependencies.push(Vec::new());
}
self.dependencies[v].push(p);
}
let boxed = Box::new(state);
self.state.push(Rc::new(boxed));
p
}
fn push_new_prop_with_metadata(
&mut self,
state: impl Propagate,
constraint_type: crate::optimization::constraint_metadata::ConstraintType,
variables: Vec<VarId>,
metadata: crate::optimization::constraint_metadata::ConstraintData,
) -> PropId {
let prop_id = self.push_new_prop(state);
let _constraint_id =
self.constraint_registry
.register_constraint(constraint_type, variables, metadata);
prop_id
}
pub fn constraint_count(&self) -> usize {
self.state.len()
}
fn analyze_view<T: View>(
&self,
view: &T,
) -> crate::optimization::constraint_metadata::ViewInfo {
use crate::optimization::constraint_metadata::{ConstraintValue, ViewInfo};
if let Some(var_id) = view.get_underlying_var() {
ViewInfo::Variable { var_id }
} else {
let mut vars = crate::variables::Vars::default();
let mut events = Vec::new();
let ctx = crate::variables::views::Context::new(&mut vars, &mut events);
let min_val = view.min(&ctx);
let max_val = view.max(&ctx);
if min_val == max_val {
let value = match min_val {
crate::variables::Val::ValI(i) => ConstraintValue::Integer(i),
crate::variables::Val::ValF(f) => ConstraintValue::Float(f),
};
ViewInfo::Constant { value }
} else {
ViewInfo::Complex
}
}
}
pub fn less_than_or_equals_with_metadata(&mut self, x: impl View, y: impl View) -> PropId {
use crate::optimization::constraint_metadata::{ConstraintData, ConstraintType};
let x_info = self.analyze_view(&x);
let y_info = self.analyze_view(&y);
let variables: Vec<_> = x
.get_underlying_var()
.into_iter()
.chain(y.get_underlying_var())
.collect();
let metadata = ConstraintData::Binary {
left: x_info,
right: y_info,
};
self.push_new_prop_with_metadata(
self::leq::LessThanOrEquals::new(x, y),
ConstraintType::LessThanOrEquals,
variables,
metadata,
)
}
pub fn less_than_with_metadata(&mut self, x: impl View, y: impl View) -> PropId {
use crate::optimization::constraint_metadata::{
ConstraintData, ConstraintType, TransformationType, ViewInfo,
};
let x_info = self.analyze_view(&x);
let y_info = self.analyze_view(&y);
let transformed_x_info = if let ViewInfo::Variable { var_id } = x_info {
ViewInfo::Transformed {
base_var: var_id,
transformation: TransformationType::Next,
}
} else {
ViewInfo::Complex
};
let variables: Vec<_> = x
.get_underlying_var()
.into_iter()
.chain(y.get_underlying_var())
.collect();
let metadata = ConstraintData::Binary {
left: transformed_x_info,
right: y_info,
};
self.push_new_prop_with_metadata(
self::leq::LessThanOrEquals::new(x.next(), y),
ConstraintType::LessThan,
variables,
metadata,
)
}
pub fn bool_and(&mut self, operands: Vec<VarId>, result: VarId) -> PropId {
use crate::optimization::constraint_metadata::{ConstraintData, ConstraintType, ViewInfo};
let operand_infos: Vec<_> = operands
.iter()
.map(|&var_id| ViewInfo::Variable { var_id })
.collect();
let variables: Vec<_> = operands
.iter()
.cloned()
.chain(std::iter::once(result))
.collect();
let metadata = ConstraintData::NAry {
operands: operand_infos,
};
self.push_new_prop_with_metadata(
self::bool_logic::BoolAnd::new(operands, result),
ConstraintType::BooleanAnd,
variables,
metadata,
)
}
pub fn bool_or(&mut self, operands: Vec<VarId>, result: VarId) -> PropId {
use crate::optimization::constraint_metadata::{ConstraintData, ConstraintType, ViewInfo};
let operand_infos: Vec<_> = operands
.iter()
.map(|&var_id| ViewInfo::Variable { var_id })
.collect();
let variables: Vec<_> = operands
.iter()
.cloned()
.chain(std::iter::once(result))
.collect();
let metadata = ConstraintData::NAry {
operands: operand_infos,
};
self.push_new_prop_with_metadata(
self::bool_logic::BoolOr::new(operands, result),
ConstraintType::BooleanOr,
variables,
metadata,
)
}
pub fn bool_not(&mut self, operand: VarId, result: VarId) -> PropId {
use crate::optimization::constraint_metadata::{ConstraintData, ConstraintType, ViewInfo};
let operand_info = ViewInfo::Variable { var_id: operand };
let result_info = ViewInfo::Variable { var_id: result };
let variables = vec![operand, result];
let metadata = ConstraintData::Binary {
left: operand_info,
right: result_info,
};
self.push_new_prop_with_metadata(
self::bool_logic::BoolNot::new(operand, result),
ConstraintType::BooleanNot,
variables,
metadata,
)
}
pub fn bool_xor(&mut self, x: VarId, y: VarId, result: VarId) -> PropId {
use crate::optimization::constraint_metadata::{ConstraintData, ConstraintType, ViewInfo};
let x_info = ViewInfo::Variable { var_id: x };
let y_info = ViewInfo::Variable { var_id: y };
let result_info = ViewInfo::Variable { var_id: result };
let variables = vec![x, y, result];
let metadata = ConstraintData::NAry {
operands: vec![x_info, y_info, result_info],
};
self.push_new_prop_with_metadata(
self::bool_logic::BoolXor::new(x, y, result),
ConstraintType::BooleanXor,
variables,
metadata,
)
}
pub fn int_eq_reif(&mut self, x: VarId, y: VarId, b: VarId) -> PropId {
use crate::optimization::constraint_metadata::{ConstraintData, ConstraintType, ViewInfo};
let x_info = ViewInfo::Variable { var_id: x };
let y_info = ViewInfo::Variable { var_id: y };
let variables = vec![x, y, b];
let metadata = ConstraintData::Binary {
left: x_info,
right: y_info,
};
self.push_new_prop_with_metadata(
self::reification::IntEqReif::new(x, y, b),
ConstraintType::EqualityReified,
variables,
metadata,
)
}
pub fn int_ne_reif(&mut self, x: VarId, y: VarId, b: VarId) -> PropId {
use crate::optimization::constraint_metadata::{ConstraintData, ConstraintType, ViewInfo};
let x_info = ViewInfo::Variable { var_id: x };
let y_info = ViewInfo::Variable { var_id: y };
let variables = vec![x, y, b];
let metadata = ConstraintData::Binary {
left: x_info,
right: y_info,
};
self.push_new_prop_with_metadata(
self::reification::IntNeReif::new(x, y, b),
ConstraintType::InequalityReified,
variables,
metadata,
)
}
pub fn int_lt_reif(&mut self, x: VarId, y: VarId, b: VarId) -> PropId {
use crate::optimization::constraint_metadata::{ConstraintData, ConstraintType, ViewInfo};
let x_info = ViewInfo::Variable { var_id: x };
let y_info = ViewInfo::Variable { var_id: y };
let variables = vec![x, y, b];
let metadata = ConstraintData::Binary {
left: x_info,
right: y_info,
};
self.push_new_prop_with_metadata(
self::reification::IntLtReif::new(x, y, b),
ConstraintType::LessThanReified,
variables,
metadata,
)
}
pub fn int_le_reif(&mut self, x: VarId, y: VarId, b: VarId) -> PropId {
use crate::optimization::constraint_metadata::{ConstraintData, ConstraintType, ViewInfo};
let x_info = ViewInfo::Variable { var_id: x };
let y_info = ViewInfo::Variable { var_id: y };
let variables = vec![x, y, b];
let metadata = ConstraintData::Binary {
left: x_info,
right: y_info,
};
self.push_new_prop_with_metadata(
self::reification::IntLeReif::new(x, y, b),
ConstraintType::LessEqualReified,
variables,
metadata,
)
}
pub fn int_gt_reif(&mut self, x: VarId, y: VarId, b: VarId) -> PropId {
use crate::optimization::constraint_metadata::{ConstraintData, ConstraintType, ViewInfo};
let x_info = ViewInfo::Variable { var_id: x };
let y_info = ViewInfo::Variable { var_id: y };
let variables = vec![x, y, b];
let metadata = ConstraintData::Binary {
left: x_info,
right: y_info,
};
self.push_new_prop_with_metadata(
self::reification::IntGtReif::new(x, y, b),
ConstraintType::GreaterThanReified,
variables,
metadata,
)
}
pub fn int_ge_reif(&mut self, x: VarId, y: VarId, b: VarId) -> PropId {
use crate::optimization::constraint_metadata::{ConstraintData, ConstraintType, ViewInfo};
let x_info = ViewInfo::Variable { var_id: x };
let y_info = ViewInfo::Variable { var_id: y };
let variables = vec![x, y, b];
let metadata = ConstraintData::Binary {
left: x_info,
right: y_info,
};
self.push_new_prop_with_metadata(
self::reification::IntGeReif::new(x, y, b),
ConstraintType::GreaterEqualReified,
variables,
metadata,
)
}
pub fn int_lin_eq(
&mut self,
coefficients: Vec<i32>,
variables: Vec<VarId>,
constant: i32,
) -> PropId {
use crate::optimization::constraint_metadata::{ConstraintData, ConstraintType, ViewInfo};
let operands: Vec<ViewInfo> = variables
.iter()
.map(|&v| ViewInfo::Variable { var_id: v })
.collect();
let metadata = ConstraintData::NAry { operands };
self.push_new_prop_with_metadata(
self::linear::IntLinEq::new(coefficients, variables.clone(), constant),
ConstraintType::Equals,
variables,
metadata,
)
}
pub fn int_lin_le(
&mut self,
coefficients: Vec<i32>,
variables: Vec<VarId>,
constant: i32,
) -> PropId {
use crate::optimization::constraint_metadata::{ConstraintData, ConstraintType, ViewInfo};
let operands: Vec<ViewInfo> = variables
.iter()
.map(|&v| ViewInfo::Variable { var_id: v })
.collect();
let metadata = ConstraintData::NAry { operands };
self.push_new_prop_with_metadata(
self::linear::IntLinLe::new(coefficients, variables.clone(), constant),
ConstraintType::LessThanOrEquals,
variables,
metadata,
)
}
pub fn int_lin_ne(
&mut self,
coefficients: Vec<i32>,
variables: Vec<VarId>,
constant: i32,
) -> PropId {
use crate::optimization::constraint_metadata::{ConstraintData, ConstraintType, ViewInfo};
let operands: Vec<ViewInfo> = variables
.iter()
.map(|&v| ViewInfo::Variable { var_id: v })
.collect();
let metadata = ConstraintData::NAry { operands };
self.push_new_prop_with_metadata(
self::linear::IntLinNe::new(coefficients, variables.clone(), constant),
ConstraintType::NotEquals,
variables,
metadata,
)
}
pub fn int_lin_eq_reif(
&mut self,
coefficients: Vec<i32>,
variables: Vec<VarId>,
constant: i32,
reif_var: VarId,
) -> PropId {
use crate::optimization::constraint_metadata::{ConstraintData, ConstraintType, ViewInfo};
let mut operands: Vec<ViewInfo> = variables
.iter()
.map(|&v| ViewInfo::Variable { var_id: v })
.collect();
operands.push(ViewInfo::Variable { var_id: reif_var });
let metadata = ConstraintData::NAry { operands };
let mut all_vars = variables.clone();
all_vars.push(reif_var);
self.push_new_prop_with_metadata(
self::linear::IntLinEqReif::new(coefficients, variables, constant, reif_var),
ConstraintType::EqualityReified,
all_vars,
metadata,
)
}
pub fn int_lin_le_reif(
&mut self,
coefficients: Vec<i32>,
variables: Vec<VarId>,
constant: i32,
reif_var: VarId,
) -> PropId {
use crate::optimization::constraint_metadata::{ConstraintData, ConstraintType, ViewInfo};
let mut operands: Vec<ViewInfo> = variables
.iter()
.map(|&v| ViewInfo::Variable { var_id: v })
.collect();
operands.push(ViewInfo::Variable { var_id: reif_var });
let metadata = ConstraintData::NAry { operands };
let mut all_vars = variables.clone();
all_vars.push(reif_var);
self.push_new_prop_with_metadata(
self::linear::IntLinLeReif::new(coefficients, variables, constant, reif_var),
ConstraintType::InequalityReified,
all_vars,
metadata,
)
}
pub fn int_lin_ne_reif(
&mut self,
coefficients: Vec<i32>,
variables: Vec<VarId>,
constant: i32,
reif_var: VarId,
) -> PropId {
use crate::optimization::constraint_metadata::{ConstraintData, ConstraintType, ViewInfo};
let mut operands: Vec<ViewInfo> = variables
.iter()
.map(|&v| ViewInfo::Variable { var_id: v })
.collect();
operands.push(ViewInfo::Variable { var_id: reif_var });
let metadata = ConstraintData::NAry { operands };
let mut all_vars = variables.clone();
all_vars.push(reif_var);
self.push_new_prop_with_metadata(
self::linear::IntLinNeReif::new(coefficients, variables, constant, reif_var),
ConstraintType::InequalityReified,
all_vars,
metadata,
)
}
pub fn float_lin_eq(
&mut self,
coefficients: Vec<f64>,
variables: Vec<VarId>,
constant: f64,
) -> PropId {
use crate::optimization::constraint_metadata::{ConstraintData, ConstraintType, ViewInfo};
let operands: Vec<ViewInfo> = variables
.iter()
.map(|&v| ViewInfo::Variable { var_id: v })
.collect();
let metadata = ConstraintData::NAry { operands };
self.push_new_prop_with_metadata(
self::linear::FloatLinEq::new(coefficients, variables.clone(), constant),
ConstraintType::Equals,
variables,
metadata,
)
}
pub fn float_lin_le(
&mut self,
coefficients: Vec<f64>,
variables: Vec<VarId>,
constant: f64,
) -> PropId {
use crate::optimization::constraint_metadata::{ConstraintData, ConstraintType, ViewInfo};
let operands: Vec<ViewInfo> = variables
.iter()
.map(|&v| ViewInfo::Variable { var_id: v })
.collect();
let metadata = ConstraintData::NAry { operands };
self.push_new_prop_with_metadata(
self::linear::FloatLinLe::new(coefficients, variables.clone(), constant),
ConstraintType::LessThanOrEquals,
variables,
metadata,
)
}
pub fn float_lin_ne(
&mut self,
coefficients: Vec<f64>,
variables: Vec<VarId>,
constant: f64,
) -> PropId {
use crate::optimization::constraint_metadata::{ConstraintData, ConstraintType, ViewInfo};
let operands: Vec<ViewInfo> = variables
.iter()
.map(|&v| ViewInfo::Variable { var_id: v })
.collect();
let metadata = ConstraintData::NAry { operands };
self.push_new_prop_with_metadata(
self::linear::FloatLinNe::new(coefficients, variables.clone(), constant),
ConstraintType::NotEquals,
variables,
metadata,
)
}
pub fn float_lin_eq_reif(
&mut self,
coefficients: Vec<f64>,
variables: Vec<VarId>,
constant: f64,
reif_var: VarId,
) -> PropId {
use crate::optimization::constraint_metadata::{ConstraintData, ConstraintType, ViewInfo};
let mut operands: Vec<ViewInfo> = variables
.iter()
.map(|&v| ViewInfo::Variable { var_id: v })
.collect();
operands.push(ViewInfo::Variable { var_id: reif_var });
let metadata = ConstraintData::NAry { operands };
let mut all_vars = variables.clone();
all_vars.push(reif_var);
self.push_new_prop_with_metadata(
self::linear::FloatLinEqReif::new(coefficients, variables, constant, reif_var),
ConstraintType::EqualityReified,
all_vars,
metadata,
)
}
pub fn float_lin_le_reif(
&mut self,
coefficients: Vec<f64>,
variables: Vec<VarId>,
constant: f64,
reif_var: VarId,
) -> PropId {
use crate::optimization::constraint_metadata::{ConstraintData, ConstraintType, ViewInfo};
let mut operands: Vec<ViewInfo> = variables
.iter()
.map(|&v| ViewInfo::Variable { var_id: v })
.collect();
operands.push(ViewInfo::Variable { var_id: reif_var });
let metadata = ConstraintData::NAry { operands };
let mut all_vars = variables.clone();
all_vars.push(reif_var);
self.push_new_prop_with_metadata(
self::linear::FloatLinLeReif::new(coefficients, variables, constant, reif_var),
ConstraintType::InequalityReified,
all_vars,
metadata,
)
}
pub fn float_lin_ne_reif(
&mut self,
coefficients: Vec<f64>,
variables: Vec<VarId>,
constant: f64,
reif_var: VarId,
) -> PropId {
use crate::optimization::constraint_metadata::{ConstraintData, ConstraintType, ViewInfo};
let mut operands: Vec<ViewInfo> = variables
.iter()
.map(|&v| ViewInfo::Variable { var_id: v })
.collect();
operands.push(ViewInfo::Variable { var_id: reif_var });
let metadata = ConstraintData::NAry { operands };
let mut all_vars = variables.clone();
all_vars.push(reif_var);
self.push_new_prop_with_metadata(
self::linear::FloatLinNeReif::new(coefficients, variables, constant, reif_var),
ConstraintType::InequalityReified,
all_vars,
metadata,
)
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct PropId(pub usize);
impl Index<PropId> for Vec<Box<dyn Prune>> {
type Output = Box<dyn Prune>;
fn index(&self, index: PropId) -> &Self::Output {
&self[index.0]
}
}
impl IndexMut<PropId> for Vec<Box<dyn Prune>> {
fn index_mut(&mut self, index: PropId) -> &mut Self::Output {
&mut self[index.0]
}
}
#[doc(hidden)]
pub use alldiff::AllDiff;
#[doc(hidden)]
pub use allequal::AllEqual;
#[doc(hidden)]
pub use count::Count;