use crate::model::Model;
use crate::core::solution::Solution;
use crate::variables::{Var, VarId, Val};
use crate::optimization::subproblem_solving::{CombinedSolution, SubproblemValue};
use crate::optimization::variable_partitioning::PartitionResult;
use std::collections::HashMap;
use std::time::{Duration, Instant};
#[derive(Debug)]
pub struct SolutionIntegrator {
validation_timeout: Duration,
full_validation: bool,
}
#[derive(Debug)]
pub struct IntegratedSolution {
pub solution: Solution,
pub variable_assignments: HashMap<VarId, Val>,
pub is_fully_validated: bool,
pub integration_time: Duration,
pub constraints_validated: usize,
pub validation_issues: Vec<ValidationIssue>,
}
#[derive(Debug, Clone, PartialEq)]
pub enum ValidationIssue {
VariableOutOfBounds { var_id: VarId, value: Val, expected_min: Val, expected_max: Val },
ConstraintViolation { constraint_type: String, variables: Vec<VarId> },
TypeMismatch { var_id: VarId, expected_type: VariableType, actual_value: Val },
MissingAssignment { var_id: VarId },
ValidationTimeout,
}
#[derive(Debug, Clone, PartialEq)]
pub enum VariableType {
Float,
Integer,
}
#[derive(Debug, Clone, PartialEq)]
pub enum IntegrationError {
MergingFailed(MergingError),
ValidationFailed(ValidationError),
ConstructionFailed(ConstructionError),
TimeoutExceeded,
NoSolution,
}
#[derive(Debug, Clone, PartialEq)]
pub enum MergingError {
ConflictingAssignments { var_id: VarId, value1: SubproblemValue, value2: SubproblemValue },
MissingVariable { var_id: VarId },
TypeConversionFailed { var_id: VarId, value: SubproblemValue },
}
#[derive(Debug, Clone, PartialEq)]
pub enum ValidationError {
CriticalViolation { constraint_info: String },
InvalidModel,
TooManyErrors { error_count: usize },
}
#[derive(Debug, Clone, PartialEq)]
pub enum ConstructionError {
InvalidAssignments,
IncompatibleFormat,
MissingRequiredVariables { missing_count: usize },
}
impl SolutionIntegrator {
pub fn new() -> Self {
Self {
validation_timeout: Duration::from_millis(5000), full_validation: true,
}
}
pub fn with_timeout(mut self, timeout: Duration) -> Self {
self.validation_timeout = timeout;
self
}
pub fn with_full_validation(mut self, full_validation: bool) -> Self {
self.full_validation = full_validation;
self
}
pub fn integrate_solution(
&self,
model: &Model,
combined_solution: &CombinedSolution,
partition_result: &PartitionResult,
) -> Result<IntegratedSolution, IntegrationError> {
let start_time = Instant::now();
if !combined_solution.is_complete {
return Err(IntegrationError::NoSolution);
}
let variable_assignments = self.merge_subproblem_solutions(model, combined_solution)?;
let (validation_issues, constraints_validated) = self.validate_assignments(
model,
&variable_assignments,
partition_result
)?;
let solution = self.construct_solution(model, &variable_assignments)?;
let integration_time = start_time.elapsed();
if integration_time > self.validation_timeout {
return Err(IntegrationError::TimeoutExceeded);
}
let is_fully_validated = validation_issues.is_empty();
Ok(IntegratedSolution {
solution,
variable_assignments,
is_fully_validated,
integration_time,
constraints_validated,
validation_issues,
})
}
fn merge_subproblem_solutions(
&self,
model: &Model,
combined_solution: &CombinedSolution,
) -> Result<HashMap<VarId, Val>, IntegrationError> {
let mut merged_assignments = HashMap::new();
for (var_id, subproblem_value) in &combined_solution.all_assignments {
if merged_assignments.contains_key(var_id) {
return Err(IntegrationError::MergingFailed(
MergingError::ConflictingAssignments {
var_id: *var_id,
value1: subproblem_value.clone(),
value2: subproblem_value.clone(), }
));
}
let val = self.convert_subproblem_value_to_val(model, *var_id, subproblem_value)?;
merged_assignments.insert(*var_id, val);
}
Ok(merged_assignments)
}
fn convert_subproblem_value_to_val(
&self,
model: &Model,
var_id: VarId,
subproblem_value: &SubproblemValue,
) -> Result<Val, IntegrationError> {
let var = &model[var_id];
match (var, subproblem_value) {
(Var::VarF(_), SubproblemValue::Float(f)) => Ok(Val::ValF(*f)),
(Var::VarI(_), SubproblemValue::Integer(i)) => Ok(Val::ValI(*i)),
(Var::VarF(_), SubproblemValue::Integer(i)) => Ok(Val::ValF(*i as f64)),
(Var::VarI(_), SubproblemValue::Float(f)) => {
let rounded = f.round();
if (f - rounded).abs() < f64::EPSILON {
Ok(Val::ValI(rounded as i32))
} else {
Err(IntegrationError::MergingFailed(
MergingError::TypeConversionFailed {
var_id,
value: subproblem_value.clone(),
}
))
}
}
}
}
fn validate_assignments(
&self,
model: &Model,
assignments: &HashMap<VarId, Val>,
_partition_result: &PartitionResult,
) -> Result<(Vec<ValidationIssue>, usize), IntegrationError> {
let mut issues = Vec::new();
let mut constraints_validated = 0;
for (var_id, val) in assignments {
let var = &model[*var_id];
self.validate_variable_bounds(var, *var_id, val, &mut issues);
}
if self.full_validation {
constraints_validated = self.validate_model_constraints(model, assignments, &mut issues)?;
}
if issues.len() > 100 {
return Err(IntegrationError::ValidationFailed(
ValidationError::TooManyErrors { error_count: issues.len() }
));
}
Ok((issues, constraints_validated))
}
fn validate_variable_bounds(
&self,
var: &Var,
var_id: VarId,
val: &Val,
issues: &mut Vec<ValidationIssue>,
) {
match (var, val) {
(Var::VarF(interval), Val::ValF(f)) => {
if *f < interval.min || *f > interval.max {
issues.push(ValidationIssue::VariableOutOfBounds {
var_id,
value: *val,
expected_min: Val::ValF(interval.min),
expected_max: Val::ValF(interval.max),
});
}
},
(Var::VarI(sparse_set), Val::ValI(i)) => {
if !sparse_set.contains(*i) {
issues.push(ValidationIssue::VariableOutOfBounds {
var_id,
value: *val,
expected_min: Val::ValI(sparse_set.min()),
expected_max: Val::ValI(sparse_set.max()),
});
}
},
(Var::VarF(_), Val::ValI(_)) => {
issues.push(ValidationIssue::TypeMismatch {
var_id,
expected_type: VariableType::Float,
actual_value: *val,
});
},
(Var::VarI(_), Val::ValF(_)) => {
issues.push(ValidationIssue::TypeMismatch {
var_id,
expected_type: VariableType::Integer,
actual_value: *val,
});
},
}
}
fn validate_model_constraints(
&self,
_model: &Model,
_assignments: &HashMap<VarId, Val>,
_issues: &mut [ValidationIssue],
) -> Result<usize, IntegrationError> {
Ok(0)
}
fn construct_solution(
&self,
model: &Model,
assignments: &HashMap<VarId, Val>,
) -> Result<Solution, IntegrationError> {
let vars = model.get_vars();
let mut solution_values = Vec::new();
for i in 0..vars.iter().count() {
let var_id = crate::optimization::model_integration::index_to_var_id(i);
if let Some(val) = assignments.get(&var_id) {
solution_values.push(*val);
} else {
return Err(IntegrationError::ConstructionFailed(
ConstructionError::MissingRequiredVariables { missing_count: 1 }
));
}
}
Ok(Solution::from(solution_values))
}
}
impl Default for SolutionIntegrator {
fn default() -> Self {
Self::new()
}
}
impl std::fmt::Display for ValidationIssue {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ValidationIssue::VariableOutOfBounds { var_id, value, expected_min, expected_max } => {
write!(f, "Variable {:?} value {:?} is out of bounds [{:?}, {:?}]",
var_id, value, expected_min, expected_max)
},
ValidationIssue::ConstraintViolation { constraint_type, variables } => {
write!(f, "Constraint '{}' violated by variables {:?}", constraint_type, variables)
},
ValidationIssue::TypeMismatch { var_id, expected_type, actual_value } => {
write!(f, "Variable {:?} expected {:?} type but got value {:?}",
var_id, expected_type, actual_value)
},
ValidationIssue::MissingAssignment { var_id } => {
write!(f, "Missing assignment for variable {:?}", var_id)
},
ValidationIssue::ValidationTimeout => {
write!(f, "Validation process timed out")
},
}
}
}
impl std::fmt::Display for IntegrationError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
IntegrationError::MergingFailed(err) => {
write!(f, "Solution merging failed: {}", err)
},
IntegrationError::ValidationFailed(err) => {
write!(f, "Solution validation failed: {}", err)
},
IntegrationError::ConstructionFailed(err) => {
write!(f, "Solution construction failed: {}", err)
},
IntegrationError::TimeoutExceeded => {
write!(f, "Integration process timed out")
},
IntegrationError::NoSolution => {
write!(f, "No solution provided to integrate")
},
}
}
}
impl std::fmt::Display for MergingError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
MergingError::ConflictingAssignments { var_id, value1, value2 } => {
write!(f, "Conflicting assignments for variable {:?}: {:?} vs {:?}",
var_id, value1, value2)
},
MergingError::MissingVariable { var_id } => {
write!(f, "Variable {:?} missing from all subproblems", var_id)
},
MergingError::TypeConversionFailed { var_id, value } => {
write!(f, "Type conversion failed for variable {:?} with value {:?}",
var_id, value)
},
}
}
}
impl std::fmt::Display for ValidationError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ValidationError::CriticalViolation { constraint_info } => {
write!(f, "Critical constraint violation: {}", constraint_info)
},
ValidationError::InvalidModel => {
write!(f, "Model structure is invalid for validation")
},
ValidationError::TooManyErrors { error_count } => {
write!(f, "Too many validation errors: {}", error_count)
},
}
}
}
impl std::fmt::Display for ConstructionError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ConstructionError::InvalidAssignments => {
write!(f, "Cannot create solution with current assignments")
},
ConstructionError::IncompatibleFormat => {
write!(f, "Solution format is incompatible with model")
},
ConstructionError::MissingRequiredVariables { missing_count } => {
write!(f, "Missing {} required variables from assignments", missing_count)
},
}
}
}
impl std::error::Error for IntegrationError {}
impl std::error::Error for MergingError {}
impl std::error::Error for ValidationError {}
impl std::error::Error for ConstructionError {}
pub fn integrate_subproblem_solution(
model: &Model,
combined_solution: &CombinedSolution,
partition_result: &PartitionResult,
) -> Result<IntegratedSolution, IntegrationError> {
let integrator = SolutionIntegrator::new();
integrator.integrate_solution(model, combined_solution, partition_result)
}