1use oxilean_kernel::{BinderInfo, Declaration, Environment, Expr, Level, Name};
6
7use super::types::{
8 BanditEnvironment, BellmanFord, Dijkstra, DynamicProgramming, FloydWarshall, FordFulkerson,
9 HungarianSolver, InventoryModel, JobScheduler, KnapsackDP, LagrangianRelaxationSolver,
10 MdpSolver, MultiArmedBanditUcb, NetworkFlowGraph, NewsvendorModel, PrimMst, QueueingSystem,
11 ReliabilitySystem, SimplexSolver, TwoStageStochasticLP,
12};
13
14pub fn app(f: Expr, a: Expr) -> Expr {
15 Expr::App(Box::new(f), Box::new(a))
16}
17pub fn app2(f: Expr, a: Expr, b: Expr) -> Expr {
18 app(app(f, a), b)
19}
20pub fn app3(f: Expr, a: Expr, b: Expr, c: Expr) -> Expr {
21 app(app2(f, a, b), c)
22}
23pub fn cst(s: &str) -> Expr {
24 Expr::Const(Name::str(s), vec![])
25}
26pub fn prop() -> Expr {
27 Expr::Sort(Level::zero())
28}
29pub fn type0() -> Expr {
30 Expr::Sort(Level::succ(Level::zero()))
31}
32pub fn pi(bi: BinderInfo, name: &str, dom: Expr, body: Expr) -> Expr {
33 Expr::Pi(bi, Name::str(name), Box::new(dom), Box::new(body))
34}
35pub fn arrow(a: Expr, b: Expr) -> Expr {
36 pi(BinderInfo::Default, "_", a, b)
37}
38pub fn nat_ty() -> Expr {
39 cst("Nat")
40}
41pub fn real_ty() -> Expr {
42 cst("Real")
43}
44pub fn list_ty(elem: Expr) -> Expr {
45 app(cst("List"), elem)
46}
47pub fn fn_ty(dom: Expr, cod: Expr) -> Expr {
48 arrow(dom, cod)
49}
50pub fn bool_ty() -> Expr {
51 cst("Bool")
52}
53pub fn network_flow_ty() -> Expr {
56 arrow(
57 nat_ty(),
58 arrow(
59 list_ty(nat_ty()),
60 arrow(nat_ty(), arrow(nat_ty(), arrow(nat_ty(), prop()))),
61 ),
62 )
63}
64pub fn scheduling_ty() -> Expr {
67 let pair = list_ty(nat_ty());
68 arrow(list_ty(pair), arrow(list_ty(nat_ty()), prop()))
69}
70pub fn inventory_ty() -> Expr {
73 arrow(
74 real_ty(),
75 arrow(real_ty(), arrow(real_ty(), arrow(real_ty(), prop()))),
76 )
77}
78pub fn queuing_ty() -> Expr {
81 arrow(real_ty(), arrow(real_ty(), arrow(nat_ty(), prop())))
82}
83pub fn max_flow_min_cut_ty() -> Expr {
86 prop()
87}
88pub fn ford_fulkerson_ty() -> Expr {
91 prop()
92}
93pub fn bellman_optimality_ty() -> Expr {
96 prop()
97}
98pub fn little_law_ty() -> Expr {
101 prop()
102}
103pub fn eoq_formula_ty() -> Expr {
106 prop()
107}
108pub fn lp_feasible_ty() -> Expr {
111 arrow(
112 nat_ty(),
113 arrow(
114 nat_ty(),
115 arrow(
116 list_ty(real_ty()),
117 arrow(
118 list_ty(list_ty(real_ty())),
119 arrow(list_ty(real_ty()), prop()),
120 ),
121 ),
122 ),
123 )
124}
125pub fn simplex_optimal_ty() -> Expr {
129 prop()
130}
131pub fn lp_duality_ty() -> Expr {
135 prop()
136}
137pub fn revised_simplex_ty() -> Expr {
140 arrow(nat_ty(), arrow(nat_ty(), arrow(list_ty(real_ty()), prop())))
141}
142pub fn complementary_slackness_ty() -> Expr {
145 prop()
146}
147pub fn integer_programming_ty() -> Expr {
150 arrow(nat_ty(), arrow(nat_ty(), arrow(list_ty(real_ty()), prop())))
151}
152pub fn branch_and_bound_ty() -> Expr {
155 prop()
156}
157pub fn cutting_plane_ty() -> Expr {
160 prop()
161}
162pub fn branch_and_cut_ty() -> Expr {
165 prop()
166}
167pub fn set_cover_ty() -> Expr {
170 arrow(
171 nat_ty(),
172 arrow(list_ty(list_ty(nat_ty())), arrow(list_ty(nat_ty()), prop())),
173 )
174}
175pub fn knapsack_ty() -> Expr {
178 arrow(
179 nat_ty(),
180 arrow(
181 list_ty(nat_ty()),
182 arrow(list_ty(nat_ty()), arrow(nat_ty(), prop())),
183 ),
184 )
185}
186pub fn graph_coloring_ty() -> Expr {
189 arrow(nat_ty(), arrow(list_ty(nat_ty()), arrow(nat_ty(), prop())))
190}
191pub fn chromatic_number_ty() -> Expr {
194 arrow(nat_ty(), arrow(nat_ty(), prop()))
195}
196pub fn minimum_spanning_tree_ty() -> Expr {
199 arrow(nat_ty(), arrow(list_ty(nat_ty()), arrow(nat_ty(), prop())))
200}
201pub fn shortest_path_ty() -> Expr {
204 arrow(
205 nat_ty(),
206 arrow(
207 list_ty(nat_ty()),
208 arrow(nat_ty(), arrow(nat_ty(), arrow(list_ty(nat_ty()), prop()))),
209 ),
210 )
211}
212pub fn dijkstra_correctness_ty() -> Expr {
215 prop()
216}
217pub fn bellman_ford_correctness_ty() -> Expr {
220 prop()
221}
222pub fn floyd_warshall_correctness_ty() -> Expr {
225 prop()
226}
227pub fn tsp_tour_ty() -> Expr {
230 arrow(
231 nat_ty(),
232 arrow(
233 list_ty(nat_ty()),
234 arrow(list_ty(nat_ty()), arrow(nat_ty(), prop())),
235 ),
236 )
237}
238pub fn tsp_lower_bound_ty() -> Expr {
241 prop()
242}
243pub fn vehicle_routing_ty() -> Expr {
246 arrow(nat_ty(), arrow(nat_ty(), arrow(list_ty(nat_ty()), prop())))
247}
248pub fn makespan_minimization_ty() -> Expr {
251 arrow(list_ty(list_ty(nat_ty())), arrow(nat_ty(), prop()))
252}
253pub fn flow_shop_scheduling_ty() -> Expr {
256 arrow(
257 nat_ty(),
258 arrow(
259 nat_ty(),
260 arrow(list_ty(list_ty(nat_ty())), arrow(nat_ty(), prop())),
261 ),
262 )
263}
264pub fn assignment_problem_ty() -> Expr {
267 arrow(
268 nat_ty(),
269 arrow(list_ty(list_ty(nat_ty())), arrow(list_ty(nat_ty()), prop())),
270 )
271}
272pub fn hungarian_algorithm_ty() -> Expr {
275 prop()
276}
277pub fn quadratic_program_ty() -> Expr {
280 arrow(
281 nat_ty(),
282 arrow(list_ty(real_ty()), arrow(list_ty(real_ty()), prop())),
283 )
284}
285pub fn two_stage_stochastic_ty() -> Expr {
288 prop()
289}
290pub fn scenario_programming_ty() -> Expr {
293 arrow(nat_ty(), arrow(list_ty(real_ty()), prop()))
294}
295pub fn robust_optimization_ty() -> Expr {
298 prop()
299}
300pub fn bellman_equation_ty() -> Expr {
303 arrow(
304 fn_ty(nat_ty(), real_ty()),
305 arrow(fn_ty(nat_ty(), real_ty()), prop()),
306 )
307}
308pub fn mg1_queue_ty() -> Expr {
311 arrow(real_ty(), arrow(real_ty(), arrow(real_ty(), prop())))
312}
313pub fn pollaczek_khinchine_ty() -> Expr {
316 prop()
317}
318pub fn newsvendor_model_ty() -> Expr {
321 arrow(
322 real_ty(),
323 arrow(real_ty(), arrow(real_ty(), arrow(real_ty(), prop()))),
324 )
325}
326pub fn series_system_ty() -> Expr {
329 arrow(list_ty(real_ty()), arrow(real_ty(), prop()))
330}
331pub fn parallel_system_ty() -> Expr {
334 arrow(list_ty(real_ty()), arrow(real_ty(), prop()))
335}
336pub fn event_driven_simulation_ty() -> Expr {
339 arrow(nat_ty(), arrow(real_ty(), prop()))
340}
341pub fn monte_carlo_estimator_ty() -> Expr {
344 arrow(nat_ty(), arrow(real_ty(), arrow(real_ty(), prop())))
345}
346pub fn build_operations_research_env() -> Environment {
348 let mut env = Environment::new();
349 let axioms: &[(&str, Expr)] = &[
350 ("NetworkFlow", network_flow_ty()),
351 ("Scheduling", scheduling_ty()),
352 ("Inventory", inventory_ty()),
353 ("QueueingSystem", queuing_ty()),
354 ("MaxFlowMinCut", max_flow_min_cut_ty()),
355 ("ford_fulkerson", ford_fulkerson_ty()),
356 ("bellman_optimality", bellman_optimality_ty()),
357 ("little_law", little_law_ty()),
358 ("eoq_formula", eoq_formula_ty()),
359 ("EdfSchedule", prop()),
360 ("SjfSchedule", prop()),
361 ("DpOptimal", prop()),
362 ("LpFeasible", lp_feasible_ty()),
363 ("simplex_optimal", simplex_optimal_ty()),
364 ("lp_duality", lp_duality_ty()),
365 ("RevisedSimplex", revised_simplex_ty()),
366 ("complementary_slackness", complementary_slackness_ty()),
367 ("IntegerProgramming", integer_programming_ty()),
368 ("branch_and_bound", branch_and_bound_ty()),
369 ("cutting_plane", cutting_plane_ty()),
370 ("branch_and_cut", branch_and_cut_ty()),
371 ("SetCover", set_cover_ty()),
372 ("Knapsack", knapsack_ty()),
373 ("GraphColoring", graph_coloring_ty()),
374 ("ChromaticNumber", chromatic_number_ty()),
375 ("MinimumSpanningTree", minimum_spanning_tree_ty()),
376 ("ShortestPath", shortest_path_ty()),
377 ("dijkstra_correctness", dijkstra_correctness_ty()),
378 ("bellman_ford_correctness", bellman_ford_correctness_ty()),
379 (
380 "floyd_warshall_correctness",
381 floyd_warshall_correctness_ty(),
382 ),
383 ("TspTour", tsp_tour_ty()),
384 ("tsp_lower_bound", tsp_lower_bound_ty()),
385 ("VehicleRouting", vehicle_routing_ty()),
386 ("MakespanMinimization", makespan_minimization_ty()),
387 ("FlowShopScheduling", flow_shop_scheduling_ty()),
388 ("AssignmentProblem", assignment_problem_ty()),
389 ("hungarian_algorithm", hungarian_algorithm_ty()),
390 ("QuadraticProgram", quadratic_program_ty()),
391 ("two_stage_stochastic", two_stage_stochastic_ty()),
392 ("ScenarioProgramming", scenario_programming_ty()),
393 ("robust_optimization", robust_optimization_ty()),
394 ("BellmanEquation", bellman_equation_ty()),
395 ("MG1Queue", mg1_queue_ty()),
396 ("pollaczek_khinchine", pollaczek_khinchine_ty()),
397 ("NewsvendorModel", newsvendor_model_ty()),
398 ("SeriesSystem", series_system_ty()),
399 ("ParallelSystem", parallel_system_ty()),
400 ("EventDrivenSimulation", event_driven_simulation_ty()),
401 ("MonteCarloEstimator", monte_carlo_estimator_ty()),
402 ];
403 for (name, ty) in axioms {
404 env.add(Declaration::Axiom {
405 name: Name::str(*name),
406 univ_params: vec![],
407 ty: ty.clone(),
408 })
409 .ok();
410 }
411 env
412}
413pub(super) fn binomial_coeff(n: usize, k: usize) -> u64 {
415 if k > n {
416 return 0;
417 }
418 let k = k.min(n - k);
419 let mut result = 1_u64;
420 for i in 0..k {
421 result = result * (n - i) as u64 / (i + 1) as u64;
422 }
423 result
424}
425pub fn branch_and_bound_completeness_ty() -> Expr {
429 prop()
430}
431pub fn lp_relaxation_bound_ty() -> Expr {
434 prop()
435}
436pub fn gomory_cut_ty() -> Expr {
439 arrow(
440 nat_ty(),
441 arrow(list_ty(real_ty()), arrow(real_ty(), prop())),
442 )
443}
444pub fn mixed_integer_gomory_cut_ty() -> Expr {
447 prop()
448}
449pub fn lift_and_project_cut_ty() -> Expr {
452 arrow(nat_ty(), prop())
453}
454pub fn split_cut_ty() -> Expr {
457 arrow(list_ty(real_ty()), arrow(real_ty(), prop()))
458}
459pub fn dantzig_wolfe_decomposition_ty() -> Expr {
462 prop()
463}
464pub fn dantzig_wolfe_master_problem_ty() -> Expr {
467 arrow(nat_ty(), arrow(list_ty(real_ty()), prop()))
468}
469pub fn benders_decomposition_ty() -> Expr {
472 prop()
473}
474pub fn benders_feasibility_cut_ty() -> Expr {
477 arrow(list_ty(real_ty()), arrow(real_ty(), prop()))
478}
479pub fn benders_optimality_cut_ty() -> Expr {
482 arrow(list_ty(real_ty()), arrow(real_ty(), prop()))
483}
484pub fn column_generation_ty() -> Expr {
487 arrow(fn_ty(list_ty(real_ty()), real_ty()), prop())
488}
489pub fn lagrangian_relaxation_ty() -> Expr {
493 arrow(
494 list_ty(real_ty()),
495 arrow(fn_ty(list_ty(real_ty()), real_ty()), real_ty()),
496 )
497}
498pub fn subgradient_update_ty() -> Expr {
502 arrow(
503 list_ty(real_ty()),
504 arrow(list_ty(real_ty()), arrow(real_ty(), list_ty(real_ty()))),
505 )
506}
507pub fn lagrangian_dual_ty() -> Expr {
510 arrow(fn_ty(list_ty(real_ty()), real_ty()), real_ty())
511}
512pub fn semidefinite_program_ty() -> Expr {
515 arrow(nat_ty(), arrow(list_ty(real_ty()), prop()))
516}
517pub fn psd_constraint_ty() -> Expr {
520 arrow(type0(), prop())
521}
522pub fn sdp_duality_ty() -> Expr {
525 prop()
526}
527pub fn second_order_cone_program_ty() -> Expr {
530 arrow(nat_ty(), arrow(nat_ty(), prop()))
531}
532pub fn socp_duality_ty() -> Expr {
535 prop()
536}
537pub fn cone_program_ty() -> Expr {
540 arrow(type0(), prop())
541}
542pub fn minimax_regret_ty() -> Expr {
545 arrow(type0(), prop())
546}
547pub fn box_uncertainty_set_ty() -> Expr {
550 arrow(list_ty(real_ty()), arrow(list_ty(real_ty()), type0()))
551}
552pub fn ellipsoidal_uncertainty_set_ty() -> Expr {
555 arrow(nat_ty(), arrow(real_ty(), type0()))
556}
557pub fn robust_counterpart_ty() -> Expr {
560 arrow(type0(), prop())
561}
562pub fn distributionally_robust_optimization_ty() -> Expr {
565 arrow(type0(), prop())
566}
567pub fn chance_constraint_ty() -> Expr {
570 arrow(real_ty(), prop())
571}
572pub fn cvar_ty() -> Expr {
575 arrow(real_ty(), arrow(fn_ty(real_ty(), real_ty()), real_ty()))
576}
577pub fn sample_average_approximation_ty() -> Expr {
580 arrow(nat_ty(), prop())
581}
582pub fn progressive_hedging_ty() -> Expr {
585 arrow(nat_ty(), arrow(real_ty(), prop()))
586}
587pub fn markov_decision_process_ty() -> Expr {
590 arrow(nat_ty(), arrow(nat_ty(), arrow(real_ty(), prop())))
591}
592pub fn bellman_optimality_equation_ty() -> Expr {
595 arrow(fn_ty(nat_ty(), real_ty()), prop())
596}
597pub fn value_iteration_convergence_ty() -> Expr {
600 arrow(real_ty(), prop())
601}
602pub fn policy_iteration_convergence_ty() -> Expr {
605 arrow(nat_ty(), prop())
606}
607pub fn optimal_policy_ty() -> Expr {
610 arrow(nat_ty(), arrow(nat_ty(), prop()))
611}
612pub fn q_function_ty() -> Expr {
615 arrow(nat_ty(), arrow(nat_ty(), real_ty()))
616}
617pub fn bellman_contraction_ty() -> Expr {
620 arrow(real_ty(), prop())
621}
622pub fn pomdp_ty() -> Expr {
625 arrow(
626 nat_ty(),
627 arrow(nat_ty(), arrow(nat_ty(), arrow(real_ty(), prop()))),
628 )
629}
630pub fn belief_state_ty() -> Expr {
633 arrow(nat_ty(), type0())
634}
635pub fn pomdp_value_function_ty() -> Expr {
638 arrow(nat_ty(), prop())
639}
640pub fn multi_armed_bandit_ty() -> Expr {
643 arrow(nat_ty(), prop())
644}
645pub fn ucb1_index_ty() -> Expr {
648 arrow(nat_ty(), arrow(real_ty(), arrow(nat_ty(), real_ty())))
649}
650pub fn ucb_regret_bound_ty() -> Expr {
653 arrow(nat_ty(), arrow(nat_ty(), real_ty()))
654}
655pub fn thompson_sampling_ty() -> Expr {
658 arrow(nat_ty(), prop())
659}
660pub fn explore_exploit_tradeoff_ty() -> Expr {
663 prop()
664}
665pub fn register_operations_research_extended(env: &mut Environment) -> Result<(), String> {
667 let axioms: &[(&str, Expr)] = &[
668 (
669 "BranchAndBoundCompleteness",
670 branch_and_bound_completeness_ty(),
671 ),
672 ("LpRelaxationBound", lp_relaxation_bound_ty()),
673 ("GomoryCut", gomory_cut_ty()),
674 ("MixedIntegerGomoryCut", mixed_integer_gomory_cut_ty()),
675 ("LiftAndProjectCut", lift_and_project_cut_ty()),
676 ("SplitCut", split_cut_ty()),
677 (
678 "DantzigWolfeDecomposition",
679 dantzig_wolfe_decomposition_ty(),
680 ),
681 (
682 "DantzigWolfeMasterProblem",
683 dantzig_wolfe_master_problem_ty(),
684 ),
685 ("BendersDecomposition", benders_decomposition_ty()),
686 ("BendersFeasibilityCut", benders_feasibility_cut_ty()),
687 ("BendersOptimalityCut", benders_optimality_cut_ty()),
688 ("ColumnGeneration", column_generation_ty()),
689 ("LagrangianRelaxation", lagrangian_relaxation_ty()),
690 ("SubgradientUpdate", subgradient_update_ty()),
691 ("LagrangianDual", lagrangian_dual_ty()),
692 ("SemidefiniteProgram", semidefinite_program_ty()),
693 ("PSDConstraint", psd_constraint_ty()),
694 ("SDPDuality", sdp_duality_ty()),
695 ("SecondOrderConeProgram", second_order_cone_program_ty()),
696 ("SOCPDuality", socp_duality_ty()),
697 ("ConeProgram", cone_program_ty()),
698 ("MinimaxRegret", minimax_regret_ty()),
699 ("BoxUncertaintySet", box_uncertainty_set_ty()),
700 (
701 "EllipsoidalUncertaintySet",
702 ellipsoidal_uncertainty_set_ty(),
703 ),
704 ("RobustCounterpart", robust_counterpart_ty()),
705 (
706 "DistributionallyRobustOptimization",
707 distributionally_robust_optimization_ty(),
708 ),
709 ("ChanceConstraint", chance_constraint_ty()),
710 ("CVaR", cvar_ty()),
711 (
712 "SampleAverageApproximation",
713 sample_average_approximation_ty(),
714 ),
715 ("ProgressiveHedging", progressive_hedging_ty()),
716 ("MarkovDecisionProcess", markov_decision_process_ty()),
717 (
718 "BellmanOptimalityEquation",
719 bellman_optimality_equation_ty(),
720 ),
721 (
722 "ValueIterationConvergence",
723 value_iteration_convergence_ty(),
724 ),
725 (
726 "PolicyIterationConvergence",
727 policy_iteration_convergence_ty(),
728 ),
729 ("OptimalPolicy", optimal_policy_ty()),
730 ("QFunction", q_function_ty()),
731 ("BellmanContraction", bellman_contraction_ty()),
732 ("POMDP", pomdp_ty()),
733 ("BeliefState", belief_state_ty()),
734 ("POMDPValueFunction", pomdp_value_function_ty()),
735 ("MultiArmedBandit", multi_armed_bandit_ty()),
736 ("UCB1Index", ucb1_index_ty()),
737 ("UCBRegretBound", ucb_regret_bound_ty()),
738 ("ThompsonSampling", thompson_sampling_ty()),
739 ("ExploreExploitTradeoff", explore_exploit_tradeoff_ty()),
740 ];
741 for (name, ty) in axioms {
742 env.add(Declaration::Axiom {
743 name: Name::str(*name),
744 univ_params: vec![],
745 ty: ty.clone(),
746 })
747 .map_err(|e| format!("Failed to add {name}: {e:?}"))?;
748 }
749 Ok(())
750}
751#[cfg(test)]
752mod tests {
753 use super::*;
754 #[test]
755 fn test_queueing_utilization() {
756 let qs = QueueingSystem::new(3.0, 5.0, 1);
757 assert!((qs.utilization() - 0.6).abs() < 1e-9);
758 }
759 #[test]
760 fn test_queueing_mean_queue_length() {
761 let qs = QueueingSystem::new(1.0, 2.0, 1);
762 let l = qs
763 .mean_queue_length_m_m_1()
764 .expect("mean_queue_length_m_m_1 should succeed");
765 assert!((l - 1.0).abs() < 1e-9, "L={l}");
766 }
767 #[test]
768 fn test_network_flow_simple() {
769 let mut g = NetworkFlowGraph::new(4);
770 g.add_edge(0, 1, 10);
771 g.add_edge(0, 2, 10);
772 g.add_edge(1, 3, 10);
773 g.add_edge(2, 3, 10);
774 assert_eq!(g.max_flow_bfs(0, 3), 20);
775 }
776 #[test]
777 fn test_job_scheduler_edf() {
778 let mut sched = JobScheduler::new();
779 sched.add_job("A", 2, 5);
780 sched.add_job("B", 3, 3);
781 sched.add_job("C", 1, 7);
782 let order = sched.earliest_deadline_first();
783 assert_eq!(order, vec!["B", "A", "C"]);
784 }
785 #[test]
786 fn test_dp_knapsack() {
787 let weights = [2, 3, 4, 5];
788 let values = [3, 4, 5, 6];
789 assert_eq!(DynamicProgramming::knapsack(5, &weights, &values), 7);
790 }
791 #[test]
792 fn test_dp_lcs() {
793 let s1 = b"ABCBDAB";
794 let s2 = b"BDCABA";
795 assert_eq!(DynamicProgramming::longest_common_subseq(s1, s2), 4);
796 }
797 #[test]
798 fn test_dp_coin_change() {
799 assert_eq!(
800 DynamicProgramming::coin_change(&[1, 5, 10, 25], 41),
801 Some(4)
802 );
803 assert_eq!(DynamicProgramming::coin_change(&[2], 3), None);
804 }
805 #[test]
806 fn test_inventory_eoq() {
807 let inv = InventoryModel::new(1000.0, 50.0, 5.0, 1.0);
808 let q = inv.eoq();
809 assert!((q - 200.0_f64.sqrt() * 10.0).abs() < 1e-3, "eoq={q}");
810 let tc = inv.total_cost(q);
811 assert!(tc > 0.0);
812 }
813 #[test]
814 fn test_simplex_solver_basic() {
815 let obj = vec![-1.0, -1.0];
816 let a = vec![vec![1.0, 1.0], vec![1.0, 0.0], vec![0.0, 1.0]];
817 let b = vec![4.0, 3.0, 3.0];
818 let solver = SimplexSolver::new(obj, a, b);
819 let val = solver.solve().expect("solve should succeed");
820 assert!((val - (-4.0)).abs() < 1e-6, "simplex val={val}");
821 }
822 #[test]
823 fn test_ford_fulkerson_simple() {
824 let mut ff = FordFulkerson::new(4);
825 ff.add_edge(0, 1, 10);
826 ff.add_edge(0, 2, 10);
827 ff.add_edge(1, 3, 10);
828 ff.add_edge(2, 3, 10);
829 assert_eq!(ff.max_flow(0, 3), 20);
830 }
831 #[test]
832 fn test_hungarian_solver_simple() {
833 let cost = vec![vec![9, 2, 7], vec![3, 6, 1], vec![5, 8, 4]];
834 let (min_cost, assignment) = HungarianSolver::new(cost).solve();
835 assert_eq!(
836 min_cost, 8,
837 "Hungarian cost={min_cost}, assignment={assignment:?}"
838 );
839 }
840 #[test]
841 fn test_bellman_ford_basic() {
842 let mut bf = BellmanFord::new(5);
843 bf.add_edge(0, 1, 6);
844 bf.add_edge(0, 2, 7);
845 bf.add_edge(1, 2, 8);
846 bf.add_edge(1, 3, 5);
847 bf.add_edge(1, 4, -4);
848 bf.add_edge(2, 3, -3);
849 bf.add_edge(2, 4, 9);
850 bf.add_edge(3, 1, -2);
851 bf.add_edge(4, 0, 2);
852 bf.add_edge(4, 3, 7);
853 let dist = bf.shortest_paths(0).expect("shortest_paths should succeed");
854 assert_eq!(dist[0], 0);
855 assert_eq!(dist[1], 2);
856 assert_eq!(dist[2], 7);
857 }
858 #[test]
859 fn test_knapsack_dp_with_selection() {
860 let solver = KnapsackDP::new(5, vec![2, 3, 4, 5], vec![3, 4, 5, 6]);
861 let (val, selected) = solver.solve();
862 assert_eq!(val, 7, "knapsack value={val}");
863 assert!(
864 selected.contains(&0) && selected.contains(&1),
865 "selected={selected:?}"
866 );
867 }
868 #[test]
869 fn test_dijkstra_basic() {
870 let mut g = Dijkstra::new(5);
871 g.add_edge(0, 1, 10);
872 g.add_edge(0, 3, 5);
873 g.add_edge(1, 2, 1);
874 g.add_edge(3, 1, 3);
875 g.add_edge(3, 2, 9);
876 g.add_edge(2, 4, 4);
877 g.add_edge(3, 4, 2);
878 let dist = g.shortest_paths(0);
879 assert_eq!(dist[0], 0);
880 assert_eq!(dist[3], 5);
881 assert_eq!(dist[1], 8);
882 assert_eq!(dist[4], 7);
883 }
884 #[test]
885 fn test_floyd_warshall_basic() {
886 let mut fw = FloydWarshall::new(4);
887 fw.add_edge(0, 1, 3);
888 fw.add_edge(0, 3, 7);
889 fw.add_edge(1, 0, 8);
890 fw.add_edge(1, 2, 2);
891 fw.add_edge(2, 0, 5);
892 fw.add_edge(2, 3, 1);
893 fw.add_edge(3, 0, 2);
894 let d = fw.run().expect("run should succeed");
895 assert_eq!(d[0][2], 5);
896 assert_eq!(d[3][1], 5);
897 }
898 #[test]
899 fn test_prim_mst() {
900 let mut prim = PrimMst::new(4);
901 prim.add_edge(0, 1, 1);
902 prim.add_edge(0, 2, 4);
903 prim.add_edge(1, 2, 2);
904 prim.add_edge(1, 3, 5);
905 prim.add_edge(2, 3, 3);
906 let (total, _edges) = prim.run();
907 assert_eq!(total, 6, "MST weight={total}");
908 }
909 #[test]
910 fn test_newsvendor_optimal_quantity() {
911 let nv = NewsvendorModel::new(0.0, 100.0, 5.0, 10.0, 2.0);
912 let q = nv.optimal_quantity();
913 assert!((q - 62.5).abs() < 1e-6, "Q*={q}");
914 }
915 #[test]
916 fn test_reliability_series() {
917 let sys = ReliabilitySystem::new(vec![0.9, 0.8, 0.95]);
918 let r = sys.series_reliability();
919 assert!((r - 0.9 * 0.8 * 0.95).abs() < 1e-9, "series R={r}");
920 }
921 #[test]
922 fn test_reliability_parallel() {
923 let sys = ReliabilitySystem::new(vec![0.9, 0.9]);
924 let r = sys.parallel_reliability();
925 assert!((r - 0.99).abs() < 1e-9, "parallel R={r}");
926 }
927 #[test]
928 fn test_build_env_has_axioms() {
929 let env = build_operations_research_env();
930 assert!(env.get(&Name::str("ford_fulkerson")).is_some());
931 assert!(env.get(&Name::str("simplex_optimal")).is_some());
932 assert!(env.get(&Name::str("branch_and_bound")).is_some());
933 assert!(env.get(&Name::str("dijkstra_correctness")).is_some());
934 assert!(env.get(&Name::str("hungarian_algorithm")).is_some());
935 assert!(env.get(&Name::str("robust_optimization")).is_some());
936 assert!(env.get(&Name::str("pollaczek_khinchine")).is_some());
937 assert!(env.get(&Name::str("MonteCarloEstimator")).is_some());
938 }
939 #[test]
940 fn test_extended_env_has_axioms() {
941 use oxilean_kernel::Environment;
942 let mut env = Environment::new();
943 register_operations_research_extended(&mut env).expect("Environment::new should succeed");
944 let names = [
945 "BranchAndBoundCompleteness",
946 "GomoryCut",
947 "DantzigWolfeDecomposition",
948 "BendersDecomposition",
949 "ColumnGeneration",
950 "LagrangianRelaxation",
951 "SemidefiniteProgram",
952 "SecondOrderConeProgram",
953 "MinimaxRegret",
954 "ChanceConstraint",
955 "MarkovDecisionProcess",
956 "BellmanOptimalityEquation",
957 "ValueIterationConvergence",
958 "PolicyIterationConvergence",
959 "POMDP",
960 "MultiArmedBandit",
961 "UCB1Index",
962 "ThompsonSampling",
963 ];
964 for name in &names {
965 assert!(
966 env.get(&Name::str(*name)).is_some(),
967 "Extended axiom '{name}' not found"
968 );
969 }
970 }
971 #[test]
972 fn test_mdp_value_iteration_simple() {
973 let n_states = 2;
974 let n_actions = 2;
975 let discount = 0.9_f64;
976 let rewards = vec![vec![-1.0, 0.0], vec![0.0, 1.0]];
977 let transitions = vec![
978 vec![vec![1.0, 0.0], vec![0.0, 1.0]],
979 vec![vec![0.0, 1.0], vec![0.0, 1.0]],
980 ];
981 let mdp = MdpSolver::new(n_states, n_actions, discount, rewards, transitions);
982 let (v, _iters) = mdp.value_iteration(1e-6, 1000);
983 assert!(
984 v[1] > v[0],
985 "V(good state) should exceed V(bad state): V={v:?}"
986 );
987 }
988 #[test]
989 fn test_mdp_policy_extraction() {
990 let n_states = 2;
991 let n_actions = 2;
992 let discount = 0.9_f64;
993 let rewards = vec![vec![0.0, 0.5], vec![0.0, 1.0]];
994 let transitions = vec![
995 vec![vec![1.0, 0.0], vec![0.0, 1.0]],
996 vec![vec![0.0, 1.0], vec![0.0, 1.0]],
997 ];
998 let mdp = MdpSolver::new(n_states, n_actions, discount, rewards, transitions);
999 let (v, _) = mdp.value_iteration(1e-6, 1000);
1000 let policy = mdp.extract_policy(&v);
1001 assert_eq!(policy[1], 1, "At state 1, should choose action 1");
1002 }
1003 #[test]
1004 fn test_ucb_bandit_selects_best() {
1005 let means = vec![0.2, 0.5, 0.8];
1006 let mut env = BanditEnvironment::new(means.clone());
1007 assert_eq!(env.optimal_arm(), 2, "Optimal arm should be 2");
1008 let regret = env.run_ucb1(200);
1009 assert!(regret >= 0.0, "Regret should be non-negative, got {regret}");
1010 assert!(regret.is_finite(), "Regret should be finite, got {regret}");
1011 }
1012 #[test]
1013 fn test_ucb1_index_infinite_for_unplayed() {
1014 let bandit = MultiArmedBanditUcb::new(3);
1015 assert_eq!(
1016 bandit.ucb_index(0),
1017 f64::INFINITY,
1018 "Unplayed arm should have infinite UCB"
1019 );
1020 assert_eq!(bandit.select_arm(), 0, "Should select first unplayed arm");
1021 }
1022 #[test]
1023 fn test_lagrangian_solver_polyak_step() {
1024 let mut solver = LagrangianRelaxationSolver::new(2, 1.0);
1025 let subgradient = [0.5, -0.3];
1026 let step = solver.polyak_step(10.0, 8.0, &subgradient);
1027 assert!(step > 0.0, "Polyak step should be positive, got {step}");
1028 solver.subgradient_update(&subgradient, step);
1029 assert!(
1030 solver.multipliers[0] > 0.0,
1031 "Multiplier 0 should increase: {}",
1032 solver.multipliers[0]
1033 );
1034 assert!(
1035 solver.multipliers[1] >= 0.0,
1036 "Multiplier 1 should be non-negative: {}",
1037 solver.multipliers[1]
1038 );
1039 }
1040 #[test]
1041 fn test_two_stage_stochastic_cost() {
1042 let model = TwoStageStochasticLP::new(
1043 vec![2.0],
1044 vec![3.0],
1045 vec![0.4, 0.6],
1046 vec![vec![5.0], vec![7.0]],
1047 );
1048 let x = vec![1.0];
1049 let y = vec![vec![2.0], vec![3.0]];
1050 let expected_recourse = 0.4 * 6.0 + 0.6 * 9.0;
1051 let recourse = model.expected_recourse_cost(&y);
1052 assert!(
1053 (recourse - expected_recourse).abs() < 1e-9,
1054 "Expected recourse={expected_recourse}, got {recourse}"
1055 );
1056 let total = model.total_cost(&x, &y);
1057 assert!(
1058 (total - (2.0 + expected_recourse)).abs() < 1e-9,
1059 "Total cost={total}"
1060 );
1061 assert!(model.is_stage1_feasible(&x), "x=[1.0] should be feasible");
1062 }
1063}