Nonconvex constrained optimization
Loading...
Searching...
No Matches
l1-norm.hpp
Go to the documentation of this file.
1#pragma once
2
5#include <guanaqo/lifetime.hpp>
6#include <cassert>
7#include <cmath>
8#include <stdexcept>
9
11
12/// ℓ₁-norm.
13/// @ingroup grp_Functions
14/// @tparam Weight
15/// Type of weighting factors. Either scalar or vector.
16template <Config Conf, class Weight = typename Conf::real_t>
17 requires(std::is_same_v<Weight, typename Conf::real_t> ||
18 std::is_same_v<Weight, typename Conf::vec> ||
19 std::is_same_v<Weight, typename Conf::rvec> ||
20 std::is_same_v<Weight, typename Conf::crvec> ||
21 std::is_same_v<Weight, typename Conf::mat> ||
22 std::is_same_v<Weight, typename Conf::rmat> ||
23 std::is_same_v<Weight, typename Conf::crmat>)
24struct L1Norm {
26 using weight_t = Weight;
27 static constexpr bool scalar_weight = std::is_same_v<weight_t, real_t>;
28
29 L1Norm(weight_t λ) : λ{std::move(λ)} {
30 const char *msg = "L1Norm::λ must be nonnegative";
31 if constexpr (scalar_weight) {
32 if (λ < 0 || !std::isfinite(λ))
33 throw std::invalid_argument(msg);
34 } else {
35 if ((λ.array() < 0).any() || !λ.allFinite())
36 throw std::invalid_argument(msg);
37 }
38 }
39
41 requires(scalar_weight)
42 : λ{1} {}
44 requires(!scalar_weight)
45 = default;
46
48
49 real_t prox(crmat in, rmat out, real_t γ = 1) {
50 assert(in.cols() == 1);
51 assert(out.cols() == 1);
52 assert(in.size() == out.size());
53 using vec_util::norm_1;
54 const length_t n = in.size();
55 if constexpr (scalar_weight) {
56 assert(λ >= 0);
57 if (λ == 0) {
58 out = in;
59 return 0;
60 }
61 auto step = vec::Constant(n, λ * γ);
62 out = vec::Zero(n).cwiseMax(in - step).cwiseMin(in + step);
63 return λ * norm_1(out.reshaped());
64 } else {
65 if constexpr (std::is_same_v<weight_t, vec>)
66 if (λ.size() == 0)
67 λ = weight_t::Ones(n);
68 assert(λ.cols() == 1);
69 assert(in.size() == λ.size());
70 assert((λ.array() >= 0).all());
71 auto step = λ * γ;
72 out = vec::Zero(n).cwiseMax(in - step).cwiseMin(in + step);
73 return norm_1(out.cwiseProduct(λ).reshaped());
74 }
75 }
76
77 friend real_t guanaqo_tag_invoke(tag_t<alpaqa::prox>, L1Norm &self,
78 crmat in, rmat out, real_t γ) {
79 return self.prox(std::move(in), std::move(out), γ);
80 }
81};
82
83/// ℓ₁-norm of complex numbers.
84/// @ingroup grp_Functions
85/// @tparam Weight
86/// Type of weighting factors. Either scalar or vector.
87template <Config Conf, class Weight = typename Conf::real_t>
88 requires(std::is_same_v<Weight, typename Conf::real_t> ||
89 std::is_same_v<Weight, typename Conf::vec> ||
90 std::is_same_v<Weight, typename Conf::rvec> ||
91 std::is_same_v<Weight, typename Conf::crvec> ||
92 std::is_same_v<Weight, typename Conf::mat> ||
93 std::is_same_v<Weight, typename Conf::rmat> ||
94 std::is_same_v<Weight, typename Conf::crmat>)
97 using weight_t = Weight;
98 static constexpr bool scalar_weight = std::is_same_v<weight_t, real_t>;
99
100 L1NormComplex(weight_t λ) : λ{std::move(λ)} {
101 const char *msg = "L1NormComplex::λ must be nonnegative";
102 if constexpr (scalar_weight) {
103 if (λ < 0 || !std::isfinite(λ))
104 throw std::invalid_argument(msg);
105 } else {
106 if ((λ.array() < 0).any() || !λ.allFinite())
107 throw std::invalid_argument(msg);
108 }
109 }
110
112 requires(scalar_weight)
113 : λ{1} {}
115 requires(!scalar_weight)
116 = default;
117
119
120 real_t prox(crcmat in, rcmat out, real_t γ = 1) {
121 assert(in.cols() == 1);
122 assert(out.cols() == 1);
123 assert(in.size() == out.size());
124 using vec_util::norm_1;
125 const length_t n = in.size();
126 if constexpr (scalar_weight) {
127 assert(λ >= 0);
128 if (λ == 0) {
129 out = in;
130 return 0;
131 }
132 auto soft_thres = [γλ{γ * λ}](cplx_t x) {
133 auto mag2 = x.real() * x.real() + x.imag() * x.imag();
134 return mag2 <= γλ * γλ ? 0 : x * (1 - γλ / std::sqrt(mag2));
135 };
136 out = in.unaryExpr(soft_thres);
137 return λ * norm_1(out.reshaped());
138 } else {
139 if constexpr (std::is_same_v<weight_t, vec>)
140 if (λ.size() == 0)
141 λ = weight_t::Ones(n);
142 assert(λ.cols() == 1);
143 assert(in.size() == λ.size());
144 assert((λ.array() >= 0).all());
145 auto soft_thres = [γ](cplx_t x, real_t λ) {
146 real_t γλ = γ * λ;
147 auto mag2 = x.real() * x.real() + x.imag() * x.imag();
148 return mag2 <= γλ * γλ ? 0 : x * (1 - γλ / std::sqrt(mag2));
149 };
150 out = in.binaryExpr(λ, soft_thres);
151 return norm_1(out.cwiseProduct(λ).reshaped());
152 }
153 }
154
155 /// Note: a complex vector in ℂⁿ is represented by a real vector in ℝ²ⁿ.
156 real_t prox(crmat in, rmat out, real_t γ = 1) {
157 assert(in.rows() % 2 == 0);
158 assert(out.rows() % 2 == 0);
159 using guanaqo::start_lifetime_as_array;
160 cmcmat cplx_in{
161 start_lifetime_as_array<cplx_t>(in.data(), in.size() / 2),
162 in.rows() / 2,
163 in.cols(),
164 };
165 mcmat cplx_out{
166 start_lifetime_as_array<cplx_t>(out.data(), out.size() / 2),
167 out.rows() / 2,
168 out.cols(),
169 };
170 return prox(cplx_in, cplx_out, γ);
171 }
172
173 friend real_t guanaqo_tag_invoke(tag_t<alpaqa::prox>, L1NormComplex &self,
174 crmat in, rmat out, real_t γ) {
175 return self.prox(std::move(in), std::move(out), γ);
176 }
177};
178
179} // namespace alpaqa::functions
#define USING_ALPAQA_CONFIG(Conf)
Definition config.hpp:77
struct alpaqa::prox_fn prox
Compute the proximal mapping.
auto norm_1(const Eigen::MatrixBase< Derived > &v)
Get the 1-norm of the given vector.
Definition config.hpp:212
typename Conf::crcmat crcmat
Definition config.hpp:102
typename Conf::crmat crmat
Definition config.hpp:97
typename Conf::rmat rmat
Definition config.hpp:96
typename Conf::cmcmat cmcmat
Definition config.hpp:100
typename Conf::real_t real_t
Definition config.hpp:86
typename Conf::rcmat rcmat
Definition config.hpp:101
typename Conf::length_t length_t
Definition config.hpp:103
typename Conf::cplx_t cplx_t
Definition config.hpp:87
typename Conf::mcmat mcmat
Definition config.hpp:99
real_t prox(crmat in, rmat out, real_t γ=1)
Note: a complex vector in ℂⁿ is represented by a real vector in ℝ²ⁿ.
Definition l1-norm.hpp:156
real_t prox(crcmat in, rcmat out, real_t γ=1)
Definition l1-norm.hpp:120
static constexpr bool scalar_weight
Definition l1-norm.hpp:98
friend real_t guanaqo_tag_invoke(tag_t< alpaqa::prox >, L1NormComplex &self, crmat in, rmat out, real_t γ)
Definition l1-norm.hpp:173
real_t prox(crmat in, rmat out, real_t γ=1)
Definition l1-norm.hpp:49
static constexpr bool scalar_weight
Definition l1-norm.hpp:27
friend real_t guanaqo_tag_invoke(tag_t< alpaqa::prox >, L1Norm &self, crmat in, rmat out, real_t γ)
Definition l1-norm.hpp:77