use crate::constraint::Constraint;
use crate::model::domain::Domain;
use crate::model::variable::{Variable, VariableId};
use crate::score::Objective;
use std::collections::HashMap;
use std::fmt;
use std::sync::Arc;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ConstraintId(pub u32);
impl fmt::Display for ConstraintId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "c{}", self.0)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ModelError {
UnknownVariable {
item: String,
var: VariableId,
},
EmptyDomain {
var: VariableId,
},
DuplicateVariableId {
var: VariableId,
},
InvalidConstraint {
name: String,
reason: String,
},
}
impl fmt::Display for ModelError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ModelError::UnknownVariable { item, var } => {
write!(f, "{item} references unregistered variable {var:?}")
}
ModelError::EmptyDomain { var } => write!(f, "variable {var:?} has an empty domain"),
ModelError::DuplicateVariableId { var } => {
write!(f, "variable id {var:?} was registered more than once")
}
ModelError::InvalidConstraint { name, reason } => write!(f, "{name}: {reason}"),
}
}
}
impl std::error::Error for ModelError {}
#[derive(Clone, Default)]
pub struct ConstraintGraph {
variables: HashMap<VariableId, Variable>,
domains: HashMap<VariableId, Domain>,
constraints: Vec<Arc<dyn Constraint>>,
objectives: Vec<Arc<dyn Objective>>,
var_to_constraints: HashMap<VariableId, Vec<ConstraintId>>,
duplicate_variable_ids: Vec<VariableId>,
}
impl ConstraintGraph {
pub fn new() -> Self {
Self::default()
}
pub fn add_variable(&mut self, variable: Variable, domain: Domain) {
let id = variable.id();
if self.variables.contains_key(&id) {
self.duplicate_variable_ids.push(id);
}
self.variables.insert(id, variable);
self.domains.insert(id, domain);
self.var_to_constraints.entry(id).or_default();
}
pub fn add_constraint(&mut self, constraint: Arc<dyn Constraint>) -> ConstraintId {
let cid = ConstraintId(self.constraints.len() as u32);
for &var_id in constraint.scope() {
self.var_to_constraints.entry(var_id).or_default().push(cid);
}
self.constraints.push(constraint);
cid
}
#[inline]
pub fn variables(&self) -> &HashMap<VariableId, Variable> {
&self.variables
}
#[inline]
pub fn domains(&self) -> &HashMap<VariableId, Domain> {
&self.domains
}
#[inline]
pub fn domains_mut(&mut self) -> &mut HashMap<VariableId, Domain> {
&mut self.domains
}
#[inline]
pub fn constraints(&self) -> &[Arc<dyn Constraint>] {
&self.constraints
}
pub fn constraints_for_variable(&self, var_id: VariableId) -> &[ConstraintId] {
self.var_to_constraints
.get(&var_id)
.map(|vec| vec.as_slice())
.unwrap_or(&[])
}
pub fn get_constraint(&self, id: ConstraintId) -> Option<&Arc<dyn Constraint>> {
self.constraints.get(id.0 as usize)
}
pub fn add_objective(&mut self, objective: Arc<dyn Objective>) {
self.objectives.push(objective);
}
#[inline]
pub fn objectives(&self) -> &[Arc<dyn Objective>] {
&self.objectives
}
pub fn validate(&self) -> Result<(), Vec<ModelError>> {
let mut errors = Vec::new();
for &var in &self.duplicate_variable_ids {
errors.push(ModelError::DuplicateVariableId { var });
}
for (&var, domain) in &self.domains {
if domain.is_empty() {
errors.push(ModelError::EmptyDomain { var });
}
}
for constraint in &self.constraints {
for &var in constraint.scope() {
if !self.variables.contains_key(&var) {
errors.push(ModelError::UnknownVariable {
item: constraint.name().to_string(),
var,
});
}
}
if let Err(reason) = constraint.validate() {
errors.push(ModelError::InvalidConstraint {
name: constraint.name().to_string(),
reason,
});
}
}
for objective in &self.objectives {
for &var in objective.scope() {
if !self.variables.contains_key(&var) {
errors.push(ModelError::UnknownVariable {
item: objective.name().to_string(),
var,
});
}
}
}
if errors.is_empty() {
Ok(())
} else {
Err(errors)
}
}
pub fn finalize(self) -> Result<ValidatedGraph, Vec<ModelError>> {
self.validate()?;
Ok(ValidatedGraph(self))
}
}
impl fmt::Debug for ConstraintGraph {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ConstraintGraph")
.field("num_variables", &self.variables.len())
.field("num_constraints", &self.constraints.len())
.finish()
}
}
#[derive(Debug, Clone)]
pub struct ValidatedGraph(ConstraintGraph);
impl ValidatedGraph {
#[inline]
pub fn graph(&self) -> &ConstraintGraph {
&self.0
}
pub(crate) fn assume_valid(graph: ConstraintGraph) -> Self {
Self(graph)
}
}
impl std::ops::Deref for ValidatedGraph {
type Target = ConstraintGraph;
fn deref(&self) -> &ConstraintGraph {
&self.0
}
}