File: power_method.cc

package info (click to toggle)
sopt 5.0.1%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 6,704 kB
  • sloc: cpp: 13,620; xml: 182; makefile: 6
file content (104 lines) | stat: -rw-r--r-- 4,132 bytes parent folder | download | duplicates (2)
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
#include <numeric>
#include <random>
#include <Eigen/Eigenvalues>
#include "catch2/catch_all.hpp"

#include "sopt/power_method.h"

using Catch::Approx;

TEST_CASE("Power Method") {
  using namespace sopt;
  using Scalar = t_real;
  auto constexpr N = 10;

  Eigen::EigenSolver<Matrix<Scalar>> es;
  Matrix<Scalar> A(N, N);
  std::iota(A.data(), A.data() + A.size(), 0);
  es.compute(A.adjoint() * A, true);

  auto const eigenvalues = es.eigenvalues();
  auto const eigenvectors = es.eigenvectors();
  Eigen::DenseIndex index;
  (eigenvalues.transpose() * eigenvalues).real().maxCoeff(&index);
  auto const eigenvalue = eigenvalues(index);
  Vector<t_complex> const eigenvector = eigenvectors.col(index);
  // Create input vector close to solution
  Vector<t_complex> const input = eigenvector * 1e-4 + Vector<t_complex>::Random(N);
  auto const pm = algorithm::PowerMethod<t_complex>().tolerance(1e-12);

  SECTION("AtA") {
    auto const lt = linear_transform(A.cast<t_complex>());
    auto const result = pm.AtA(lt, input);
    CHECK(result.good);
    CAPTURE(eigenvalue);
    CAPTURE(result.magnitude);
    CAPTURE(result.eigenvector.transpose() * eigenvector);
    CHECK(std::abs(result.magnitude - std::abs(eigenvalue)) < 1e-8);
  }

  SECTION("A") {
    auto const result = pm((A.adjoint() * A).cast<t_complex>(), input);
    CHECK(result.good);
    CAPTURE(eigenvalue);
    CAPTURE(result.magnitude);
    CAPTURE(result.eigenvector.transpose() * eigenvector);
    CHECK(std::abs(result.magnitude - std::abs(eigenvalue)) < 1e-8);
  }
}

TEST_CASE("Power Method (from Purify)") {
  using namespace sopt;
  using Scalar = t_real;
  auto constexpr N = 10;
  constexpr t_uint power_iters = 100000;
  constexpr t_real power_tol = 1e-6;
  Eigen::EigenSolver<Matrix<Scalar>> es;
  Matrix<Scalar> A(N, N);
  std::iota(A.data(), A.data() + A.size(), 0);
  es.compute(A.adjoint() * A, true);

  auto const eigenvalues = es.eigenvalues();
  auto const eigenvectors = es.eigenvectors();
  Eigen::DenseIndex index;
  (eigenvalues.transpose() * eigenvalues).real().maxCoeff(&index);
  auto const eigenvalue = eigenvalues(index);
  Vector<t_complex> const eigenvector = eigenvectors.col(index);
  // Create input vector close to solution
  Vector<t_complex> const input = eigenvector * 1e-4 + Vector<t_complex>::Random(N);

  const auto forward = [=](Vector<t_complex> &out, const Vector<t_complex> &in) { out = A * in; };
  const auto backward = [=](Vector<t_complex> &out, const Vector<t_complex> &in) {
    out = A.adjoint() * in;
  };

  SECTION("Power Method") {
    const sopt::LinearTransform<Vector<t_complex>> op = {forward, backward};
    auto const result =
        algorithm::power_method<Vector<t_complex>>(op, power_iters, power_tol, input);
    const t_real op_norm = std::get<0>(result);
    const Vector<t_complex> op_eigen_vector_c = std::get<1>(result);
    CHECK(op_eigen_vector_c.unaryExpr([](t_complex x) { return std::arg(x); })
              .isApprox(Vector<t_complex>::Constant(op_eigen_vector_c.size(),
                                                    std::arg(op_eigen_vector_c(0))),
                        power_tol));
    const Vector<t_complex> op_eigen_vector =
        op_eigen_vector_c * std::polar<t_real>(1, -std::arg(op_eigen_vector_c(0)));
    CAPTURE(eigenvalue);
    CAPTURE(op_norm * op_norm);
    CAPTURE(op_eigen_vector);
    CAPTURE(eigenvector);
    CHECK(op_norm == Approx(std::sqrt(std::abs(eigenvalue))).epsilon(power_tol));
    CHECK(op_eigen_vector.isApprox(eigenvector, power_tol));
    auto const norm_operator_result =
        algorithm::normalise_operator<Vector<t_complex>>(op, power_iters, power_tol, input);
    CHECK(std::get<0>(norm_operator_result) == Approx(op_norm).epsilon(1e-12));
    CHECK(std::get<1>(norm_operator_result).isApprox(op_eigen_vector_c, 1e-12));
    CHECK(((op * input) / op_norm)
              .eval()
              .isApprox((std::get<2>(norm_operator_result) * input).eval(), 1e-12));
    CHECK(((op.adjoint() * input) / op_norm)
              .eval()
              .isApprox((std::get<2>(norm_operator_result).adjoint() * input).eval(), 1e-12));
  }
}