1use oxilean_kernel::Node;
6use oxilean_kernel::{BinderInfo, Declaration, Environment, Expr, Level, Name};
7
8use super::types::{
9 BipartiteMatchingGraph, BranchBoundData, CuttingPlane, FacilityLocation, FlowNetwork,
10 FlowNetworkSpec, GraphColoring, KnapsackSolver, LPRelaxation, MatchingProblem,
11 MatroidIntersection, SetCoverData, ShortestPath, SpanningTree, SteinerTree, TravelingSalesman,
12 VehicleRouting,
13};
14
15pub fn app(f: Expr, a: Expr) -> Expr {
16 Expr::App(Node::new(f), Node::new(a))
17}
18pub fn app2(f: Expr, a: Expr, b: Expr) -> Expr {
19 app(app(f, a), b)
20}
21pub fn app3(f: Expr, a: Expr, b: Expr, c: Expr) -> Expr {
22 app(app2(f, a, b), c)
23}
24pub fn cst(s: &str) -> Expr {
25 Expr::Const(Name::str(s), vec![])
26}
27pub fn prop() -> Expr {
28 Expr::Sort(Level::zero())
29}
30pub fn type0() -> Expr {
31 Expr::Sort(Level::succ(Level::zero()))
32}
33pub fn pi(bi: BinderInfo, name: &str, dom: Expr, body: Expr) -> Expr {
34 Expr::Pi(bi, Name::str(name), Node::new(dom), Node::new(body))
35}
36pub fn arrow(a: Expr, b: Expr) -> Expr {
37 pi(BinderInfo::Default, "_", a, b)
38}
39pub fn bvar(n: u32) -> Expr {
40 Expr::BVar(n)
41}
42pub fn nat_ty() -> Expr {
43 cst("Nat")
44}
45pub fn bool_ty() -> Expr {
46 cst("Bool")
47}
48pub fn real_ty() -> Expr {
49 cst("Real")
50}
51pub fn int_ty() -> Expr {
52 cst("Int")
53}
54pub fn list_ty(elem: Expr) -> Expr {
55 app(cst("List"), elem)
56}
57pub fn pair_ty(a: Expr, b: Expr) -> Expr {
58 app2(cst("Prod"), a, b)
59}
60pub fn option_ty(a: Expr) -> Expr {
61 app(cst("Option"), a)
62}
63pub fn graph_ty() -> Expr {
65 type0()
66}
67pub fn bipartite_graph_ty() -> Expr {
69 type0()
70}
71pub fn matching_ty() -> Expr {
73 arrow(graph_ty(), type0())
74}
75pub fn perfect_matching_ty() -> Expr {
77 arrow(graph_ty(), prop())
78}
79pub fn max_matching_ty() -> Expr {
81 arrow(graph_ty(), arrow(matching_ty(), prop()))
82}
83pub fn alternating_path_ty() -> Expr {
85 arrow(
86 graph_ty(),
87 arrow(matching_ty(), arrow(list_ty(nat_ty()), prop())),
88 )
89}
90pub fn augmenting_path_ty() -> Expr {
92 arrow(
93 graph_ty(),
94 arrow(matching_ty(), arrow(list_ty(nat_ty()), prop())),
95 )
96}
97pub fn blossom_ty() -> Expr {
99 arrow(
100 graph_ty(),
101 arrow(matching_ty(), arrow(list_ty(nat_ty()), prop())),
102 )
103}
104pub fn hall_condition_ty() -> Expr {
107 arrow(bipartite_graph_ty(), prop())
108}
109pub fn hall_theorem_ty() -> Expr {
111 pi(
112 BinderInfo::Default,
113 "G",
114 bipartite_graph_ty(),
115 app2(
116 cst("Iff"),
117 app(cst("HallCondition"), bvar(0)),
118 app(cst("PerfectMatching"), bvar(0)),
119 ),
120 )
121}
122pub fn konig_theorem_ty() -> Expr {
124 pi(
125 BinderInfo::Default,
126 "G",
127 bipartite_graph_ty(),
128 app2(
129 cst("Eq"),
130 app(cst("MaxMatchingSize"), bvar(0)),
131 app(cst("MinVertexCoverSize"), bvar(0)),
132 ),
133 )
134}
135pub fn tutte_condition_ty() -> Expr {
138 arrow(graph_ty(), prop())
139}
140pub fn tutte_theorem_ty() -> Expr {
142 pi(
143 BinderInfo::Default,
144 "G",
145 graph_ty(),
146 app2(
147 cst("Iff"),
148 app(cst("PerfectMatching"), bvar(0)),
149 app(cst("TutteCondition"), bvar(0)),
150 ),
151 )
152}
153pub fn berge_theorem_ty() -> Expr {
155 pi(
156 BinderInfo::Default,
157 "G",
158 graph_ty(),
159 pi(
160 BinderInfo::Default,
161 "M",
162 matching_ty(),
163 app2(
164 cst("Iff"),
165 app2(cst("MaxMatching"), bvar(1), bvar(0)),
166 app(
167 cst("NoAugmentingPath"),
168 app2(cst("mk_pair"), bvar(1), bvar(0)),
169 ),
170 ),
171 ),
172 )
173}
174pub fn flow_network_ty() -> Expr {
176 type0()
177}
178pub fn flow_ty() -> Expr {
180 arrow(flow_network_ty(), type0())
181}
182pub fn flow_value_ty() -> Expr {
184 arrow(flow_network_ty(), arrow(flow_ty(), real_ty()))
185}
186pub fn max_flow_ty() -> Expr {
188 arrow(flow_network_ty(), real_ty())
189}
190pub fn cut_ty() -> Expr {
192 arrow(flow_network_ty(), arrow(arrow(nat_ty(), bool_ty()), prop()))
193}
194pub fn cut_capacity_ty() -> Expr {
196 arrow(
197 flow_network_ty(),
198 arrow(arrow(nat_ty(), bool_ty()), real_ty()),
199 )
200}
201pub fn min_cut_ty() -> Expr {
203 arrow(flow_network_ty(), real_ty())
204}
205pub fn max_flow_min_cut_ty() -> Expr {
207 pi(
208 BinderInfo::Default,
209 "N",
210 flow_network_ty(),
211 app2(
212 cst("Eq"),
213 app(cst("MaxFlow"), bvar(0)),
214 app(cst("MinCut"), bvar(0)),
215 ),
216 )
217}
218pub fn ford_fulkerson_termination_ty() -> Expr {
220 pi(
221 BinderInfo::Default,
222 "N",
223 flow_network_ty(),
224 arrow(
225 app(cst("IntegerCapacities"), bvar(0)),
226 app(cst("FordFulkersonTerminates"), bvar(0)),
227 ),
228 )
229}
230pub fn dinics_complexity_ty() -> Expr {
232 pi(
233 BinderInfo::Default,
234 "N",
235 flow_network_ty(),
236 app(cst("DinicsTimeBound"), bvar(0)),
237 )
238}
239pub fn cost_matrix_ty() -> Expr {
241 arrow(nat_ty(), arrow(nat_ty(), real_ty()))
242}
243pub fn assignment_ty() -> Expr {
245 arrow(nat_ty(), arrow(arrow(nat_ty(), nat_ty()), prop()))
246}
247pub fn optimal_assignment_ty() -> Expr {
249 arrow(cost_matrix_ty(), arrow(arrow(nat_ty(), nat_ty()), prop()))
250}
251pub fn hungarian_algorithm_ty() -> Expr {
253 arrow(cost_matrix_ty(), arrow(nat_ty(), nat_ty()))
254}
255pub fn hungarian_correctness_ty() -> Expr {
257 pi(
258 BinderInfo::Default,
259 "C",
260 cost_matrix_ty(),
261 app2(
262 cst("OptimalAssignment"),
263 bvar(0),
264 app(cst("HungarianAlgorithm"), bvar(0)),
265 ),
266 )
267}
268pub fn tsp_instance_ty() -> Expr {
270 type0()
271}
272pub fn tsp_tour_ty() -> Expr {
274 arrow(tsp_instance_ty(), arrow(list_ty(nat_ty()), prop()))
275}
276pub fn tsp_optimal_ty() -> Expr {
278 arrow(tsp_instance_ty(), real_ty())
279}
280pub fn held_karp_bound_ty() -> Expr {
282 arrow(tsp_instance_ty(), real_ty())
283}
284pub fn held_karp_lower_bound_ty() -> Expr {
286 pi(
287 BinderInfo::Default,
288 "I",
289 tsp_instance_ty(),
290 app2(
291 cst("Le"),
292 app(cst("HeldKarpBound"), bvar(0)),
293 app(cst("TSPOptimal"), bvar(0)),
294 ),
295 )
296}
297pub fn christofides_approximation_ty() -> Expr {
299 pi(
300 BinderInfo::Default,
301 "I",
302 tsp_instance_ty(),
303 arrow(
304 app(cst("MetricTSP"), bvar(0)),
305 app2(
306 cst("Le"),
307 app(cst("ChristofidesValue"), bvar(0)),
308 app2(
309 cst("RealMul"),
310 cst("ThreeHalves"),
311 app(cst("TSPOptimal"), bvar(0)),
312 ),
313 ),
314 ),
315 )
316}
317pub fn vertex_cover_approx_ty() -> Expr {
319 pi(
320 BinderInfo::Default,
321 "G",
322 graph_ty(),
323 app2(
324 cst("Le"),
325 app(cst("ApproxVertexCoverSize"), bvar(0)),
326 app2(
327 cst("NatMul"),
328 cst("Nat.two"),
329 app(cst("MinVertexCoverSize"), bvar(0)),
330 ),
331 ),
332 )
333}
334pub fn set_cover_approx_ty() -> Expr {
336 pi(
337 BinderInfo::Default,
338 "n",
339 nat_ty(),
340 app2(
341 cst("Le"),
342 app(cst("GreedySetCoverSize"), bvar(0)),
343 app2(
344 cst("NatMul"),
345 app(cst("HarmonicNumber"), bvar(0)),
346 app(cst("OptSetCoverSize"), bvar(0)),
347 ),
348 ),
349 )
350}
351pub fn matroid_ty() -> Expr {
353 type0()
354}
355pub fn independent_set_ty() -> Expr {
357 arrow(matroid_ty(), arrow(list_ty(nat_ty()), prop()))
358}
359pub fn matroid_base_ty() -> Expr {
361 arrow(matroid_ty(), arrow(list_ty(nat_ty()), prop()))
362}
363pub fn matroid_rank_ty() -> Expr {
365 arrow(matroid_ty(), nat_ty())
366}
367pub fn greedy_optimality_ty() -> Expr {
369 pi(
370 BinderInfo::Default,
371 "M",
372 matroid_ty(),
373 pi(
374 BinderInfo::Default,
375 "w",
376 arrow(nat_ty(), real_ty()),
377 app2(cst("GreedyIsOptimal"), bvar(1), bvar(0)),
378 ),
379 )
380}
381pub fn matroid_intersection_ty() -> Expr {
383 arrow(matroid_ty(), arrow(matroid_ty(), list_ty(nat_ty())))
384}
385pub fn matroid_intersection_optimality_ty() -> Expr {
387 pi(
388 BinderInfo::Default,
389 "M1",
390 matroid_ty(),
391 pi(
392 BinderInfo::Default,
393 "M2",
394 matroid_ty(),
395 pi(
396 BinderInfo::Default,
397 "w",
398 arrow(nat_ty(), real_ty()),
399 app3(cst("MatroidIntersectionOptimal"), bvar(2), bvar(1), bvar(0)),
400 ),
401 ),
402 )
403}
404pub fn graphic_matroid_ty() -> Expr {
406 arrow(graph_ty(), matroid_ty())
407}
408pub fn uniform_matroid_ty() -> Expr {
410 arrow(nat_ty(), arrow(nat_ty(), matroid_ty()))
411}
412pub fn submodular_function_ty() -> Expr {
415 arrow(arrow(list_ty(nat_ty()), real_ty()), prop())
416}
417pub fn submodular_maximization_ty() -> Expr {
419 arrow(arrow(list_ty(nat_ty()), real_ty()), list_ty(nat_ty()))
420}
421pub fn submodular_greedy_approx_ty() -> Expr {
423 pi(
424 BinderInfo::Default,
425 "f",
426 arrow(list_ty(nat_ty()), real_ty()),
427 arrow(
428 app(cst("MonotoneSubmodular"), bvar(0)),
429 app2(
430 cst("Le"),
431 app2(
432 cst("RealMul"),
433 cst("OneHalf"),
434 app(cst("SubmodularOpt"), bvar(0)),
435 ),
436 app(cst("SubmodularGreedyValue"), bvar(0)),
437 ),
438 ),
439 )
440}
441pub fn supermodular_function_ty() -> Expr {
443 arrow(arrow(list_ty(nat_ty()), real_ty()), prop())
444}
445pub fn polymatroid_rank_ty() -> Expr {
447 arrow(arrow(list_ty(nat_ty()), real_ty()), prop())
448}
449pub fn ilp_instance_ty() -> Expr {
451 type0()
452}
453pub fn ilp_solution_ty() -> Expr {
455 arrow(ilp_instance_ty(), arrow(list_ty(int_ty()), prop()))
456}
457pub fn ilp_optimal_ty() -> Expr {
459 arrow(ilp_instance_ty(), arrow(list_ty(int_ty()), prop()))
460}
461pub fn gomory_cut_ty() -> Expr {
463 arrow(ilp_instance_ty(), ilp_instance_ty())
464}
465pub fn branch_and_bound_ty() -> Expr {
467 arrow(ilp_instance_ty(), list_ty(int_ty()))
468}
469pub fn lp_relaxation_ty() -> Expr {
471 arrow(ilp_instance_ty(), real_ty())
472}
473pub fn lp_relaxation_lower_bound_ty() -> Expr {
475 pi(
476 BinderInfo::Default,
477 "P",
478 ilp_instance_ty(),
479 app2(
480 cst("Le"),
481 app(cst("LPRelaxation"), bvar(0)),
482 app(cst("ILPOptimalValue"), bvar(0)),
483 ),
484 )
485}
486pub fn polytope_ty() -> Expr {
488 type0()
489}
490pub fn polytope_vertex_ty() -> Expr {
492 arrow(polytope_ty(), arrow(list_ty(real_ty()), prop()))
493}
494pub fn totally_unimodular_ty() -> Expr {
496 arrow(arrow(nat_ty(), arrow(nat_ty(), int_ty())), prop())
497}
498pub fn tu_integral_polyhedra_ty() -> Expr {
500 pi(
501 BinderInfo::Default,
502 "A",
503 arrow(nat_ty(), arrow(nat_ty(), int_ty())),
504 arrow(
505 app(cst("TotallyUnimodular"), bvar(0)),
506 app(cst("IntegralPolyhedron"), bvar(0)),
507 ),
508 )
509}
510pub fn bipartite_incidence_tu_ty() -> Expr {
512 pi(
513 BinderInfo::Default,
514 "G",
515 bipartite_graph_ty(),
516 app(
517 cst("TotallyUnimodular"),
518 app(cst("IncidenceMatrix"), bvar(0)),
519 ),
520 )
521}
522pub fn facet_defining_ty() -> Expr {
524 arrow(
525 polytope_ty(),
526 arrow(arrow(list_ty(real_ty()), prop()), prop()),
527 )
528}
529pub fn weak_duality_ty() -> Expr {
531 pi(
532 BinderInfo::Default,
533 "P",
534 ilp_instance_ty(),
535 app2(
536 cst("Le"),
537 app(cst("PrimalObjective"), bvar(0)),
538 app(cst("DualObjective"), bvar(0)),
539 ),
540 )
541}
542pub fn strong_duality_ty() -> Expr {
544 pi(
545 BinderInfo::Default,
546 "P",
547 ilp_instance_ty(),
548 arrow(
549 app(cst("BothFeasible"), bvar(0)),
550 app2(
551 cst("Eq"),
552 app(cst("PrimalObjective"), bvar(0)),
553 app(cst("DualObjective"), bvar(0)),
554 ),
555 ),
556 )
557}
558pub fn build_combinatorial_optimization_env(env: &mut Environment) -> Result<(), String> {
560 let axioms: &[(&str, Expr)] = &[
561 ("Graph", graph_ty()),
562 ("BipartiteGraph", bipartite_graph_ty()),
563 ("Matching", matching_ty()),
564 ("PerfectMatching", perfect_matching_ty()),
565 ("MaxMatching", max_matching_ty()),
566 ("AlternatingPath", alternating_path_ty()),
567 ("AugmentingPath", augmenting_path_ty()),
568 ("Blossom", blossom_ty()),
569 ("MaxMatchingSize", arrow(graph_ty(), nat_ty())),
570 ("MinVertexCoverSize", arrow(graph_ty(), nat_ty())),
571 ("ApproxVertexCoverSize", arrow(graph_ty(), nat_ty())),
572 (
573 "NoAugmentingPath",
574 arrow(pair_ty(graph_ty(), matching_ty()), prop()),
575 ),
576 (
577 "mk_pair",
578 arrow(
579 graph_ty(),
580 arrow(matching_ty(), pair_ty(graph_ty(), matching_ty())),
581 ),
582 ),
583 ("HallCondition", hall_condition_ty()),
584 ("hall_theorem", hall_theorem_ty()),
585 ("konig_theorem", konig_theorem_ty()),
586 ("TutteCondition", tutte_condition_ty()),
587 ("tutte_theorem", tutte_theorem_ty()),
588 ("berge_theorem", berge_theorem_ty()),
589 ("FlowNetwork", flow_network_ty()),
590 ("Flow", flow_ty()),
591 ("FlowValue", flow_value_ty()),
592 ("MaxFlow", max_flow_ty()),
593 ("Cut", cut_ty()),
594 ("CutCapacity", cut_capacity_ty()),
595 ("MinCut", min_cut_ty()),
596 ("IntegerCapacities", arrow(flow_network_ty(), prop())),
597 ("FordFulkersonTerminates", arrow(flow_network_ty(), prop())),
598 ("DinicsTimeBound", arrow(flow_network_ty(), prop())),
599 ("max_flow_min_cut", max_flow_min_cut_ty()),
600 (
601 "ford_fulkerson_termination",
602 ford_fulkerson_termination_ty(),
603 ),
604 ("dinics_complexity", dinics_complexity_ty()),
605 ("CostMatrix", cost_matrix_ty()),
606 ("Assignment", assignment_ty()),
607 ("OptimalAssignment", optimal_assignment_ty()),
608 ("HungarianAlgorithm", hungarian_algorithm_ty()),
609 ("hungarian_correctness", hungarian_correctness_ty()),
610 ("TSPInstance", tsp_instance_ty()),
611 ("TSPTour", tsp_tour_ty()),
612 ("TSPOptimal", tsp_optimal_ty()),
613 ("HeldKarpBound", held_karp_bound_ty()),
614 ("MetricTSP", arrow(tsp_instance_ty(), prop())),
615 ("ChristofidesValue", arrow(tsp_instance_ty(), real_ty())),
616 ("ThreeHalves", real_ty()),
617 ("OneHalf", real_ty()),
618 ("RealMul", arrow(real_ty(), arrow(real_ty(), real_ty()))),
619 ("NatMul", arrow(nat_ty(), arrow(nat_ty(), nat_ty()))),
620 ("Nat.two", nat_ty()),
621 ("HarmonicNumber", arrow(nat_ty(), nat_ty())),
622 ("OptSetCoverSize", arrow(nat_ty(), nat_ty())),
623 ("GreedySetCoverSize", arrow(nat_ty(), nat_ty())),
624 ("held_karp_lower_bound", held_karp_lower_bound_ty()),
625 (
626 "christofides_approximation",
627 christofides_approximation_ty(),
628 ),
629 ("vertex_cover_approximation", vertex_cover_approx_ty()),
630 ("set_cover_approximation", set_cover_approx_ty()),
631 ("Matroid", matroid_ty()),
632 ("IndependentSet", independent_set_ty()),
633 ("MatroidBase", matroid_base_ty()),
634 ("MatroidRank", matroid_rank_ty()),
635 (
636 "GreedyIsOptimal",
637 arrow(matroid_ty(), arrow(arrow(nat_ty(), real_ty()), prop())),
638 ),
639 ("MatroidIntersection", matroid_intersection_ty()),
640 (
641 "MatroidIntersectionOptimal",
642 arrow(
643 matroid_ty(),
644 arrow(matroid_ty(), arrow(arrow(nat_ty(), real_ty()), prop())),
645 ),
646 ),
647 ("GraphicMatroid", graphic_matroid_ty()),
648 ("UniformMatroid", uniform_matroid_ty()),
649 ("greedy_optimality", greedy_optimality_ty()),
650 (
651 "matroid_intersection_optimality",
652 matroid_intersection_optimality_ty(),
653 ),
654 ("SubmodularFunction", submodular_function_ty()),
655 ("SubmodularMaximization", submodular_maximization_ty()),
656 (
657 "MonotoneSubmodular",
658 arrow(arrow(list_ty(nat_ty()), real_ty()), prop()),
659 ),
660 (
661 "SubmodularOpt",
662 arrow(arrow(list_ty(nat_ty()), real_ty()), real_ty()),
663 ),
664 (
665 "SubmodularGreedyValue",
666 arrow(arrow(list_ty(nat_ty()), real_ty()), real_ty()),
667 ),
668 ("SupermodularFunction", supermodular_function_ty()),
669 ("PolymatroidRankFunction", polymatroid_rank_ty()),
670 ("submodular_greedy_approx", submodular_greedy_approx_ty()),
671 ("ILPInstance", ilp_instance_ty()),
672 ("ILPSolution", ilp_solution_ty()),
673 ("ILPOptimal", ilp_optimal_ty()),
674 ("GomoryCut", gomory_cut_ty()),
675 ("BranchAndBound", branch_and_bound_ty()),
676 ("LPRelaxation", lp_relaxation_ty()),
677 ("ILPOptimalValue", arrow(ilp_instance_ty(), real_ty())),
678 ("PrimalObjective", arrow(ilp_instance_ty(), real_ty())),
679 ("DualObjective", arrow(ilp_instance_ty(), real_ty())),
680 ("BothFeasible", arrow(ilp_instance_ty(), prop())),
681 ("lp_relaxation_lower_bound", lp_relaxation_lower_bound_ty()),
682 ("weak_duality", weak_duality_ty()),
683 ("strong_duality", strong_duality_ty()),
684 ("Polytope", polytope_ty()),
685 ("PolytopeVertex", polytope_vertex_ty()),
686 ("TotallyUnimodular", totally_unimodular_ty()),
687 (
688 "IntegralPolyhedron",
689 arrow(arrow(nat_ty(), arrow(nat_ty(), int_ty())), prop()),
690 ),
691 (
692 "IncidenceMatrix",
693 arrow(
694 bipartite_graph_ty(),
695 arrow(nat_ty(), arrow(nat_ty(), int_ty())),
696 ),
697 ),
698 ("FacetDefining", facet_defining_ty()),
699 ("tu_integral_polyhedra", tu_integral_polyhedra_ty()),
700 ("bipartite_incidence_tu", bipartite_incidence_tu_ty()),
701 ];
702 for (name, ty) in axioms {
703 env.add(Declaration::Axiom {
704 name: Name::str(*name),
705 univ_params: vec![],
706 ty: ty.clone(),
707 })
708 .ok();
709 }
710 Ok(())
711}
712pub fn hungarian(cost: &[Vec<i64>]) -> (i64, Vec<usize>) {
715 let n = cost.len();
716 if n == 0 {
717 return (0, vec![]);
718 }
719 let inf = i64::MAX / 2;
720 let mut u = vec![0i64; n + 1];
721 let mut v = vec![0i64; n + 1];
722 let mut p = vec![0usize; n + 1];
723 let mut way = vec![0usize; n + 1];
724 for i in 1..=n {
725 p[0] = i;
726 let mut j0 = 0usize;
727 let mut min_val = vec![inf; n + 1];
728 let mut used = vec![false; n + 1];
729 loop {
730 used[j0] = true;
731 let i0 = p[j0];
732 let mut delta = inf;
733 let mut j1 = 0usize;
734 for j in 1..=n {
735 if !used[j] {
736 let cur = cost[i0 - 1][j - 1] - u[i0] - v[j];
737 if cur < min_val[j] {
738 min_val[j] = cur;
739 way[j] = j0;
740 }
741 if min_val[j] < delta {
742 delta = min_val[j];
743 j1 = j;
744 }
745 }
746 }
747 for j in 0..=n {
748 if used[j] {
749 u[p[j]] += delta;
750 v[j] -= delta;
751 } else {
752 min_val[j] -= delta;
753 }
754 }
755 j0 = j1;
756 if p[j0] == 0 {
757 break;
758 }
759 }
760 loop {
761 let j1 = way[j0];
762 p[j0] = p[j1];
763 j0 = j1;
764 if j0 == 0 {
765 break;
766 }
767 }
768 }
769 let mut ans = vec![0usize; n];
770 for j in 1..=n {
771 if p[j] != 0 {
772 ans[p[j] - 1] = j - 1;
773 }
774 }
775 let total_cost = (1..=n).map(|i| cost[i - 1][ans[i - 1]]).sum();
776 (total_cost, ans)
777}
778pub fn uniform_matroid_greedy(weights: &[f64], k: usize) -> Vec<usize> {
781 let mut indexed: Vec<(usize, f64)> = weights.iter().enumerate().map(|(i, &w)| (i, w)).collect();
782 indexed.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal));
783 indexed[..k.min(indexed.len())]
784 .iter()
785 .map(|&(i, _)| i)
786 .collect()
787}
788pub fn greedy_set_cover(n_elem: usize, sets: &[Vec<usize>]) -> Vec<usize> {
790 let mut covered = vec![false; n_elem];
791 let mut remaining = n_elem;
792 let mut selected = vec![];
793 while remaining > 0 {
794 let best = sets
795 .iter()
796 .enumerate()
797 .filter(|(i, _)| !selected.contains(i))
798 .max_by_key(|(_, s)| s.iter().filter(|&&e| !covered[e]).count());
799 match best {
800 Some((idx, set)) => {
801 let newly_covered = set.iter().filter(|&&e| !covered[e]).count();
802 if newly_covered == 0 {
803 break;
804 }
805 selected.push(idx);
806 for &e in set {
807 if !covered[e] {
808 covered[e] = true;
809 remaining -= 1;
810 }
811 }
812 }
813 None => break,
814 }
815 }
816 selected
817}
818pub fn knapsack_01(items: &[(i64, i64)], capacity: i64) -> (i64, Vec<usize>) {
822 let _n = items.len();
823 let mut best = 0i64;
824 let mut best_sol = vec![];
825 fn backtrack(
826 idx: usize,
827 cap: i64,
828 current_val: i64,
829 current_sol: &mut Vec<usize>,
830 best: &mut i64,
831 best_sol: &mut Vec<usize>,
832 items: &[(i64, i64)],
833 ) {
834 if current_val > *best {
835 *best = current_val;
836 *best_sol = current_sol.clone();
837 }
838 if idx == items.len() {
839 return;
840 }
841 let ub: i64 = current_val
842 + items[idx..]
843 .iter()
844 .scan(cap, |c, &(v, w)| {
845 if *c >= w {
846 *c -= w;
847 Some(v)
848 } else {
849 let frac = (*c * v) / w.max(1);
850 *c = 0;
851 Some(frac)
852 }
853 })
854 .sum::<i64>();
855 if ub <= *best {
856 return;
857 }
858 let (v, w) = items[idx];
859 if cap >= w {
860 current_sol.push(idx);
861 backtrack(
862 idx + 1,
863 cap - w,
864 current_val + v,
865 current_sol,
866 best,
867 best_sol,
868 items,
869 );
870 current_sol.pop();
871 }
872 backtrack(
873 idx + 1,
874 cap,
875 current_val,
876 current_sol,
877 best,
878 best_sol,
879 items,
880 );
881 }
882 let mut sol = vec![];
883 backtrack(0, capacity, 0, &mut sol, &mut best, &mut best_sol, items);
884 (best, best_sol)
885}
886pub fn tsp_nearest_neighbor(dist: &[Vec<f64>]) -> (Vec<usize>, f64) {
888 let n = dist.len();
889 if n == 0 {
890 return (vec![], 0.0);
891 }
892 let mut visited = vec![false; n];
893 let mut tour = vec![0usize];
894 visited[0] = true;
895 let mut cost = 0.0;
896 for _ in 1..n {
897 let last = *tour
898 .last()
899 .expect("tour is non-empty: initialized with element 0");
900 let next = (0..n).filter(|&j| !visited[j]).min_by(|&a, &b| {
901 dist[last][a]
902 .partial_cmp(&dist[last][b])
903 .unwrap_or(std::cmp::Ordering::Equal)
904 });
905 if let Some(next) = next {
906 cost += dist[last][next];
907 tour.push(next);
908 visited[next] = true;
909 }
910 }
911 cost += dist[*tour
912 .last()
913 .expect("tour is non-empty: initialized with element 0")][tour[0]];
914 (tour, cost)
915}
916pub fn tsp_held_karp(dist: &[Vec<f64>]) -> f64 {
918 let n = dist.len();
919 if n <= 1 {
920 return 0.0;
921 }
922 let full = 1usize << n;
923 let inf = f64::INFINITY;
924 let mut dp = vec![vec![inf; n]; full];
925 dp[1][0] = 0.0;
926 for mask in 1..full {
927 for u in 0..n {
928 if dp[mask][u] == inf {
929 continue;
930 }
931 if mask & (1 << u) == 0 {
932 continue;
933 }
934 for v in 0..n {
935 if mask & (1 << v) != 0 {
936 continue;
937 }
938 let next_mask = mask | (1 << v);
939 let new_cost = dp[mask][u] + dist[u][v];
940 if new_cost < dp[next_mask][v] {
941 dp[next_mask][v] = new_cost;
942 }
943 }
944 }
945 }
946 let last_mask = full - 1;
947 (1..n)
948 .filter_map(|u| {
949 let c = dp[last_mask][u] + dist[u][0];
950 if c < inf {
951 Some(c)
952 } else {
953 None
954 }
955 })
956 .fold(inf, f64::min)
957}
958#[cfg(test)]
959mod tests {
960 use super::*;
961 #[test]
962 fn test_bipartite_matching_perfect() {
963 let mut g = BipartiteMatchingGraph::new(3, 3);
964 for u in 0..3 {
965 for v in 0..3 {
966 g.add_edge(u, v);
967 }
968 }
969 let (size, _, _) = g.hopcroft_karp();
970 assert_eq!(size, 3);
971 }
972 #[test]
973 fn test_bipartite_matching_partial() {
974 let mut g = BipartiteMatchingGraph::new(3, 2);
975 g.add_edge(0, 0);
976 g.add_edge(1, 0);
977 g.add_edge(1, 1);
978 g.add_edge(2, 1);
979 let (size, _, _) = g.hopcroft_karp();
980 assert_eq!(size, 2);
981 }
982 #[test]
983 fn test_flow_network_max_flow() {
984 let mut net = FlowNetwork::new(4);
985 net.add_edge(0, 1, 3);
986 net.add_edge(0, 2, 2);
987 net.add_edge(1, 3, 3);
988 net.add_edge(2, 3, 2);
989 let flow = net.max_flow(0, 3);
990 assert_eq!(flow, 5);
991 }
992 #[test]
993 fn test_hungarian_algorithm() {
994 let cost = vec![vec![4, 1, 3], vec![2, 0, 5], vec![3, 2, 2]];
995 let (total, assignment) = hungarian(&cost);
996 let mut cols: Vec<usize> = assignment.clone();
997 cols.sort();
998 cols.dedup();
999 assert_eq!(cols.len(), 3, "Assignment must be a permutation");
1000 let computed: i64 = assignment
1001 .iter()
1002 .enumerate()
1003 .map(|(i, &j)| cost[i][j])
1004 .sum();
1005 assert_eq!(total, computed);
1006 assert!(total <= 6, "Should find near-optimal solution");
1007 }
1008 #[test]
1009 fn test_uniform_matroid_greedy() {
1010 let weights = [0.5, 3.0, 1.2, 2.8, 0.9];
1011 let selected = uniform_matroid_greedy(&weights, 3);
1012 assert_eq!(selected.len(), 3);
1013 assert!(selected.contains(&1));
1014 assert!(selected.contains(&3));
1015 }
1016 #[test]
1017 fn test_greedy_set_cover() {
1018 let sets = vec![vec![0, 1, 2], vec![1, 3, 4], vec![2, 4, 5]];
1019 let cover = greedy_set_cover(6, &sets);
1020 let mut covered = [false; 6];
1021 for &idx in &cover {
1022 for &e in &sets[idx] {
1023 covered[e] = true;
1024 }
1025 }
1026 assert!(covered.iter().all(|&c| c), "All elements should be covered");
1027 }
1028 #[test]
1029 fn test_knapsack_01() {
1030 let items = vec![(4, 2), (5, 3), (3, 2), (7, 4)];
1031 let (val, sel) = knapsack_01(&items, 7);
1032 assert_eq!(val, 12);
1033 let total_weight: i64 = sel.iter().map(|&i| items[i].1).sum();
1034 assert!(total_weight <= 7, "selected items exceed capacity");
1035 let total_value: i64 = sel.iter().map(|&i| items[i].0).sum();
1036 assert_eq!(total_value, 12, "selected items should have value 12");
1037 }
1038 #[test]
1039 fn test_tsp_held_karp_small() {
1040 let d = vec![
1041 vec![0.0, 10.0, 15.0, 20.0],
1042 vec![10.0, 0.0, 35.0, 25.0],
1043 vec![15.0, 35.0, 0.0, 30.0],
1044 vec![20.0, 25.0, 30.0, 0.0],
1045 ];
1046 let opt = tsp_held_karp(&d);
1047 assert!(
1048 (opt - 80.0).abs() < 1e-9,
1049 "Held-Karp should find optimal tour of length 80, got {}",
1050 opt
1051 );
1052 }
1053 #[test]
1054 fn test_build_combinatorial_optimization_env() {
1055 let mut env = Environment::new();
1056 let result = build_combinatorial_optimization_env(&mut env);
1057 assert!(
1058 result.is_ok(),
1059 "build_combinatorial_optimization_env failed: {:?}",
1060 result.err()
1061 );
1062 }
1063}
1064#[cfg(test)]
1065mod spec_wrapper_tests {
1066 use super::*;
1067 #[test]
1068 fn test_flow_network_spec() {
1069 let mut net = FlowNetworkSpec::new(4);
1070 net.add_edge(0, 1, 3);
1071 net.add_edge(0, 2, 2);
1072 net.add_edge(1, 3, 3);
1073 net.add_edge(2, 3, 2);
1074 assert_eq!(net.max_flow_ford_fulkerson(0, 3), 5);
1075 assert_eq!(net.min_cut(0, 3), 5);
1076 assert!(net.augmenting_path(0, 3));
1077 }
1078 #[test]
1079 fn test_matching_problem_bipartite() {
1080 let mut mp = MatchingProblem::new(true, 3, 3);
1081 mp.add_edge(0, 0, 1.0);
1082 mp.add_edge(1, 1, 2.0);
1083 mp.add_edge(2, 2, 3.0);
1084 assert_eq!(mp.max_matching(), 3);
1085 }
1086 #[test]
1087 fn test_spanning_tree_kruskal() {
1088 let mut st = SpanningTree::new(4);
1089 st.add_edge(0, 1, 1.0);
1090 st.add_edge(1, 2, 2.0);
1091 st.add_edge(2, 3, 3.0);
1092 st.add_edge(0, 3, 10.0);
1093 let mst = st.kruskal();
1094 assert_eq!(mst.len(), 3);
1095 let w: f64 = mst.iter().map(|&(_, _, w)| w).sum();
1096 assert!((w - 6.0).abs() < 1e-9);
1097 }
1098 #[test]
1099 fn test_shortest_path_dijkstra() {
1100 let mut sp = ShortestPath::new(3);
1101 sp.add_edge(0, 1, 1.0);
1102 sp.add_edge(1, 2, 2.0);
1103 sp.add_edge(0, 2, 5.0);
1104 let dist = sp.dijkstra(0);
1105 assert!((dist[2] - 3.0).abs() < 1e-9);
1106 }
1107 #[test]
1108 fn test_bellman_ford() {
1109 let mut sp = ShortestPath::new(3);
1110 sp.add_edge(0, 1, 1.0);
1111 sp.add_edge(1, 2, 2.0);
1112 sp.add_edge(0, 2, 5.0);
1113 let dist = sp.bellman_ford(0);
1114 assert!((dist[2] - 3.0).abs() < 1e-9);
1115 }
1116 #[test]
1117 fn test_floyd_warshall() {
1118 let mut sp = ShortestPath::new(3);
1119 sp.add_edge(0, 1, 1.0);
1120 sp.add_edge(1, 2, 2.0);
1121 sp.add_edge(0, 2, 5.0);
1122 let d = sp.floyd_warshall();
1123 assert!((d[0][2] - 3.0).abs() < 1e-9);
1124 }
1125 #[test]
1126 fn test_knapsack_solver() {
1127 let solver = KnapsackSolver::new(vec![(4, 2), (5, 3), (3, 2), (7, 4)], 7);
1128 let (val, _) = solver.dynamic_programming();
1129 assert_eq!(val, 12);
1130 }
1131 #[test]
1132 fn test_graph_coloring() {
1133 let gc = GraphColoring::new(3, vec![(0, 1), (1, 2), (0, 2)]);
1134 let (k, _) = gc.greedy_color();
1135 assert!(k >= 3);
1136 assert!(gc.chromatic_number_bound() >= 3);
1137 let (dk, _) = gc.dsatur();
1138 assert!(dk >= 3);
1139 }
1140 #[test]
1141 fn test_traveling_salesman() {
1142 let d = vec![
1143 vec![0.0, 10.0, 15.0, 20.0],
1144 vec![10.0, 0.0, 35.0, 25.0],
1145 vec![15.0, 35.0, 0.0, 30.0],
1146 vec![20.0, 25.0, 30.0, 0.0],
1147 ];
1148 let tsp = TravelingSalesman::new(d);
1149 let hk = tsp.held_karp();
1150 assert!((hk - 80.0).abs() < 1e-9);
1151 let nn = tsp.nearest_neighbor();
1152 assert!(nn > 0.0);
1153 }
1154 #[test]
1155 fn test_steiner_tree() {
1156 let st = SteinerTree::new(5, vec![0, 2, 4]);
1157 assert!(st.approx_2() >= 0.0);
1158 assert!(st.dreyfus_wagner() >= 0.0);
1159 }
1160 #[test]
1161 fn test_matroid_intersection() {
1162 let mi = MatroidIntersection::new("graphic", "partition");
1163 assert!(mi.exchange_property());
1164 }
1165}
1166#[cfg(test)]
1167mod extended_comb_opt_tests {
1168 use super::*;
1169 #[test]
1170 fn test_branch_bound() {
1171 let mut bb = BranchBoundData::integer_program("most-fractional", "LP relaxation");
1172 bb.explore(100);
1173 assert_eq!(bb.nodes_explored, 100);
1174 assert!(bb.description().contains("B&B"));
1175 }
1176 #[test]
1177 fn test_cutting_plane() {
1178 let mut cp = CuttingPlane::gomory();
1179 cp.add_cut();
1180 cp.add_cut();
1181 assert_eq!(cp.num_cuts_added, 2);
1182 assert!(cp.description().contains("Gomory"));
1183 }
1184 #[test]
1185 fn test_set_cover() {
1186 let sc = SetCoverData::greedy(5, 10);
1187 assert!(sc.approximation_ratio > 1.0);
1188 assert!(sc.approx_description().contains("Greedy"));
1189 }
1190 #[test]
1191 fn test_facility_location() {
1192 let fl = FacilityLocation::new(3, 10, vec![1.0, 2.0, 3.0], 5.0);
1193 assert_eq!(fl.total_opening_cost(), 6.0);
1194 assert!((FacilityLocation::jms_approximation_ratio() - 1.861).abs() < 0.001);
1195 }
1196 #[test]
1197 fn test_vehicle_routing() {
1198 let vr = VehicleRouting::capacitated(3, 20, 100.0);
1199 assert_eq!(vr.num_vehicles, 3);
1200 assert!(vr.christofides_description().contains("Christofides"));
1201 }
1202}