akreon-convex
Typed convex modeling, conic canonicalization, and primal-dual optimization in C++26.
Loading...
Searching...
No Matches
soc.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
8#include "akreon/linalg/constants.hpp"
9#include "cone.hpp"
10
11namespace conic {
12
39template <affine::AffineMapLike Map>
42 using Mat = affine::affine_mat_t<Map>;
43 using T = linalg::matrix_scalar_t<Mat>;
44 static constexpr std::size_t M = linalg::mat_rows_v<Mat>;
45 static constexpr std::size_t N = linalg::mat_cols_v<Mat>;
46
58 constexpr auto operator()(const Map& map) {
59 static_assert(M >= 3,
60 "Rotated2SOC requires RSOC coords with at least 3 entries "
61 "(u, v, w)");
62
63 const auto I = linalg::make_identity<T, M - 2>();
64
65 linalg::Matrix<T, M, M> A{};
66
67 A = linalg::set_block<0, M - 2, 0, M - 2>(A, I);
68 A(M - 2, M - 2) = linalg::inv_sqrt2_v<T>;
69 A(M - 2, M - 1) = -linalg::inv_sqrt2_v<T>;
70 A(M - 1, M - 2) = linalg::inv_sqrt2_v<T>;
71 A(M - 1, M - 1) = linalg::inv_sqrt2_v<T>;
72
73 affine::AffineMap<decltype(A)> r2soc(A);
74 return r2soc.compose(map);
75 }
76};
77
89template <linalg::VectorContainer Vec>
90struct SOCCirc {
92 using T = linalg::vector_element_t<Vec>;
93 static constexpr std::size_t N = linalg::vector_dim_v<Vec>;
94
105 constexpr auto operator()(const Vec& x, const Vec& y) const {
106 const auto primal = std::get<0>(linalg::splice<N - 1>(x));
107 const auto t1 = x(N - 1);
108 const auto dual = std::get<0>(linalg::splice<N - 1>(y));
109 const auto t2 = y(N - 1);
110
111 const auto a = t2 * primal + t1 * dual;
112 const auto b = linalg::inner_product(primal, dual) + t1 * t2;
113
114 Vec res{};
115 for (std::size_t i = 0; i < N - 1; ++i) res(i) = a(i);
116 res(N - 1) = b;
117 return res;
118 }
119};
120
126template <linalg::VectorContainer Slack>
128 : public Cone<Slack, SecondOrderCone<Slack>, ConeType::SYMMETRIC> {
131
133 using ConeBase::eps;
134 using ConeBase::M;
135 using typename ConeBase::HessianMat;
136 using typename ConeBase::T;
137
143 constexpr Slack x0_impl() const {
144 Slack s{};
145 s(M - 1) = T(1);
146 return s;
147 }
148
154 constexpr Slack y0_impl() const { return x0_impl(); }
155
163 constexpr bool in_cone_impl(const Slack& x) const {
164 T res{};
165 for (std::size_t i = 0; i < M - 1; ++i) res += x(i) * x(i);
166 res = linalg::constexpr_sqrt_safe(res);
167 return res < x(M - 1);
168 }
169
177 constexpr bool in_dual_cone_impl(const Slack& y) const {
178 return in_cone_impl(y);
179 }
180
218 constexpr T alpha_max_impl(const Slack& x,
219 const linalg::Vector<T, M>& p) const {
220 constexpr T eta{0.99};
221
223 if (in_cone_impl(x + p)) return eta;
224
225 T alpha{1};
226 T alpha_t{1};
227
228 const auto var = std::get<0>(linalg::splice<M - 1>(x));
229 const auto t = x(M - 1);
230 const auto dvar = std::get<0>(linalg::splice<M - 1>(p));
231 const auto dt = p(M - 1);
232
233 const auto a = dt * dt - linalg::inner_product(dvar, dvar);
234 const auto b = T(2) * (t * dt - linalg::inner_product(var, dvar));
235 const auto c = t * t - linalg::inner_product(var, var);
236
237 if (linalg::constexpr_abs(a) <= eps) {
238 if (b < T(0)) alpha = std::min(alpha, -c / b);
239 } else {
240 const T disc = b * b - T(4) * a * c;
241 if (disc > T(0)) {
242 const T s = linalg::constexpr_sqrt_safe(disc);
243 const T inv2a = T(1) / (T(2) * a);
244 const T r1 = (-b - s) * inv2a;
245 const T r2 = (-b + s) * inv2a;
246
249 T hit = T(1);
250 if (r1 > T(0)) hit = std::min(hit, r1);
251 if (r2 > T(0)) hit = std::min(hit, r2);
252
253 alpha = std::min(alpha, hit);
254 }
255 }
256
258 if (dt < T(0)) alpha_t = std::min(alpha_t, -t / dt);
259
261 alpha = std::max(T(0), std::min(T(1), alpha));
262
263 return eta * std::min(alpha, alpha_t);
264 }
265
274 constexpr T alpha_max_dual_impl(const Slack& y,
275 const linalg::Vector<T, M>& p) const {
276 return alpha_max_impl(y, p);
277 }
278
312 constexpr auto H_impl(const Slack& x, const Slack& y) const {
313 const auto [w, event] = w_impl(x, y);
314 if (linalg::is_fatal_event(event))
315 return std::tuple(
316 linalg::QuadraticRep<HessianMat, SOCCirc<Slack>>(e_impl()), event);
317 const auto [w_inv, inv_event] = inv_impl(w);
318 if (linalg::is_fatal_event(inv_event))
319 return std::tuple(
320 linalg::QuadraticRep<HessianMat, SOCCirc<Slack>>(e_impl()),
321 inv_event);
322 const auto out_event = (event != linalg::NumericalEvent::NONE ||
323 inv_event != linalg::NumericalEvent::NONE)
324 ? linalg::NumericalEvent::NUMERICAL_SAFEGUARD
325 : linalg::NumericalEvent::NONE;
326 return std::tuple(linalg::QuadraticRep<HessianMat, SOCCirc<Slack>>(w_inv),
327 out_event);
328 }
329
339 constexpr auto grad_impl(const Slack& x) const {
340 const auto [x_inv, event] = inv_impl(x);
341 return std::tuple(T(-1) * x_inv, event);
342 }
343
356 constexpr T nu_impl() const { return T(2); }
357
366 constexpr auto e_impl() const {
367 Slack e{};
368 e(M - 1) = T(1);
369 return e;
370 }
371
394 constexpr auto w_impl(const Slack& x, const Slack& y) const {
395 const auto x_sqrt = sqrt(x);
396 linalg::QuadraticRep<HessianMat, SOCCirc<Slack>> P(x_sqrt);
397
398 const auto b = P.apply(y);
399 const auto [inner, event] = inv_impl(sqrt(b));
400 if (linalg::is_fatal_event(event)) return std::tuple(Slack{}, event);
401
402 return std::tuple(P.apply(inner), event);
403 }
404
415 constexpr auto circ_impl(const Slack& x, const Slack& y) const {
416 return SOCCirc<Slack>()(x, y);
417 }
418
429 constexpr T trace_impl(const Slack& x, const Slack& y) const {
430 return nu_impl() * circ_impl(x, y)(M - 1);
431 }
432
441 constexpr auto gamma_F_impl(const Slack& x, const Slack& y) const {
442 const auto lambdas_x = eigs(x);
443 const auto lambdas_y = eigs(y);
444
445 const auto det_x = std::get<0>(lambdas_x) * std::get<1>(lambdas_x);
446 const auto det_y = std::get<0>(lambdas_y) * std::get<1>(lambdas_y);
447
448 const auto tr_xy = trace_impl(x, y);
449
450 return std::tuple(nu_impl() * linalg::constexpr_ln_safe(tr_xy / nu_impl()) -
451 linalg::constexpr_ln_safe(det_x) -
452 linalg::constexpr_ln_safe(det_y),
453 linalg::NumericalEvent::NONE);
454 }
455
466 constexpr auto inv_impl(const Slack& x) const {
467 const auto v = std::get<0>(linalg::splice<M - 1>(x));
468 const auto t = x(M - 1);
469 Slack adj_v;
470 for (std::size_t i = 0; i < M - 1; ++i) adj_v(i) = T(-1) * v(i);
471 adj_v(M - 1) = t;
472
473 const auto det = t * t - linalg::inner_product(v, v);
474 if (std::isnan(det) || linalg::constexpr_abs(det) <= eps)
475 return std::tuple((T(1) / eps) * adj_v,
476 linalg::NumericalEvent::NUMERICAL_SAFEGUARD);
477
478 const auto res = (T(1) / det) * adj_v;
479 return std::tuple(res, linalg::NumericalEvent::NONE);
480 }
481
483 constexpr auto sqrt_impl(const Slack& x) const {
484 return std::tuple(sqrt(x), linalg::NumericalEvent::NONE);
485 }
486
499 constexpr auto sqrt(const Slack& x) const {
500 const auto v = std::get<0>(linalg::splice<M - 1>(x));
501 const auto v_norm = linalg::L2<decltype(v)>()(v);
502
503 const auto lambdas = eigs(x);
504 const auto lam1 = std::get<0>(lambdas);
505 const auto lam2 = std::get<1>(lambdas);
506
507 const auto alpha = T(0.5) * (linalg::constexpr_sqrt_safe(lam1) +
508 linalg::constexpr_sqrt_safe(lam2));
509 const auto beta = T(0.5) * (linalg::constexpr_sqrt_safe(lam1) -
510 linalg::constexpr_sqrt_safe(lam2));
511
512 Slack v_zero{};
513 for (std::size_t i = 0; i < M - 1; ++i) v_zero(i) = v(i);
514
515 auto x_sqrt = alpha * e_impl();
516 if (v_norm > eps) x_sqrt = x_sqrt + beta * (T(1) / v_norm) * v_zero;
517
518 return x_sqrt;
519 }
520
531 constexpr auto eigs(const Slack& x) const {
532 const auto v = std::get<0>(linalg::splice<M - 1>(x));
533 const auto t = x(M - 1);
534
535 const auto v_norm = linalg::L2<decltype(v)>()(v);
536 const auto lam1 = t + v_norm;
537 const auto lam2 = t - v_norm;
538
539 return std::tuple(lam1, lam2);
540 }
541};
542
543} // namespace conic
cone base class
Definition cone.hpp:19
static constexpr std::size_t M
helpers to get M slack size
Definition cone.hpp:24
linalg::vector_element_t< Slack > T
alias for mat type
Definition cone.hpp:21
constexpr auto w(const Slack &x, const Slack &y) const
w (self-scaling point)
Definition cone.hpp:166
RSOC -> SOC.
Definition soc.hpp:40
linalg::matrix_scalar_t< Mat > T
Definition soc.hpp:43
static constexpr std::size_t M
Definition soc.hpp:44
constexpr auto operator()(const Map &map)
transform native RSOC coord map to SOC map
Definition soc.hpp:58
static constexpr std::size_t N
Definition soc.hpp:45
affine::affine_mat_t< Map > Mat
extract alias & types
Definition soc.hpp:42
Circ - Jordan Map/Product for SOC.
Definition soc.hpp:90
constexpr auto operator()(const Vec &x, const Vec &y) const
circ (jordan map/product)
Definition soc.hpp:105
static constexpr std::size_t N
Definition soc.hpp:93
linalg::vector_element_t< Vec > T
types and size extraction
Definition soc.hpp:92
forward decl for soc
Definition soc.hpp:128
constexpr T trace_impl(const Slack &x, const Slack &y) const
trace
Definition soc.hpp:429
constexpr auto sqrt_impl(const Slack &x) const
Event-returning Jordan square root for the common cone implementation.
Definition soc.hpp:483
constexpr Slack y0_impl() const
default dual point
Definition soc.hpp:154
constexpr T alpha_max_impl(const Slack &x, const linalg::Vector< T, M > &p) const
fraction to boundary dK
Definition soc.hpp:218
constexpr T nu_impl() const
barrier param
Definition soc.hpp:356
constexpr auto gamma_F_impl(const Slack &x, const Slack &y) const
gamma_F
Definition soc.hpp:441
constexpr T alpha_max_dual_impl(const Slack &y, const linalg::Vector< T, M > &p) const
fracto to dK^*
Definition soc.hpp:274
constexpr auto circ_impl(const Slack &x, const Slack &y) const
circ
Definition soc.hpp:415
constexpr bool in_dual_cone_impl(const Slack &y) const
in interior of K^*
Definition soc.hpp:177
constexpr auto grad_impl(const Slack &x) const
grad
Definition soc.hpp:339
constexpr auto sqrt(const Slack &x) const
sqrt
Definition soc.hpp:499
constexpr auto H_impl(const Slack &x, const Slack &y) const
hessian
Definition soc.hpp:312
constexpr Slack x0_impl() const
default interior point
Definition soc.hpp:143
constexpr auto w_impl(const Slack &x, const Slack &y) const
w (self-scaled point)
Definition soc.hpp:394
constexpr auto e_impl() const
e (jordan identity)
Definition soc.hpp:366
constexpr auto eigs(const Slack &x) const
eigs
Definition soc.hpp:531
constexpr bool in_cone_impl(const Slack &x) const
in interior of K
Definition soc.hpp:163
constexpr auto inv_impl(const Slack &x) const
inv
Definition soc.hpp:466