akreon-convex
Typed convex modeling, conic canonicalization, and primal-dual optimization in C++26.
Loading...
Searching...
No Matches
sum.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/affine/affine_map.hpp"
9#include "cone_traits.hpp"
10#include "convex_traits.hpp"
11
12namespace convex {
13
22template <typename SumCodomain, typename Child>
25 static_assert(
26 std::same_as<dom_space_t<SumCodomain>, dom_space_t<ChildCodomain>>,
27 "Sum child codomain must match the sum codomain space");
28}
29
38template <affine::AffineMapLike PrevMap, affine::AffineMapLike NextMap>
39constexpr auto update_selector(const PrevMap& prev, const NextMap& next) {
40 using PrevMatT = affine::affine_mat_t<PrevMap>;
41 using NextMatT = affine::affine_mat_t<NextMap>;
42 using T = linalg::matrix_scalar_t<PrevMatT>;
43
44 static constexpr std::size_t PrevDom = linalg::mat_cols_v<PrevMatT>;
45 static constexpr std::size_t PrevCodom = linalg::mat_rows_v<PrevMatT>;
46
47 static constexpr std::size_t NextDom = linalg::mat_cols_v<NextMatT>;
48 static constexpr std::size_t NextCodom = linalg::mat_rows_v<NextMatT>;
49
52 if constexpr (PrevCodom != NextCodom) {
53 return affine::AffineMap<linalg::remove_cvref_t<NextMatT>>(
54 next.linear_part(), next.translation());
55 } else {
56 static constexpr std::size_t FullDom =
58 linalg::Matrix<T, PrevCodom, FullDom> F{};
59 for (std::size_t i = 0; i < PrevCodom; ++i) {
60 for (std::size_t j = 0; j < FullDom; ++j) {
61 if (j < PrevDom) F(i, j) += prev.linear_part()(i, j);
62 if (j < NextDom) F(i, j) += next.linear_part()(i, j);
63 }
64 }
65
66 linalg::Vector<T, PrevCodom> g{};
67 for (std::size_t i = 0; i < PrevCodom; ++i) {
68 g(i) = prev.translation()(i) + next.translation()(i);
69 }
70 return affine::AffineMap<decltype(F)>(F, g);
71 }
72}
73
90template <std::size_t idx = 0, typename Children, typename Builder, typename Fn,
91 affine::AffineMapLike Map>
92constexpr auto tuple_fold_sum(Children&& children, Builder&& builder, Fn&& fn,
93 Map&& selector) {
94 constexpr std::size_t N =
95 std::tuple_size_v<std::remove_reference_t<Children>>;
96 if constexpr (idx == N)
97 return std::tuple(selector, std::forward<Builder>(builder));
98 else {
99 auto&& child = std::get<idx>(std::forward<Children>(children));
100 auto new_builder =
101 std::forward<Fn>(fn)(child, std::forward<Builder>(builder));
102 auto new_selector = [&] {
103 if constexpr (idx == 0) return new_builder.coord_map;
104 else return update_selector(selector, new_builder.coord_map);
105 }();
106 return tuple_fold_sum<idx + 1>(std::forward<Children>(children),
107 std::move(new_builder), std::forward<Fn>(fn),
108 std::move(new_selector));
109 }
110}
111
115template <typename Meta, typename... Cs>
116struct Sum : Atom<Meta, Sum<Meta, Cs...>, Cs...> {
118 using BaseT = Atom<Meta, Sum<Meta, Cs...>, Cs...>;
126 using typename BaseT::T;
127 static_assert(dom_size_v<Domain> == sizeof...(Cs),
128 "Size of domain is not equal to children");
129 static_assert(sizeof...(Cs) > 1, "Number of children not greater than 1");
130
131 using MatT = linalg::Matrix<T, 1, 1>;
132
138 constexpr explicit Sum(const Cs&... cs)
139 : BaseT(std::make_tuple(cs...)), map(affine::make_map_id<MatT>()) {
141 }
142
149 constexpr explicit Sum(const affine::AffineMap<MatT>& map_in,
150 const std::tuple<Cs...>& cs)
151 : BaseT(cs), map(map_in) {
153 }
154
162 template <T Scalar>
163 constexpr auto mul() const {
164 affine::AffineMap<MatT> new_map(Scalar * map.linear_part(),
165 map.translation());
166 return Sum<Meta, Cs...>(new_map, this->children);
167 }
168
176 constexpr auto add(T beta) const {
177 affine::AffineMap<MatT> new_map(
178 map.linear_part(), linalg::Vector<T, 1>{beta} + map.translation());
179 return Sum<Meta, Cs...>(new_map, this->children);
180 }
181
192 template <AtomType Type, conic::BuilderLike Builder>
193 constexpr auto canonicalize_impl(const Builder& builder) const {
194 static_assert(Type != AtomType::SET, "Minkowski sum is not yet supported");
195
196 auto canonicalize_child = [&](const auto& child, const auto& builder_in) {
198 };
199 const auto res = tuple_fold_sum(this->children, builder, canonicalize_child,
200 builder.coord_map);
201 const auto selector = std::get<0>(res);
202 const auto F = map.linear_part()(0, 0) * selector.linear_part();
203 const auto g =
204 map.linear_part()(0, 0) * selector.translation() + map.translation();
205 const auto new_selector =
206 affine::AffineMap<decltype(selector.linear_part())>(F, g);
207
208 const auto new_builder = std::get<1>(res);
209 // Summation changes only the current selector; canonical rows and their
210 // trace have already been realized by the folded child builders.
213 decltype(new_builder.cb), decltype(new_builder.eq_map),
214 decltype(new_builder.canonical_trace)>(new_selector, new_builder.cb,
215 new_builder.eq_map,
216 new_builder.canonical_trace);
217 }
218
219 affine::AffineMap<MatT>
221};
222
226template <typename C0, typename... Cs>
227Sum(const C0&, const Cs&...)
229 1 + sizeof...(Cs)>,
232
233} // namespace convex
typename builder_traits< linalg::remove_cvref_t< Builder > >::Dom builder_dom_t
builder domain
typename dom_traits< linalg::remove_cvref_t< Dom > >::Space dom_space_t
constexpr void validate_sum_child_codomain()
Validate that a sum child has the same codomain as the parent sum.
Definition sum.hpp:23
constexpr std::size_t dom_size_v
linalg::remove_cvref_t< T > atom_decay_t
normalize atom-facing types to a stable internal form.
constexpr auto update_selector(const PrevMap &prev, const NextMap &next)
Accumulate the affine selector for a sum atom.
Definition sum.hpp:39
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.
constexpr auto tuple_fold_sum(Children &&children, Builder &&builder, Fn &&fn, Map &&selector)
Canonicalize and fold all sum children.
Definition sum.hpp:92
Builder for conic canonical forms.
CRTP base for convex atoms.
Definition atom.hpp:22
meta_codom_t< Meta > Codomain
Atom output codomain.
Definition atom.hpp:26
ChildrenT children
Tuple of normalized child atoms.
Definition atom.hpp:240
dom_point_t< Domain > PointT
Point type accepted by the atom domain.
Definition atom.hpp:32
meta_dom_t< Meta > Domain
Atom input domain.
Definition atom.hpp:24
dom_type_t< Domain > T
Scalar field for the atom domain.
Definition atom.hpp:30
forward declaration - sum op
Definition sum.hpp:116
constexpr auto mul() const
Scale this sum by a compile-time scalar.
Definition sum.hpp:163
constexpr Sum(const Cs &... cs)
Construct a sum atom from child atoms.
Definition sum.hpp:138
constexpr auto canonicalize_impl(const Builder &builder) const
Canonicalize this sum into a cone builder.
Definition sum.hpp:193
constexpr auto add(T beta) const
Add a scalar offset to this sum.
Definition sum.hpp:176
affine::AffineMap< MatT > map
Scalar affine transform applied after summing children.
Definition sum.hpp:220
linalg::Matrix< T, 1, 1 > MatT
Scalar affine map matrix type.
Definition sum.hpp:131
constexpr Sum(const affine::AffineMap< MatT > &map_in, const std::tuple< Cs... > &cs)
Construct a transformed sum atom from a scalar affine map.
Definition sum.hpp:149