use crate::variables::{Vars, VarId};
use crate::constraints::props::Propagators;
use crate::optimization::constraint_integration::{ConstraintAwareOptimizer};
use crate::optimization::float_direct::{OptimizationResult, OptimizationOperation, DomainError};
use crate::variables::domain::FloatInterval;
#[derive(Debug)]
pub struct PrecisionAwareOptimizer {
base_optimizer: ConstraintAwareOptimizer,
}
impl PrecisionAwareOptimizer {
pub fn new() -> Self {
Self {
base_optimizer: ConstraintAwareOptimizer::new(),
}
}
fn is_fallback_domain(&self, domain: &FloatInterval) -> bool {
let min = domain.min;
let max = domain.max;
if (min + max).abs() < 0.0001 { let abs_bound = max.abs();
match abs_bound {
50.0 | 100.0 | 1000.0 | 10000.0 => true,
_ => false,
}
} else {
false
}
}
pub fn maximize_with_precision(
&self,
vars: &Vars,
props: &Propagators,
var_id: VarId,
) -> OptimizationResult {
if let Some(result) = self.try_metadata_precision_optimization(vars, props, var_id, true) {
return result;
}
self.base_optimizer.maximize_with_constraints(vars, props, var_id)
}
pub fn minimize_with_precision(
&self,
vars: &Vars,
props: &Propagators,
var_id: VarId,
) -> OptimizationResult {
if let Some(result) = self.try_metadata_precision_optimization(vars, props, var_id, false) {
return result;
}
self.base_optimizer.minimize_with_constraints(vars, props, var_id)
}
fn try_metadata_precision_optimization(
&self,
vars: &Vars,
props: &Propagators,
var_id: VarId,
is_maximization: bool,
) -> Option<OptimizationResult> {
let var_domain = match &vars[var_id] {
crate::variables::Var::VarF(interval) => {
if interval.is_empty() {
return Some(OptimizationResult::domain_error(DomainError::EmptyDomain));
}
interval
},
crate::variables::Var::VarI(_) => {
return None;
}
};
let registry = props.get_constraint_registry();
let analysis = registry.analyze_variable_constraints(var_id);
let step_size = self.calculate_precision_step_size(var_domain);
let constraint_upper = analysis.get_effective_upper_bound(step_size);
let constraint_lower = analysis.get_effective_lower_bound(step_size);
let optimal_value = if is_maximization {
if let Some(upper) = constraint_upper {
upper
} else {
if self.is_fallback_domain(var_domain) {
return None;
}
var_domain.max
}
} else {
if let Some(lower) = constraint_lower {
lower
} else {
if self.is_fallback_domain(var_domain) {
return None;
}
var_domain.min
}
};
if optimal_value < var_domain.min || optimal_value > var_domain.max {
return None;
}
let used_constraints = constraint_upper.is_some() || constraint_lower.is_some();
if used_constraints {
#[cfg(debug_assertions)]
{
eprintln!(
"Precision optimization for {:?}: constraint_lower={:?}, constraint_upper={:?}, optimal={:?}",
var_id,
constraint_lower,
constraint_upper,
optimal_value
);
}
}
let operation = if is_maximization {
OptimizationOperation::Maximization
} else {
OptimizationOperation::Minimization
};
Some(OptimizationResult::success(optimal_value, operation, var_id))
}
fn calculate_precision_step_size(&self, interval: &FloatInterval) -> f64 {
let domain_size = interval.max - interval.min;
if domain_size > 1000.0 {
1e-6 } else if domain_size > 100.0 {
1e-8 } else if domain_size > 10.0 {
1e-10 } else {
1e-12 }
}
}
impl Default for PrecisionAwareOptimizer {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::variables::Vars;
use crate::constraints::props::Propagators;
fn create_test_vars_with_float(min: f64, max: f64) -> (Vars, VarId) {
let mut vars = Vars::new();
let var_id = vars.new_var_with_bounds(
crate::variables::Val::float(min),
crate::variables::Val::float(max)
);
(vars, var_id)
}
fn create_test_props_with_constraint() -> Propagators {
Propagators::default()
}
#[test]
fn test_precision_aware_maximization() {
let optimizer = PrecisionAwareOptimizer::new();
let (vars, var_id) = create_test_vars_with_float(1.0, 10.0);
let props = create_test_props_with_constraint();
let result = optimizer.maximize_with_precision(&vars, &props, var_id);
assert!(result.success, "Optimization should succeed");
assert!(result.optimal_value >= 1.0 && result.optimal_value <= 10.0,
"Result should be within domain bounds");
}
}