Special Relativity in Financial Modeling 1.0.0
Lorentz transforms, spacetime classification, and geodesic price paths for quantitative finance
Loading...
Searching...
No Matches
geodesic_n.cpp
Go to the documentation of this file.
1/**
2 * @file geodesic_n.cpp
3 * @brief Implementation of GeodesicSolverN.
4 *
5 * See include/srfm/tensor/geodesic_n.hpp for the public API contract.
6 *
7 * ## RK4 scheme
8 * State s = (x, u). Derivative f(s) = (u, acc) where
9 *
10 * acc^λ = -Σ_{μν} Γ^λ_μν u^μ u^ν
11 *
12 * Standard RK4:
13 * k1 = f(s)
14 * k2 = f(s + h/2 · k1)
15 * k3 = f(s + h/2 · k2)
16 * k4 = f(s + h · k3)
17 * s_new = s + (h/6)(k1 + 2k2 + 2k3 + k4)
18 */
19
20#include "../../include/srfm/tensor/geodesic_n.hpp"
21
22#include <cmath>
23
24namespace srfm::tensor {
25
26// ── Constructor ───────────────────────────────────────────────────────────────
27
29 const ChristoffelN& christoffel) noexcept
30 : manifold_(manifold)
31 , christoffel_(christoffel)
32{}
33
34// ── Internal helpers ──────────────────────────────────────────────────────────
35
36std::optional<std::pair<Eigen::VectorXd, Eigen::VectorXd>>
37GeodesicSolverN::rhs(const GeodesicState& s) const noexcept {
38 const int D = manifold_.dim();
39
40 // dx/dτ = u.
41 Eigen::VectorXd dx = s.u;
42
43 // du^λ/dτ = -Σ_{μν} Γ^λ_μν u^μ u^ν.
44 Eigen::VectorXd du = Eigen::VectorXd::Zero(D);
45
46 // Evaluate the whole Christoffel tensor once per right-hand side. Calling
47 // symbol() per (lambda, mu, nu) recomputed the inverse metric and 3*D
48 // finite differences for each of the D^3 entries, O(D^6) metric
49 // evaluations per step, which never finished for D = 51.
50 auto gamma_opt = christoffel_.all_symbols(s.x);
51 if (!gamma_opt) { return std::nullopt; }
52 const auto& gamma = *gamma_opt;
53
54 for (int lambda = 0; lambda < D; ++lambda) {
55 double acc = 0.0;
56 for (int mu = 0; mu < D; ++mu) {
57 for (int nu = 0; nu < D; ++nu) {
58 acc += gamma[lambda][mu][nu] * s.u(mu) * s.u(nu);
59 }
60 }
61 du(lambda) = -acc;
62 }
63
64 return std::make_pair(std::move(dx), std::move(du));
65}
66
67GeodesicState
68GeodesicSolverN::advance(const GeodesicState& s,
69 const Eigen::VectorXd& dx,
70 const Eigen::VectorXd& du,
71 double scale) noexcept {
72 GeodesicState result;
73 result.x = s.x + scale * dx;
74 result.u = s.u + scale * du;
75 return result;
76}
77
78// ── Public integration ────────────────────────────────────────────────────────
79
80std::optional<GeodesicState>
81GeodesicSolverN::step(const GeodesicState& s, double dtau) const noexcept {
82 // k1.
83 auto f1 = rhs(s);
84 if (!f1) { return std::nullopt; }
85 const auto& [dx1, du1] = *f1;
86
87 // k2.
88 GeodesicState s2 = advance(s, dx1, du1, dtau * 0.5);
89 auto f2 = rhs(s2);
90 if (!f2) { return std::nullopt; }
91 const auto& [dx2, du2] = *f2;
92
93 // k3.
94 GeodesicState s3 = advance(s, dx2, du2, dtau * 0.5);
95 auto f3 = rhs(s3);
96 if (!f3) { return std::nullopt; }
97 const auto& [dx3, du3] = *f3;
98
99 // k4.
100 GeodesicState s4 = advance(s, dx3, du3, dtau);
101 auto f4 = rhs(s4);
102 if (!f4) { return std::nullopt; }
103 const auto& [dx4, du4] = *f4;
104
105 // Combine: s_new = s + (h/6)(k1 + 2k2 + 2k3 + k4).
106 GeodesicState result;
107 result.x = s.x + (dtau / 6.0) * (dx1 + 2.0 * dx2 + 2.0 * dx3 + dx4);
108 result.u = s.u + (dtau / 6.0) * (du1 + 2.0 * du2 + 2.0 * du3 + du4);
109
110 return result;
111}
112
113std::optional<std::vector<GeodesicState>>
115 double dtau,
116 int n_steps) const noexcept {
117 if (n_steps <= 0) { return std::vector<GeodesicState>{}; }
118
119 std::vector<GeodesicState> traj;
120 traj.reserve(static_cast<std::size_t>(n_steps));
121
122 GeodesicState current = std::move(initial);
123 for (int i = 0; i < n_steps; ++i) {
124 auto next = step(current, dtau);
125 if (!next) { return std::nullopt; }
126 current = std::move(*next);
127 traj.push_back(current);
128 }
129
130 return traj;
131}
132
133std::optional<double>
135 const std::vector<GeodesicState>& traj) const noexcept {
136 if (traj.size() < 2) { return std::nullopt; }
137
138 const Eigen::VectorXd& x0 = traj.front().x;
139 const Eigen::VectorXd& x1 = traj.back().x;
140
141 const int N = static_cast<int>(traj.size());
142 double max_dev = 0.0;
143
144 for (int i = 1; i < N - 1; ++i) {
145 // Parameter t ∈ (0,1) for this point along the trajectory.
146 double t = static_cast<double>(i) / static_cast<double>(N - 1);
147
148 // Point on the straight line from x0 to x1.
149 Eigen::VectorXd x_line = x0 + t * (x1 - x0);
150
151 // Distance in full coordinate space.
152 double dev = (traj[static_cast<std::size_t>(i)].x - x_line).norm();
153 if (dev > max_dev) {
154 max_dev = dev;
155 }
156 }
157
158 return max_dev;
159}
160
161} // namespace srfm::tensor
Computes Christoffel symbols Γ^λ_μν for an NAssetManifold.
GeodesicSolverN(const NAssetManifold &manifold, const ChristoffelN &christoffel) noexcept
Construct from manifold and Christoffel symbol provider.
std::optional< std::vector< GeodesicState > > integrate(GeodesicState initial, double dtau, int n_steps) const noexcept
Integrate the geodesic for n_steps steps of size dtau.
std::optional< double > geodesic_deviation(const std::vector< GeodesicState > &traj) const noexcept
Measure the maximum deviation of a trajectory from a straight line.
std::optional< GeodesicState > step(const GeodesicState &s, double dtau) const noexcept
Advance the geodesic by one RK4 step of proper-time dtau.
(N+1)-dimensional Lorentzian manifold for N financial assets.
Position and 4-velocity state on the manifold.
Definition tensor.hpp:521
Eigen::VectorXd x
Position coordinates.
Eigen::VectorXd u
Tangent vector (4-velocity, dx/dτ).