sablib
Loading...
Searching...
No Matches
beads.cpp
Go to the documentation of this file.
1
6
7#define _USE_MATH_DEFINES
8#include <cmath>
9#include <functional>
10#include <Eigen/Eigen>
11
12#include "../misc/convolve.h"
13#include "../misc/diff.h"
14#include "../misc/spdiags.h"
15
16#include "beads.h"
17
18namespace sablib {
19
20namespace {
21
22const std::tuple< Eigen::SparseMatrix<double>, Eigen::SparseMatrix<double> >
23BAfilt(const unsigned int d, const double frequency, const unsigned int length)
24{
25 Eigen::VectorXd a(1), b, b1(2), v(3), v2(2);
26 double omega_c = 2 * M_PI * frequency;
27 double t;
28
29 b1 << 1, -1;
30 v << -1, 2, -1;
31
32 for(unsigned int i = 1; i < d; i++){
33 b1 = Convolve(b1, v).eval();
34 }
35
36 v2 << -1, 1;
37 b = Convolve(b1, v2);
38
39 v << 1, 2, 1;
40 a << 1;
41
42 for(unsigned int i = 0; i < d; i++){
43 a = Convolve(a, v).eval();
44 }
45
46 t = std::pow((1 - std::cos(omega_c)) / (1 + std::cos(omega_c)), d);
47 a = (b + t * a).eval();
48
49 Eigen::MatrixXd xa(a.size(), length);
50 Eigen::MatrixXd xb(b.size(), length);
51
52 for(unsigned int i = 0; i < length; i++) {
53 xa.block(0, i, a.size(), 1) = a;
54 xb.block(0, i, b.size(), 1) = b;
55 }
56
57 Eigen::VectorXi dr = Eigen::VectorXi::LinSpaced(2 * d + 1, -d, d);
58 Eigen::SparseMatrix<double> A = Spdiags(xa, dr, length, length);
59 Eigen::SparseMatrix<double> B = Spdiags(xb, dr, length, length);
60
61 return std::tuple< Eigen::SparseMatrix<double>, Eigen::SparseMatrix<double> >(A, B);
62}
63
64}; // unnamed namespace
65
66//
67// Implementation of BaselineBeads() function
68//
69const std::tuple< std::vector<double>, std::vector<double> >
71 const std::vector<double> & y, const unsigned int s, const double frequency, const double r,
72 const double lambda0, const double lambda1, const double lambda2,
73 const unsigned int loop, const double eps, const BeadsPenalty penalty
74)
75{
76 if(y.size() == 0) {
77 throw std::invalid_argument("BaselineBeads(): the length of y is zero.");
78 }
79
80 if(s == 0 || s > 3) {
81 throw std::invalid_argument("BaselineBeads(): s must be 1, 2 or 3.");
82 }
83
84 if(frequency <= 0) {
85 throw std::invalid_argument("BaselineBeads(): non-positive frequency value is given.");
86 }
87
88 if(r <= 0) {
89 throw std::invalid_argument("BaselineBeads(): non-positive r value is given.");
90 }
91
92 if(lambda0 <= 0 || lambda1 <= 0 || lambda2 <= 0) {
93 throw std::invalid_argument("BaselineBeads(): non-positive lambda value is given.");
94 }
95
96 if(loop == 0) {
97 throw std::invalid_argument("BaselineBeads(): loop is zero.");
98 }
99
100 if(eps <= 0) {
101 throw std::invalid_argument("BaselineBeads(): non-positive eps value is given.");
102 }
103
104 const double eps0 = 1e-6;
105 const double eps1 = 1e-6;
106
107 Eigen::VectorXd yy = Eigen::VectorXd::Map(y.data(), y.size());
108
109 std::function<double(const double)> phi, wfun;
110
111 switch(penalty) {
113 phi = [&](const double xx) {
114 double abs_x = std::fabs(xx);
115 return std::sqrt(abs_x * abs_x + eps1);
116 };
117 wfun = [&](const double xx) {
118 return 1 / phi(xx);
119 };
120 break;
122 phi = [&](const double xx) {
123 double abs_x = std::fabs(xx);
124 return abs_x - eps1 * std::log(abs_x + eps1);
125 };
126 wfun = [&](const double xx) {
127 return 1 / (std::fabs(xx) + eps1);
128 };
129 break;
130 default:
131 throw std::invalid_argument("invalid penalty function type.");
132 }
133
134 auto theta = [&](const double xx) {
135 if(xx > eps0) {
136 return xx;
137 }
138 else if(xx < -eps0) {
139 return -r * xx;
140 }
141 else {
142 return (1 + r) * xx * xx / (4 * eps0) + (1 - r) * xx / 2 + eps0 * (1 + r) / 4;
143 }
144 };
145
146 int length = yy.size();
147 auto [ A, B ] = BAfilt(s, frequency, length);
148
149 Eigen::SparseMatrix<double> I, D1, D2;
150
151 I.resize(length, length);
152 I.setIdentity();
153 D1 = Diff(I);
154 D2 = Diff(I, 2);
155
156 Eigen::SparseLU< Eigen::SparseMatrix<double> > solverA, solverQ;
157
158 solverA.compute(A);
159
160 if(solverA.info() != Eigen::Success) {
161 throw std::runtime_error("BaselineBeads(): solverA calculation fails.");
162 }
163
164 Eigen::SparseMatrix<double> BTB = B.transpose() * B;
165 Eigen::VectorXd b = Eigen::VectorXd::Constant(length, (1 - r) / 2);
166 Eigen::VectorXd d = BTB * (solverA.solve(yy)) - lambda0 * A.transpose() * b;
167 Eigen::VectorXd w1 = Eigen::VectorXd::Constant(length - 1, lambda1);
168 Eigen::VectorXd w2 = Eigen::VectorXd::Constant(length - 2, lambda2);
169
170 Eigen::VectorXd x = yy;
171
172 double prev_c = 0.5
173 * (B * solverA.solve(x)).unaryExpr([](const double xx){ return xx * xx; }).sum()
174 + lambda0 * x.unaryExpr(theta).sum()
175 + lambda1 * (D1 * x).unaryExpr(phi).sum()
176 + lambda2 * (D2 * x).unaryExpr(phi).sum();
177
178 for(unsigned int i = 0; i < loop; i++) {
179 Eigen::SparseMatrix<double> L1, L2, G, M;
180
181 L1 = (w1.array() * (D1 * x).unaryExpr(wfun).array()).matrix().asDiagonal();
182 L2 = (w2.array() * (D2 * x).unaryExpr(wfun).array()).matrix().asDiagonal();
183
184 G = x.unaryExpr([&](const double xx) {
185 double z = (-eps0 <= xx && xx <= eps0) ? eps0 : std::fabs(xx);
186 return (1 + r) / 4 / z;
187 }).asDiagonal();
188
189 M = 2 * lambda0 * G + D1.transpose() * L1 * D1 + D2.transpose() * L2 * D2;
190 solverQ.compute(BTB + A.transpose() * M * A);
191
192 if(solverQ.info() != Eigen::Success) {
193 throw std::runtime_error("BaselineBeads(): solverQ calculation fails.");
194 }
195
196 x = A * solverQ.solve(d);
197
198 Eigen::VectorXd a = yy - x;
199 double c = 0.5
200 * (B * solverA.solve(a)).unaryExpr([](const double xx){ return xx * xx; }).sum()
201 + lambda0 * x.unaryExpr(theta).sum()
202 + lambda1 * (D1 * x).unaryExpr(phi).sum()
203 + lambda2 * (D2 * x).unaryExpr(phi).sum();
204
205 if(std::fabs((prev_c - c) / c) < eps) {
206 break;
207 }
208
209 prev_c = c;
210 }
211
212 Eigen::VectorXd f = yy - x;
213 f = (f - B * solverA.solve(f)).eval();
214
215 std::vector<double> f_result(f.size()), x_result(x.size());
216
217 Eigen::VectorXd::Map(f_result.data(), f.size()) = f;
218 Eigen::VectorXd::Map(x_result.data(), x.size()) = x;
219
220 return std::tuple< std::vector<double>, std::vector<double> >(f_result, x_result);
221}
222
223}; // namespace sablib
const std::tuple< std::vector< double >, std::vector< double > > BaselineBeads(const std::vector< double > &y, const unsigned int s, const double frequency, const double r, const double lambda0, const double lambda1, const double lambda2, const unsigned int loop, const double eps, const BeadsPenalty penalty)
Performs baseline estimation and denoising using Sparsity (BEADS).
Definition beads.cpp:70
Baseline estimation and subtraction using Baseline Estimation And Denoising using Sparsity(BEADS).
BeadsPenalty
Penalty types for the BEADS algorithm.(see Table 1 in Duval's paper).
Definition beads.h:25
Python's NumPy-like convolve() function.
const Derived1::PlainObject Convolve(const Eigen::MatrixBase< Derived1 > &v1, const Eigen::MatrixBase< Derived2 > &v2, const ConvolveMode mode=ConvolveMode::Full)
Returns the discrete, linear convolution of two one-dimensional sequences.
Definition convolve.h:35
MATLAB-like diff() function.
const Derived::PlainObject Diff(const Eigen::MatrixBase< Derived > &m0, const int n=1, const Dir dir=Dir::RowWise)
Calculates the n-th discrete difference along the given axis.
Definition diff.h:32
Python's SciPy-like spdiags() function.
Eigen::SparseMatrix< typename Derived::PlainObject::Scalar > Spdiags(const Eigen::MatrixBase< Derived > &data, const Eigen::VectorXi &diags, const int m=-1, const int n=-1)
Returns a sparse matrix with the specified elements on its diagonals.
Definition spdiags.h:30