blob: 265cb29b10ecdabc776a6ee7523a253baa3e1937 [file]
// SPDX-FileCopyrightText: The Eigen Authors
// SPDX-License-Identifier: MPL-2.0
// Deconvolution as a rectangular Toeplitz least-squares problem. A signal s of
// length n recorded through a causal kernel h of length k is the full
// convolution y = T s, with T the (n + k - 1) x n Toeplitz matrix whose first
// column holds h. LSMR recovers s from y without forming T: every iteration
// applies T and T^T through the FFT, in O(p log p) for the embedding length
// p >= n + k - 1, the adjoint reusing the embedding symbol. For the kernel
// h_j = a^j, |a| < 1, the system is well conditioned, kappa ~ (1 + a)/(1 - a),
// so noise in y passes into s amplified by at most that factor. The dense QR
// of the materialized matrix gives the same solution at O(m n^2).
#include <contrib/Eigen/StructuredMatrices>
#include <cmath>
#include <iostream>
#include <random>
using namespace Eigen;
int main() {
const int n = 1000, k = 50, m = n + k - 1;
const double a = 0.9;
// The signal: two Gaussian pulses and a step.
VectorXd s(n);
for (int i = 0; i < n; ++i)
s[i] = std::exp(-std::pow((i - 300) / 20.0, 2)) + 0.5 * std::exp(-std::pow((i - 450) / 10.0, 2)) +
(i > 700 && i < 850 ? 0.8 : 0.0);
VectorXd column = VectorXd::Zero(m), row = VectorXd::Zero(n);
for (int j = 0; j < k; ++j) column[j] = std::pow(a, j);
row[0] = column[0];
const auto T = makeToeplitz(column, row); // m x n
const VectorXd y = T * s;
LSMR<std::decay_t<decltype(T)>, IdentityPreconditioner> lsmr(T);
lsmr.setTolerance(1e-12);
const VectorXd recovered = lsmr.solve(y);
std::cout << "LSMR, exact data: " << lsmr.iterations() << " iterations, max error "
<< (recovered - s).cwiseAbs().maxCoeff() << "\n";
// 1% noise from a generator whose sequence the C++ standard fixes.
std::minstd_rand generator(1);
VectorXd noisy = y;
for (int i = 0; i < m; ++i) noisy[i] += 0.01 * (2.0 * generator() / generator.max() - 1);
const VectorXd fromNoisy = lsmr.solve(noisy);
std::cout << "LSMR, noisy data: " << lsmr.iterations() << " iterations, max error "
<< (fromNoisy - s).cwiseAbs().maxCoeff() << "\n";
const MatrixXd dense = T;
std::cout << "dense QR of the " << m << " x " << n << " matrix, noisy data: max error "
<< (dense.householderQr().solve(noisy) - s).cwiseAbs().maxCoeff() << "\n";
}