38#define _USE_MATH_DEFINES
53const std::tuple< Eigen::SparseMatrix<double>, Eigen::SparseMatrix<double> >
54BAfilt(
const unsigned int d,
const double frequency,
const unsigned int length)
56 Eigen::VectorXd a(1), b, b1(2), v(3), v2(2);
57 double omega_c = 2 * M_PI * frequency;
63 for(
unsigned int i = 1; i < d; i++){
73 for(
unsigned int i = 0; i < d; i++){
77 t = std::pow((1 - std::cos(omega_c)) / (1 + std::cos(omega_c)), d);
78 a = (b + t * a).eval();
80 Eigen::MatrixXd xa(a.size(), length);
81 Eigen::MatrixXd xb(b.size(), length);
83 for(
unsigned int i = 0; i < length; i++) {
84 xa.block(0, i, a.size(), 1) = a;
85 xb.block(0, i, b.size(), 1) = b;
88 Eigen::VectorXi dr = Eigen::VectorXi::LinSpaced(2 * d + 1, -d, d);
89 Eigen::SparseMatrix<double> A =
Spdiags(xa, dr, length, length);
90 Eigen::SparseMatrix<double> B =
Spdiags(xb, dr, length, length);
92 return std::tuple< Eigen::SparseMatrix<double>, Eigen::SparseMatrix<double> >(A, B);
100const std::tuple< std::vector<double>, std::vector<double> >
102 const std::vector<double> & y,
const unsigned int s,
const double frequency,
const double r,
103 const double lambda0,
const double lambda1,
const double lambda2,
104 const unsigned int loop,
const double eps,
const BeadsPenalty penalty
108 throw std::invalid_argument(
"BaselineBeads(): the length of y is zero.");
111 if(s == 0 || s > 3) {
112 throw std::invalid_argument(
"BaselineBeads(): s must be 1, 2 or 3.");
116 throw std::invalid_argument(
"BaselineBeads(): non-positive frequency value is given.");
120 throw std::invalid_argument(
"BaselineBeads(): non-positive r value is given.");
123 if(lambda0 <= 0 || lambda1 <= 0 || lambda2 <= 0) {
124 throw std::invalid_argument(
"BaselineBeads(): non-positive lambda value is given.");
128 throw std::invalid_argument(
"BaselineBeads(): loop is zero.");
132 throw std::invalid_argument(
"BaselineBeads(): non-positive eps value is given.");
135 const double eps0 = 1e-6;
136 const double eps1 = 1e-6;
138 Eigen::VectorXd yy = Eigen::VectorXd::Map(y.data(), y.size());
140 std::function<double(
const double)> phi, wfun;
144 phi = [&](
const double xx) {
145 double abs_x = std::fabs(xx);
146 return std::sqrt(abs_x * abs_x + eps1);
148 wfun = [&](
const double xx) {
153 phi = [&](
const double xx) {
154 double abs_x = std::fabs(xx);
155 return abs_x - eps1 * std::log(abs_x + eps1);
157 wfun = [&](
const double xx) {
158 return 1 / (std::fabs(xx) + eps1);
162 throw std::invalid_argument(
"invalid penalty function type.");
165 auto theta = [&](
const double xx) {
169 else if(xx < -eps0) {
173 return (1 + r) * xx * xx / (4 * eps0) + (1 - r) * xx / 2 + eps0 * (1 + r) / 4;
177 int length = yy.size();
178 auto [ A, B ] = BAfilt(s, frequency, length);
180 Eigen::SparseMatrix<double> I, D1, D2;
182 I.resize(length, length);
187 Eigen::SparseLU< Eigen::SparseMatrix<double> > solverA, solverQ;
191 if(solverA.info() != Eigen::Success) {
192 throw std::runtime_error(
"BaselineBeads(): solverA calculation fails.");
195 Eigen::SparseMatrix<double> BTB = B.transpose() * B;
196 Eigen::VectorXd b = Eigen::VectorXd::Constant(length, (1 - r) / 2);
197 Eigen::VectorXd d = BTB * (solverA.solve(yy)) - lambda0 * A.transpose() * b;
198 Eigen::VectorXd w1 = Eigen::VectorXd::Constant(length - 1, lambda1);
199 Eigen::VectorXd w2 = Eigen::VectorXd::Constant(length - 2, lambda2);
201 Eigen::VectorXd x = yy;
204 * (B * solverA.solve(x)).array().square().sum()
205 + lambda0 * x.unaryExpr(theta).sum()
206 + lambda1 * (D1 * x).unaryExpr(phi).sum()
207 + lambda2 * (D2 * x).unaryExpr(phi).sum();
209 for(
unsigned int i = 0; i < loop; i++) {
210 Eigen::SparseMatrix<double> L1, L2, G, M;
212 L1 = (w1.array() * (D1 * x).unaryExpr(wfun).array()).matrix().asDiagonal();
213 L2 = (w2.array() * (D2 * x).unaryExpr(wfun).array()).matrix().asDiagonal();
215 G = x.unaryExpr([&](
const double xx) {
216 double z = (-eps0 <= xx && xx <= eps0) ? eps0 : std::fabs(xx);
217 return (1 + r) / 4 / z;
220 M = 2 * lambda0 * G + D1.transpose() * L1 * D1 + D2.transpose() * L2 * D2;
221 solverQ.compute(BTB + A.transpose() * M * A);
223 if(solverQ.info() != Eigen::Success) {
224 throw std::runtime_error(
"BaselineBeads(): solverQ calculation fails.");
227 x = A * solverQ.solve(d);
229 Eigen::VectorXd a = yy - x;
231 * (B * solverA.solve(a)).array().square().sum()
232 + lambda0 * x.unaryExpr(theta).sum()
233 + lambda1 * (D1 * x).unaryExpr(phi).sum()
234 + lambda2 * (D2 * x).unaryExpr(phi).sum();
236 if(std::fabs((prev_c - c) / c) < eps) {
243 Eigen::VectorXd f = yy - x;
244 f = (f - B * solverA.solve(f)).eval();
246 std::vector<double> f_result(f.size()), x_result(x.size());
248 Eigen::VectorXd::Map(f_result.data(), f.size()) = f;
249 Eigen::VectorXd::Map(x_result.data(), x.size()) = x;
251 return std::tuple< std::vector<double>, std::vector<double> >(f_result, x_result);
260 throw std::invalid_argument(
"BeadsExpandBoundaries(): the length of y is zero.");
263 std::vector<double> result(y.size() + 2 * n);
265 auto it1 = result.begin();
266 auto it2 = result.rbegin();
267 for(
unsigned int i = 0; i < n; i++, it1++, it2++) {
268 double x = (double)i / (n - 1);
269 double t = std::sqrt(x);
270 *it1 = y.front() * t;
274 std::copy(y.begin(), y.end(), result.begin() + n);
285 throw std::invalid_argument(
"BeadsTrimBoundaries(): the length of y is zero.");
288 if(y.size() < 2 * n) {
289 throw std::invalid_argument(
"BeadsTrimBoundaries(): the length of y is too short.");
292 std::vector<double> result(y.size() - 2 * n);
293 auto it = y.begin() + n;
295 std::copy(it, it + result.size(), result.begin());
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).
const std::vector< double > BeadsTrimBoundaries(const std::vector< double > &y, const unsigned int n)
Trims the expanded signal boundaries.
const std::vector< double > BeadsExpandBoundaries(const std::vector< double > &y, const unsigned int n)
Expands the signal boundaries by padding with a tapered sequence.
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).
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.
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.
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.