1use oxilean_kernel::Node;
6use oxilean_kernel::{BinderInfo, Declaration, Environment, Expr, Level, Name};
7
8use super::types::{
9 BundleMethodSolver, CuttingPlaneSolver, FISTASolver, GeometricProgramSolver, GradientDescent,
10 L1NormFunction, MirrorDescentSolver, OnlineLearner, ProjectedGradient, ProximalGradientSolver,
11 QuadraticFunction, RipVerifier, SDPRelaxation, SinkhornSolver, ADMM,
12};
13
14pub fn app(f: Expr, a: Expr) -> Expr {
15 Expr::App(Node::new(f), Node::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), Node::new(dom), Node::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 convex_set_ty() -> Expr {
53 arrow(fn_ty(list_ty(real_ty()), prop()), prop())
54}
55pub fn convex_function_ty() -> Expr {
58 arrow(fn_ty(list_ty(real_ty()), real_ty()), prop())
59}
60pub fn kkt_conditions_ty() -> Expr {
63 let rn_to_r = fn_ty(list_ty(real_ty()), real_ty());
64 let list_rn_to_r = list_ty(rn_to_r.clone());
65 arrow(
66 rn_to_r,
67 arrow(list_rn_to_r, arrow(list_ty(real_ty()), prop())),
68 )
69}
70pub fn lagrangian_ty() -> Expr {
73 let rn_to_r = fn_ty(list_ty(real_ty()), real_ty());
74 let list_rn_to_r = list_ty(rn_to_r.clone());
75 arrow(
76 rn_to_r,
77 arrow(
78 list_rn_to_r,
79 arrow(list_ty(real_ty()), arrow(list_ty(real_ty()), real_ty())),
80 ),
81 )
82}
83pub fn strong_duality_ty() -> Expr {
86 prop()
87}
88pub fn projection_theorem_ty() -> Expr {
91 prop()
92}
93pub fn supporting_hyperplane_ty() -> Expr {
96 prop()
97}
98pub fn jensen_inequality_ty() -> Expr {
101 arrow(fn_ty(list_ty(real_ty()), real_ty()), prop())
102}
103pub fn slater_condition_ty() -> Expr {
106 prop()
107}
108pub fn fenchel_conjugate_ty() -> Expr {
111 let rn_to_r = fn_ty(list_ty(real_ty()), real_ty());
112 arrow(rn_to_r, fn_ty(list_ty(real_ty()), real_ty()))
113}
114pub fn fenchel_rockafellar_duality_ty() -> Expr {
117 prop()
118}
119pub fn conjugate_subgradient_ty() -> Expr {
122 let rn_to_r = fn_ty(list_ty(real_ty()), real_ty());
123 let lr = list_ty(real_ty());
124 arrow(rn_to_r, arrow(lr.clone(), arrow(lr, prop())))
125}
126pub fn lagrangian_dual_function_ty() -> Expr {
129 let rn_to_r = fn_ty(list_ty(real_ty()), real_ty());
130 let list_rn_to_r = list_ty(rn_to_r.clone());
131 arrow(
132 rn_to_r,
133 arrow(list_rn_to_r, fn_ty(list_ty(real_ty()), real_ty())),
134 )
135}
136pub fn licq_ty() -> Expr {
139 prop()
140}
141pub fn mfcq_ty() -> Expr {
144 prop()
145}
146pub fn complementary_slackness_ty() -> Expr {
149 let lr = list_ty(real_ty());
150 arrow(lr.clone(), arrow(lr, prop()))
151}
152pub fn kkt_sufficiency_ty() -> Expr {
155 prop()
156}
157pub fn barrier_function_ty() -> Expr {
160 let rn_to_prop = fn_ty(list_ty(real_ty()), prop());
161 let rn_to_r = fn_ty(list_ty(real_ty()), real_ty());
162 arrow(rn_to_prop, arrow(rn_to_r, prop()))
163}
164pub fn path_following_method_ty() -> Expr {
167 prop()
168}
169pub fn predictor_corrector_method_ty() -> Expr {
172 prop()
173}
174pub fn central_path_convergence_ty() -> Expr {
177 arrow(real_ty(), prop())
178}
179pub fn positive_semidefinite_ty() -> Expr {
182 let mat = list_ty(list_ty(real_ty()));
183 arrow(mat, prop())
184}
185pub fn sdp_feasibility_ty() -> Expr {
188 let mat = list_ty(list_ty(real_ty()));
189 let mats = list_ty(mat);
190 let lr = list_ty(real_ty());
191 arrow(mats, arrow(lr, prop()))
192}
193pub fn sdp_duality_ty() -> Expr {
196 prop()
197}
198pub fn sdp_optimality_ty() -> Expr {
201 let mat = list_ty(list_ty(real_ty()));
202 let mats = list_ty(mat);
203 let lr = list_ty(real_ty());
204 arrow(mats, arrow(lr.clone(), arrow(lr, arrow(real_ty(), prop()))))
205}
206pub fn second_order_cone_ty() -> Expr {
209 let lr = list_ty(real_ty());
210 arrow(lr, arrow(real_ty(), prop()))
211}
212pub fn socp_feasibility_ty() -> Expr {
215 prop()
216}
217pub fn monomial_ty() -> Expr {
220 let lr = list_ty(real_ty());
221 arrow(lr.clone(), arrow(lr, arrow(real_ty(), real_ty())))
222}
223pub fn posynomial_ty() -> Expr {
226 let rn_to_r = fn_ty(list_ty(real_ty()), real_ty());
227 let lr = list_ty(real_ty());
228 arrow(list_ty(rn_to_r), arrow(lr, real_ty()))
229}
230pub fn geometric_program_duality_ty() -> Expr {
233 prop()
234}
235pub fn smooth_gradient_convergence_ty() -> Expr {
238 arrow(real_ty(), arrow(real_ty(), arrow(nat_ty(), prop())))
239}
240pub fn strongly_convex_convergence_ty() -> Expr {
243 arrow(real_ty(), arrow(real_ty(), arrow(nat_ty(), prop())))
244}
245pub fn proximal_operator_ty() -> Expr {
248 let rn_to_r = fn_ty(list_ty(real_ty()), real_ty());
249 let lr = list_ty(real_ty());
250 arrow(rn_to_r, arrow(real_ty(), fn_ty(lr.clone(), lr)))
251}
252pub fn ista_convergence_ty() -> Expr {
255 arrow(real_ty(), arrow(real_ty(), arrow(nat_ty(), prop())))
256}
257pub fn fista_convergence_ty() -> Expr {
260 arrow(real_ty(), arrow(nat_ty(), prop()))
261}
262pub fn proximal_gradient_descent_ty() -> Expr {
265 let rn_to_r = fn_ty(list_ty(real_ty()), real_ty());
266 arrow(rn_to_r.clone(), arrow(rn_to_r, prop()))
267}
268pub fn douglas_rachford_splitting_ty() -> Expr {
271 prop()
272}
273pub fn chambolle_pock_ty() -> Expr {
276 arrow(real_ty(), arrow(real_ty(), arrow(nat_ty(), prop())))
277}
278pub fn augmented_lagrangian_ty() -> Expr {
281 let rn_to_r = fn_ty(list_ty(real_ty()), real_ty());
282 let list_rn_to_r = list_ty(rn_to_r.clone());
283 arrow(
284 rn_to_r,
285 arrow(
286 list_rn_to_r,
287 arrow(
288 real_ty(),
289 arrow(list_ty(real_ty()), arrow(list_ty(real_ty()), real_ty())),
290 ),
291 ),
292 )
293}
294pub fn admm_convergence_ty() -> Expr {
297 arrow(real_ty(), prop())
298}
299pub fn supporting_hyperplane_cut_ty() -> Expr {
302 let rn_to_r = fn_ty(list_ty(real_ty()), real_ty());
303 let lr = list_ty(real_ty());
304 arrow(
305 rn_to_r,
306 arrow(lr.clone(), arrow(lr, arrow(real_ty(), prop()))),
307 )
308}
309pub fn kelley_method_ty() -> Expr {
312 prop()
313}
314pub fn bundle_method_convergence_ty() -> Expr {
317 arrow(real_ty(), prop())
318}
319pub fn ellipsoid_method_complexity_ty() -> Expr {
322 arrow(real_ty(), arrow(real_ty(), arrow(nat_ty(), prop())))
323}
324pub fn center_of_gravity_method_ty() -> Expr {
327 arrow(nat_ty(), prop())
328}
329pub fn subgradient_inequality_ty() -> Expr {
332 let rn_to_r = fn_ty(list_ty(real_ty()), real_ty());
333 let lr = list_ty(real_ty());
334 arrow(rn_to_r, arrow(lr.clone(), arrow(lr, prop())))
335}
336pub fn subgradient_method_convergence_ty() -> Expr {
339 arrow(real_ty(), arrow(real_ty(), arrow(nat_ty(), prop())))
340}
341pub fn polyak_stepsize_ty() -> Expr {
344 arrow(real_ty(), arrow(real_ty(), prop()))
345}
346pub fn sgd_convergence_ty() -> Expr {
349 arrow(real_ty(), arrow(nat_ty(), prop()))
350}
351pub fn svrg_convergence_ty() -> Expr {
354 arrow(real_ty(), arrow(real_ty(), arrow(nat_ty(), prop())))
355}
356pub fn sarah_convergence_ty() -> Expr {
359 arrow(real_ty(), arrow(real_ty(), arrow(nat_ty(), prop())))
360}
361pub fn spider_convergence_ty() -> Expr {
364 arrow(real_ty(), arrow(real_ty(), arrow(nat_ty(), prop())))
365}
366pub fn dcp_atom_convex_ty() -> Expr {
369 let rn_to_r = fn_ty(list_ty(real_ty()), real_ty());
370 arrow(rn_to_r, prop())
371}
372pub fn dcp_composition_rule_ty() -> Expr {
375 prop()
376}
377pub fn dcp_verification_ty() -> Expr {
380 let rn_to_r = fn_ty(list_ty(real_ty()), real_ty());
381 arrow(rn_to_r, prop())
382}
383pub fn self_concordant_barrier_ty() -> Expr {
386 let rn_to_r = fn_ty(list_ty(real_ty()), real_ty());
387 arrow(rn_to_r, arrow(real_ty(), prop()))
388}
389pub fn self_concordant_complexity_ty() -> Expr {
392 arrow(real_ty(), arrow(nat_ty(), prop()))
393}
394pub fn logarithmic_barrier_ty() -> Expr {
397 let rn_to_r = fn_ty(list_ty(real_ty()), real_ty());
398 arrow(nat_ty(), arrow(rn_to_r, prop()))
399}
400pub fn newton_decrement_ty() -> Expr {
403 let rn_to_r = fn_ty(list_ty(real_ty()), real_ty());
404 let lr = list_ty(real_ty());
405 arrow(rn_to_r, arrow(lr, real_ty()))
406}
407pub fn sdp_slater_condition_ty() -> Expr {
410 prop()
411}
412pub fn sdp_complementarity_ty() -> Expr {
415 let mat = list_ty(list_ty(real_ty()));
416 arrow(mat.clone(), arrow(mat, prop()))
417}
418pub fn sdp_duality_gap_ty() -> Expr {
421 let mat = list_ty(list_ty(real_ty()));
422 let lr = list_ty(real_ty());
423 arrow(mat, arrow(lr, real_ty()))
424}
425pub fn lorentz_cone_ty() -> Expr {
428 arrow(nat_ty(), prop())
429}
430pub fn socp_duality_ty() -> Expr {
433 prop()
434}
435pub fn rotated_lorentz_cone_ty() -> Expr {
438 let lr = list_ty(real_ty());
439 arrow(lr, arrow(real_ty(), arrow(real_ty(), prop())))
440}
441pub fn admm_linear_convergence_ty() -> Expr {
444 arrow(real_ty(), arrow(real_ty(), arrow(nat_ty(), prop())))
445}
446pub fn admm_primal_residual_ty() -> Expr {
449 let lr = list_ty(real_ty());
450 arrow(lr.clone(), arrow(lr, arrow(real_ty(), prop())))
451}
452pub fn admm_dual_residual_ty() -> Expr {
455 let lr = list_ty(real_ty());
456 arrow(lr, arrow(real_ty(), prop()))
457}
458pub fn proximal_point_algorithm_ty() -> Expr {
461 let rn_to_r = fn_ty(list_ty(real_ty()), real_ty());
462 arrow(rn_to_r, arrow(real_ty(), arrow(nat_ty(), prop())))
463}
464pub fn resolvent_operator_ty() -> Expr {
467 let rn_rn_prop = fn_ty(list_ty(real_ty()), fn_ty(list_ty(real_ty()), prop()));
468 let lr = list_ty(real_ty());
469 arrow(rn_rn_prop, arrow(real_ty(), fn_ty(lr.clone(), lr)))
470}
471pub fn firmly_nonexpansive_ty() -> Expr {
474 let lr = list_ty(real_ty());
475 let rn_to_rn = fn_ty(lr.clone(), lr);
476 arrow(rn_to_rn, prop())
477}
478pub fn bregman_divergence_ty() -> Expr {
481 let rn_to_r = fn_ty(list_ty(real_ty()), real_ty());
482 let lr = list_ty(real_ty());
483 arrow(rn_to_r, arrow(lr.clone(), arrow(lr, real_ty())))
484}
485pub fn mirror_descent_convergence_ty() -> Expr {
488 arrow(real_ty(), arrow(real_ty(), arrow(nat_ty(), prop())))
489}
490pub fn negative_entropy_function_ty() -> Expr {
493 let lr = list_ty(real_ty());
494 fn_ty(lr, real_ty())
495}
496pub fn exponential_weights_algorithm_ty() -> Expr {
499 arrow(real_ty(), arrow(nat_ty(), prop()))
500}
501pub fn saga_convergence_ty() -> Expr {
504 arrow(real_ty(), arrow(real_ty(), arrow(nat_ty(), prop())))
505}
506pub fn adam_convergence_ty() -> Expr {
509 arrow(
510 real_ty(),
511 arrow(real_ty(), arrow(real_ty(), arrow(nat_ty(), prop()))),
512 )
513}
514pub fn momentum_sgd_ty() -> Expr {
517 arrow(real_ty(), arrow(real_ty(), arrow(nat_ty(), prop())))
518}
519pub fn maximal_monotone_operator_ty() -> Expr {
522 let lr = list_ty(real_ty());
523 let set_valued = fn_ty(lr.clone(), list_ty(lr));
524 arrow(set_valued, prop())
525}
526pub fn monotone_inclusion_problem_ty() -> Expr {
529 let lr = list_ty(real_ty());
530 let set_valued = fn_ty(lr.clone(), list_ty(lr.clone()));
531 arrow(set_valued, arrow(lr, prop()))
532}
533pub fn splitting_convergence_ty() -> Expr {
536 prop()
537}
538pub fn kantorovich_problem_ty() -> Expr {
541 let lr = list_ty(real_ty());
542 let cost = fn_ty(lr.clone(), fn_ty(lr.clone(), real_ty()));
543 arrow(cost, arrow(lr.clone(), arrow(lr, real_ty())))
544}
545pub fn kantorovich_duality_ty() -> Expr {
548 prop()
549}
550pub fn wasserstein_distance_ty() -> Expr {
553 let lr = list_ty(real_ty());
554 arrow(real_ty(), arrow(lr.clone(), arrow(lr, real_ty())))
555}
556pub fn sinkhorn_algorithm_ty() -> Expr {
559 arrow(real_ty(), arrow(nat_ty(), prop()))
560}
561pub fn restricted_isometry_property_ty() -> Expr {
564 let mat = list_ty(list_ty(real_ty()));
565 arrow(nat_ty(), arrow(real_ty(), arrow(mat, prop())))
566}
567pub fn basis_pursuit_recovery_ty() -> Expr {
570 let mat = list_ty(list_ty(real_ty()));
571 let lr = list_ty(real_ty());
572 arrow(mat, arrow(lr, arrow(nat_ty(), prop())))
573}
574pub fn lasso_sparsity_ty() -> Expr {
577 arrow(real_ty(), arrow(nat_ty(), prop()))
578}
579pub fn nuclear_norm_ty() -> Expr {
582 let mat = list_ty(list_ty(real_ty()));
583 fn_ty(mat, real_ty())
584}
585pub fn matrix_completion_theorem_ty() -> Expr {
589 arrow(
590 nat_ty(),
591 arrow(nat_ty(), arrow(nat_ty(), arrow(real_ty(), prop()))),
592 )
593}
594pub fn robust_pca_ty() -> Expr {
597 let mat = list_ty(list_ty(real_ty()));
598 arrow(mat.clone(), arrow(mat.clone(), arrow(mat, prop())))
599}
600pub fn chance_constraint_ty() -> Expr {
603 let rn_prop = fn_ty(list_ty(real_ty()), prop());
604 arrow(rn_prop, arrow(real_ty(), prop()))
605}
606pub fn distributionally_robust_objective_ty() -> Expr {
609 let rn_to_r = fn_ty(list_ty(real_ty()), real_ty());
610 arrow(rn_to_r, arrow(real_ty(), prop()))
611}
612pub fn wasserstein_ambiguity_set_ty() -> Expr {
615 let lr = list_ty(real_ty());
616 arrow(lr, arrow(real_ty(), prop()))
617}
618pub fn cvar_constraint_ty() -> Expr {
621 let rn_to_r = fn_ty(list_ty(real_ty()), real_ty());
622 arrow(rn_to_r, arrow(real_ty(), arrow(real_ty(), prop())))
623}
624pub fn online_convex_optimization_ty() -> Expr {
627 arrow(nat_ty(), arrow(real_ty(), prop()))
628}
629pub fn ftrl_regret_bound_ty() -> Expr {
632 arrow(real_ty(), arrow(nat_ty(), prop()))
633}
634pub fn adaptive_regret_bound_ty() -> Expr {
637 arrow(real_ty(), arrow(real_ty(), arrow(nat_ty(), prop())))
638}
639pub fn online_gradient_descent_regret_ty() -> Expr {
642 arrow(real_ty(), arrow(nat_ty(), prop()))
643}
644pub fn build_convex_optimization_env() -> Environment {
646 let mut env = Environment::new();
647 let axioms: &[(&str, Expr)] = &[
648 ("ConvexSet", convex_set_ty()),
649 ("ConvexFunction", convex_function_ty()),
650 ("KktConditions", kkt_conditions_ty()),
651 ("Lagrangian", lagrangian_ty()),
652 ("StrongDuality", strong_duality_ty()),
653 ("projection_theorem", projection_theorem_ty()),
654 ("supporting_hyperplane", supporting_hyperplane_ty()),
655 ("jensen_inequality", jensen_inequality_ty()),
656 ("slater_condition", slater_condition_ty()),
657 ("ConvexProgram", prop()),
658 ("DualProgram", prop()),
659 ("OptimalityGap", arrow(real_ty(), prop())),
660 ("FenchelConjugate", fenchel_conjugate_ty()),
661 (
662 "FenchelRockafellarDuality",
663 fenchel_rockafellar_duality_ty(),
664 ),
665 ("ConjugateSubgradient", conjugate_subgradient_ty()),
666 ("LagrangianDualFunction", lagrangian_dual_function_ty()),
667 ("LinearIndependenceCQ", licq_ty()),
668 ("MangasarianFromovitzCQ", mfcq_ty()),
669 ("ComplementarySlackness", complementary_slackness_ty()),
670 ("KktSufficiency", kkt_sufficiency_ty()),
671 ("BarrierFunction", barrier_function_ty()),
672 ("PathFollowingMethod", path_following_method_ty()),
673 ("PredictorCorrectorMethod", predictor_corrector_method_ty()),
674 ("CentralPathConvergence", central_path_convergence_ty()),
675 ("PositiveSemidefinite", positive_semidefinite_ty()),
676 ("SdpFeasibility", sdp_feasibility_ty()),
677 ("SdpDuality", sdp_duality_ty()),
678 ("SdpOptimality", sdp_optimality_ty()),
679 ("SecondOrderCone", second_order_cone_ty()),
680 ("SocpFeasibility", socp_feasibility_ty()),
681 ("Monomial", monomial_ty()),
682 ("Posynomial", posynomial_ty()),
683 ("GeometricProgramDuality", geometric_program_duality_ty()),
684 (
685 "SmoothGradientConvergence",
686 smooth_gradient_convergence_ty(),
687 ),
688 (
689 "StronglyConvexConvergence",
690 strongly_convex_convergence_ty(),
691 ),
692 ("ProximalOperator", proximal_operator_ty()),
693 ("IstaConvergence", ista_convergence_ty()),
694 ("FistaConvergence", fista_convergence_ty()),
695 ("ProximalGradientDescent", proximal_gradient_descent_ty()),
696 ("DouglasRachfordSplitting", douglas_rachford_splitting_ty()),
697 ("ChambollePockAlgorithm", chambolle_pock_ty()),
698 ("AugmentedLagrangian", augmented_lagrangian_ty()),
699 ("AdmmConvergence", admm_convergence_ty()),
700 ("SupportingHyperplaneCut", supporting_hyperplane_cut_ty()),
701 ("KelleyMethod", kelley_method_ty()),
702 ("BundleMethodConvergence", bundle_method_convergence_ty()),
703 (
704 "EllipsoidMethodComplexity",
705 ellipsoid_method_complexity_ty(),
706 ),
707 ("CenterOfGravityMethod", center_of_gravity_method_ty()),
708 ("SubgradientInequality", subgradient_inequality_ty()),
709 (
710 "SubgradientMethodConvergence",
711 subgradient_method_convergence_ty(),
712 ),
713 ("PolyakStepsize", polyak_stepsize_ty()),
714 ("SgdConvergence", sgd_convergence_ty()),
715 ("SvrgConvergence", svrg_convergence_ty()),
716 ("SarahConvergence", sarah_convergence_ty()),
717 ("SpiderConvergence", spider_convergence_ty()),
718 ("DcpAtomConvex", dcp_atom_convex_ty()),
719 ("DcpCompositionRule", dcp_composition_rule_ty()),
720 ("DcpVerification", dcp_verification_ty()),
721 ("SelfConcordantBarrier", self_concordant_barrier_ty()),
722 ("SelfConcordantComplexity", self_concordant_complexity_ty()),
723 ("LogarithmicBarrier", logarithmic_barrier_ty()),
724 ("NewtonDecrement", newton_decrement_ty()),
725 ("SdpSlaterCondition", sdp_slater_condition_ty()),
726 ("SdpComplementarity", sdp_complementarity_ty()),
727 ("SdpDualityGap", sdp_duality_gap_ty()),
728 ("LorentzCone", lorentz_cone_ty()),
729 ("SocpDuality", socp_duality_ty()),
730 ("RotatedLorentzCone", rotated_lorentz_cone_ty()),
731 ("AdmmLinearConvergence", admm_linear_convergence_ty()),
732 ("AdmmPrimalResidual", admm_primal_residual_ty()),
733 ("AdmmDualResidual", admm_dual_residual_ty()),
734 ("ProximalPointAlgorithm", proximal_point_algorithm_ty()),
735 ("ResolventOperator", resolvent_operator_ty()),
736 ("FirmlyNonexpansive", firmly_nonexpansive_ty()),
737 ("BregmanDivergence", bregman_divergence_ty()),
738 ("MirrorDescentConvergence", mirror_descent_convergence_ty()),
739 ("NegativeEntropyFunction", negative_entropy_function_ty()),
740 (
741 "ExponentialWeightsAlgorithm",
742 exponential_weights_algorithm_ty(),
743 ),
744 ("SagaConvergence", saga_convergence_ty()),
745 ("AdamConvergence", adam_convergence_ty()),
746 ("MomentumSgd", momentum_sgd_ty()),
747 ("MaximalMonotoneOperator", maximal_monotone_operator_ty()),
748 ("MonotoneInclusionProblem", monotone_inclusion_problem_ty()),
749 ("SplittingConvergence", splitting_convergence_ty()),
750 ("KantorovichProblem", kantorovich_problem_ty()),
751 ("KantorovichDuality", kantorovich_duality_ty()),
752 ("WassersteinDistance", wasserstein_distance_ty()),
753 ("SinkhornAlgorithm", sinkhorn_algorithm_ty()),
754 (
755 "RestrictedIsometryProperty",
756 restricted_isometry_property_ty(),
757 ),
758 ("BasisPursuitRecovery", basis_pursuit_recovery_ty()),
759 ("LassoSparsity", lasso_sparsity_ty()),
760 ("NuclearNorm", nuclear_norm_ty()),
761 ("MatrixCompletionTheorem", matrix_completion_theorem_ty()),
762 ("RobustPca", robust_pca_ty()),
763 ("ChanceConstraint", chance_constraint_ty()),
764 (
765 "DistributionallyRobustObjective",
766 distributionally_robust_objective_ty(),
767 ),
768 ("WassersteinAmbiguitySet", wasserstein_ambiguity_set_ty()),
769 ("CvarConstraint", cvar_constraint_ty()),
770 ("OnlineConvexOptimization", online_convex_optimization_ty()),
771 ("FtrlRegretBound", ftrl_regret_bound_ty()),
772 ("AdaptiveRegretBound", adaptive_regret_bound_ty()),
773 (
774 "OnlineGradientDescentRegret",
775 online_gradient_descent_regret_ty(),
776 ),
777 ];
778 for (name, ty) in axioms {
779 env.add(Declaration::Axiom {
780 name: Name::str(*name),
781 univ_params: vec![],
782 ty: ty.clone(),
783 })
784 .ok();
785 }
786 env
787}
788pub trait ConvexFunction {
790 fn eval(&self, x: &[f64]) -> f64;
792 fn gradient(&self, x: &[f64]) -> Vec<f64>;
794 fn is_strongly_convex(&self) -> bool;
796}
797pub trait ProxableFunction: ConvexFunction {
799 fn prox(&self, v: &[f64], t: f64) -> Vec<f64>;
801}
802#[cfg(test)]
803mod tests {
804 use super::*;
805 #[test]
806 fn test_quadratic_eval_origin() {
807 let q = vec![vec![1.0, 0.0], vec![0.0, 1.0]];
808 let f = QuadraticFunction::new(q, vec![0.0, 0.0], 0.0);
809 assert!((f.eval(&[0.0, 0.0])).abs() < 1e-12);
810 }
811 #[test]
812 fn test_quadratic_eval_nonzero() {
813 let q = vec![vec![2.0]];
814 let f = QuadraticFunction::new(q, vec![0.0], 0.0);
815 assert!((f.eval(&[2.0]) - 4.0).abs() < 1e-9);
816 }
817 #[test]
818 fn test_quadratic_gradient() {
819 let q = vec![vec![1.0, 0.0], vec![0.0, 1.0]];
820 let f = QuadraticFunction::new(q, vec![0.0, 0.0], 0.0);
821 let grad = f.gradient(&[3.0, -1.0]);
822 assert!((grad[0] - 3.0).abs() < 1e-9);
823 assert!((grad[1] + 1.0).abs() < 1e-9);
824 }
825 #[test]
826 fn test_strongly_convex() {
827 let q = vec![vec![2.0, 0.0], vec![0.0, 3.0]];
828 let f = QuadraticFunction::new(q, vec![0.0, 0.0], 0.0);
829 assert!(f.is_strongly_convex());
830 }
831 #[test]
832 fn test_gradient_descent_minimizes_quadratic() {
833 let q = vec![vec![1.0, 0.0], vec![0.0, 1.0]];
834 let f = QuadraticFunction::new(q, vec![0.0, 0.0], 0.0);
835 let gd = GradientDescent::new(0.1, 500, 1e-6);
836 let (x, fval, _iters) = gd.minimize(&f, &[5.0, -3.0]);
837 assert!(fval < 1e-6, "fval={fval}");
838 assert!(x[0].abs() < 1e-3);
839 assert!(x[1].abs() < 1e-3);
840 }
841 #[test]
842 fn test_projected_gradient_box_constraint() {
843 let q = vec![vec![1.0]];
844 let f = QuadraticFunction::new(q, vec![0.0], 0.0);
845 let pg = ProjectedGradient::new(0.1, 300, 1e-6, vec![1.0], vec![5.0]);
846 let (x, fval) = pg.minimize(&f, &[4.0]);
847 assert!((x[0] - 1.0).abs() < 1e-3, "x={}", x[0]);
848 assert!((fval - 0.5).abs() < 1e-3, "fval={fval}");
849 }
850 #[test]
851 fn test_admm_solve_lasso_stub() {
852 let a = vec![vec![1.0, 0.0], vec![0.0, 1.0]];
853 let b = vec![1.0, 2.0];
854 let admm = ADMM::new(1.0);
855 let x = admm.solve_lasso(&a, &b, 0.1);
856 assert_eq!(x.len(), 2);
857 assert_eq!(x, vec![0.0, 0.0]);
858 }
859 #[test]
860 fn test_build_convex_optimization_env() {
861 let env = build_convex_optimization_env();
862 assert!(env.get(&Name::str("ConvexSet")).is_some());
863 assert!(env.get(&Name::str("ConvexFunction")).is_some());
864 assert!(env.get(&Name::str("KktConditions")).is_some());
865 assert!(env.get(&Name::str("projection_theorem")).is_some());
866 assert!(env.get(&Name::str("jensen_inequality")).is_some());
867 assert!(env.get(&Name::str("FenchelConjugate")).is_some());
868 assert!(env.get(&Name::str("FenchelRockafellarDuality")).is_some());
869 assert!(env.get(&Name::str("PositiveSemidefinite")).is_some());
870 assert!(env.get(&Name::str("SdpDuality")).is_some());
871 assert!(env.get(&Name::str("FistaConvergence")).is_some());
872 assert!(env.get(&Name::str("ProximalOperator")).is_some());
873 assert!(env.get(&Name::str("DouglasRachfordSplitting")).is_some());
874 assert!(env.get(&Name::str("ChambollePockAlgorithm")).is_some());
875 assert!(env.get(&Name::str("EllipsoidMethodComplexity")).is_some());
876 assert!(env.get(&Name::str("SvrgConvergence")).is_some());
877 assert!(env.get(&Name::str("SpiderConvergence")).is_some());
878 assert!(env.get(&Name::str("DcpVerification")).is_some());
879 }
880 #[test]
881 fn test_l1_norm_prox_soft_threshold() {
882 let g = L1NormFunction::new(1.0);
883 let result = g.prox(&[3.0, -0.5], 1.0);
884 assert!((result[0] - 2.0).abs() < 1e-12, "result[0]={}", result[0]);
885 assert!(result[1].abs() < 1e-12, "result[1]={}", result[1]);
886 }
887 #[test]
888 fn test_fista_minimizes_smooth_quadratic() {
889 let smooth = QuadraticFunction::new(vec![vec![1.0]], vec![0.0], 0.0);
890 let reg = L1NormFunction::new(0.0);
891 let solver = FISTASolver::new(1.0, 200, 1e-6);
892 let (x, _iters) = solver.minimize(&smooth, ®, &[5.0]);
893 assert!(x[0].abs() < 1e-3, "x[0]={}", x[0]);
894 }
895 #[test]
896 fn test_sdp_relaxation_psd_check() {
897 let id = vec![vec![1.0, 0.0], vec![0.0, 1.0]];
898 assert!(SDPRelaxation::is_psd(&id));
899 let neg = vec![vec![-1.0, 0.0], vec![0.0, 1.0]];
900 assert!(!SDPRelaxation::is_psd(&neg));
901 }
902 #[test]
903 fn test_sdp_relaxation_solve_returns_bound() {
904 let q = vec![vec![2.0, 0.0], vec![0.0, 2.0]];
905 let c = vec![0.0, 0.0];
906 let a = vec![vec![1.0, 0.0]];
907 let b = vec![1.0];
908 let sdp = SDPRelaxation::new(q, c, a, b);
909 let bound = sdp.solve();
910 assert!((bound - 0.0).abs() < 1e-12);
911 }
912 #[test]
913 fn test_geometric_program_log_sum_exp() {
914 let monomials = vec![(0.0_f64, vec![1.0_f64])];
915 let lse = GeometricProgramSolver::log_sum_exp_posynomial(&monomials, &[1.0]);
916 assert!((lse - 1.0).abs() < 1e-9, "lse={lse}");
917 }
918 #[test]
919 fn test_cutting_plane_minimizes_quadratic() {
920 let f = QuadraticFunction::new(vec![vec![2.0]], vec![0.0], 0.0);
921 let solver = CuttingPlaneSolver::new(100, 1e-4, 2.0);
922 let (x, fval, _iters) = solver.minimize(&f, &[3.0]);
923 assert!(fval < 1.0, "fval={fval}");
924 let _ = x;
925 }
926 #[test]
927 fn test_bundle_method_minimizes_quadratic() {
928 let f = QuadraticFunction::new(vec![vec![2.0]], vec![0.0], 0.0);
929 let solver = BundleMethodSolver::new(1.0, 0.5, 20, 200, 1e-5);
930 let (x, fval, _iters) = solver.minimize(&f, &[4.0]);
931 assert!(fval < 1.0, "fval={fval}");
932 let _ = x;
933 }
934 #[test]
935 fn test_new_axioms_in_env() {
936 let env = build_convex_optimization_env();
937 assert!(env.get(&Name::str("SelfConcordantBarrier")).is_some());
938 assert!(env.get(&Name::str("SelfConcordantComplexity")).is_some());
939 assert!(env.get(&Name::str("LogarithmicBarrier")).is_some());
940 assert!(env.get(&Name::str("NewtonDecrement")).is_some());
941 assert!(env.get(&Name::str("SdpSlaterCondition")).is_some());
942 assert!(env.get(&Name::str("SdpComplementarity")).is_some());
943 assert!(env.get(&Name::str("SdpDualityGap")).is_some());
944 assert!(env.get(&Name::str("LorentzCone")).is_some());
945 assert!(env.get(&Name::str("SocpDuality")).is_some());
946 assert!(env.get(&Name::str("RotatedLorentzCone")).is_some());
947 assert!(env.get(&Name::str("AdmmLinearConvergence")).is_some());
948 assert!(env.get(&Name::str("AdmmPrimalResidual")).is_some());
949 assert!(env.get(&Name::str("AdmmDualResidual")).is_some());
950 assert!(env.get(&Name::str("ProximalPointAlgorithm")).is_some());
951 assert!(env.get(&Name::str("ResolventOperator")).is_some());
952 assert!(env.get(&Name::str("FirmlyNonexpansive")).is_some());
953 assert!(env.get(&Name::str("BregmanDivergence")).is_some());
954 assert!(env.get(&Name::str("MirrorDescentConvergence")).is_some());
955 assert!(env.get(&Name::str("NegativeEntropyFunction")).is_some());
956 assert!(env.get(&Name::str("ExponentialWeightsAlgorithm")).is_some());
957 assert!(env.get(&Name::str("SagaConvergence")).is_some());
958 assert!(env.get(&Name::str("AdamConvergence")).is_some());
959 assert!(env.get(&Name::str("MomentumSgd")).is_some());
960 assert!(env.get(&Name::str("MaximalMonotoneOperator")).is_some());
961 assert!(env.get(&Name::str("MonotoneInclusionProblem")).is_some());
962 assert!(env.get(&Name::str("SplittingConvergence")).is_some());
963 assert!(env.get(&Name::str("KantorovichProblem")).is_some());
964 assert!(env.get(&Name::str("KantorovichDuality")).is_some());
965 assert!(env.get(&Name::str("WassersteinDistance")).is_some());
966 assert!(env.get(&Name::str("SinkhornAlgorithm")).is_some());
967 assert!(env.get(&Name::str("RestrictedIsometryProperty")).is_some());
968 assert!(env.get(&Name::str("BasisPursuitRecovery")).is_some());
969 assert!(env.get(&Name::str("LassoSparsity")).is_some());
970 assert!(env.get(&Name::str("NuclearNorm")).is_some());
971 assert!(env.get(&Name::str("MatrixCompletionTheorem")).is_some());
972 assert!(env.get(&Name::str("RobustPca")).is_some());
973 assert!(env.get(&Name::str("ChanceConstraint")).is_some());
974 assert!(env
975 .get(&Name::str("DistributionallyRobustObjective"))
976 .is_some());
977 assert!(env.get(&Name::str("WassersteinAmbiguitySet")).is_some());
978 assert!(env.get(&Name::str("CvarConstraint")).is_some());
979 assert!(env.get(&Name::str("OnlineConvexOptimization")).is_some());
980 assert!(env.get(&Name::str("FtrlRegretBound")).is_some());
981 assert!(env.get(&Name::str("AdaptiveRegretBound")).is_some());
982 assert!(env.get(&Name::str("OnlineGradientDescentRegret")).is_some());
983 }
984 #[test]
985 fn test_mirror_descent_project_simplex() {
986 let v = vec![0.5, 0.5];
987 let p = MirrorDescentSolver::project_simplex(&v);
988 assert!((p.iter().sum::<f64>() - 1.0).abs() < 1e-9);
989 assert!(p.iter().all(|&x| x >= 0.0));
990 }
991 #[test]
992 fn test_mirror_descent_simplex_sum_to_one() {
993 let v = vec![3.0, -1.0, 2.0];
994 let p = MirrorDescentSolver::project_simplex(&v);
995 assert!((p.iter().sum::<f64>() - 1.0).abs() < 1e-9);
996 assert!(p.iter().all(|&x| x >= 0.0));
997 }
998 #[test]
999 fn test_mirror_descent_bregman_kl_zero() {
1000 let x = vec![0.25, 0.25, 0.5];
1001 let kl = MirrorDescentSolver::bregman_kl(&x, &x);
1002 assert!(kl.abs() < 1e-10, "kl={kl}");
1003 }
1004 #[test]
1005 fn test_mirror_descent_minimizes_quadratic() {
1006 let f = QuadraticFunction::new(vec![vec![1.0]], vec![0.0], 0.0);
1007 let solver = MirrorDescentSolver::new(0.05, 500, 1e-6, false);
1008 let (x, fval, _iters) = solver.minimize(&f, &[3.0]);
1009 assert!(fval < 0.1, "fval={fval}");
1010 let _ = x;
1011 }
1012 #[test]
1013 fn test_proximal_gradient_ista_minimizes() {
1014 let smooth = QuadraticFunction::new(vec![vec![2.0]], vec![0.0], 0.0);
1015 let reg = L1NormFunction::new(0.0);
1016 let solver = ProximalGradientSolver::new(2.0, 300, 1e-7, false);
1017 let (x, iters) = solver.minimize(&smooth, ®, &[5.0]);
1018 assert!(x[0].abs() < 0.01, "x[0]={}", x[0]);
1019 let _ = iters;
1020 }
1021 #[test]
1022 fn test_proximal_gradient_fista_accelerated() {
1023 let smooth = QuadraticFunction::new(vec![vec![2.0]], vec![0.0], 0.0);
1024 let reg = L1NormFunction::new(0.0);
1025 let solver_fista = ProximalGradientSolver::new(2.0, 200, 1e-7, true);
1026 let solver_ista = ProximalGradientSolver::new(2.0, 200, 1e-7, false);
1027 let (x_fista, iters_fista) = solver_fista.minimize(&smooth, ®, &[5.0]);
1028 let (x_ista, iters_ista) = solver_ista.minimize(&smooth, ®, &[5.0]);
1029 assert!(
1030 iters_fista <= iters_ista + 10,
1031 "fista={iters_fista}, ista={iters_ista}"
1032 );
1033 assert!(x_fista[0].abs() < 0.01, "x_fista={}", x_fista[0]);
1034 let _ = x_ista;
1035 }
1036 #[test]
1037 fn test_proximal_gradient_estimate_lipschitz() {
1038 let f = QuadraticFunction::new(vec![vec![2.0]], vec![0.0], 0.0);
1039 let l_est = ProximalGradientSolver::estimate_lipschitz(&f, &[1.0], 1);
1040 assert!(l_est > 0.5, "L_est={l_est}");
1041 }
1042 #[test]
1043 fn test_sinkhorn_uniform_transport() {
1044 let mu = vec![0.5, 0.5];
1045 let nu = vec![0.5, 0.5];
1046 let cost = vec![vec![0.0, 1.0], vec![1.0, 0.0]];
1047 let solver = SinkhornSolver::new(0.1, 200, 1e-8);
1048 let (gamma, w) = solver.solve(&mu, &nu, &cost);
1049 let total: f64 = gamma.iter().flat_map(|r| r.iter()).sum();
1050 assert!((total - 1.0).abs() < 1e-4, "total={total}");
1051 assert!(w >= 0.0, "w={w}");
1052 }
1053 #[test]
1054 fn test_sinkhorn_same_distribution() {
1055 let mu = vec![0.5, 0.5];
1056 let nu = vec![0.5, 0.5];
1057 let cost = vec![vec![0.0, 1.0], vec![1.0, 0.0]];
1058 let solver = SinkhornSolver::new(0.01, 500, 1e-10);
1059 let (_gamma, w) = solver.solve(&mu, &nu, &cost);
1060 assert!(w < 0.6, "w={w}");
1061 }
1062 #[test]
1063 fn test_sinkhorn_wasserstein2_1d_zero() {
1064 let pts = vec![0.0, 1.0];
1065 let wts = vec![0.5, 0.5];
1066 let w2 = SinkhornSolver::wasserstein2_1d(&pts, &wts, &pts, &wts);
1067 assert!(w2 < 0.2, "w2={w2}");
1068 }
1069 #[test]
1070 fn test_rip_identity_satisfies_rip() {
1071 let id = vec![vec![1.0, 0.0], vec![0.0, 1.0]];
1072 let verifier = RipVerifier::new(1, 10);
1073 let (dl, du) = verifier.estimate_rip_constant(&id);
1074 assert!(du < 0.01, "delta_upper={du}");
1075 let _ = dl;
1076 }
1077 #[test]
1078 fn test_rip_soft_threshold() {
1079 let x = vec![3.0, -0.5, 0.2];
1080 let result = RipVerifier::soft_threshold(&x, 1.0);
1081 assert!((result[0] - 2.0).abs() < 1e-12);
1082 assert!(result[1].abs() < 1e-12);
1083 assert!(result[2].abs() < 1e-12);
1084 }
1085 #[test]
1086 fn test_rip_satisfies_rip_check() {
1087 let id = vec![vec![1.0, 0.0], vec![0.0, 1.0]];
1088 let verifier = RipVerifier::new(1, 5);
1089 assert!(verifier.satisfies_rip(&id, 0.5));
1090 }
1091 #[test]
1092 fn test_online_learner_initial_decision() {
1093 let learner = OnlineLearner::new(0.1, 3);
1094 let x = learner.current_decision();
1095 assert_eq!(x.len(), 3);
1096 assert!(x.iter().all(|&xi| xi.abs() < 1e-12));
1097 }
1098 #[test]
1099 fn test_online_learner_update_accumulates() {
1100 let mut learner = OnlineLearner::new(0.1, 2);
1101 learner.update(&[1.0, 0.0]);
1102 let x = learner.current_decision();
1103 assert!((x[0] + 0.1).abs() < 1e-9, "x[0]={}", x[0]);
1104 assert!(x[1].abs() < 1e-9, "x[1]={}", x[1]);
1105 }
1106 #[test]
1107 fn test_online_learner_regret_bound_positive() {
1108 let mut learner = OnlineLearner::new(0.01, 2);
1109 for _ in 0..10 {
1110 learner.update(&[0.5, -0.5]);
1111 }
1112 let bound = learner.regret_bound(1.0, 1.0);
1113 assert!(bound >= 0.0, "bound={bound}");
1114 }
1115 #[test]
1116 fn test_online_learner_reset() {
1117 let mut learner = OnlineLearner::new(0.1, 2);
1118 learner.update(&[1.0, 2.0]);
1119 learner.reset();
1120 assert_eq!(learner.round, 0);
1121 let x = learner.current_decision();
1122 assert!(x.iter().all(|&xi| xi.abs() < 1e-12));
1123 }
1124}