use crate::model::core::Model;
use crate::variables::{Val, VarId, VarIdBin};
use crate::core::error::SolverError;
const DEFAULT_INT_FALLBACK_MIN: i32 = -100_000;
const DEFAULT_INT_FALLBACK_MAX: i32 = 100_000;
const DEFAULT_FLOAT_FALLBACK_MIN: f64 = -1e9;
const DEFAULT_FLOAT_FALLBACK_MAX: f64 = 1e9;
impl Model {
#[doc(hidden)]
pub fn new_var(&mut self, min: Val, max: Val) -> VarId {
if min < max {
self.new_var_unchecked(min, max)
} else {
self.new_var_unchecked(max, min)
}
}
#[doc(hidden)]
pub fn new_vars(&mut self, n: usize, min: Val, max: Val) -> impl Iterator<Item = VarId> + '_ {
let (actual_min, actual_max) = if min < max { (min, max) } else { (max, min) };
std::iter::repeat_with(move || self.new_var_unchecked(actual_min, actual_max)).take(n)
}
#[doc(hidden)]
pub fn new_var_unchecked(&mut self, min: Val, max: Val) -> VarId {
match self.new_var_checked(min, max) {
Ok(var_id) => var_id,
Err(_error) => {
self.set_memory_limit_exceeded();
VarId::from_index(0)
}
}
}
pub(crate) fn new_var_checked(&mut self, min: Val, max: Val) -> Result<VarId, SolverError> {
if self.memory_limit_exceeded() {
return Err(SolverError::MemoryLimit {
usage_mb: Some(self.estimated_memory_mb() as usize),
limit_mb: self.config().max_memory_mb.map(|x| x as usize),
});
}
let (inferred_min, inferred_max) = self.infer_bounds(min, max);
let estimated_memory = self.estimate_variable_memory(inferred_min, inferred_max);
self.add_memory_usage(estimated_memory)?;
self.props_mut().on_new_var();
let step_size = self.float_step_size();
let var_id = self.vars_mut().new_var_with_bounds_and_step(inferred_min, inferred_max, step_size);
Ok(var_id)
}
fn infer_bounds(&self, min: Val, max: Val) -> (Val, Val) {
match (min, max) {
(Val::ValI(min_i), Val::ValI(max_i)) => {
let is_unbounded = min_i == i32::MIN || max_i == i32::MAX;
if !is_unbounded {
return (min, max); }
let mut global_min: Option<i32> = None;
let mut global_max: Option<i32> = None;
for var_idx in 0..self.vars.count() {
let var_id = crate::variables::VarId::from_index(var_idx);
match &self.vars[var_id] {
crate::variables::Var::VarI(sparse_set) => {
if !sparse_set.is_empty() {
let v_min = sparse_set.min();
let v_max = sparse_set.max();
if v_min != i32::MIN && v_max != i32::MAX {
global_min = Some(global_min.map_or(v_min, |gmin| gmin.min(v_min)));
global_max = Some(global_max.map_or(v_max, |gmax| gmax.max(v_max)));
}
}
}
_ => {} }
}
let (inferred_min, inferred_max) = if let (Some(gmin), Some(gmax)) = (global_min, global_max) {
let factor = self.config().unbounded_inference_factor as i64;
let span = (gmax as i64) - (gmin as i64);
let expansion = span.saturating_mul(factor);
let new_min = ((gmin as i64).saturating_sub(expansion)).max(i32::MIN as i64 + 1) as i32;
let new_max = ((gmax as i64).saturating_add(expansion)).min(i32::MAX as i64 - 1) as i32;
let domain_size = (new_max as i64) - (new_min as i64) + 1;
if domain_size > crate::variables::domain::MAX_SPARSE_SET_DOMAIN_SIZE as i64 {
let center = ((gmin as i64 + gmax as i64) / 2) as i32;
let half_limit = (crate::variables::domain::MAX_SPARSE_SET_DOMAIN_SIZE / 2) as i32;
(center.saturating_sub(half_limit), center.saturating_add(half_limit - 1))
} else {
(new_min, new_max)
}
} else {
(DEFAULT_INT_FALLBACK_MIN, DEFAULT_INT_FALLBACK_MAX)
};
(Val::ValI(inferred_min), Val::ValI(inferred_max))
}
(Val::ValF(min_f), Val::ValF(max_f)) => {
let is_unbounded = min_f.is_infinite() || max_f.is_infinite() ||
min_f.is_nan() || max_f.is_nan();
if !is_unbounded {
return (min, max); }
let mut global_min: Option<f64> = None;
let mut global_max: Option<f64> = None;
for var_idx in 0..self.vars.count() {
let var_id = crate::variables::VarId::from_index(var_idx);
match &self.vars[var_id] {
crate::variables::Var::VarF(interval) => {
let v_min = interval.min;
let v_max = interval.max;
if v_min.is_finite() && v_max.is_finite() {
global_min = Some(global_min.map_or(v_min, |gmin| gmin.min(v_min)));
global_max = Some(global_max.map_or(v_max, |gmax| gmax.max(v_max)));
}
}
_ => {} }
}
let (inferred_min, inferred_max) = if let (Some(gmin), Some(gmax)) = (global_min, global_max) {
let factor = self.config().unbounded_inference_factor as f64;
let span = gmax - gmin;
let expansion = span * factor;
let new_min = (gmin - expansion).max(-1e308);
let new_max = (gmax + expansion).min(1e308);
(new_min, new_max)
} else {
(DEFAULT_FLOAT_FALLBACK_MIN, DEFAULT_FLOAT_FALLBACK_MAX)
};
(Val::ValF(inferred_min), Val::ValF(inferred_max))
}
(Val::ValI(min_i), Val::ValF(max_f)) => {
let min_as_float = min_i as f64;
self.infer_bounds(Val::ValF(min_as_float), Val::ValF(max_f))
}
(Val::ValF(min_f), Val::ValI(max_i)) => {
let max_as_float = max_i as f64;
self.infer_bounds(Val::ValF(min_f), Val::ValF(max_as_float))
}
}
}
pub(crate) fn add_memory_usage(&mut self, bytes: u64) -> Result<(), SolverError> {
self.add_estimated_memory(bytes);
if let Some(limit_mb) = self.config().max_memory_mb {
let limit_bytes = limit_mb * 1024 * 1024;
if self.estimated_memory_bytes() > limit_bytes {
self.set_memory_limit_exceeded();
return Err(SolverError::MemoryLimit {
usage_mb: Some(self.estimated_memory_mb() as usize),
limit_mb: Some(limit_mb as usize),
});
}
}
Ok(())
}
pub(crate) fn estimate_variable_memory(&self, min: Val, max: Val) -> u64 {
match (min, max) {
(Val::ValI(min_i), Val::ValI(max_i)) => {
if min_i > max_i {
return 96; }
let domain_size = match max_i.checked_sub(min_i) {
Some(diff) => match diff.checked_add(1) {
Some(size) => size as u64,
None => u64::MAX, },
None => u64::MAX, };
let base_cost = 96;
let domain_cost = if domain_size > 1000 {
let vec_overhead = 24 * 2; let data_cost = domain_size.saturating_mul(4).saturating_mul(2); vec_overhead + data_cost / 8 } else {
let vec_overhead = 24 * 2;
let data_cost = domain_size.saturating_mul(4).saturating_mul(2); vec_overhead + data_cost
};
base_cost + domain_cost
}
(Val::ValF(_), Val::ValF(_)) => {
let base_cost = 32; let wrapper_cost = 32; base_cost + wrapper_cost
}
_ => {
64
}
}
}
#[doc(hidden)]
pub fn int_vars(
&mut self,
n: usize,
min: i32,
max: i32,
) -> impl Iterator<Item = VarId> + '_ {
self.new_vars(n, Val::ValI(min), Val::ValI(max))
}
#[doc(hidden)]
pub fn float_vars(
&mut self,
n: usize,
min: f64,
max: f64,
) -> impl Iterator<Item = VarId> + '_ {
self.new_vars(n, Val::ValF(min), Val::ValF(max))
}
#[doc(hidden)]
pub fn new_var_binary(&mut self) -> VarIdBin {
VarIdBin(self.new_var_unchecked(Val::ValI(0), Val::ValI(1)))
}
#[doc(hidden)]
pub fn new_vars_binary(&mut self, n: usize) -> impl Iterator<Item = VarIdBin> + '_ {
std::iter::repeat_with(|| self.new_var_binary()).take(n)
}
#[doc(hidden)]
pub fn binary(&mut self) -> VarIdBin {
self.new_var_binary()
}
}