alpaqa 1.0.0a16
Nonconvex constrained optimization
Loading...
Searching...
No Matches
l1-norm.hpp
Go to the documentation of this file.
1#pragma once
2
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>)
21struct L1Norm {
24 static constexpr bool scalar_weight = std::is_same_v<weight_t, real_t>;
25
26 L1Norm(weight_t λ) : λ{std::move(λ)} {
27 const char *msg = "L1Norm::λ must be nonnegative";
28 if constexpr (scalar_weight) {
29 if (λ < 0 || !std::isfinite(λ))
30 throw std::invalid_argument(msg);
31 } else {
32 if ((λ.array() < 0).any() || !λ.allFinite())
33 throw std::invalid_argument(msg);
34 }
35 }
36
38 requires(scalar_weight)
39 : λ{1} {}
41 requires(!scalar_weight)
42 = default;
43
45
46 real_t prox(crmat in, rmat out, real_t γ = 1) {
47 assert(in.cols() == 1);
48 assert(out.cols() == 1);
49 assert(in.size() == out.size());
50 const length_t n = in.size();
51 if constexpr (scalar_weight) {
52 assert(λ >= 0);
53 if (λ == 0) {
54 out = in;
55 return 0;
56 }
57 auto step = vec::Constant(n, λ * γ);
58 out = vec::Zero(n).cwiseMax(in - step).cwiseMin(in + step);
59 return λ * out.template lpNorm<1>();
60 } else {
61 if constexpr (std::is_same_v<weight_t, vec>)
62 if (λ.size() == 0)
63 λ = weight_t::Ones(n);
64 assert(λ.cols() == 1);
65 assert(in.size() == λ.size());
66 assert((λ.array() >= 0).all());
67 auto step = λ * γ;
68 out = vec::Zero(n).cwiseMax(in - step).cwiseMin(in + step);
69 return out.cwiseProduct(λ).template lpNorm<1>();
70 }
71 }
72
74 rmat out, real_t γ) {
75 return self.prox(std::move(in), std::move(out), γ);
76 }
77};
78
79/// ℓ₁-norm of complex numbers.
80/// @ingroup grp_Functions
81/// @tparam Weight
82/// Type of weighting factors. Either scalar or vector.
83template <Config Conf, class Weight = typename Conf::real_t>
84 requires(std::is_same_v<Weight, typename Conf::real_t> ||
85 std::is_same_v<Weight, typename Conf::vec> ||
86 std::is_same_v<Weight, typename Conf::rvec> ||
87 std::is_same_v<Weight, typename Conf::crvec>)
91 static constexpr bool scalar_weight = std::is_same_v<weight_t, real_t>;
92
93 L1NormComplex(weight_t λ) : λ{std::move(λ)} {
94 const char *msg = "L1NormComplex::λ must be nonnegative";
95 if constexpr (scalar_weight) {
96 if (λ < 0 || !std::isfinite(λ))
97 throw std::invalid_argument(msg);
98 } else {
99 if ((λ.array() < 0).any() || !λ.allFinite())
100 throw std::invalid_argument(msg);
101 }
102 }
103
105 requires(scalar_weight)
106 : λ{1} {}
108 requires(!scalar_weight)
109 = default;
110
112
113 real_t prox(crcmat in, rcmat out, real_t γ = 1) {
114 assert(in.cols() == 1);
115 assert(out.cols() == 1);
116 assert(in.size() == out.size());
117 const length_t n = in.size();
118 if constexpr (scalar_weight) {
119 assert(λ >= 0);
120 if (λ == 0) {
121 out = in;
122 return 0;
123 }
124 auto soft_thres = [γλ{γ * λ}](cplx_t x) {
125 auto mag = std::abs(x), arg = std::arg(x);
126 return mag <= γλ ? 0 : std::polar(mag - γλ, arg);
127 };
128 out = in.unaryExpr(soft_thres);
129 return λ * out.template lpNorm<1>();
130 } else {
131 if constexpr (std::is_same_v<weight_t, vec>)
132 if (λ.size() == 0)
133 λ = weight_t::Ones(n);
134 assert(λ.cols() == 1);
135 assert(in.size() == λ.size());
136 assert((λ.array() >= 0).all());
137 auto soft_thres = [γ](cplx_t x, real_t λ) {
138 real_t γλ = γ * λ;
139 auto mag = std::abs(x), arg = std::arg(x);
140 return mag <= γλ ? 0 : std::polar(mag - γλ, arg);
141 };
142 out = in.binaryExpr(λ, soft_thres);
143 return out.cwiseProduct(λ).template lpNorm<1>();
144 }
145 }
146
147 /// Note: a complex vector in ℂⁿ is represented by a real vector in ℝ²ⁿ.
148 real_t prox(crmat in, rmat out, real_t γ = 1) {
149 assert(in.rows() % 2 == 0);
150 assert(out.rows() % 2 == 0);
152 util::start_lifetime_as_array<cplx_t>(in.data(), in.size() / 2),
153 in.rows() / 2,
154 in.cols(),
155 };
157 util::start_lifetime_as_array<cplx_t>(out.data(), out.size() / 2),
158 out.rows() / 2,
159 out.cols(),
160 };
161 return prox(cplx_in, cplx_out, γ);
162 }
163
165 crmat in, rmat out, real_t γ) {
166 return self.prox(std::move(in), std::move(out), γ);
167 }
168};
169
170} // namespace alpaqa::functions
#define USING_ALPAQA_CONFIG(Conf)
Definition config.hpp:63
struct alpaqa::prox_fn prox
Compute the proximal mapping.
std::decay_t< decltype(Tag)> tag_t
typename Conf::crcmat crcmat
Definition config.hpp:88
typename Conf::crmat crmat
Definition config.hpp:83
typename Conf::rmat rmat
Definition config.hpp:82
typename Conf::cmcmat cmcmat
Definition config.hpp:86
typename Conf::real_t real_t
Definition config.hpp:72
typename Conf::rcmat rcmat
Definition config.hpp:87
typename Conf::length_t length_t
Definition config.hpp:89
constexpr const auto inf
Definition config.hpp:98
typename Conf::cplx_t cplx_t
Definition config.hpp:73
typename Conf::mcmat mcmat
Definition config.hpp:85
ℓ₁-norm of complex numbers.
Definition l1-norm.hpp:88
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:148
friend real_t alpaqa_tag_invoke(tag_t< alpaqa::prox >, L1NormComplex &self, crmat in, rmat out, real_t γ)
Definition l1-norm.hpp:164
real_t prox(crcmat in, rcmat out, real_t γ=1)
Definition l1-norm.hpp:113
real_t prox(crmat in, rmat out, real_t γ=1)
Definition l1-norm.hpp:46
friend real_t alpaqa_tag_invoke(tag_t< alpaqa::prox >, L1Norm &self, crmat in, rmat out, real_t γ)
Definition l1-norm.hpp:73