summaryrefslogtreecommitdiff
path: root/stereographic.hpp
blob: a3f2cc6f0d497c8bcdb6c4fb30c9c23c7aa85c53 (plain)
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
105
106
107
108
109
110
111
112
113
114
115
116
117
#include <eigen3/Eigen/Cholesky>

#include "Eigen/src/Core/util/Meta.h"
#include "p-spin.hpp"
#include "unsupported/Eigen/CXX11/src/Tensor/TensorMeta.h"

Vector stereographicToEuclidean(const Vector& ζ) {
  unsigned N = ζ.size() + 1;
  Vector z(N);
  Scalar a = ζ.transpose() * ζ;
  Scalar b = 2 * sqrt(N) / (1.0 + a);

  for (unsigned i = 0; i < N - 1; i++) {
    z(i) = b * ζ(i);
  }

  z(N - 1) = b * (a - 1.0) / 2.0;

  return z;
}

Vector euclideanToStereographic(const Vector& z) {
  unsigned N = z.size();
  Vector ζ(N - 1);

  for (unsigned i = 0; i < N - 1; i++) {
    ζ(i) = z(i) / (sqrt(N) - z(N - 1));
  }

  return ζ;
}

Matrix stereographicJacobian(const Vector& ζ) {
  unsigned N = ζ.size() + 1;
  Matrix J(N - 1, N);

  Scalar b = ζ.transpose() * ζ;

  for (unsigned i = 0; i < N - 1; i++) {
    for (unsigned j = 0; j < N - 1; j++) {
      J(i, j) = - 4 * sqrt(N) * ζ(i) * ζ(j) / pow(1.0 + b, 2);
      if (i == j) {
        J(i, j) += 2 * sqrt(N) * (1.0 + b) / pow(1.0 + b, 2);
      }
    }

    J(i, N - 1) = 4.0 * sqrt(N) * ζ(i) / pow(1.0 + b, 2);
  }

  return J;
}

Matrix stereographicMetric(const Matrix& J) {
  return J * J.adjoint();
}

// Gives the Christoffel symbol, with Γ_(i1, i2)^(i3).
Eigen::Tensor<Scalar, 3> stereographicChristoffel(const Vector& ζ, const Matrix& gInvJacConj) {
  unsigned N = ζ.size() + 1;
  Eigen::Tensor<Scalar, 3> dJ(N - 1, N - 1, N);

  Scalar b = 1.0 + (Scalar)(ζ.transpose() * ζ);

  for (unsigned i = 0; i < N - 1; i++) {
    for (unsigned j = 0; j < N - 1; j++) {
      for (unsigned k = 0; k < N - 1; k++) {
        dJ(i, j, k) = 16 * sqrt(N) * ζ(i) * ζ(j) * ζ(k) / pow(b, 3);
        if (i == j) {
          dJ(i, j, k) -= 4 * sqrt(N) * ζ(k) / pow(b, 2);
        }
        if (i == k) {
          dJ(i, j, k) -= 4 * sqrt(N) * ζ(j) / pow(b, 2);
        }
        if (j == k) {
          dJ(i, j, k) -= 4 * sqrt(N) * ζ(i) / pow(b, 2);
        }
      }
      dJ(i, j, N - 1) = - 16 * sqrt(N) * ζ(i) * ζ(j) / pow(b, 3);
      if (i == j) {
        dJ(i, j, N - 1) += 4 * sqrt(N) * ζ(i) / pow(b, 2);
      }
    }
  }

  std::array<Eigen::IndexPair<int>, 1> ip = {Eigen::IndexPair<int>(2, 1)};

  return dJ.contract(Eigen::TensorMap<Eigen::Tensor<const Scalar, 2>>(gInvJacConj.data(), N - 1, N), ip);
}

std::tuple<Scalar, Vector, Matrix> stereographicHamGradHess(const Tensor& J, const Vector& ζ) {
  Vector grad;
  Matrix hess;

  Matrix jacobian = stereographicJacobian(ζ);
  Vector z = stereographicToEuclidean(ζ);

  std::complex<double> hamiltonian;
  Vector gradZ;
  Matrix hessZ;
  std::tie(hamiltonian, gradZ, hessZ) = hamGradHess(J, z);

  Matrix metric = stereographicMetric(jacobian);

  grad = metric.llt().solve(jacobian) * gradZ;

  /* This is much slower to calculate than the marginal speedup it offers...
  Eigen::Tensor<Scalar, 3> Γ = stereographicChristoffel(ζ, gInvJac.conjugate());
  Vector df = jacobian * gradZ;
  Eigen::Tensor<Scalar, 1> dfT = Eigen::TensorMap<Eigen::Tensor<Scalar, 1>>(df.data(), {df.size()});
  std::array<Eigen::IndexPair<int>, 1> ip = {Eigen::IndexPair<int>(2, 0)};
  Eigen::Tensor<Scalar, 2> H2 = Γ.contract(dfT, ip);
  */

  hess = jacobian * hessZ * jacobian.transpose(); // - Eigen::Map<Matrix>(H2.data(), ζ.size(), ζ.size());

  return {hamiltonian, grad, hess};
}