Skip to main content

oxilean_std/combinatorial_optimization/
functions.rs

1//! Auto-generated module
2//!
3//! ๐Ÿค– Generated with [SplitRS](https://github.com/cool-japan/splitrs)
4
5use 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}
63/// `Graph : Type` โ€” a simple undirected graph (vertices: Nat, edges: pairs).
64pub fn graph_ty() -> Expr {
65    type0()
66}
67/// `BipartiteGraph : Type` โ€” bipartite graph with two vertex sets.
68pub fn bipartite_graph_ty() -> Expr {
69    type0()
70}
71/// `Matching : Graph โ†’ Type` โ€” a set of non-adjacent edges.
72pub fn matching_ty() -> Expr {
73    arrow(graph_ty(), type0())
74}
75/// `PerfectMatching : Graph โ†’ Prop` โ€” every vertex is matched.
76pub fn perfect_matching_ty() -> Expr {
77    arrow(graph_ty(), prop())
78}
79/// `MaxMatching : Graph โ†’ Matching โ†’ Prop` โ€” a maximum cardinality matching.
80pub fn max_matching_ty() -> Expr {
81    arrow(graph_ty(), arrow(matching_ty(), prop()))
82}
83/// `Alternating_path : Graph โ†’ Matching โ†’ List Nat โ†’ Prop` โ€” alternating path wrt matching.
84pub fn alternating_path_ty() -> Expr {
85    arrow(
86        graph_ty(),
87        arrow(matching_ty(), arrow(list_ty(nat_ty()), prop())),
88    )
89}
90/// `AugmentingPath : Graph โ†’ Matching โ†’ List Nat โ†’ Prop` โ€” augmenting path.
91pub fn augmenting_path_ty() -> Expr {
92    arrow(
93        graph_ty(),
94        arrow(matching_ty(), arrow(list_ty(nat_ty()), prop())),
95    )
96}
97/// `Blossom : Graph โ†’ Matching โ†’ List Nat โ†’ Prop` โ€” odd cycle contracted in Edmonds' algorithm.
98pub fn blossom_ty() -> Expr {
99    arrow(
100        graph_ty(),
101        arrow(matching_ty(), arrow(list_ty(nat_ty()), prop())),
102    )
103}
104/// `HallCondition : BipartiteGraph โ†’ Prop`
105/// For every subset S of left vertices, |N(S)| โ‰ฅ |S|.
106pub fn hall_condition_ty() -> Expr {
107    arrow(bipartite_graph_ty(), prop())
108}
109/// `HallTheorem : โˆ€ G : BipartiteGraph, HallCondition G โ†” PerfectMatching G`
110pub 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}
122/// `KonigTheorem : MaxMatchingSize = MinVertexCoverSize` for bipartite graphs.
123pub 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}
135/// `TutteCondition : Graph โ†’ Prop`
136/// For every S โІ V, the number of odd components of G-S is โ‰ค |S|.
137pub fn tutte_condition_ty() -> Expr {
138    arrow(graph_ty(), prop())
139}
140/// `TutteTheorem : โˆ€ G, PerfectMatching G โ†” TutteCondition G`
141pub 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}
153/// `BergeTheorem : max matching augmenting path characterization`
154pub 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}
174/// `FlowNetwork : Type` โ€” directed graph with capacities.
175pub fn flow_network_ty() -> Expr {
176    type0()
177}
178/// `Flow : FlowNetwork โ†’ Type` โ€” feasible flow satisfying capacity and conservation.
179pub fn flow_ty() -> Expr {
180    arrow(flow_network_ty(), type0())
181}
182/// `FlowValue : FlowNetwork โ†’ Flow โ†’ Real` โ€” the value of a flow (net flow out of source).
183pub fn flow_value_ty() -> Expr {
184    arrow(flow_network_ty(), arrow(flow_ty(), real_ty()))
185}
186/// `MaxFlow : FlowNetwork โ†’ Real` โ€” maximum flow value.
187pub fn max_flow_ty() -> Expr {
188    arrow(flow_network_ty(), real_ty())
189}
190/// `Cut : FlowNetwork โ†’ (Nat โ†’ Bool) โ†’ Prop` โ€” s-t cut (partition of vertices).
191pub fn cut_ty() -> Expr {
192    arrow(flow_network_ty(), arrow(arrow(nat_ty(), bool_ty()), prop()))
193}
194/// `CutCapacity : FlowNetwork โ†’ (Nat โ†’ Bool) โ†’ Real` โ€” total capacity of a cut.
195pub fn cut_capacity_ty() -> Expr {
196    arrow(
197        flow_network_ty(),
198        arrow(arrow(nat_ty(), bool_ty()), real_ty()),
199    )
200}
201/// `MinCut : FlowNetwork โ†’ Real` โ€” minimum cut capacity.
202pub fn min_cut_ty() -> Expr {
203    arrow(flow_network_ty(), real_ty())
204}
205/// `MaxFlowMinCut : โˆ€ N, MaxFlow N = MinCut N` โ€” the max-flow min-cut theorem.
206pub 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}
218/// `FordFulkersonTermination : Ford-Fulkerson terminates on integer capacities.`
219pub 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}
230/// `DinicsComplexity : Dinic's algorithm runs in O(Vยฒ E).`
231pub fn dinics_complexity_ty() -> Expr {
232    pi(
233        BinderInfo::Default,
234        "N",
235        flow_network_ty(),
236        app(cst("DinicsTimeBound"), bvar(0)),
237    )
238}
239/// `CostMatrix : Nat โ†’ Nat โ†’ Real` โ€” cost matrix for assignment.
240pub fn cost_matrix_ty() -> Expr {
241    arrow(nat_ty(), arrow(nat_ty(), real_ty()))
242}
243/// `Assignment : Nat โ†’ (Nat โ†’ Nat) โ†’ Prop` โ€” a permutation assignment for nร—n matrix.
244pub fn assignment_ty() -> Expr {
245    arrow(nat_ty(), arrow(arrow(nat_ty(), nat_ty()), prop()))
246}
247/// `OptimalAssignment : CostMatrix โ†’ (Nat โ†’ Nat) โ†’ Prop` โ€” assignment minimizing total cost.
248pub fn optimal_assignment_ty() -> Expr {
249    arrow(cost_matrix_ty(), arrow(arrow(nat_ty(), nat_ty()), prop()))
250}
251/// `HungarianAlgorithm : CostMatrix โ†’ (Nat โ†’ Nat)` โ€” solves assignment in O(nยณ).
252pub fn hungarian_algorithm_ty() -> Expr {
253    arrow(cost_matrix_ty(), arrow(nat_ty(), nat_ty()))
254}
255/// `HungarianCorrectness : HungarianAlgorithm gives OptimalAssignment.`
256pub 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}
268/// `TSPInstance : Type` โ€” complete graph with edge weights.
269pub fn tsp_instance_ty() -> Expr {
270    type0()
271}
272/// `TSPTour : TSPInstance โ†’ List Nat โ†’ Prop` โ€” a Hamiltonian cycle.
273pub fn tsp_tour_ty() -> Expr {
274    arrow(tsp_instance_ty(), arrow(list_ty(nat_ty()), prop()))
275}
276/// `TSPOptimal : TSPInstance โ†’ Real` โ€” optimal TSP tour length.
277pub fn tsp_optimal_ty() -> Expr {
278    arrow(tsp_instance_ty(), real_ty())
279}
280/// `HeldKarpBound : TSPInstance โ†’ Real` โ€” Held-Karp lower bound.
281pub fn held_karp_bound_ty() -> Expr {
282    arrow(tsp_instance_ty(), real_ty())
283}
284/// `HeldKarpLowerBound : HeldKarpBound โ‰ค TSPOptimal.`
285pub 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}
297/// `ChristofidesApproximation : 3/2-approximation for metric TSP.`
298pub 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}
317/// `VertexCoverApproximation : 2-approx for vertex cover.`
318pub 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}
334/// `SetCoverApproximation : H_n-approximation (log n) for set cover.`
335pub 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}
351/// `Matroid : Type` โ€” a matroid (ground set + independent sets satisfying axioms).
352pub fn matroid_ty() -> Expr {
353    type0()
354}
355/// `IndependentSet : Matroid โ†’ List Nat โ†’ Prop`
356pub fn independent_set_ty() -> Expr {
357    arrow(matroid_ty(), arrow(list_ty(nat_ty()), prop()))
358}
359/// `MatroidBase : Matroid โ†’ List Nat โ†’ Prop` โ€” maximal independent set.
360pub fn matroid_base_ty() -> Expr {
361    arrow(matroid_ty(), arrow(list_ty(nat_ty()), prop()))
362}
363/// `MatroidRank : Matroid โ†’ Nat` โ€” rank of a matroid.
364pub fn matroid_rank_ty() -> Expr {
365    arrow(matroid_ty(), nat_ty())
366}
367/// `GreedyOptimality : Greedy algorithm is optimal for matroid weight maximization.`
368pub 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}
381/// `MatroidIntersection : common independent set in two matroids.`
382pub fn matroid_intersection_ty() -> Expr {
383    arrow(matroid_ty(), arrow(matroid_ty(), list_ty(nat_ty())))
384}
385/// `MatroidIntersectionOptimality : the max weight common independent set.`
386pub 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}
404/// `GraphicMatroid : Graph โ†’ Matroid` โ€” cycle matroid of a graph.
405pub fn graphic_matroid_ty() -> Expr {
406    arrow(graph_ty(), matroid_ty())
407}
408/// `UniformMatroid : Nat โ†’ Nat โ†’ Matroid` โ€” U(k,n) uniform matroid.
409pub fn uniform_matroid_ty() -> Expr {
410    arrow(nat_ty(), arrow(nat_ty(), matroid_ty()))
411}
412/// `SubmodularFunction : (List Nat โ†’ Real) โ†’ Prop`
413/// f is submodular if f(A) + f(B) โ‰ฅ f(AโˆชB) + f(AโˆฉB).
414pub fn submodular_function_ty() -> Expr {
415    arrow(arrow(list_ty(nat_ty()), real_ty()), prop())
416}
417/// `SubmodularMaximization : (List Nat โ†’ Real) โ†’ List Nat` โ€” greedy maximization.
418pub fn submodular_maximization_ty() -> Expr {
419    arrow(arrow(list_ty(nat_ty()), real_ty()), list_ty(nat_ty()))
420}
421/// `SubmodularGreedy_1_2_approx : greedy gives 1/2-approximation for monotone submodular max.`
422pub 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}
441/// `SupermodularFunction : (List Nat โ†’ Real) โ†’ Prop` โ€” supermodular (negation of submodular).
442pub fn supermodular_function_ty() -> Expr {
443    arrow(arrow(list_ty(nat_ty()), real_ty()), prop())
444}
445/// `PolymatroidRankFunction : axioms for polymatroid rank.`
446pub fn polymatroid_rank_ty() -> Expr {
447    arrow(arrow(list_ty(nat_ty()), real_ty()), prop())
448}
449/// `ILPInstance : Type` โ€” integer linear program: min cยทx s.t. Ax โ‰ค b, x โˆˆ โ„ค^n.
450pub fn ilp_instance_ty() -> Expr {
451    type0()
452}
453/// `ILPSolution : ILPInstance โ†’ List Int โ†’ Prop`
454pub fn ilp_solution_ty() -> Expr {
455    arrow(ilp_instance_ty(), arrow(list_ty(int_ty()), prop()))
456}
457/// `ILPOptimal : ILPInstance โ†’ List Int โ†’ Prop`
458pub fn ilp_optimal_ty() -> Expr {
459    arrow(ilp_instance_ty(), arrow(list_ty(int_ty()), prop()))
460}
461/// `GomoryCut : ILPInstance โ†’ ILPInstance` โ€” adds a Gomory cutting plane.
462pub fn gomory_cut_ty() -> Expr {
463    arrow(ilp_instance_ty(), ilp_instance_ty())
464}
465/// `BranchAndBound : ILPInstance โ†’ List Int` โ€” branch-and-bound solver.
466pub fn branch_and_bound_ty() -> Expr {
467    arrow(ilp_instance_ty(), list_ty(int_ty()))
468}
469/// `LPRelaxation : ILPInstance โ†’ Real` โ€” optimal LP relaxation value.
470pub fn lp_relaxation_ty() -> Expr {
471    arrow(ilp_instance_ty(), real_ty())
472}
473/// `LPRelaxationLowerBound : LPRelaxation โ‰ค ILP optimal.`
474pub 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}
486/// `Polytope : Type` โ€” a convex polytope (intersection of halfspaces).
487pub fn polytope_ty() -> Expr {
488    type0()
489}
490/// `Vertex : Polytope โ†’ List Real โ†’ Prop` โ€” extreme point of polytope.
491pub fn polytope_vertex_ty() -> Expr {
492    arrow(polytope_ty(), arrow(list_ty(real_ty()), prop()))
493}
494/// `TotallyUnimodular : (Nat โ†’ Nat โ†’ Int) โ†’ Prop` โ€” every square submatrix has det โˆˆ {-1,0,1}.
495pub fn totally_unimodular_ty() -> Expr {
496    arrow(arrow(nat_ty(), arrow(nat_ty(), int_ty())), prop())
497}
498/// `TUIntegralPolyhedra : TU matrix โ†’ LP has integer optimal vertex.`
499pub 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}
510/// `BipartiteIncidenceTU : incidence matrix of bipartite graph is TU.`
511pub 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}
522/// `FacetDefiningInequality : Polytope โ†’ (List Real โ†’ Prop) โ†’ Prop`
523pub fn facet_defining_ty() -> Expr {
524    arrow(
525        polytope_ty(),
526        arrow(arrow(list_ty(real_ty()), prop()), prop()),
527    )
528}
529/// `WeakDuality : for LP, dual objective โ‰ฅ primal objective.`
530pub 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}
542/// `StrongDuality : for LP, dual objective = primal when both feasible.`
543pub 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}
558/// Register all combinatorial optimization axioms into the kernel environment.
559pub 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}
712/// Solve the minimum-weight assignment problem using the Hungarian algorithm.
713/// Returns (total cost, assignment vector where assignment\[i\] = j means row i โ†’ col j).
714pub 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}
778/// Greedy maximum weight independent set in uniform matroid U(k, n).
779/// Returns indices of k elements with highest weights.
780pub 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}
788/// Greedy set cover: returns selected set indices covering all elements 0..n_elem.
789pub 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}
818/// Branch-and-bound for 0-1 knapsack (maximize).
819/// items: (value, weight), capacity: max weight.
820/// Returns (max_value, selected_item_indices).
821pub 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}
886/// Nearest-neighbor heuristic for TSP. Returns tour (list of vertex indices).
887pub 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}
916/// Held-Karp DP lower bound (exact TSP for small n โ‰ค 20).
917pub 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}