akreon-convex
Typed convex modeling, conic canonicalization, and primal-dual optimization in C++26.
Loading...
Searching...
No Matches
inequality.hpp
Go to the documentation of this file.
1/*
2 * Copyright © 2025-2026 Akreon LLC
3 * SPDX-License-Identifier: Apache-2.0
4 */
5
6#pragma once
7
9#include "cone_traits.hpp"
10#include "convex_traits.hpp"
11
12namespace convex {
13
21template <InequalityType IType, AtomLike LHS, typename RHS>
22struct Inequality {
29 static constexpr std::size_t M = dom_size_v<Codom>;
30
32 "LHS Atom must be fn, index, or var type");
33
40 constexpr explicit Inequality(const LHS& lhs_in, const RHS& rhs_in)
41 : lhs(lhs_in), rhs(rhs_in) {}
42
58 template <AtomType Type, conic::BuilderLike Builder>
59 constexpr auto canonicalize(const Builder& builder) const {
60 auto canonicalize_atom = [&](const auto& atom, const auto& parent_builder) {
61 using AtomT = linalg::remove_cvref_t<decltype(atom)>;
62 if constexpr (is_var_type_v<AtomT>)
65 };
66
67 auto negate_atom = [](const auto& atom) {
68 using AtomT = linalg::remove_cvref_t<decltype(atom)>;
70 return atom.mul(T(-1));
71 } else {
72 return atom.template mul<T(-1)>();
73 }
74 };
75
76 auto make_broadcast = [](T scalar) {
77 linalg::Vector<T, M> out{};
78 for (std::size_t i = 0; i < M; ++i) out(i) = scalar;
79 return out;
80 };
81
82 auto append_equality = [](const auto& child_builder, const auto& eq_linear,
83 const auto& eq_bias) {
84 const auto eq_map =
85 affine::AffineMap<decltype(eq_linear)>(eq_linear, eq_bias);
86 const auto emission = conic::LocalEqualityEmission(eq_map);
87 return child_builder.realize(emission);
88 };
89
90 if constexpr (dom_vartype_v<Codom> == VarType::PSD &&
91 linalg::MatrixContainer<RHS>) {
92 static_assert(is_var_type_v<LHS>,
93 "PSD inequalities only support variable lhs");
94 static_assert(IType == InequalityType::GEQ,
95 "PSD inequalities currently only support X >= C");
96
98 using RHSMat = linalg::remove_cvref_t<RHS>;
99 using RHSScalar = linalg::matrix_scalar_t<RHSMat>;
100 static_assert(linalg::mat_rows_v<RHSMat> ==
101 linalg::mat_rows_v<CodomPoint>,
102 "PSD inequality rhs row dim mismatch");
103 static_assert(linalg::mat_cols_v<RHSMat> ==
104 linalg::mat_cols_v<CodomPoint>,
105 "PSD inequality rhs col dim mismatch");
106 static_assert(std::convertible_to<RHSScalar, T>,
107 "PSD inequality rhs scalar type must convert to lhs type");
108
109 const auto child_builder = canonicalize_atom(lhs, builder);
110 const auto child_coord = child_builder.coord_map;
111
112 static constexpr std::size_t MatDim = linalg::mat_rows_v<CodomPoint>;
113 static constexpr std::size_t K = MatDim * (MatDim + 1) / 2;
114
115 if constexpr (linalg::Real<RHSScalar>)
116 AK_ASSERT(linalg::all_equal(rhs, linalg::transpose(rhs)) &&
117 "PSD inequality expects symmetric matrix rhs");
118 else
119 AK_ASSERT(linalg::all_equal(rhs, linalg::conjugate_transpose(rhs)) &&
120 "PSD inequality expects Hermitian matrix rhs");
121
122 const auto F = child_coord.linear_part();
123 const auto g = child_coord.translation() - linalg::get_svec(rhs);
124 const auto coord_map_up = affine::AffineMap<decltype(F)>(F, g);
125 const auto psd_cone =
127 const auto cones = std::make_tuple(psd_cone);
128 const auto emission =
131 } else if constexpr (linalg::Field<RHS>) {
132 const auto child_builder = canonicalize_atom(lhs, builder);
133 const auto child_coord = child_builder.coord_map;
134
135 using ChildMat = decltype(child_coord.linear_part());
137
138 if constexpr (dom_vartype_v<Codom> == VarType::PSD) {
139 static_assert(is_var_type_v<LHS>,
140 "PSD inequalities only support variable lhs");
141 static_assert(IType == InequalityType::GEQ,
142 "PSD inequalities currently only support X >= 0");
143 AK_ASSERT(rhs == T(0) && "PSD inequality expects zero rhs");
144
145 static constexpr std::size_t MatDim = linalg::mat_rows_v<CodomPoint>;
146 static constexpr std::size_t K = MatDim * (MatDim + 1) / 2;
147
148 const auto F = child_coord.linear_part();
149 const auto g = child_coord.translation();
150 const auto coord_map_up = affine::AffineMap<decltype(F)>(F, g);
151 const auto psd_cone =
153 const auto cones = std::make_tuple(psd_cone);
154 const auto emission =
157 } else {
158 if constexpr (IType == InequalityType::LEQ) {
159 ChildMat F{T(-1) * child_coord.linear_part()};
160 linalg::Vector<T, M> g{make_broadcast(rhs) -
161 child_coord.translation()};
162 const auto coord_map_up = affine::AffineMap<decltype(F)>(F, g);
163 const auto cones =
164 std::make_tuple(conic::NonnegOrthantCone<linalg::Vector<T, M>>());
165 const auto emission =
168 } else if constexpr (IType == InequalityType::GEQ) {
169 ChildMat F{child_coord.linear_part()};
170 linalg::Vector<T, M> g{child_coord.translation() -
172 const auto coord_map_up = affine::AffineMap<decltype(F)>(F, g);
173 const auto cones =
174 std::make_tuple(conic::NonnegOrthantCone<linalg::Vector<T, M>>());
175 const auto emission =
178 } else {
179 const auto A_eq = child_coord.linear_part();
180 linalg::Vector<T, M> b_eq{make_broadcast(rhs) -
181 child_coord.translation()};
183 }
184 }
185 } else {
186 if constexpr (!linalg::MatrixContainer<RHS>) {
187 static_assert(
189 "RHS Atom must be fn, index, var, scalar, or matrix type");
190 }
191
192 if constexpr (IType == InequalityType::LEQ) {
193 const auto new_lhs = negate_atom(lhs);
194 const Sum sum(new_lhs, rhs);
195 const auto new_builder =
196 sum.template canonicalize<AtomType::ADD>(builder);
197 const auto cones =
198 std::make_tuple(conic::NonnegOrthantCone<linalg::Vector<T, 1>>());
199 const auto emission = conic::LocalEmission(new_builder.coord_map, cones,
200 new_builder.eq_map);
202 } else if constexpr (IType == InequalityType::GEQ) {
203 const auto new_rhs = negate_atom(rhs);
204 const Sum sum(lhs, new_rhs);
205 const auto new_builder =
206 sum.template canonicalize<AtomType::ADD>(builder);
207 const auto cones =
208 std::make_tuple(conic::NonnegOrthantCone<linalg::Vector<T, 1>>());
209 const auto emission = conic::LocalEmission(new_builder.coord_map, cones,
210 new_builder.eq_map);
212 } else {
213 const auto new_rhs = negate_atom(rhs);
214 const Sum sum(lhs, new_rhs);
215 const auto child_builder =
216 sum.template canonicalize<AtomType::ADD>(builder);
217 const auto child_coord = child_builder.coord_map;
218 const auto A_eq = child_coord.linear_part();
219 linalg::Vector<T, 1> b_eq{child_coord.translation()(0)};
221 }
222 }
223 }
224
226 LHS lhs;
228};
229
230} // namespace convex
constexpr std::size_t dom_size_v
typename dom_traits< linalg::remove_cvref_t< Dom > >::T dom_type_t
typename dom_traits< linalg::remove_cvref_t< Dom > >::Point dom_point_t
typename meta_traits< linalg::remove_cvref_t< Meta > >::Codomain meta_codom_t
typename atom_traits< atom_decay_t< Atom > >::ConvexMeta atom_meta_t
Extract metadata type from atom.
typename meta_traits< linalg::remove_cvref_t< Meta > >::Domain meta_dom_t
Atom-local canonical emission bundle.
Atom-local equality emission bundle.
forward decl for nonneg orthant cone
Definition rplus.hpp:47
forward decl for psd
Definition psd_cone.hpp:49
forward declaration of inequality
dom_point_t< Domain > Point
atom_meta_t< LHS > Meta
extract alias & types from base
LHS lhs
member vars
constexpr auto canonicalize(const Builder &builder) const
canonicalize
constexpr Inequality(const LHS &lhs_in, const RHS &rhs_in)
CTOR.
dom_type_t< Domain > T
meta_dom_t< Meta > Domain
static constexpr std::size_t M
meta_codom_t< Meta > Codom
forward declaration - sum op
Definition sum.hpp:116