#[cfg(feature = "clp")]
use cobre_solver::{ClpAlgorithm, ClpProfile};
#[cfg(feature = "highs")]
use cobre_solver::{DEFAULT_PROFILE_HEURISTIC_SENTINEL, HighsProfile};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Phase {
Forward,
Backward,
Simulation,
}
pub trait PhaseProfiles: Sized {
const FORWARD: Self;
const BACKWARD: Self;
const SIMULATION: Self;
}
#[cfg(feature = "highs")]
impl PhaseProfiles for HighsProfile {
const FORWARD: Self = HighsProfile {
primal_feasibility_tolerance: 1e-9,
dual_feasibility_tolerance: 1e-9,
simplex_iteration_limit: DEFAULT_PROFILE_HEURISTIC_SENTINEL,
ipm_iteration_limit: 10_000,
simplex_dual_edge_weight_strategy: 1, simplex_scale_strategy: 0, simplex_price_strategy: 2, };
const BACKWARD: Self = HighsProfile {
primal_feasibility_tolerance: 1e-9,
dual_feasibility_tolerance: 1e-9,
simplex_iteration_limit: DEFAULT_PROFILE_HEURISTIC_SENTINEL,
ipm_iteration_limit: 10_000,
simplex_dual_edge_weight_strategy: 1, simplex_scale_strategy: 0, simplex_price_strategy: 2, };
const SIMULATION: Self = HighsProfile {
primal_feasibility_tolerance: 1e-9,
dual_feasibility_tolerance: 1e-9,
simplex_iteration_limit: DEFAULT_PROFILE_HEURISTIC_SENTINEL,
ipm_iteration_limit: 10_000,
simplex_dual_edge_weight_strategy: 1, simplex_scale_strategy: 0, simplex_price_strategy: 2, };
}
#[cfg(feature = "clp")]
impl PhaseProfiles for ClpProfile {
const FORWARD: Self = ClpProfile {
perturbation: 102,
scaling: 0,
primal_feasibility_tolerance: 1e-9,
dual_feasibility_tolerance: 1e-9,
simplex_iteration_limit: cobre_solver::DEFAULT_PROFILE_HEURISTIC_SENTINEL,
algorithm: ClpAlgorithm::Dual,
dual_pricing_mode: 1, factorization_frequency: 200, };
const BACKWARD: Self = ClpProfile {
perturbation: 102,
scaling: 0,
primal_feasibility_tolerance: 1e-9,
dual_feasibility_tolerance: 1e-9,
simplex_iteration_limit: cobre_solver::DEFAULT_PROFILE_HEURISTIC_SENTINEL,
algorithm: ClpAlgorithm::Dual,
dual_pricing_mode: 1, factorization_frequency: 200, };
const SIMULATION: Self = ClpProfile {
perturbation: 102,
scaling: 0,
primal_feasibility_tolerance: 1e-9,
dual_feasibility_tolerance: 1e-9,
simplex_iteration_limit: cobre_solver::DEFAULT_PROFILE_HEURISTIC_SENTINEL,
algorithm: ClpAlgorithm::Primal,
dual_pricing_mode: 1, factorization_frequency: 200, };
}
#[cfg(feature = "highs")]
pub const FORWARD_PROFILE: HighsProfile = HighsProfile {
primal_feasibility_tolerance: 1e-9,
dual_feasibility_tolerance: 1e-9,
simplex_iteration_limit: DEFAULT_PROFILE_HEURISTIC_SENTINEL,
ipm_iteration_limit: 10_000,
simplex_dual_edge_weight_strategy: 1, simplex_scale_strategy: 0, simplex_price_strategy: 2, };
#[cfg(feature = "highs")]
pub const BACKWARD_PROFILE: HighsProfile = HighsProfile {
primal_feasibility_tolerance: 1e-9,
dual_feasibility_tolerance: 1e-9,
simplex_iteration_limit: DEFAULT_PROFILE_HEURISTIC_SENTINEL,
ipm_iteration_limit: 10_000,
simplex_dual_edge_weight_strategy: 1, simplex_scale_strategy: 0, simplex_price_strategy: 2, };
#[cfg(feature = "highs")]
pub const SIMULATION_PROFILE: HighsProfile = HighsProfile {
primal_feasibility_tolerance: 1e-9,
dual_feasibility_tolerance: 1e-9,
simplex_iteration_limit: DEFAULT_PROFILE_HEURISTIC_SENTINEL,
ipm_iteration_limit: 10_000,
simplex_dual_edge_weight_strategy: 1, simplex_scale_strategy: 0, simplex_price_strategy: 2, };
impl Phase {
#[must_use]
pub fn profile(self) -> cobre_solver::ActiveProfile {
match self {
Phase::Forward => <cobre_solver::ActiveProfile as PhaseProfiles>::FORWARD,
Phase::Backward => <cobre_solver::ActiveProfile as PhaseProfiles>::BACKWARD,
Phase::Simulation => <cobre_solver::ActiveProfile as PhaseProfiles>::SIMULATION,
}
}
}
#[cfg(feature = "highs")]
const _: () = {
assert!(FORWARD_PROFILE.primal_feasibility_tolerance == 1e-9);
assert!(FORWARD_PROFILE.dual_feasibility_tolerance == 1e-9);
assert!(FORWARD_PROFILE.simplex_iteration_limit == DEFAULT_PROFILE_HEURISTIC_SENTINEL);
assert!(FORWARD_PROFILE.ipm_iteration_limit == 10_000);
assert!(FORWARD_PROFILE.simplex_dual_edge_weight_strategy == 1);
assert!(FORWARD_PROFILE.simplex_scale_strategy == 0);
assert!(FORWARD_PROFILE.simplex_price_strategy == 2);
assert!(BACKWARD_PROFILE.primal_feasibility_tolerance == 1e-9);
assert!(BACKWARD_PROFILE.dual_feasibility_tolerance == 1e-9);
assert!(BACKWARD_PROFILE.simplex_iteration_limit == DEFAULT_PROFILE_HEURISTIC_SENTINEL);
assert!(BACKWARD_PROFILE.ipm_iteration_limit == 10_000);
assert!(BACKWARD_PROFILE.simplex_dual_edge_weight_strategy == 1);
assert!(BACKWARD_PROFILE.simplex_scale_strategy == 0);
assert!(BACKWARD_PROFILE.simplex_price_strategy == 2);
assert!(SIMULATION_PROFILE.primal_feasibility_tolerance == 1e-9);
assert!(SIMULATION_PROFILE.dual_feasibility_tolerance == 1e-9);
assert!(SIMULATION_PROFILE.simplex_iteration_limit == DEFAULT_PROFILE_HEURISTIC_SENTINEL);
assert!(SIMULATION_PROFILE.ipm_iteration_limit == 10_000);
assert!(SIMULATION_PROFILE.simplex_dual_edge_weight_strategy == 1);
assert!(SIMULATION_PROFILE.simplex_scale_strategy == 0);
assert!(SIMULATION_PROFILE.simplex_price_strategy == 2);
assert!(matches!(
<HighsProfile as PhaseProfiles>::FORWARD.simplex_price_strategy,
2
));
assert!(matches!(
<HighsProfile as PhaseProfiles>::BACKWARD.simplex_price_strategy,
2
));
assert!(matches!(
<HighsProfile as PhaseProfiles>::SIMULATION.simplex_price_strategy,
2
));
};
#[cfg(all(test, feature = "highs"))]
mod highs_tests {
use cobre_solver::HighsProfile;
use super::{BACKWARD_PROFILE, FORWARD_PROFILE, Phase, PhaseProfiles, SIMULATION_PROFILE};
#[test]
fn phase_profile_returns_matching_constant() {
assert_eq!(Phase::Forward.profile(), FORWARD_PROFILE);
assert_eq!(Phase::Backward.profile(), BACKWARD_PROFILE);
assert_eq!(Phase::Simulation.profile(), SIMULATION_PROFILE);
}
#[test]
fn forward_simulation_equal_tuned_profile() {
let default = HighsProfile::default();
assert_eq!(FORWARD_PROFILE, BACKWARD_PROFILE);
assert_eq!(SIMULATION_PROFILE, BACKWARD_PROFILE);
assert_ne!(FORWARD_PROFILE, default);
assert_ne!(SIMULATION_PROFILE, default);
assert_eq!(FORWARD_PROFILE.simplex_price_strategy, 2);
assert_eq!(SIMULATION_PROFILE.simplex_price_strategy, 2);
assert_eq!(default.simplex_price_strategy, 1);
let mut forward_relaxed = FORWARD_PROFILE;
forward_relaxed.simplex_price_strategy = default.simplex_price_strategy;
assert_eq!(forward_relaxed, default);
}
#[test]
fn backward_profile_overrides_only_price_strategy() {
let default = HighsProfile::default();
assert_ne!(BACKWARD_PROFILE, default);
assert_eq!(BACKWARD_PROFILE.simplex_price_strategy, 2);
assert_eq!(
BACKWARD_PROFILE.simplex_dual_edge_weight_strategy,
default.simplex_dual_edge_weight_strategy
);
assert_eq!(
BACKWARD_PROFILE.primal_feasibility_tolerance,
default.primal_feasibility_tolerance
);
assert_eq!(
BACKWARD_PROFILE.dual_feasibility_tolerance,
default.dual_feasibility_tolerance
);
assert_eq!(
BACKWARD_PROFILE.simplex_iteration_limit,
default.simplex_iteration_limit
);
assert_eq!(
BACKWARD_PROFILE.ipm_iteration_limit,
default.ipm_iteration_limit
);
assert_eq!(
BACKWARD_PROFILE.simplex_scale_strategy,
default.simplex_scale_strategy
);
}
#[test]
fn phase_profiles_forward_simulation_equal_tuned_profile() {
let default = HighsProfile::default();
let forward = <HighsProfile as PhaseProfiles>::FORWARD;
let simulation = <HighsProfile as PhaseProfiles>::SIMULATION;
let backward = <HighsProfile as PhaseProfiles>::BACKWARD;
assert_eq!(forward, backward);
assert_eq!(simulation, backward);
assert_ne!(forward, default);
assert_ne!(simulation, default);
assert_eq!(forward.simplex_price_strategy, 2);
assert_eq!(simulation.simplex_price_strategy, 2);
assert_eq!(default.simplex_price_strategy, 1);
}
#[test]
fn phase_profiles_backward_overrides_only_price_strategy() {
let default = HighsProfile::default();
let backward = <HighsProfile as PhaseProfiles>::BACKWARD;
assert_ne!(backward, default);
assert_eq!(backward.simplex_price_strategy, 2);
assert_eq!(
backward.simplex_dual_edge_weight_strategy,
default.simplex_dual_edge_weight_strategy
);
assert_eq!(
backward.primal_feasibility_tolerance,
default.primal_feasibility_tolerance
);
assert_eq!(
backward.dual_feasibility_tolerance,
default.dual_feasibility_tolerance
);
assert_eq!(
backward.simplex_iteration_limit,
default.simplex_iteration_limit
);
assert_eq!(backward.ipm_iteration_limit, default.ipm_iteration_limit);
assert_eq!(
backward.simplex_scale_strategy,
default.simplex_scale_strategy
);
}
#[test]
fn phase_profiles_bit_for_bit_match_named_constants() {
assert_eq!(<HighsProfile as PhaseProfiles>::FORWARD, FORWARD_PROFILE);
assert_eq!(<HighsProfile as PhaseProfiles>::BACKWARD, BACKWARD_PROFILE);
assert_eq!(
<HighsProfile as PhaseProfiles>::SIMULATION,
SIMULATION_PROFILE
);
}
}
#[cfg(all(test, feature = "clp"))]
mod clp_tests {
use cobre_solver::{ClpAlgorithm, ClpProfile};
use super::{Phase, PhaseProfiles};
#[test]
fn clp_phase_profiles_tuned_with_primal_simulation() {
let default = ClpProfile::default();
let forward = <ClpProfile as PhaseProfiles>::FORWARD;
let simulation = <ClpProfile as PhaseProfiles>::SIMULATION;
let backward = <ClpProfile as PhaseProfiles>::BACKWARD;
assert_eq!(forward, backward);
assert_ne!(simulation, backward);
assert_eq!(forward.algorithm, ClpAlgorithm::Dual);
assert_eq!(backward.algorithm, ClpAlgorithm::Dual);
assert_eq!(simulation.algorithm, ClpAlgorithm::Primal);
assert_eq!(
ClpProfile {
algorithm: ClpAlgorithm::Dual,
..simulation
},
backward,
"SIMULATION must equal the tuned profile except for the primal algorithm"
);
assert_ne!(forward, default);
assert_ne!(simulation, default);
assert_eq!(forward.dual_pricing_mode, 1);
assert_eq!(forward.factorization_frequency, 200);
assert_eq!(simulation.dual_pricing_mode, 1);
assert_eq!(simulation.factorization_frequency, 200);
assert_eq!(default.dual_pricing_mode, 3);
assert_eq!(default.factorization_frequency, 0);
}
#[test]
fn clp_backward_profile_overrides_only_pricing_and_factorization() {
let default = ClpProfile::default();
let backward = <ClpProfile as PhaseProfiles>::BACKWARD;
assert_ne!(backward, default);
assert_eq!(backward.dual_pricing_mode, 1);
assert_eq!(backward.factorization_frequency, 200);
assert_eq!(backward.perturbation, default.perturbation);
assert_eq!(backward.scaling, default.scaling);
assert_eq!(
backward.primal_feasibility_tolerance,
default.primal_feasibility_tolerance
);
assert_eq!(
backward.dual_feasibility_tolerance,
default.dual_feasibility_tolerance
);
assert_eq!(
backward.simplex_iteration_limit,
default.simplex_iteration_limit
);
assert_eq!(backward.algorithm, default.algorithm);
}
#[test]
fn phase_profile_returns_matching_clp_profile() {
assert_eq!(
Phase::Forward.profile(),
<ClpProfile as PhaseProfiles>::FORWARD
);
assert_eq!(
Phase::Backward.profile(),
<ClpProfile as PhaseProfiles>::BACKWARD
);
assert_eq!(
Phase::Simulation.profile(),
<ClpProfile as PhaseProfiles>::SIMULATION
);
}
}