/usr/include/sopt/wavelets/innards.impl.h is in libsopt-dev 2.0.0-4.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 | #ifndef SOPT_WAVELETS_INNARDS_H
#define SOPT_WAVELETS_INNARDS_H
#include "sopt/config.h"
#include <Eigen/Core>
// Function inside anonymouns namespace won't appear in library
namespace sopt {
namespace wavelets {
namespace {
//! \brief Returns evaluated expression or copy of input
//! \details Gets C++ to figure out what the exact type is. Eigen tries and avoids copies. But
//! sometimes we actually want a copy, to make sure the arguments of a function are not modified
template <class T0>
auto copy(Eigen::ArrayBase<T0> const &a) ->
typename std::remove_const<typename std::remove_reference<decltype(a.eval())>::type>::type {
return a.eval();
}
//! \brief Applies scalar product of the size b
//! \details In practice, the operation is equivalent to the following
//! - `a` can be seen as a periodic vector: `a[i] == a[i % a.size()]`
//! - `a'` is the segment `a` = a[offset:offset+b.size()]`
//! - the result is the scalar product of `a'` by `b`.
template <class T0, class T2>
typename T0::Scalar
periodic_scalar_product(Eigen::ArrayBase<T0> const &a, Eigen::ArrayBase<T2> const &b,
typename T0::Index offset) {
auto const Na = static_cast<typename T0::Index>(a.size());
auto const Nb = static_cast<typename T0::Index>(b.size());
// offset in [0, a.size()[
offset %= Na;
if(offset < 0)
offset += Na;
// Simple case, just do it
if(Na > Nb + offset) {
return (a.segment(offset, Nb) * b).sum();
}
// Wrap around, do it, but carefully
typename T0::Scalar result(0);
for(typename T0::Index i(0), j(offset); i < Nb; ++i, ++j)
result += a(j % Na) * b(i);
return result;
}
//! \brief Convolves the signal by the filter
//! \details The signal is seen as a periodic vector during convolution
template <class T0, class T1, class T2>
void convolve(Eigen::ArrayBase<T0> &result, Eigen::ArrayBase<T1> const &signal,
Eigen::ArrayBase<T2> const &filter) {
assert(result.size() == signal.size());
for(typename T0::Index i(0); i < t_int(signal.size()); ++i)
result(i) = periodic_scalar_product(signal, filter, i);
}
//! \brief Convolve variation for vector blocks
template <class T0, class T1, class T2>
void convolve(Eigen::VectorBlock<T0> &&result, Eigen::ArrayBase<T1> const &signal,
Eigen::ArrayBase<T2> const &filter) {
return convolve(result, signal, filter);
}
//! \brief Convolves and down-samples the signal by the filter
//! \details Just like convolve, but does every other point.
template <class T0, class T1, class T2>
void down_convolve(Eigen::ArrayBase<T0> &result, Eigen::ArrayBase<T1> const &signal,
Eigen::ArrayBase<T2> const &filter) {
assert(result.size() * 2 <= signal.size());
if(signal.rows() == 1)
for(typename T0::Index i(0); i < result.size(); ++i)
result(i) = periodic_scalar_product(signal.transpose(), filter, 2 * i);
else
for(typename T0::Index i(0); i < result.size(); ++i)
result(i) = periodic_scalar_product(signal, filter, 2 * i);
}
//! \brief Dowsampling + convolve variation for vector blocks
template <class T0, class T1, class T2>
void down_convolve(Eigen::VectorBlock<T0> &&result, Eigen::ArrayBase<T1> const &signal,
Eigen::ArrayBase<T2> const &filter) {
down_convolve(result, signal, filter);
}
//! Convolve and sims low and high pass of a signal
template <class T0, class T1, class T2, class T3, class T4>
void convolve_sum(Eigen::ArrayBase<T0> &result, Eigen::ArrayBase<T1> const &low_pass_signal,
Eigen::ArrayBase<T2> const &low_pass,
Eigen::ArrayBase<T3> const &high_pass_signal,
Eigen::ArrayBase<T4> const &high_pass) {
assert(result.size() == low_pass_signal.size());
assert(result.size() == high_pass_signal.size());
assert(low_pass.size() == high_pass.size());
static_assert(std::is_signed<typename T0::Index>::value, "loffset, hoffset expect signed values");
auto const loffset = 1 - static_cast<typename T0::Index>(low_pass.size());
auto const hoffset = 1 - static_cast<typename T0::Index>(high_pass.size());
for(typename T0::Index i(0); i < result.size(); ++i) {
result(i) = periodic_scalar_product(low_pass_signal, low_pass, i + loffset)
+ periodic_scalar_product(high_pass_signal, high_pass, i + hoffset);
}
}
//! \brief Convolves and up-samples at the same time
//! \details If Cn shuffles the (periodic) vector right, U does the upsampling,
//! and O is the convolution operation, then the normal upsample + convolution
//! is O(Cn[U [a]], b). What we are doing here is U[O(Cn'[a'], b')]. This avoids
//! some unnecessary operations (multiplying and summing zeros) and removes the
//! need for temporary copies. Testing shows the operations are equivalent. But
//! I certainly cannot show how on paper.
template <class T0, class T1, class T2, class T3, class T4, class T5>
void up_convolve_sum(Eigen::ArrayBase<T0> &result, Eigen::ArrayBase<T1> const &coeffs,
Eigen::ArrayBase<T2> const &low_even, Eigen::ArrayBase<T3> const &low_odd,
Eigen::ArrayBase<T4> const &high_even, Eigen::ArrayBase<T5> const &high_odd) {
assert(result.size() <= coeffs.size());
assert(result.size() % 2 == 0);
assert(low_even.size() == high_even.size());
assert(low_odd.size() == high_odd.size());
auto const Nlow = (coeffs.size() + 1) / 2;
auto const Nhigh = coeffs.size() / 2;
auto const size = low_even.size() + low_odd.size();
auto const is_even = size % 2 == 0;
auto const even_offset = (1 - size) / 2;
auto const odd_offset = (1 - size) / 2 + (is_even ? 0 : 1);
for(typename T0::Index i(0); i + 1 < result.size(); i += 2) {
result(i + (is_even ? 1 : 0))
= periodic_scalar_product(coeffs.head(Nlow), low_even, i / 2 + even_offset)
+ periodic_scalar_product(coeffs.tail(Nhigh), high_even, i / 2 + even_offset);
result(i + (is_even ? 0 : 1))
= periodic_scalar_product(coeffs.head(Nlow), low_odd, i / 2 + odd_offset)
+ periodic_scalar_product(coeffs.tail(Nhigh), high_odd, i / 2 + odd_offset);
}
}
}
}
}
#endif
|