use oxilean_kernel::{BinderInfo, Declaration, Environment, Expr, Level, Name};
use super::types::{
BanditEnvironment, BellmanFord, Dijkstra, DynamicProgramming, FloydWarshall, FordFulkerson,
HungarianSolver, InventoryModel, JobScheduler, KnapsackDP, LagrangianRelaxationSolver,
MdpSolver, MultiArmedBanditUcb, NetworkFlowGraph, NewsvendorModel, PrimMst, QueueingSystem,
ReliabilitySystem, SimplexSolver, TwoStageStochasticLP,
};
pub fn app(f: Expr, a: Expr) -> Expr {
Expr::App(Box::new(f), Box::new(a))
}
pub fn app2(f: Expr, a: Expr, b: Expr) -> Expr {
app(app(f, a), b)
}
pub fn app3(f: Expr, a: Expr, b: Expr, c: Expr) -> Expr {
app(app2(f, a, b), c)
}
pub fn cst(s: &str) -> Expr {
Expr::Const(Name::str(s), vec![])
}
pub fn prop() -> Expr {
Expr::Sort(Level::zero())
}
pub fn type0() -> Expr {
Expr::Sort(Level::succ(Level::zero()))
}
pub fn pi(bi: BinderInfo, name: &str, dom: Expr, body: Expr) -> Expr {
Expr::Pi(bi, Name::str(name), Box::new(dom), Box::new(body))
}
pub fn arrow(a: Expr, b: Expr) -> Expr {
pi(BinderInfo::Default, "_", a, b)
}
pub fn nat_ty() -> Expr {
cst("Nat")
}
pub fn real_ty() -> Expr {
cst("Real")
}
pub fn list_ty(elem: Expr) -> Expr {
app(cst("List"), elem)
}
pub fn fn_ty(dom: Expr, cod: Expr) -> Expr {
arrow(dom, cod)
}
pub fn bool_ty() -> Expr {
cst("Bool")
}
pub fn network_flow_ty() -> Expr {
arrow(
nat_ty(),
arrow(
list_ty(nat_ty()),
arrow(nat_ty(), arrow(nat_ty(), arrow(nat_ty(), prop()))),
),
)
}
pub fn scheduling_ty() -> Expr {
let pair = list_ty(nat_ty());
arrow(list_ty(pair), arrow(list_ty(nat_ty()), prop()))
}
pub fn inventory_ty() -> Expr {
arrow(
real_ty(),
arrow(real_ty(), arrow(real_ty(), arrow(real_ty(), prop()))),
)
}
pub fn queuing_ty() -> Expr {
arrow(real_ty(), arrow(real_ty(), arrow(nat_ty(), prop())))
}
pub fn max_flow_min_cut_ty() -> Expr {
prop()
}
pub fn ford_fulkerson_ty() -> Expr {
prop()
}
pub fn bellman_optimality_ty() -> Expr {
prop()
}
pub fn little_law_ty() -> Expr {
prop()
}
pub fn eoq_formula_ty() -> Expr {
prop()
}
pub fn lp_feasible_ty() -> Expr {
arrow(
nat_ty(),
arrow(
nat_ty(),
arrow(
list_ty(real_ty()),
arrow(
list_ty(list_ty(real_ty())),
arrow(list_ty(real_ty()), prop()),
),
),
),
)
}
pub fn simplex_optimal_ty() -> Expr {
prop()
}
pub fn lp_duality_ty() -> Expr {
prop()
}
pub fn revised_simplex_ty() -> Expr {
arrow(nat_ty(), arrow(nat_ty(), arrow(list_ty(real_ty()), prop())))
}
pub fn complementary_slackness_ty() -> Expr {
prop()
}
pub fn integer_programming_ty() -> Expr {
arrow(nat_ty(), arrow(nat_ty(), arrow(list_ty(real_ty()), prop())))
}
pub fn branch_and_bound_ty() -> Expr {
prop()
}
pub fn cutting_plane_ty() -> Expr {
prop()
}
pub fn branch_and_cut_ty() -> Expr {
prop()
}
pub fn set_cover_ty() -> Expr {
arrow(
nat_ty(),
arrow(list_ty(list_ty(nat_ty())), arrow(list_ty(nat_ty()), prop())),
)
}
pub fn knapsack_ty() -> Expr {
arrow(
nat_ty(),
arrow(
list_ty(nat_ty()),
arrow(list_ty(nat_ty()), arrow(nat_ty(), prop())),
),
)
}
pub fn graph_coloring_ty() -> Expr {
arrow(nat_ty(), arrow(list_ty(nat_ty()), arrow(nat_ty(), prop())))
}
pub fn chromatic_number_ty() -> Expr {
arrow(nat_ty(), arrow(nat_ty(), prop()))
}
pub fn minimum_spanning_tree_ty() -> Expr {
arrow(nat_ty(), arrow(list_ty(nat_ty()), arrow(nat_ty(), prop())))
}
pub fn shortest_path_ty() -> Expr {
arrow(
nat_ty(),
arrow(
list_ty(nat_ty()),
arrow(nat_ty(), arrow(nat_ty(), arrow(list_ty(nat_ty()), prop()))),
),
)
}
pub fn dijkstra_correctness_ty() -> Expr {
prop()
}
pub fn bellman_ford_correctness_ty() -> Expr {
prop()
}
pub fn floyd_warshall_correctness_ty() -> Expr {
prop()
}
pub fn tsp_tour_ty() -> Expr {
arrow(
nat_ty(),
arrow(
list_ty(nat_ty()),
arrow(list_ty(nat_ty()), arrow(nat_ty(), prop())),
),
)
}
pub fn tsp_lower_bound_ty() -> Expr {
prop()
}
pub fn vehicle_routing_ty() -> Expr {
arrow(nat_ty(), arrow(nat_ty(), arrow(list_ty(nat_ty()), prop())))
}
pub fn makespan_minimization_ty() -> Expr {
arrow(list_ty(list_ty(nat_ty())), arrow(nat_ty(), prop()))
}
pub fn flow_shop_scheduling_ty() -> Expr {
arrow(
nat_ty(),
arrow(
nat_ty(),
arrow(list_ty(list_ty(nat_ty())), arrow(nat_ty(), prop())),
),
)
}
pub fn assignment_problem_ty() -> Expr {
arrow(
nat_ty(),
arrow(list_ty(list_ty(nat_ty())), arrow(list_ty(nat_ty()), prop())),
)
}
pub fn hungarian_algorithm_ty() -> Expr {
prop()
}
pub fn quadratic_program_ty() -> Expr {
arrow(
nat_ty(),
arrow(list_ty(real_ty()), arrow(list_ty(real_ty()), prop())),
)
}
pub fn two_stage_stochastic_ty() -> Expr {
prop()
}
pub fn scenario_programming_ty() -> Expr {
arrow(nat_ty(), arrow(list_ty(real_ty()), prop()))
}
pub fn robust_optimization_ty() -> Expr {
prop()
}
pub fn bellman_equation_ty() -> Expr {
arrow(
fn_ty(nat_ty(), real_ty()),
arrow(fn_ty(nat_ty(), real_ty()), prop()),
)
}
pub fn mg1_queue_ty() -> Expr {
arrow(real_ty(), arrow(real_ty(), arrow(real_ty(), prop())))
}
pub fn pollaczek_khinchine_ty() -> Expr {
prop()
}
pub fn newsvendor_model_ty() -> Expr {
arrow(
real_ty(),
arrow(real_ty(), arrow(real_ty(), arrow(real_ty(), prop()))),
)
}
pub fn series_system_ty() -> Expr {
arrow(list_ty(real_ty()), arrow(real_ty(), prop()))
}
pub fn parallel_system_ty() -> Expr {
arrow(list_ty(real_ty()), arrow(real_ty(), prop()))
}
pub fn event_driven_simulation_ty() -> Expr {
arrow(nat_ty(), arrow(real_ty(), prop()))
}
pub fn monte_carlo_estimator_ty() -> Expr {
arrow(nat_ty(), arrow(real_ty(), arrow(real_ty(), prop())))
}
pub fn build_operations_research_env() -> Environment {
let mut env = Environment::new();
let axioms: &[(&str, Expr)] = &[
("NetworkFlow", network_flow_ty()),
("Scheduling", scheduling_ty()),
("Inventory", inventory_ty()),
("QueueingSystem", queuing_ty()),
("MaxFlowMinCut", max_flow_min_cut_ty()),
("ford_fulkerson", ford_fulkerson_ty()),
("bellman_optimality", bellman_optimality_ty()),
("little_law", little_law_ty()),
("eoq_formula", eoq_formula_ty()),
("EdfSchedule", prop()),
("SjfSchedule", prop()),
("DpOptimal", prop()),
("LpFeasible", lp_feasible_ty()),
("simplex_optimal", simplex_optimal_ty()),
("lp_duality", lp_duality_ty()),
("RevisedSimplex", revised_simplex_ty()),
("complementary_slackness", complementary_slackness_ty()),
("IntegerProgramming", integer_programming_ty()),
("branch_and_bound", branch_and_bound_ty()),
("cutting_plane", cutting_plane_ty()),
("branch_and_cut", branch_and_cut_ty()),
("SetCover", set_cover_ty()),
("Knapsack", knapsack_ty()),
("GraphColoring", graph_coloring_ty()),
("ChromaticNumber", chromatic_number_ty()),
("MinimumSpanningTree", minimum_spanning_tree_ty()),
("ShortestPath", shortest_path_ty()),
("dijkstra_correctness", dijkstra_correctness_ty()),
("bellman_ford_correctness", bellman_ford_correctness_ty()),
(
"floyd_warshall_correctness",
floyd_warshall_correctness_ty(),
),
("TspTour", tsp_tour_ty()),
("tsp_lower_bound", tsp_lower_bound_ty()),
("VehicleRouting", vehicle_routing_ty()),
("MakespanMinimization", makespan_minimization_ty()),
("FlowShopScheduling", flow_shop_scheduling_ty()),
("AssignmentProblem", assignment_problem_ty()),
("hungarian_algorithm", hungarian_algorithm_ty()),
("QuadraticProgram", quadratic_program_ty()),
("two_stage_stochastic", two_stage_stochastic_ty()),
("ScenarioProgramming", scenario_programming_ty()),
("robust_optimization", robust_optimization_ty()),
("BellmanEquation", bellman_equation_ty()),
("MG1Queue", mg1_queue_ty()),
("pollaczek_khinchine", pollaczek_khinchine_ty()),
("NewsvendorModel", newsvendor_model_ty()),
("SeriesSystem", series_system_ty()),
("ParallelSystem", parallel_system_ty()),
("EventDrivenSimulation", event_driven_simulation_ty()),
("MonteCarloEstimator", monte_carlo_estimator_ty()),
];
for (name, ty) in axioms {
env.add(Declaration::Axiom {
name: Name::str(*name),
univ_params: vec![],
ty: ty.clone(),
})
.ok();
}
env
}
pub(super) fn binomial_coeff(n: usize, k: usize) -> u64 {
if k > n {
return 0;
}
let k = k.min(n - k);
let mut result = 1_u64;
for i in 0..k {
result = result * (n - i) as u64 / (i + 1) as u64;
}
result
}
pub fn branch_and_bound_completeness_ty() -> Expr {
prop()
}
pub fn lp_relaxation_bound_ty() -> Expr {
prop()
}
pub fn gomory_cut_ty() -> Expr {
arrow(
nat_ty(),
arrow(list_ty(real_ty()), arrow(real_ty(), prop())),
)
}
pub fn mixed_integer_gomory_cut_ty() -> Expr {
prop()
}
pub fn lift_and_project_cut_ty() -> Expr {
arrow(nat_ty(), prop())
}
pub fn split_cut_ty() -> Expr {
arrow(list_ty(real_ty()), arrow(real_ty(), prop()))
}
pub fn dantzig_wolfe_decomposition_ty() -> Expr {
prop()
}
pub fn dantzig_wolfe_master_problem_ty() -> Expr {
arrow(nat_ty(), arrow(list_ty(real_ty()), prop()))
}
pub fn benders_decomposition_ty() -> Expr {
prop()
}
pub fn benders_feasibility_cut_ty() -> Expr {
arrow(list_ty(real_ty()), arrow(real_ty(), prop()))
}
pub fn benders_optimality_cut_ty() -> Expr {
arrow(list_ty(real_ty()), arrow(real_ty(), prop()))
}
pub fn column_generation_ty() -> Expr {
arrow(fn_ty(list_ty(real_ty()), real_ty()), prop())
}
pub fn lagrangian_relaxation_ty() -> Expr {
arrow(
list_ty(real_ty()),
arrow(fn_ty(list_ty(real_ty()), real_ty()), real_ty()),
)
}
pub fn subgradient_update_ty() -> Expr {
arrow(
list_ty(real_ty()),
arrow(list_ty(real_ty()), arrow(real_ty(), list_ty(real_ty()))),
)
}
pub fn lagrangian_dual_ty() -> Expr {
arrow(fn_ty(list_ty(real_ty()), real_ty()), real_ty())
}
pub fn semidefinite_program_ty() -> Expr {
arrow(nat_ty(), arrow(list_ty(real_ty()), prop()))
}
pub fn psd_constraint_ty() -> Expr {
arrow(type0(), prop())
}
pub fn sdp_duality_ty() -> Expr {
prop()
}
pub fn second_order_cone_program_ty() -> Expr {
arrow(nat_ty(), arrow(nat_ty(), prop()))
}
pub fn socp_duality_ty() -> Expr {
prop()
}
pub fn cone_program_ty() -> Expr {
arrow(type0(), prop())
}
pub fn minimax_regret_ty() -> Expr {
arrow(type0(), prop())
}
pub fn box_uncertainty_set_ty() -> Expr {
arrow(list_ty(real_ty()), arrow(list_ty(real_ty()), type0()))
}
pub fn ellipsoidal_uncertainty_set_ty() -> Expr {
arrow(nat_ty(), arrow(real_ty(), type0()))
}
pub fn robust_counterpart_ty() -> Expr {
arrow(type0(), prop())
}
pub fn distributionally_robust_optimization_ty() -> Expr {
arrow(type0(), prop())
}
pub fn chance_constraint_ty() -> Expr {
arrow(real_ty(), prop())
}
pub fn cvar_ty() -> Expr {
arrow(real_ty(), arrow(fn_ty(real_ty(), real_ty()), real_ty()))
}
pub fn sample_average_approximation_ty() -> Expr {
arrow(nat_ty(), prop())
}
pub fn progressive_hedging_ty() -> Expr {
arrow(nat_ty(), arrow(real_ty(), prop()))
}
pub fn markov_decision_process_ty() -> Expr {
arrow(nat_ty(), arrow(nat_ty(), arrow(real_ty(), prop())))
}
pub fn bellman_optimality_equation_ty() -> Expr {
arrow(fn_ty(nat_ty(), real_ty()), prop())
}
pub fn value_iteration_convergence_ty() -> Expr {
arrow(real_ty(), prop())
}
pub fn policy_iteration_convergence_ty() -> Expr {
arrow(nat_ty(), prop())
}
pub fn optimal_policy_ty() -> Expr {
arrow(nat_ty(), arrow(nat_ty(), prop()))
}
pub fn q_function_ty() -> Expr {
arrow(nat_ty(), arrow(nat_ty(), real_ty()))
}
pub fn bellman_contraction_ty() -> Expr {
arrow(real_ty(), prop())
}
pub fn pomdp_ty() -> Expr {
arrow(
nat_ty(),
arrow(nat_ty(), arrow(nat_ty(), arrow(real_ty(), prop()))),
)
}
pub fn belief_state_ty() -> Expr {
arrow(nat_ty(), type0())
}
pub fn pomdp_value_function_ty() -> Expr {
arrow(nat_ty(), prop())
}
pub fn multi_armed_bandit_ty() -> Expr {
arrow(nat_ty(), prop())
}
pub fn ucb1_index_ty() -> Expr {
arrow(nat_ty(), arrow(real_ty(), arrow(nat_ty(), real_ty())))
}
pub fn ucb_regret_bound_ty() -> Expr {
arrow(nat_ty(), arrow(nat_ty(), real_ty()))
}
pub fn thompson_sampling_ty() -> Expr {
arrow(nat_ty(), prop())
}
pub fn explore_exploit_tradeoff_ty() -> Expr {
prop()
}
pub fn register_operations_research_extended(env: &mut Environment) -> Result<(), String> {
let axioms: &[(&str, Expr)] = &[
(
"BranchAndBoundCompleteness",
branch_and_bound_completeness_ty(),
),
("LpRelaxationBound", lp_relaxation_bound_ty()),
("GomoryCut", gomory_cut_ty()),
("MixedIntegerGomoryCut", mixed_integer_gomory_cut_ty()),
("LiftAndProjectCut", lift_and_project_cut_ty()),
("SplitCut", split_cut_ty()),
(
"DantzigWolfeDecomposition",
dantzig_wolfe_decomposition_ty(),
),
(
"DantzigWolfeMasterProblem",
dantzig_wolfe_master_problem_ty(),
),
("BendersDecomposition", benders_decomposition_ty()),
("BendersFeasibilityCut", benders_feasibility_cut_ty()),
("BendersOptimalityCut", benders_optimality_cut_ty()),
("ColumnGeneration", column_generation_ty()),
("LagrangianRelaxation", lagrangian_relaxation_ty()),
("SubgradientUpdate", subgradient_update_ty()),
("LagrangianDual", lagrangian_dual_ty()),
("SemidefiniteProgram", semidefinite_program_ty()),
("PSDConstraint", psd_constraint_ty()),
("SDPDuality", sdp_duality_ty()),
("SecondOrderConeProgram", second_order_cone_program_ty()),
("SOCPDuality", socp_duality_ty()),
("ConeProgram", cone_program_ty()),
("MinimaxRegret", minimax_regret_ty()),
("BoxUncertaintySet", box_uncertainty_set_ty()),
(
"EllipsoidalUncertaintySet",
ellipsoidal_uncertainty_set_ty(),
),
("RobustCounterpart", robust_counterpart_ty()),
(
"DistributionallyRobustOptimization",
distributionally_robust_optimization_ty(),
),
("ChanceConstraint", chance_constraint_ty()),
("CVaR", cvar_ty()),
(
"SampleAverageApproximation",
sample_average_approximation_ty(),
),
("ProgressiveHedging", progressive_hedging_ty()),
("MarkovDecisionProcess", markov_decision_process_ty()),
(
"BellmanOptimalityEquation",
bellman_optimality_equation_ty(),
),
(
"ValueIterationConvergence",
value_iteration_convergence_ty(),
),
(
"PolicyIterationConvergence",
policy_iteration_convergence_ty(),
),
("OptimalPolicy", optimal_policy_ty()),
("QFunction", q_function_ty()),
("BellmanContraction", bellman_contraction_ty()),
("POMDP", pomdp_ty()),
("BeliefState", belief_state_ty()),
("POMDPValueFunction", pomdp_value_function_ty()),
("MultiArmedBandit", multi_armed_bandit_ty()),
("UCB1Index", ucb1_index_ty()),
("UCBRegretBound", ucb_regret_bound_ty()),
("ThompsonSampling", thompson_sampling_ty()),
("ExploreExploitTradeoff", explore_exploit_tradeoff_ty()),
];
for (name, ty) in axioms {
env.add(Declaration::Axiom {
name: Name::str(*name),
univ_params: vec![],
ty: ty.clone(),
})
.map_err(|e| format!("Failed to add {name}: {e:?}"))?;
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_queueing_utilization() {
let qs = QueueingSystem::new(3.0, 5.0, 1);
assert!((qs.utilization() - 0.6).abs() < 1e-9);
}
#[test]
fn test_queueing_mean_queue_length() {
let qs = QueueingSystem::new(1.0, 2.0, 1);
let l = qs
.mean_queue_length_m_m_1()
.expect("mean_queue_length_m_m_1 should succeed");
assert!((l - 1.0).abs() < 1e-9, "L={l}");
}
#[test]
fn test_network_flow_simple() {
let mut g = NetworkFlowGraph::new(4);
g.add_edge(0, 1, 10);
g.add_edge(0, 2, 10);
g.add_edge(1, 3, 10);
g.add_edge(2, 3, 10);
assert_eq!(g.max_flow_bfs(0, 3), 20);
}
#[test]
fn test_job_scheduler_edf() {
let mut sched = JobScheduler::new();
sched.add_job("A", 2, 5);
sched.add_job("B", 3, 3);
sched.add_job("C", 1, 7);
let order = sched.earliest_deadline_first();
assert_eq!(order, vec!["B", "A", "C"]);
}
#[test]
fn test_dp_knapsack() {
let weights = [2, 3, 4, 5];
let values = [3, 4, 5, 6];
assert_eq!(DynamicProgramming::knapsack(5, &weights, &values), 7);
}
#[test]
fn test_dp_lcs() {
let s1 = b"ABCBDAB";
let s2 = b"BDCABA";
assert_eq!(DynamicProgramming::longest_common_subseq(s1, s2), 4);
}
#[test]
fn test_dp_coin_change() {
assert_eq!(
DynamicProgramming::coin_change(&[1, 5, 10, 25], 41),
Some(4)
);
assert_eq!(DynamicProgramming::coin_change(&[2], 3), None);
}
#[test]
fn test_inventory_eoq() {
let inv = InventoryModel::new(1000.0, 50.0, 5.0, 1.0);
let q = inv.eoq();
assert!((q - 200.0_f64.sqrt() * 10.0).abs() < 1e-3, "eoq={q}");
let tc = inv.total_cost(q);
assert!(tc > 0.0);
}
#[test]
fn test_simplex_solver_basic() {
let obj = vec![-1.0, -1.0];
let a = vec![vec![1.0, 1.0], vec![1.0, 0.0], vec![0.0, 1.0]];
let b = vec![4.0, 3.0, 3.0];
let solver = SimplexSolver::new(obj, a, b);
let val = solver.solve().expect("solve should succeed");
assert!((val - (-4.0)).abs() < 1e-6, "simplex val={val}");
}
#[test]
fn test_ford_fulkerson_simple() {
let mut ff = FordFulkerson::new(4);
ff.add_edge(0, 1, 10);
ff.add_edge(0, 2, 10);
ff.add_edge(1, 3, 10);
ff.add_edge(2, 3, 10);
assert_eq!(ff.max_flow(0, 3), 20);
}
#[test]
fn test_hungarian_solver_simple() {
let cost = vec![vec![9, 2, 7], vec![3, 6, 1], vec![5, 8, 4]];
let (min_cost, assignment) = HungarianSolver::new(cost).solve();
assert_eq!(
min_cost, 8,
"Hungarian cost={min_cost}, assignment={assignment:?}"
);
}
#[test]
fn test_bellman_ford_basic() {
let mut bf = BellmanFord::new(5);
bf.add_edge(0, 1, 6);
bf.add_edge(0, 2, 7);
bf.add_edge(1, 2, 8);
bf.add_edge(1, 3, 5);
bf.add_edge(1, 4, -4);
bf.add_edge(2, 3, -3);
bf.add_edge(2, 4, 9);
bf.add_edge(3, 1, -2);
bf.add_edge(4, 0, 2);
bf.add_edge(4, 3, 7);
let dist = bf.shortest_paths(0).expect("shortest_paths should succeed");
assert_eq!(dist[0], 0);
assert_eq!(dist[1], 2);
assert_eq!(dist[2], 7);
}
#[test]
fn test_knapsack_dp_with_selection() {
let solver = KnapsackDP::new(5, vec![2, 3, 4, 5], vec![3, 4, 5, 6]);
let (val, selected) = solver.solve();
assert_eq!(val, 7, "knapsack value={val}");
assert!(
selected.contains(&0) && selected.contains(&1),
"selected={selected:?}"
);
}
#[test]
fn test_dijkstra_basic() {
let mut g = Dijkstra::new(5);
g.add_edge(0, 1, 10);
g.add_edge(0, 3, 5);
g.add_edge(1, 2, 1);
g.add_edge(3, 1, 3);
g.add_edge(3, 2, 9);
g.add_edge(2, 4, 4);
g.add_edge(3, 4, 2);
let dist = g.shortest_paths(0);
assert_eq!(dist[0], 0);
assert_eq!(dist[3], 5);
assert_eq!(dist[1], 8);
assert_eq!(dist[4], 7);
}
#[test]
fn test_floyd_warshall_basic() {
let mut fw = FloydWarshall::new(4);
fw.add_edge(0, 1, 3);
fw.add_edge(0, 3, 7);
fw.add_edge(1, 0, 8);
fw.add_edge(1, 2, 2);
fw.add_edge(2, 0, 5);
fw.add_edge(2, 3, 1);
fw.add_edge(3, 0, 2);
let d = fw.run().expect("run should succeed");
assert_eq!(d[0][2], 5);
assert_eq!(d[3][1], 5);
}
#[test]
fn test_prim_mst() {
let mut prim = PrimMst::new(4);
prim.add_edge(0, 1, 1);
prim.add_edge(0, 2, 4);
prim.add_edge(1, 2, 2);
prim.add_edge(1, 3, 5);
prim.add_edge(2, 3, 3);
let (total, _edges) = prim.run();
assert_eq!(total, 6, "MST weight={total}");
}
#[test]
fn test_newsvendor_optimal_quantity() {
let nv = NewsvendorModel::new(0.0, 100.0, 5.0, 10.0, 2.0);
let q = nv.optimal_quantity();
assert!((q - 62.5).abs() < 1e-6, "Q*={q}");
}
#[test]
fn test_reliability_series() {
let sys = ReliabilitySystem::new(vec![0.9, 0.8, 0.95]);
let r = sys.series_reliability();
assert!((r - 0.9 * 0.8 * 0.95).abs() < 1e-9, "series R={r}");
}
#[test]
fn test_reliability_parallel() {
let sys = ReliabilitySystem::new(vec![0.9, 0.9]);
let r = sys.parallel_reliability();
assert!((r - 0.99).abs() < 1e-9, "parallel R={r}");
}
#[test]
fn test_build_env_has_axioms() {
let env = build_operations_research_env();
assert!(env.get(&Name::str("ford_fulkerson")).is_some());
assert!(env.get(&Name::str("simplex_optimal")).is_some());
assert!(env.get(&Name::str("branch_and_bound")).is_some());
assert!(env.get(&Name::str("dijkstra_correctness")).is_some());
assert!(env.get(&Name::str("hungarian_algorithm")).is_some());
assert!(env.get(&Name::str("robust_optimization")).is_some());
assert!(env.get(&Name::str("pollaczek_khinchine")).is_some());
assert!(env.get(&Name::str("MonteCarloEstimator")).is_some());
}
#[test]
fn test_extended_env_has_axioms() {
use oxilean_kernel::Environment;
let mut env = Environment::new();
register_operations_research_extended(&mut env).expect("Environment::new should succeed");
let names = [
"BranchAndBoundCompleteness",
"GomoryCut",
"DantzigWolfeDecomposition",
"BendersDecomposition",
"ColumnGeneration",
"LagrangianRelaxation",
"SemidefiniteProgram",
"SecondOrderConeProgram",
"MinimaxRegret",
"ChanceConstraint",
"MarkovDecisionProcess",
"BellmanOptimalityEquation",
"ValueIterationConvergence",
"PolicyIterationConvergence",
"POMDP",
"MultiArmedBandit",
"UCB1Index",
"ThompsonSampling",
];
for name in &names {
assert!(
env.get(&Name::str(*name)).is_some(),
"Extended axiom '{name}' not found"
);
}
}
#[test]
fn test_mdp_value_iteration_simple() {
let n_states = 2;
let n_actions = 2;
let discount = 0.9_f64;
let rewards = vec![vec![-1.0, 0.0], vec![0.0, 1.0]];
let transitions = vec![
vec![vec![1.0, 0.0], vec![0.0, 1.0]],
vec![vec![0.0, 1.0], vec![0.0, 1.0]],
];
let mdp = MdpSolver::new(n_states, n_actions, discount, rewards, transitions);
let (v, _iters) = mdp.value_iteration(1e-6, 1000);
assert!(
v[1] > v[0],
"V(good state) should exceed V(bad state): V={v:?}"
);
}
#[test]
fn test_mdp_policy_extraction() {
let n_states = 2;
let n_actions = 2;
let discount = 0.9_f64;
let rewards = vec![vec![0.0, 0.5], vec![0.0, 1.0]];
let transitions = vec![
vec![vec![1.0, 0.0], vec![0.0, 1.0]],
vec![vec![0.0, 1.0], vec![0.0, 1.0]],
];
let mdp = MdpSolver::new(n_states, n_actions, discount, rewards, transitions);
let (v, _) = mdp.value_iteration(1e-6, 1000);
let policy = mdp.extract_policy(&v);
assert_eq!(policy[1], 1, "At state 1, should choose action 1");
}
#[test]
fn test_ucb_bandit_selects_best() {
let means = vec![0.2, 0.5, 0.8];
let mut env = BanditEnvironment::new(means.clone());
assert_eq!(env.optimal_arm(), 2, "Optimal arm should be 2");
let regret = env.run_ucb1(200);
assert!(regret >= 0.0, "Regret should be non-negative, got {regret}");
assert!(regret.is_finite(), "Regret should be finite, got {regret}");
}
#[test]
fn test_ucb1_index_infinite_for_unplayed() {
let bandit = MultiArmedBanditUcb::new(3);
assert_eq!(
bandit.ucb_index(0),
f64::INFINITY,
"Unplayed arm should have infinite UCB"
);
assert_eq!(bandit.select_arm(), 0, "Should select first unplayed arm");
}
#[test]
fn test_lagrangian_solver_polyak_step() {
let mut solver = LagrangianRelaxationSolver::new(2, 1.0);
let subgradient = [0.5, -0.3];
let step = solver.polyak_step(10.0, 8.0, &subgradient);
assert!(step > 0.0, "Polyak step should be positive, got {step}");
solver.subgradient_update(&subgradient, step);
assert!(
solver.multipliers[0] > 0.0,
"Multiplier 0 should increase: {}",
solver.multipliers[0]
);
assert!(
solver.multipliers[1] >= 0.0,
"Multiplier 1 should be non-negative: {}",
solver.multipliers[1]
);
}
#[test]
fn test_two_stage_stochastic_cost() {
let model = TwoStageStochasticLP::new(
vec![2.0],
vec![3.0],
vec![0.4, 0.6],
vec![vec![5.0], vec![7.0]],
);
let x = vec![1.0];
let y = vec![vec![2.0], vec![3.0]];
let expected_recourse = 0.4 * 6.0 + 0.6 * 9.0;
let recourse = model.expected_recourse_cost(&y);
assert!(
(recourse - expected_recourse).abs() < 1e-9,
"Expected recourse={expected_recourse}, got {recourse}"
);
let total = model.total_cost(&x, &y);
assert!(
(total - (2.0 + expected_recourse)).abs() < 1e-9,
"Total cost={total}"
);
assert!(model.is_stage1_feasible(&x), "x=[1.0] should be feasible");
}
}