summaryrefslogtreecommitdiffstats
path: root/src/boost/libs/math/example/daubechies_coefficients.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/boost/libs/math/example/daubechies_coefficients.cpp')
-rw-r--r--src/boost/libs/math/example/daubechies_coefficients.cpp221
1 files changed, 221 insertions, 0 deletions
diff --git a/src/boost/libs/math/example/daubechies_coefficients.cpp b/src/boost/libs/math/example/daubechies_coefficients.cpp
new file mode 100644
index 00000000..4a8c4110
--- /dev/null
+++ b/src/boost/libs/math/example/daubechies_coefficients.cpp
@@ -0,0 +1,221 @@
+/*
+ * Copyright Nick Thompson, 2018
+ * Use, modification and distribution are subject to the
+ * Boost Software License, Version 1.0. (See accompanying file
+ * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+ */
+#include <iostream>
+#include <vector>
+#include <string>
+#include <complex>
+#include <bitset>
+#include <boost/assert.hpp>
+#include <boost/multiprecision/cpp_bin_float.hpp>
+#include <boost/math/constants/constants.hpp>
+#include <boost/math/tools/polynomial.hpp>
+#include <boost/math/tools/roots.hpp>
+#include <boost/math/special_functions/binomial.hpp>
+#include <boost/multiprecision/cpp_complex.hpp>
+#include <boost/multiprecision/complex128.hpp>
+#include <boost/math/quadrature/gauss_kronrod.hpp>
+
+using std::string;
+using boost::math::tools::polynomial;
+using boost::math::binomial_coefficient;
+using boost::math::tools::schroder_iterate;
+using boost::math::tools::halley_iterate;
+using boost::math::tools::newton_raphson_iterate;
+using boost::math::tools::complex_newton;
+using boost::math::constants::half;
+using boost::math::constants::root_two;
+using boost::math::constants::pi;
+using boost::math::quadrature::gauss_kronrod;
+using boost::multiprecision::cpp_bin_float_100;
+using boost::multiprecision::cpp_complex_100;
+
+template<class Complex>
+std::vector<std::pair<Complex, Complex>> find_roots(size_t p)
+{
+ // Initialize the polynomial; see Mallat, A Wavelet Tour of Signal Processing, equation 7.96
+ BOOST_ASSERT(p>0);
+ typedef typename Complex::value_type Real;
+ std::vector<Complex> coeffs(p);
+ for (size_t k = 0; k < coeffs.size(); ++k)
+ {
+ coeffs[k] = Complex(binomial_coefficient<Real>(p-1+k, k), 0);
+ }
+
+ polynomial<Complex> P(std::move(coeffs));
+ polynomial<Complex> Pcopy = P;
+ polynomial<Complex> Pcopy_prime = P.prime();
+ auto orig = [&](Complex z) { return std::make_pair<Complex, Complex>(Pcopy(z), Pcopy_prime(z)); };
+
+ polynomial<Complex> P_prime = P.prime();
+
+ // Polynomial is of degree p-1.
+
+ std::vector<Complex> roots(p-1, {std::numeric_limits<Real>::quiet_NaN(),std::numeric_limits<Real>::quiet_NaN()});
+ size_t i = 0;
+ while(P.size() > 1)
+ {
+ Complex guess = {0.0, 1.0};
+ std::cout << std::setprecision(std::numeric_limits<Real>::digits10+3);
+
+ auto f = [&](Complex x)->std::pair<Complex, Complex>
+ {
+ return std::make_pair<Complex, Complex>(P(x), P_prime(x));
+ };
+
+ Complex r = complex_newton(f, guess);
+ using std::isnan;
+ if(isnan(r.real()))
+ {
+ int i = 50;
+ do {
+ // Try a different guess
+ guess *= Complex(1.0,-1.0);
+ r = complex_newton(f, guess);
+ std::cout << "New guess: " << guess << ", result? " << r << std::endl;
+
+ } while (isnan(r.real()) && i-- > 0);
+
+ if (isnan(r.real()))
+ {
+ std::cout << "Polynomial that killed the process: " << P << std::endl;
+ throw std::logic_error("Newton iteration did not converge");
+ }
+ }
+ // Refine r with the original function.
+ // We only use the polynomial division to ensure we don't get the same root over and over.
+ // However, the division induces error which can grow quickly-or slowly! See Numerical Recipes, section 9.5.1.
+ r = complex_newton(orig, r);
+ if (isnan(r.real()))
+ {
+ throw std::logic_error("Found a root for the deflated polynomial which is not a root for the original. Indicative of catastrophic numerical error.");
+ }
+ // Test the root:
+ using std::sqrt;
+ Real tol = sqrt(sqrt(std::numeric_limits<Real>::epsilon()));
+ if (norm(Pcopy(r)) > tol)
+ {
+ std::cout << "This is a bad root: P" << r << " = " << Pcopy(r) << std::endl;
+ std::cout << "Reduced polynomial leading to bad root: " << P << std::endl;
+ throw std::logic_error("Donezo.");
+ }
+
+ BOOST_ASSERT(i < roots.size());
+ roots[i] = r;
+ ++i;
+ polynomial<Complex> q{-r, {1,0}};
+ // This optimization breaks at p = 11. I have no clue why.
+ // Unfortunate, because I expect it to be considerably more stable than
+ // repeatedly dividing by the complex root.
+ /*polynomial<Complex> q;
+ if (r.imag() > sqrt(std::numeric_limits<Real>::epsilon()))
+ {
+ // Then the complex conjugate is also a root:
+ using std::conj;
+ using std::norm;
+ BOOST_ASSERT(i < roots.size());
+ roots[i] = conj(r);
+ ++i;
+ q = polynomial<Complex>({{norm(r), 0}, {-2*r.real(),0}, {1,0}});
+ }
+ else
+ {
+ // The imaginary part is numerical noise:
+ r.imag() = 0;
+ q = polynomial<Complex>({-r, {1,0}});
+ }*/
+
+
+ auto PR = quotient_remainder(P, q);
+ // I should validate that the remainder is small, but . . .
+ //std::cout << "Remainder = " << PR.second<< std::endl;
+
+ P = PR.first;
+ P_prime = P.prime();
+ }
+
+ std::vector<std::pair<Complex, Complex>> Qroots(p-1);
+ for (size_t i = 0; i < Qroots.size(); ++i)
+ {
+ Complex y = roots[i];
+ Complex z1 = static_cast<Complex>(1) - static_cast<Complex>(2)*y + static_cast<Complex>(2)*sqrt(y*(y-static_cast<Complex>(1)));
+ Complex z2 = static_cast<Complex>(1) - static_cast<Complex>(2)*y - static_cast<Complex>(2)*sqrt(y*(y-static_cast<Complex>(1)));
+ Qroots[i] = {z1, z2};
+ }
+
+ return Qroots;
+}
+
+template<class Complex>
+std::vector<typename Complex::value_type> daubechies_coefficients(std::vector<std::pair<Complex, Complex>> const & Qroots)
+{
+ typedef typename Complex::value_type Real;
+ size_t p = Qroots.size() + 1;
+ // Choose the minimum abs root; see Mallat, discussion just after equation 7.98
+ std::vector<Complex> chosen_roots(p-1);
+ for (size_t i = 0; i < p - 1; ++i)
+ {
+ if(norm(Qroots[i].first) <= 1)
+ {
+ chosen_roots[i] = Qroots[i].first;
+ }
+ else
+ {
+ BOOST_ASSERT(norm(Qroots[i].second) <= 1);
+ chosen_roots[i] = Qroots[i].second;
+ }
+ }
+
+ polynomial<Complex> R{1};
+ for (size_t i = 0; i < p-1; ++i)
+ {
+ Complex ak = chosen_roots[i];
+ R *= polynomial<Complex>({-ak/(static_cast<Complex>(1)-ak), static_cast<Complex>(1)/(static_cast<Complex>(1)-ak)});
+ }
+ polynomial<Complex> a{{half<Real>(), 0}, {half<Real>(),0}};
+ polynomial<Complex> poly = root_two<Real>()*pow(a, p)*R;
+ std::vector<Complex> result = poly.data();
+ // If we reverse, we get the Numerical Recipes and Daubechies convention.
+ // If we don't reverse, we get the Pywavelets and Mallat convention.
+ // I believe this is because of the sign convention on the DFT, which differs between Daubechies and Mallat.
+ // You implement a dot product in Daubechies/NR convention, and a convolution in PyWavelets/Mallat convention.
+ // I won't reverse so I can spot check against Pywavelets: http://wavelets.pybytes.com/wavelet/
+ //std::reverse(result.begin(), result.end());
+ std::vector<Real> h(result.size());
+ for (size_t i = 0; i < result.size(); ++i)
+ {
+ Complex r = result[i];
+ BOOST_ASSERT(r.imag() < sqrt(std::numeric_limits<Real>::epsilon()));
+ h[i] = r.real();
+ }
+
+ // Quick sanity check: We could check all vanishing moments, but that sum is horribly ill-conditioned too!
+ Real sum = 0;
+ Real scale = 0;
+ for (size_t i = 0; i < h.size(); ++i)
+ {
+ sum += h[i];
+ scale += h[i]*h[i];
+ }
+ BOOST_ASSERT(abs(scale -1) < sqrt(std::numeric_limits<Real>::epsilon()));
+ BOOST_ASSERT(abs(sum - root_two<Real>()) < sqrt(std::numeric_limits<Real>::epsilon()));
+ return h;
+}
+
+int main()
+{
+ typedef boost::multiprecision::cpp_complex<100> Complex;
+ for(size_t p = 1; p < 200; ++p)
+ {
+ auto roots = find_roots<Complex>(p);
+ auto h = daubechies_coefficients(roots);
+ std::cout << "h_" << p << "[] = {";
+ for (auto& x : h) {
+ std::cout << x << ", ";
+ }
+ std::cout << "} // = h_" << p << "\n\n\n\n";
+ }
+}