alpaqa 1.0.0a18
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 using vec_util::norm_1;
51 const length_t n = in.size();
52 if constexpr (scalar_weight) {
53 assert(λ >= 0);
54 if (λ == 0) {
55 out = in;
56 return 0;
57 }
58 auto step = vec::Constant(n, λ * γ);
59 out = vec::Zero(n).cwiseMax(in - step).cwiseMin(in + step);
60 return λ * norm_1(out.reshaped());
61 } else {
62 if constexpr (std::is_same_v<weight_t, vec>)
63 if (λ.size() == 0)
64 λ = weight_t::Ones(n);
65 assert(λ.cols() == 1);
66 assert(in.size() == λ.size());
67 assert((λ.array() >= 0).all());
68 auto step = λ * γ;
69 out = vec::Zero(n).cwiseMax(in - step).cwiseMin(in + step);
70 return norm_1(out.cwiseProduct(λ).reshaped());
71 }
72 }
73
75 rmat out, real_t γ) {
76 return self.prox(std::move(in), std::move(out), γ);
77 }
78};
79
80/// ℓ₁-norm of complex numbers.
81/// @ingroup grp_Functions
82/// @tparam Weight
83/// Type of weighting factors. Either scalar or vector.
84template <Config Conf, class Weight = typename Conf::real_t>
85 requires(std::is_same_v<Weight, typename Conf::real_t> ||
86 std::is_same_v<Weight, typename Conf::vec> ||
87 std::is_same_v<Weight, typename Conf::rvec> ||
88 std::is_same_v<Weight, typename Conf::crvec>)
92 static constexpr bool scalar_weight = std::is_same_v<weight_t, real_t>;
93
94 L1NormComplex(weight_t λ) : λ{std::move(λ)} {
95 const char *msg = "L1NormComplex::λ must be nonnegative";
96 if constexpr (scalar_weight) {
97 if (λ < 0 || !std::isfinite(λ))
98 throw std::invalid_argument(msg);
99 } else {
100 if ((λ.array() < 0).any() || !λ.allFinite())
101 throw std::invalid_argument(msg);
102 }
103 }
104
106 requires(scalar_weight)
107 : λ{1} {}
109 requires(!scalar_weight)
110 = default;
111
113
114 real_t prox(crcmat in, rcmat out, real_t γ = 1) {
115 assert(in.cols() == 1);
116 assert(out.cols() == 1);
117 assert(in.size() == out.size());
118 using vec_util::norm_1;
119 const length_t n = in.size();
120 if constexpr (scalar_weight) {
121 assert(λ >= 0);
122 if (λ == 0) {
123 out = in;
124 return 0;
125 }
126 auto soft_thres = [γλ{γ * λ}](cplx_t x) {
127 auto mag2 = x.real() * x.real() + x.imag() * x.imag();
128 return mag2 <= γλ * γλ ? 0 : x * (1 - γλ / std::sqrt(mag2));
129 };
130 out = in.unaryExpr(soft_thres);
131 return λ * norm_1(out);
132 } else {
133 if constexpr (std::is_same_v<weight_t, vec>)
134 if (λ.size() == 0)
135 λ = weight_t::Ones(n);
136 assert(λ.cols() == 1);
137 assert(in.size() == λ.size());
138 assert((λ.array() >= 0).all());
139 auto soft_thres = [γ](cplx_t x, real_t λ) {
140 real_t γλ = γ * λ;
141 auto mag2 = x.real() * x.real() + x.imag() * x.imag();
142 return mag2 <= γλ * γλ ? 0 : x * (1 - γλ / std::sqrt(mag2));
143 };
144 out = in.binaryExpr(λ, soft_thres);
145 return norm_1(out.cwiseProduct(λ));
146 }
147 }
148
149 /// Note: a complex vector in ℂⁿ is represented by a real vector in ℝ²ⁿ.
150 real_t prox(crmat in, rmat out, real_t γ = 1) {
151 assert(in.rows() % 2 == 0);
152 assert(out.rows() % 2 == 0);
154 util::start_lifetime_as_array<cplx_t>(in.data(), in.size() / 2),
155 in.rows() / 2,
156 in.cols(),
157 };
159 util::start_lifetime_as_array<cplx_t>(out.data(), out.size() / 2),
160 out.rows() / 2,
161 out.cols(),
162 };
163 return prox(cplx_in, cplx_out, γ);
164 }
165
167 crmat in, rmat out, real_t γ) {
168 return self.prox(std::move(in), std::move(out), γ);
169 }
170};
171
172} // 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:210
std::decay_t< decltype(Tag)> tag_t
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
constexpr const auto inf
Definition config.hpp:112
typename Conf::cplx_t cplx_t
Definition config.hpp:87
typename Conf::mcmat mcmat
Definition config.hpp:99
ℓ₁-norm of complex numbers.
Definition l1-norm.hpp:89
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:150
friend real_t alpaqa_tag_invoke(tag_t< alpaqa::prox >, L1NormComplex &self, crmat in, rmat out, real_t γ)
Definition l1-norm.hpp:166
real_t prox(crcmat in, rcmat out, real_t γ=1)
Definition l1-norm.hpp:114
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:74