20#include "../../include/srfm/tensor/christoffel_n.hpp"
35ChristoffelN::metric_deriv(
int alpha,
int mu,
int nu,
36 const Eigen::VectorXd& x)
const noexcept {
37 const int D = manifold_.dim();
39 if (alpha < 0 || alpha >= D) {
return std::nullopt; }
40 if (mu < 0 || mu >= D) {
return std::nullopt; }
41 if (nu < 0 || nu >= D) {
return std::nullopt; }
42 if (x.size() != D) {
return std::nullopt; }
45 Eigen::VectorXd xp = x;
47 auto gp = manifold_.metric_at(xp);
48 if (!gp) {
return std::nullopt; }
51 Eigen::VectorXd xm = x;
53 auto gm = manifold_.metric_at(xm);
54 if (!gm) {
return std::nullopt; }
56 return ((*gp)(mu, nu) - (*gm)(mu, nu)) / (2.0 * FD_STEP);
59std::optional<Eigen::MatrixXd>
60ChristoffelN::inv_metric_at(
const Eigen::VectorXd& x)
const noexcept {
61 return manifold_.inverse_metric_at(x);
68 const Eigen::VectorXd& x)
const noexcept {
69 const int D = manifold_.dim();
72 if (lambda < 0 || lambda >= D) {
return std::nullopt; }
73 if (mu < 0 || mu >= D) {
return std::nullopt; }
74 if (nu < 0 || nu >= D) {
return std::nullopt; }
75 if (x.size() != D) {
return std::nullopt; }
78 auto g_inv_opt = inv_metric_at(x);
79 if (!g_inv_opt) {
return std::nullopt; }
80 const Eigen::MatrixXd& g_inv = *g_inv_opt;
84 for (
int sigma = 0; sigma < D; ++sigma) {
85 auto d_mu_g_nu_sigma = metric_deriv(mu, nu, sigma, x);
86 auto d_nu_g_mu_sigma = metric_deriv(nu, mu, sigma, x);
87 auto d_sigma_g_mu_nu = metric_deriv(sigma, mu, nu, x);
89 if (!d_mu_g_nu_sigma || !d_nu_g_mu_sigma || !d_sigma_g_mu_nu) {
93 double bracket = *d_mu_g_nu_sigma + *d_nu_g_mu_sigma - *d_sigma_g_mu_nu;
94 result += g_inv(lambda, sigma) * bracket;
100std::optional<std::vector<std::vector<std::vector<double>>>>
102 const int D = manifold_.dim();
103 if (x.size() != D) {
return std::nullopt; }
106 auto g_inv_opt = inv_metric_at(x);
107 if (!g_inv_opt) {
return std::nullopt; }
108 const Eigen::MatrixXd& g_inv = *g_inv_opt;
112 std::vector<std::vector<std::vector<double>>> dg(
113 D, std::vector<std::vector<double>>(D, std::vector<double>(D, 0.0)));
118 for (
int alpha = 0; alpha < D; ++alpha) {
119 Eigen::VectorXd xp = x;
120 xp(alpha) += FD_STEP;
121 auto gp = manifold_.metric_at(xp);
122 if (!gp) {
return std::nullopt; }
124 Eigen::VectorXd xm = x;
125 xm(alpha) -= FD_STEP;
126 auto gm = manifold_.metric_at(xm);
127 if (!gm) {
return std::nullopt; }
129 for (
int mu = 0; mu < D; ++mu) {
130 for (
int nu = 0; nu < D; ++nu) {
132 ((*gp)(mu, nu) - (*gm)(mu, nu)) / (2.0 * FD_STEP);
138 std::vector<std::vector<std::vector<double>>> gamma(
139 D, std::vector<std::vector<double>>(D, std::vector<double>(D, 0.0)));
141 for (
int lambda = 0; lambda < D; ++lambda) {
142 for (
int mu = 0; mu < D; ++mu) {
143 for (
int nu = 0; nu < D; ++nu) {
145 for (
int sigma = 0; sigma < D; ++sigma) {
146 double bracket = dg[mu][nu][sigma]
149 val += g_inv(lambda, sigma) * bracket;
151 gamma[lambda][mu][nu] = 0.5 * val;
160 double tol)
const noexcept {
161 const int D = manifold_.dim();
162 if (x.size() != D) {
return false; }
164 for (
int lambda = 0; lambda < D; ++lambda) {
165 for (
int mu = 0; mu < D; ++mu) {
166 for (
int nu = mu + 1; nu < D; ++nu) {
167 auto s_mn = symbol(lambda, mu, nu, x);
168 auto s_nm = symbol(lambda, nu, mu, x);
169 if (!s_mn || !s_nm) {
return false; }
170 if (std::abs(*s_mn - *s_nm) > tol) {
return false; }
bool verify_symmetry(const Eigen::VectorXd &x, double tol=1e-10) const noexcept
Verify that Γ^λ_μν = Γ^λ_νμ for all indices at point x.
std::optional< std::vector< std::vector< std::vector< double > > > > all_symbols(const Eigen::VectorXd &x) const noexcept
Compute all Christoffel symbols at point x.
ChristoffelN(const NAssetManifold &manifold) noexcept
Construct from a manifold reference.
std::optional< double > symbol(int lambda, int mu, int nu, const Eigen::VectorXd &x) const noexcept
Compute a single Christoffel symbol Γ^lambda_mu_nu at point x.
(N+1)-dimensional Lorentzian manifold for N financial assets.