SOPT
Sparse OPTimisation
maths.h
Go to the documentation of this file.
1 #ifndef SOPT_MATHS_H
2 #define SOPT_MATHS_H
3 
4 #include "sopt/config.h"
5 #include <algorithm>
6 #include <complex>
7 #include <type_traits>
8 #include <Eigen/Core>
9 #include "sopt/exception.h"
10 #include "sopt/types.h"
11 
12 namespace sopt {
13 
15 template <typename T>
16 typename real_type<typename T::Scalar>::type standard_deviation(Eigen::ArrayBase<T> const &x) {
17  return (x - x.mean()).matrix().stableNorm() / std::sqrt(x.size());
18 }
20 template <typename T>
21 typename real_type<typename T::Scalar>::type standard_deviation(Eigen::MatrixBase<T> const &x) {
22  return standard_deviation(x.array());
23 }
24 
26 template <typename SCALAR>
27 typename std::enable_if<std::is_arithmetic<SCALAR>::value or is_complex<SCALAR>::value,
28  SCALAR>::type
29 soft_threshhold(SCALAR const &x, typename real_type<SCALAR>::type const &threshhold) {
30  auto const normalized = std::abs(x);
31  return normalized < threshhold ? SCALAR(0) : (x * (SCALAR(1) - threshhold / normalized));
32 }
33 
34 namespace details {
36 template <typename SCALAR>
38  public:
39  SCALAR operator()(const SCALAR &value) const { return std::max(value, SCALAR(0)); }
40 };
42 template <typename SCALAR>
43 class ProjectPositiveQuadrant<std::complex<SCALAR>> {
44  public:
45  SCALAR operator()(SCALAR const &value) const { return std::max(value, SCALAR(0)); }
46  std::complex<SCALAR> operator()(std::complex<SCALAR> const &value) const {
47  return {operator()(value.real()), SCALAR(0)};
48  }
49 };
50 
52 template <typename SCALAR>
53 using SoftThreshhold = decltype(std::bind(soft_threshhold<SCALAR>, std::placeholders::_1,
54  typename real_type<SCALAR>::type(1)));
55 } // namespace details
56 
58 template <typename T>
59 Eigen::CwiseUnaryOp<const details::ProjectPositiveQuadrant<typename T::Scalar>, const T>
60 positive_quadrant(Eigen::DenseBase<T> const &input) {
62  using UnaryOp = Eigen::CwiseUnaryOp<const Projector, const T>;
63  return UnaryOp(input.derived(), Projector());
64 }
65 
67 template <typename T>
68 Eigen::CwiseUnaryOp<const details::SoftThreshhold<typename T::Scalar>, const T> soft_threshhold(
69  Eigen::DenseBase<T> const &input,
70  typename real_type<typename T::Scalar>::type const &threshhold) {
71  using Scalar = typename T::Scalar;
72  using Real = typename real_type<Scalar>::type;
73  return Eigen::CwiseUnaryOp<const details::SoftThreshhold<typename T::Scalar>, const T>{
74  input.derived(), std::bind(soft_threshhold<Scalar>, std::placeholders::_1, Real(threshhold))};
75 }
76 
81 template <typename T0, typename T1>
82 typename std::enable_if<std::is_arithmetic<typename T0::Scalar>::value and
83  std::is_arithmetic<typename T1::Scalar>::value,
84  Eigen::CwiseBinaryOp<typename T0::Scalar (*)(typename T0::Scalar const &,
85  typename T1::Scalar const &),
86  const T0, const T1>>::type
87 soft_threshhold(Eigen::DenseBase<T0> const &input, Eigen::DenseBase<T1> const &threshhold) {
88  if (input.size() != threshhold.size())
89  SOPT_THROW("Threshhold and input should have the same size");
90  return {input.derived(), threshhold.derived(), soft_threshhold<typename T0::Scalar>};
91 }
92 
97 template <typename T0, typename T1>
98 typename std::enable_if<
99  is_complex<typename T0::Scalar>::value and std::is_arithmetic<typename T1::Scalar>::value,
100  Eigen::CwiseBinaryOp<
101  typename T0::Scalar (*)(typename T0::Scalar const &, typename T0::Scalar const &), const T0,
102  decltype(std::declval<const T1>().template cast<typename T0::Scalar>())>>::type
103 soft_threshhold(Eigen::DenseBase<T0> const &input, Eigen::DenseBase<T1> const &threshhold) {
104  if (input.size() != threshhold.size())
105  SOPT_THROW("Threshhold and input should have the same size: ")
106  << threshhold.size() << " vs " << input.size();
107  using Complex = typename T0::Scalar;
108  auto const func = [](Complex const &x, Complex const &t) -> Complex {
109  return soft_threshhold(x, t.real());
110  };
111  return {input.derived(), threshhold.derived().template cast<Complex>(), func};
112 }
113 
115 template <typename T0, typename T1>
116 typename real_type<typename T0::Scalar>::type l1_norm(Eigen::ArrayBase<T0> const &input,
117  Eigen::ArrayBase<T1> const &weights) {
118  if (weights.size() == 1) return input.cwiseAbs().sum() * std::abs(weights(0));
119  return (input * weights).cwiseAbs().sum();
120 }
122 template <typename T0, typename T1>
123 typename real_type<typename T0::Scalar>::type l1_norm(Eigen::MatrixBase<T0> const &input,
124  Eigen::MatrixBase<T1> const &weight) {
125  return l1_norm(input.array(), weight.array());
126 }
128 template <typename T0>
129 typename real_type<typename T0::Scalar>::type l1_norm(Eigen::ArrayBase<T0> const &input) {
130  return input.cwiseAbs().sum();
131 }
133 template <typename T0>
134 typename real_type<typename T0::Scalar>::type l1_norm(Eigen::MatrixBase<T0> const &input) {
135  return l1_norm(input.array());
136 }
137 
139 template <typename T0, typename T1>
140 typename real_type<typename T0::Scalar>::type l2_norm(Eigen::ArrayBase<T0> const &input,
141  Eigen::ArrayBase<T1> const &weights) {
142  if (weights.size() == 1) return input.matrix().stableNorm() * std::abs(weights(0));
143  return (input * weights).matrix().stableNorm();
144 }
146 template <typename T0, typename T1>
147 typename real_type<typename T0::Scalar>::type l2_norm(Eigen::MatrixBase<T0> const &input,
148  Eigen::MatrixBase<T1> const &weights) {
149  return l2_norm(input.derived().array(), weights.derived().array());
150 }
152 template <typename T0>
153 typename real_type<typename T0::Scalar>::type l2_norm(Eigen::ArrayBase<T0> const &input) {
154  typename T0::PlainObject w(1);
155  w(0) = 1;
156  return l2_norm(input, w);
157 }
159 template <typename T0>
160 typename real_type<typename T0::Scalar>::type l2_norm(Eigen::MatrixBase<T0> const &input) {
161  typename T0::PlainObject w(1);
162  w(0) = 1;
163  return l2_norm(input.derived().array(), w.array());
164 }
165 
167 template <typename T0, typename T1>
168 typename real_type<typename T0::Scalar>::type tv_norm(Eigen::ArrayBase<T0> const &input,
169  Eigen::ArrayBase<T1> const &weights) {
170  const auto size = input.size() / 2;
171  if (weights.size() == 1)
172  return (input.segment(0, size).square() + input.segment(size, size).square())
173  .cwiseAbs()
174  .sqrt()
175  .matrix()
176  .sum() *
177  std::abs(weights(0));
178  return std::abs(
179  ((input.segment(0, size).square() + input.segment(size, size).square()).cwiseAbs().sqrt() *
180  weights)
181  .matrix()
182  .sum());
183 }
185 template <typename T0, typename T1>
186 typename real_type<typename T0::Scalar>::type tv_norm(Eigen::MatrixBase<T0> const &input,
187  Eigen::MatrixBase<T1> const &weights) {
188  return tv_norm(input.derived().array(), weights.derived().array());
189 }
191 template <typename T0>
192 typename real_type<typename T0::Scalar>::type tv_norm(Eigen::ArrayBase<T0> const &input) {
193  typename T0::PlainObject w(1);
194  w(0) = 1;
195  return tv_norm(input, w);
196 }
198 template <typename T0>
199 typename real_type<typename T0::Scalar>::type tv_norm(Eigen::MatrixBase<T0> const &input) {
200  typename T0::PlainObject w(1);
201  w(0) = 1;
202  return tv_norm(input.derived().array(), w.array());
203 }
204 
205 namespace details {
207 inline t_int gcd(t_int a, t_int b) { return b == 0 ? a : gcd(b, a % b); }
208 } // namespace details
209 } // namespace sopt
210 #endif
constexpr Scalar b
sopt::t_real Scalar
constexpr Scalar a
std::complex< SCALAR > operator()(std::complex< SCALAR > const &value) const
Definition: maths.h:46
Expression to create projection onto positive orthant.
Definition: maths.h:37
SCALAR operator()(const SCALAR &value) const
Definition: maths.h:39
Computes inner-most element type.
Definition: real_type.h:42
#define SOPT_THROW(MSG)
Definition: exception.h:46
t_int gcd(t_int a, t_int b)
Greatest common divisor.
Definition: maths.h:207
decltype(std::bind(soft_threshhold< SCALAR >, std::placeholders::_1, typename real_type< SCALAR >::type(1))) SoftThreshhold
Helper template type alias to instantiate soft_threshhold that takes an Eigen object.
Definition: maths.h:54
real_type< typename T0::Scalar >::type tv_norm(Eigen::ArrayBase< T0 > const &input, Eigen::ArrayBase< T1 > const &weights)
Computes weighted TV norm.
Definition: maths.h:168
real_type< typename T::Scalar >::type standard_deviation(Eigen::ArrayBase< T > const &x)
Computes the standard deviation of a vector.
Definition: maths.h:16
Eigen::CwiseUnaryOp< const details::ProjectPositiveQuadrant< typename T::Scalar >, const T > positive_quadrant(Eigen::DenseBase< T > const &input)
Expression to create projection onto positive quadrant.
Definition: maths.h:60
int t_int
Root of the type hierarchy for signed integers.
Definition: types.h:13
std::enable_if< std::is_arithmetic< SCALAR >::value or is_complex< SCALAR >::value, SCALAR >::type soft_threshhold(SCALAR const &x, typename real_type< SCALAR >::type const &threshhold)
abs(x) < threshhold ? 0: x - sgn(x) * threshhold
Definition: maths.h:29
real_type< typename T0::Scalar >::type l1_norm(Eigen::ArrayBase< T0 > const &input, Eigen::ArrayBase< T1 > const &weights)
Computes weighted L1 norm.
Definition: maths.h:116
real_type< typename T0::Scalar >::type l2_norm(Eigen::ArrayBase< T0 > const &input, Eigen::ArrayBase< T1 > const &weights)
Computes weighted L2 norm.
Definition: maths.h:140