alpaqa cmake-targets
Nonconvex constrained optimization
Loading...
Searching...
No Matches
nuclear-norm.hpp
Go to the documentation of this file.
1#pragma once
2
4#include <Eigen/SVD>
5
6namespace alpaqa::functions {
7
8#if EIGEN_VERSION_AT_LEAST(3, 4, 1)
9template <Config Conf>
10using DefaultSVD = Eigen::BDCSVD<typename Conf::mat,
11 Eigen::ComputeThinU | Eigen::ComputeThinV>;
12#else
13template <Config Conf>
14using DefaultSVD = Eigen::BDCSVD<typename Conf::mat>;
15#endif
16
17/// Nuclear norm (ℓ₁-norm of singular values).
18/// @ingroup grp_Functions
19template <Config Conf, class SVD = DefaultSVD<Conf>>
22
23 /// Construct without pre-allocation.
25 if (λ < 0 || !std::isfinite(λ))
26 throw std::invalid_argument("NuclearNorm::λ must be nonnegative");
27 }
28 /// Construct with pre-allocation.
30 : λ{λ}, rows{rows}, cols{cols},
32 svd{rows, cols},
33#else
35#endif
37 if (λ < 0 || !std::isfinite(λ))
38 throw std::invalid_argument("NuclearNorm::λ must be nonnegative");
39 }
40
42 length_t rows = 0, cols = 0;
45
47 if (λ == 0) {
48 out = in;
49 return 0;
50 }
51 if (rows == 0 || cols == 0) { // dynamic size
52 assert(in.rows() == out.rows());
53 assert(in.cols() == out.cols());
54 svd.compute(in);
55 } else { // fixed size
56 assert(in.size() == rows * cols);
57 assert(out.size() == rows * cols);
58 svd.compute(in.reshaped(rows, cols));
59 }
60 const length_t n = svd.singularValues().size();
61 auto step = vec::Constant(n, λ * γ);
62 singular_values = vec::Zero(n).cwiseMax(svd.singularValues() - step);
63 real_t value = λ * singular_values.template lpNorm<1>();
64 auto it0 = std::find(singular_values.begin(), singular_values.end(), 0);
65 index_t rank = it0 - singular_values.begin();
66 using Eigen::placeholders::all, Eigen::seqN;
67 auto sel = seqN(0, rank);
68 auto &&U = svd.matrixU(), &&V = svd.matrixV();
69 auto &&U1 = U(all, sel);
70 auto &&Σ1 = singular_values(sel).asDiagonal();
71 auto &&V1T = V.transpose()(sel, all);
72 out.reshaped().noalias() = (U1 * Σ1 * V1T).reshaped();
73 return value;
74 }
75
77 crmat in, rmat out, real_t γ) {
78 return self.prox(std::move(in), std::move(out), γ);
79 }
80};
81
82} // namespace alpaqa::functions
#define USING_ALPAQA_CONFIG(Conf)
Definition config.hpp:56
Eigen::BDCSVD< typename Conf::mat > DefaultSVD
std::decay_t< decltype(Tag)> tag_t
typename Conf::crmat crmat
Definition config.hpp:75
typename Conf::rmat rmat
Definition config.hpp:74
typename Conf::real_t real_t
Definition config.hpp:65
typename Conf::index_t index_t
Definition config.hpp:77
typename Conf::length_t length_t
Definition config.hpp:76
constexpr const auto inf
Definition config.hpp:85
typename Conf::vec vec
Definition config.hpp:66
Nuclear norm (ℓ₁-norm of singular values).
real_t prox(crmat in, rmat out, real_t γ=1)
NuclearNorm(real_t λ=1)
Construct without pre-allocation.
NuclearNorm(real_t λ, length_t rows, length_t cols)
Construct with pre-allocation.
friend real_t alpaqa_tag_invoke(tag_t< alpaqa::prox >, NuclearNorm &self, crmat in, rmat out, real_t γ)