Special Relativity in Financial Modeling 1.0.0
Lorentz transforms, spacetime classification, and geodesic price paths for quantitative finance
Loading...
Searching...
No Matches
tensor.hpp
Go to the documentation of this file.
1#pragma once
2
3/// @file include/srfm/tensor.hpp
4/// @brief Tensor Calculus & Covariance Engine — AGT-04 public API.
5///
6/// # Module: Tensor Calculus & Covariance Engine
7///
8/// ## Responsibility
9/// Implements the differential geometry machinery for the financial spacetime
10/// manifold. Provides:
11/// - `DualNumber` — Scalar dual number for forward-mode autodiff
12/// - `MetricTensor` — 4×4 position-dependent g_μν encoding covariance
13/// - `ChristoffelSymbols` — Γ^λ_μν = ½ g^λσ(∂_μg_νσ + ∂_νg_μσ − ∂_σg_μν)
14/// - `GeodesicSolver` — integrates d²x^λ/dτ² + Γ^λ_μν ẋ^μ ẋ^ν = 0
15///
16/// ## Physical Interpretation
17/// In the financial spacetime manifold, the metric g_μν encodes the
18/// covariance structure of the market: the time-time component g₀₀ scales
19/// with market time; the spatial block g_ij carries the asset covariance
20/// matrix. Christoffel symbols Γ^λ_μν therefore measure the *rate of change
21/// of correlations* through market space. The geodesic equation describes the
22/// natural, force-free price path through this curved geometry.
23///
24/// ## Autodifferentiation (Dual Numbers)
25/// ChristoffelSymbolsDual replaces the O(h²) central finite-difference
26/// metric derivative with exact forward-mode automatic differentiation via
27/// dual numbers:
28/// x = a + b·ε, ε² = 0
29/// Evaluating g_μν at x = (x₀ + ε·ê_σ) propagates the partial derivative
30/// ∂g_μν/∂x^σ exactly through any polynomial or rational metric function,
31/// with zero truncation error and no step-size tuning.
32///
33/// ## Guarantees
34/// - No undefined behaviour: all fallible operations return std::optional
35/// - No raw pointers: ownership is by value or const-reference
36/// - Thread-safe reads: const member functions are safe to call concurrently
37/// - Eigen3 is used for all matrix arithmetic (LAPACK-quality numerics)
38///
39/// ## NOT Responsible For
40/// - Lorentz boosts (see src/lorentz/)
41/// - Market manifold topology / spacetime intervals (see src/manifold/)
42/// - Backtesting or portfolio construction (see src/backtest/)
43
44#include "srfm/types.hpp"
45#include "srfm/constants.hpp"
46
47#include <Eigen/Dense>
48#include <algorithm>
49#include <array>
50#include <execution>
51#include <functional>
52#include <optional>
53#include <utility>
54#include <vector>
55
56namespace srfm::tensor {
57
58// ─── DualNumber ───────────────────────────────────────────────────────────────
59
60/// Forward-mode automatic differentiation scalar: x = value + deriv·ε, ε² = 0.
61///
62/// A dual number carries a real value and its directional derivative
63/// simultaneously. Arithmetic operations propagate both components according
64/// to the standard rules of dual-number algebra:
65/// (a+bε) + (c+dε) = (a+c) + (b+d)ε
66/// (a+bε) × (c+dε) = ac + (ad+bc)ε [ε² = 0]
67/// (a+bε) / (c+dε) = a/c + (bc−ad)/c²ε [c≠0]
68///
69/// ## Usage for metric derivatives
70/// To compute ∂g_μν/∂x^σ exactly (without finite-difference truncation error):
71/// ```cpp
72/// // Seed coordinate σ with derivative 1; all others with derivative 0.
73/// DualSpacetimePoint xd;
74/// for (int k = 0; k < 4; ++k)
75/// xd[k] = DualNumber{x(k), k == sigma ? 1.0 : 0.0};
76/// // Evaluate the dual-number metric; extract the .deriv component.
77/// auto gd_mu_nu = dual_metric_fn(xd);
78/// // gd_mu_nu(mu, nu).deriv == ∂g_μν/∂x^σ (exact, no rounding)
79/// ```
80struct DualNumber {
81 double value; ///< Real part
82 double deriv; ///< Infinitesimal (ε) part: the directional derivative
83
84 // ── Arithmetic operators ───────────────────────────────────────────────
85
86 constexpr DualNumber operator+(const DualNumber& o) const noexcept {
87 return {value + o.value, deriv + o.deriv};
88 }
89 constexpr DualNumber operator-(const DualNumber& o) const noexcept {
90 return {value - o.value, deriv - o.deriv};
91 }
92 constexpr DualNumber operator*(const DualNumber& o) const noexcept {
93 // (a+bε)(c+dε) = ac + (ad+bc)ε
94 return {value * o.value, value * o.deriv + deriv * o.value};
95 }
96 constexpr DualNumber operator/(const DualNumber& o) const noexcept {
97 // (a+bε)/(c+dε) = a/c + (bc−ad)/c²·ε
98 const double inv_c = 1.0 / o.value;
99 return {value * inv_c,
100 (deriv * o.value - value * o.deriv) * inv_c * inv_c};
101 }
102
103 // ── Mixed real/dual arithmetic ─────────────────────────────────────────
104
105 constexpr DualNumber operator+(double s) const noexcept {
106 return {value + s, deriv};
107 }
108 constexpr DualNumber operator-(double s) const noexcept {
109 return {value - s, deriv};
110 }
111 constexpr DualNumber operator*(double s) const noexcept {
112 return {value * s, deriv * s};
113 }
114 constexpr DualNumber operator/(double s) const noexcept {
115 return {value / s, deriv / s};
116 }
117 constexpr DualNumber operator-() const noexcept {
118 return {-value, -deriv};
119 }
120
121 friend constexpr DualNumber operator+(double s, const DualNumber& d) noexcept {
122 return {s + d.value, d.deriv};
123 }
124 friend constexpr DualNumber operator*(double s, const DualNumber& d) noexcept {
125 return {s * d.value, s * d.deriv};
126 }
127};
128
129/// A 4-vector of dual numbers — one per spacetime coordinate.
130/// Used to seed the autodiff direction when computing metric derivatives.
131using DualSpacetimePoint = Eigen::Matrix<DualNumber, SPACETIME_DIM, 1>;
132
133/// A 4×4 matrix of dual numbers — the metric evaluated at a dual-number point.
134using DualMetricMatrix = Eigen::Matrix<DualNumber, SPACETIME_DIM, SPACETIME_DIM>;
135
136/// A callable mapping a dual-number spacetime point to a dual-number metric.
137/// Implement this alongside MetricFunction to enable exact autodiff derivatives.
139
140// ─── Type Aliases ─────────────────────────────────────────────────────────────
141
142/// A callable that maps a spacetime point to the metric matrix at that point.
143/// Used for position-dependent (curved) metrics.
144using MetricFunction = std::function<MetricMatrix(const SpacetimePoint&)>;
145
146/// Christoffel symbols as an array of 4×4 matrices.
147/// Access pattern: gamma[lambda](mu, nu) = Γ^λ_μν.
148using ChristoffelArray = std::array<MetricMatrix, SPACETIME_DIM>;
149
150// ─── MetricTensor ─────────────────────────────────────────────────────────────
151
152/// A position-dependent 4×4 symmetric tensor g_μν encoding the geometry of
153/// the financial spacetime manifold.
154///
155/// The metric signature is (−,+,+,+): component 0 is timelike (market time),
156/// components 1–3 are spacelike (asset returns). Off-diagonal spatial entries
157/// encode asset correlations; off-diagonal time-space entries encode
158/// temporal momentum correlations.
159///
160/// # Example
161/// ```cpp
162/// // Flat market: uncorrelated assets, equal volatility 0.2
163/// auto g = srfm::tensor::MetricTensor::make_minkowski(1.0, 0.2);
164/// srfm::SpacetimePoint origin = srfm::SpacetimePoint::Zero();
165/// auto gx = g.evaluate(origin); // diag(-1, 0.04, 0.04, 0.04)
166/// ```
168public:
169 /// Construct from an arbitrary position-dependent metric function.
170 explicit MetricTensor(MetricFunction metric_fn);
171
172 /// Evaluate g_μν at the given spacetime point.
173 ///
174 /// # Arguments
175 /// * `x` — Position in the 4D financial spacetime manifold
176 ///
177 /// # Returns
178 /// The 4×4 metric matrix at x.
179 MetricMatrix evaluate(const SpacetimePoint& x) const;
180
181 /// Evaluate g_μν at x and store the result into `out` (in-place overload).
182 ///
183 /// Avoids an extra copy compared to `evaluate()` when the caller already
184 /// holds a `MetricMatrix` to overwrite.
185 ///
186 /// # Arguments
187 /// * `x` — Position in the 4D financial spacetime manifold
188 /// * `out` — Output matrix to overwrite with g_μν(x)
189 void evaluate_into(const SpacetimePoint& x, MetricMatrix& out) const {
190 out = evaluate(x);
191 }
192
193 /// Compute the inverse metric g^μν at point x.
194 ///
195 /// # Returns
196 /// - `Some(g_inv)` if the metric is invertible at x
197 /// - `None` if the metric is singular (degenerate correlations)
198 std::optional<MetricMatrix> inverse(const SpacetimePoint& x) const;
199
200 /// Return true if the metric has Lorentzian signature (−,+,+,+) at x.
201 /// A Lorentzian metric has exactly one negative eigenvalue.
202 bool is_lorentzian(const SpacetimePoint& x) const;
203
204 /// Compute the spacetime interval ds² = g_μν dx^μ dx^ν.
205 ///
206 /// # Returns
207 /// - Negative: timelike displacement (subluminal market movement)
208 /// - Zero: null / lightlike (signal at speed of information)
209 /// - Positive: spacelike displacement (acausal — outside light cone)
210 double spacetime_interval(const SpacetimePoint& x,
211 const FourVelocity& dx) const;
212
213 // ── Factories ────────────────────────────────────────────────────────────
214
215 /// Flat Minkowski-like metric: g = diag(−time_scale², σ², σ², σ²).
216 ///
217 /// Equivalent to a market with uncorrelated assets of equal volatility σ.
218 ///
219 /// # Arguments
220 /// * `time_scale` — Scale of the time dimension (c analogue), default 1
221 /// * `spatial_scale` — Common asset volatility σ, default 1
222 static MetricTensor make_minkowski(double time_scale = 1.0,
223 double spatial_scale = 1.0);
224
225 /// Diagonal metric from per-asset volatilities:
226 /// g = diag(−time_scale², σ₁², σ₂², σ₃²).
227 ///
228 /// # Arguments
229 /// * `time_scale` — Scale of the time dimension
230 /// * `vol` — Array of three asset volatilities {σ₁, σ₂, σ₃}
231 static MetricTensor make_diagonal(double time_scale,
232 const std::array<double, 3>& vol);
233
234 /// Full covariance-based metric from a 3×3 asset covariance matrix.
235 /// g = block-diag(−time_scale², Σ) where Σ is the asset covariance.
236 ///
237 /// # Arguments
238 /// * `time_scale` — Scale of the time dimension
239 /// * `cov` — 3×3 asset covariance matrix (must be positive definite)
240 static MetricTensor make_from_covariance(double time_scale,
241 const Eigen::Matrix3d& cov);
242
243private:
244 MetricFunction metric_fn_;
245};
246
247// ─── CachedMetricTensor ───────────────────────────────────────────────────────
248
249/// Cache wrapper around MetricTensor::evaluate().
250///
251/// MetricTensor::evaluate() is called many times per geodesic integration step
252/// (once per Christoffel finite-difference probe). When the probe points are
253/// close together, the metric value changes negligibly. This wrapper avoids
254/// redundant computation by caching the most recently evaluated (point, result)
255/// pair and returning the cached result whenever the requested point is within
256/// `tol` (L∞ norm) of the cached point.
257///
258/// All other MetricTensor operations (inverse, is_lorentzian,
259/// spacetime_interval) are delegated directly to the inner MetricTensor without
260/// caching, as they are called far less frequently.
261///
262/// ## Usage
263/// ```cpp
264/// auto cached = CachedMetricTensor::make_minkowski(1.0, 0.2);
265/// auto g = cached.evaluate(pt); // fast on repeated nearby calls
266/// cached.invalidate(); // force re-evaluation on next call
267/// ```
268///
269/// ## Thread safety
270/// NOT thread-safe — one instance per thread / per GeodesicSolver.
272public:
273 /// Construct wrapping an existing MetricTensor with an optional tolerance.
274 ///
275 /// @param metric MetricTensor to wrap (copied by value).
276 /// @param tol L∞ distance threshold for cache hit (default 1e-8).
277 explicit CachedMetricTensor(MetricTensor metric, double tol = 1e-8)
278 : inner_(std::move(metric)), tol_(tol) {}
279
280 /// Evaluate g_μν at x, returning a cached result when the point is close
281 /// enough to the previously cached point (L∞ distance < tol).
283 if (valid_ && (x - pt_).cwiseAbs().maxCoeff() < tol_) {
284 return cached_;
285 }
286 cached_ = inner_.evaluate(x);
287 pt_ = x;
288 valid_ = true;
289 return cached_;
290 }
291
292 /// Delegate: compute g^μν (inverse metric) at x.
293 std::optional<MetricMatrix> inverse(const SpacetimePoint& x) const {
294 return inner_.inverse(x);
295 }
296
297 /// Delegate: return true iff metric has Lorentzian signature (−,+,+,+) at x.
298 bool is_lorentzian(const SpacetimePoint& x) const {
299 return inner_.is_lorentzian(x);
300 }
301
302 /// Delegate: compute ds² = g_μν dx^μ dx^ν at x.
304 const FourVelocity& dx) const {
305 return inner_.spacetime_interval(x, dx);
306 }
307
308 /// Invalidate the cache, forcing re-evaluation on the next call to evaluate().
309 void invalidate() const noexcept { valid_ = false; }
310
311 // ── Factories ────────────────────────────────────────────────────────────
312
313 /// Flat Minkowski-like metric: g = diag(−time_scale², σ², σ², σ²).
314 static CachedMetricTensor make_minkowski(double time_scale = 1.0,
315 double spatial_scale = 1.0) {
317 spatial_scale));
318 }
319
320 /// Diagonal metric from per-asset volatilities.
321 static CachedMetricTensor make_diagonal(double time_scale,
322 const std::array<double, 3>& vol) {
323 return CachedMetricTensor(MetricTensor::make_diagonal(time_scale, vol));
324 }
325
326 /// Full covariance-based metric from a 3×3 asset covariance matrix.
327 static CachedMetricTensor make_from_covariance(double time_scale,
328 const Eigen::Matrix3d& cov) {
329 return CachedMetricTensor(
330 MetricTensor::make_from_covariance(time_scale, cov));
331 }
332
333private:
334 MetricTensor inner_;
335 double tol_;
336 mutable bool valid_{false};
337 mutable SpacetimePoint pt_{SpacetimePoint::Zero()};
338 mutable MetricMatrix cached_{};
339};
340
341// ─── ChristoffelSymbols ───────────────────────────────────────────────────────
342
343/// Computes the Christoffel symbols of the second kind Γ^λ_μν at a spacetime
344/// point by numerically differentiating the metric tensor.
345///
346/// The Christoffel symbols measure how the metric — and therefore the market
347/// covariance structure — changes from one market state to another. They are
348/// the "connection" that converts coordinate changes into physical changes in
349/// the correlation geometry.
350///
351/// Formula (Einstein summation):
352/// Γ^λ_μν = ½ g^λσ (∂_μ g_νσ + ∂_ν g_μσ − ∂_σ g_μν)
353///
354/// Partial derivatives are computed via central finite differences:
355/// ∂g_μν/∂x^σ ≈ [g_μν(x + h·ê_σ) − g_μν(x − h·ê_σ)] / (2h)
357public:
358 /// Construct from a metric tensor.
359 ///
360 /// # Arguments
361 /// * `metric` — The position-dependent metric (held by const-reference)
362 /// * `h` — Finite-difference step for metric derivatives (default 1e-5)
363 explicit ChristoffelSymbols(const MetricTensor& metric,
364 double h = constants::DEFAULT_FD_STEP);
365
366 /// Compute all 4³ = 64 Christoffel symbols at point x.
367 ///
368 /// # Arguments
369 /// * `x` — Spacetime point at which to evaluate Γ^λ_μν
370 ///
371 /// # Returns
372 /// Array indexed as result[lambda](mu, nu) = Γ^λ_μν.
373 /// Returns all-zero array if the metric is singular at x.
375
376 /// Contract the Christoffel symbols with a four-velocity:
377 /// result^λ = Γ^λ_μν u^μ u^ν
378 ///
379 /// This is the RHS of the geodesic acceleration equation (negated).
381 const FourVelocity& u) const;
382
383private:
384 /// Compute ∂g_μν/∂x^sigma via central finite differences.
385 MetricMatrix metric_derivative(const SpacetimePoint& x, int sigma) const;
386
387 const MetricTensor& metric_;
388 double h_;
389};
390
391// ─── CachedChristoffelSymbols ────────────────────────────────────────────────
392
393/// Thread-local cache wrapper around ChristoffelSymbols.
394///
395/// Caches the most recently computed ChristoffelArray so that repeated calls
396/// from the RK4 geodesic solver at the same (or very nearby) spacetime point
397/// do not re-evaluate all 64 finite-difference metric derivatives.
398///
399/// Cache invalidation: the cached result is reused when the requested point
400/// is within `cache_tol` (default 1e-8) of the cached point in L∞ norm.
401///
402/// Thread safety: NOT thread-safe (one instance per thread / per GeodesicSolver).
404public:
405 /// Construct from a metric tensor with an optional cache tolerance.
408 double cache_tol = 1e-8)
409 : inner_{metric, h}, cache_tol_{cache_tol} {}
410
411 /// Compute (or return cached) Christoffel symbols at point x.
413 if (cache_valid_ && (x - cached_point_).cwiseAbs().maxCoeff() < cache_tol_) {
414 return cached_result_;
415 }
416 cached_result_ = inner_.compute(x);
417 cached_point_ = x;
418 cache_valid_ = true;
419 return cached_result_;
420 }
421
422 /// Contract cached symbols with a four-velocity.
424 const FourVelocity& u) const {
425 return inner_.contract(gamma, u);
426 }
427
428 /// Invalidate the cache (e.g. after a metric parameter change).
429 void invalidate() const noexcept { cache_valid_ = false; }
430
431private:
432 ChristoffelSymbols inner_;
433 double cache_tol_;
434 mutable bool cache_valid_{false};
435 mutable SpacetimePoint cached_point_{SpacetimePoint::Zero()};
436 mutable ChristoffelArray cached_result_{};
437};
438
439// ─── ChristoffelSymbolsDual ───────────────────────────────────────────────────
440
441/// Christoffel symbols computed via dual-number forward-mode autodiff.
442///
443/// Replaces the O(h²) central finite-difference approximation in
444/// ChristoffelSymbols with an exact computation. The metric function is
445/// re-evaluated at a dual-number point xd = x + ε·ê_σ; the ε-component of
446/// the result is the exact partial derivative ∂g_μν/∂x^σ, with zero
447/// truncation error and no step-size sensitivity.
448///
449/// ## Requirements
450/// The caller must supply a `DualMetricFunction` in addition to the standard
451/// `MetricFunction`. This is the same mathematical object as the metric, but
452/// templated over DualNumber arithmetic instead of double arithmetic.
453///
454/// ## Performance vs. ChristoffelSymbols
455/// Each of the 4 derivative directions requires one call to the dual metric
456/// function (vs. 2 calls each for central differences). Total cost: 4 calls
457/// vs. 8 calls per Christoffel evaluation, and no step-size h to tune.
458///
459/// ## Example
460/// ```cpp
461/// // Build a flat Minkowski dual metric function for a flat (constant) metric.
462/// auto dual_fn = [](const DualSpacetimePoint& /*xd*/) -> DualMetricMatrix {
463/// DualMetricMatrix gd = DualMetricMatrix::Zero();
464/// gd(0,0) = DualNumber{-1.0, 0.0};
465/// gd(1,1) = DualNumber{ 1.0, 0.0};
466/// gd(2,2) = DualNumber{ 1.0, 0.0};
467/// gd(3,3) = DualNumber{ 1.0, 0.0};
468/// return gd;
469/// };
470/// MetricTensor base_metric = MetricTensor::make_minkowski(1.0, 1.0);
471/// ChristoffelSymbolsDual cs(base_metric, dual_fn);
472/// auto gamma = cs.compute(SpacetimePoint::Zero());
473/// // All gamma[l](mu,nu) == 0 for flat Minkowski — exact with no FD error.
474/// ```
476public:
477 /// Construct from a base MetricTensor and a dual-number metric function.
478 ///
479 /// @param metric Base metric tensor (used for inverse metric g^λσ).
480 /// @param dual_fn Dual-number analogue of the metric function, used for
481 /// exact derivative extraction via autodiff.
483 DualMetricFunction dual_fn);
484
485 /// Compute all Γ^λ_μν at point x using dual-number autodiff.
486 ///
487 /// For each coordinate σ ∈ {0,1,2,3}:
488 /// 1. Build dual point xd: xd[k] = {x(k), k==σ ? 1.0 : 0.0}
489 /// 2. Evaluate dual_fn(xd) → DualMetricMatrix gd
490 /// 3. dg[σ](μ,ν) = gd(μ,ν).deriv (exact ∂g_μν/∂x^σ)
491 ///
492 /// Then assemble Γ^λ_μν using the standard formula with g^λσ from the
493 /// base metric inverse.
494 ///
495 /// @param x Spacetime point at which to evaluate Γ^λ_μν.
496 /// @return ChristoffelArray, or all-zero if the metric is singular at x.
498
499 /// Contract Christoffel symbols with a four-velocity (identical to
500 /// ChristoffelSymbols::contract).
502 const FourVelocity& u) const;
503
504private:
505 /// Extract ∂g_μν/∂x^σ at point x via a single dual metric evaluation.
506 MetricMatrix dual_metric_derivative(const SpacetimePoint& x,
507 int sigma) const;
508
509 const MetricTensor& metric_;
510 DualMetricFunction dual_fn_;
511};
512
513// ─── GeodesicSolver ───────────────────────────────────────────────────────────
514
515/// Phase-space state for the geodesic ODE: position x^μ and velocity u^μ.
516///
517/// The geodesic equation is a second-order ODE. We reduce it to first order
518/// by treating (x, u) as the state vector:
519/// dx^λ/dτ = u^λ
520/// du^λ/dτ = −Γ^λ_μν u^μ u^ν
522 SpacetimePoint position; ///< x^μ: position in financial spacetime
523 FourVelocity velocity; ///< u^μ = dx^μ/dτ: four-velocity tangent vector
524
525 /// Pointwise addition of two states (used internally by RK4).
526 GeodesicState operator+(const GeodesicState& o) const noexcept {
527 return {position + o.position, velocity + o.velocity};
528 }
529
530 /// Scalar multiplication (used internally by RK4).
531 friend GeodesicState operator*(double s, const GeodesicState& g) noexcept {
532 return {s * g.position, s * g.velocity};
533 }
534};
535
536/// Integrates the geodesic equation using classical 4th-order Runge-Kutta.
537///
538/// The geodesic equation:
539/// d²x^λ/dτ² + Γ^λ_μν (dx^μ/dτ)(dx^ν/dτ) = 0
540///
541/// describes the "free fall" path of an asset price in curved financial
542/// spacetime — the trajectory that minimises proper time (the path of least
543/// market resistance given the covariance geometry).
544///
545/// # Example
546/// ```cpp
547/// auto g = MetricTensor::make_minkowski(1.0, 0.2);
548/// auto sol = GeodesicSolver(g, 0.01);
549/// srfm::SpacetimePoint x0 = srfm::SpacetimePoint::Zero();
550/// srfm::FourVelocity u0; u0 << 1.0, 0.1, 0.0, 0.0;
551/// auto traj = sol.integrate(x0, u0, 100);
552/// ```
554public:
555 /// Construct with a metric, proper-time step size, and FD step for Γ.
556 ///
557 /// # Arguments
558 /// * `metric` — Position-dependent metric tensor
559 /// * `step_size` — Proper-time step dτ for RK4 integration
560 /// * `christoffel_h` — Finite-difference step for Christoffel symbols
561 GeodesicSolver(const MetricTensor& metric,
562 double step_size = constants::DEFAULT_GEODESIC_STEP,
563 double christoffel_h = constants::DEFAULT_FD_STEP);
564
565 /// Integrate the geodesic from (x0, u0) for `steps` proper-time steps.
566 ///
567 /// # Arguments
568 /// * `x0` — Initial spacetime position
569 /// * `u0` — Initial four-velocity (tangent vector)
570 /// * `steps` — Number of RK4 integration steps
571 ///
572 /// # Returns
573 /// Vector of `steps + 1` states including the initial state.
574 std::vector<GeodesicState> integrate(const SpacetimePoint& x0,
575 const FourVelocity& u0,
576 int steps) const;
577
578 /// Compute g_μν u^μ u^ν to diagnose the causal character of the geodesic.
579 ///
580 /// # Returns
581 /// - Negative: timelike (subluminal price movement — physically meaningful)
582 /// - Zero: null / lightlike (speed-of-information signal)
583 /// - Positive: spacelike (acausal — indicates model error or extreme regime)
584 double norm_squared(const SpacetimePoint& x,
585 const FourVelocity& u) const;
586
587private:
588 /// Advance state by one RK4 step of size step_size_.
589 GeodesicState rk4_step(const GeodesicState& state) const;
590
591 const MetricTensor& metric_;
592 ChristoffelSymbols christoffel_;
593 double step_size_;
594};
595
596// ─── integrate_batch ──────────────────────────────────────────────────────────
597
598/// Integrate a batch of geodesics in parallel using std::execution::par_unseq.
599///
600/// Each geodesic is independent (different initial conditions), so the
601/// per-geodesic integrations can be run concurrently without any shared mutable
602/// state. The solver object itself is read-only; each lambda call operates on
603/// its own trajectory vector.
604///
605/// This provides a straightforward way to amortise the overhead of
606/// multiple-asset geodesic integration — one call per portfolio rebalance
607/// instead of a sequential for-loop.
608///
609/// # Requirements
610/// The C++ standard library parallel STL backend must be available.
611/// - On Linux with libstdc++: link with -ltbb (Intel TBB).
612/// - On MSVC: std::execution::par_unseq works out of the box.
613/// - On libc++ (macOS/LLVM): may require -fexperimental-library and TBB.
614///
615/// # Arguments
616/// * `solver` — Configured GeodesicSolver (read-only).
617/// * `initial_conditions` — Vector of (initial_position, initial_velocity) pairs.
618/// * `steps` — Number of RK4 steps per trajectory.
619///
620/// # Returns
621/// Vector of trajectories (one per initial condition), each containing
622/// `steps + 1` GeodesicState entries in proper-time order.
623///
624/// # Thread safety
625/// The GeodesicSolver and MetricTensor are accessed as const; parallel calls
626/// are safe provided the MetricFunction stored in the solver's MetricTensor is
627/// itself thread-safe for concurrent const calls.
628inline std::vector<std::vector<GeodesicState>>
630 const GeodesicSolver& solver,
631 const std::vector<std::pair<SpacetimePoint, FourVelocity>>& initial_conditions,
632 int steps)
633{
634 std::vector<std::vector<GeodesicState>> results(initial_conditions.size());
635
636 std::transform(
637 std::execution::par_unseq,
638 initial_conditions.begin(),
639 initial_conditions.end(),
640 results.begin(),
641 [&solver, steps](const std::pair<SpacetimePoint, FourVelocity>& ic) {
642 return solver.integrate(ic.first, ic.second, steps);
643 });
644
645 return results;
646}
647
648} // namespace srfm::tensor
FourVelocity contract(const ChristoffelArray &gamma, const FourVelocity &u) const
Contract cached symbols with a four-velocity.
Definition tensor.hpp:423
void invalidate() const noexcept
Invalidate the cache (e.g. after a metric parameter change).
Definition tensor.hpp:429
CachedChristoffelSymbols(const MetricTensor &metric, double h=constants::DEFAULT_FD_STEP, double cache_tol=1e-8)
Construct from a metric tensor with an optional cache tolerance.
Definition tensor.hpp:406
ChristoffelArray compute(const SpacetimePoint &x) const
Compute (or return cached) Christoffel symbols at point x.
Definition tensor.hpp:412
bool is_lorentzian(const SpacetimePoint &x) const
Delegate: return true iff metric has Lorentzian signature (−,+,+,+) at x.
Definition tensor.hpp:298
static CachedMetricTensor make_from_covariance(double time_scale, const Eigen::Matrix3d &cov)
Full covariance-based metric from a 3×3 asset covariance matrix.
Definition tensor.hpp:327
CachedMetricTensor(MetricTensor metric, double tol=1e-8)
Definition tensor.hpp:277
MetricMatrix evaluate(const SpacetimePoint &x) const
Definition tensor.hpp:282
double spacetime_interval(const SpacetimePoint &x, const FourVelocity &dx) const
Delegate: compute ds² = g_μν dx^μ dx^ν at x.
Definition tensor.hpp:303
void invalidate() const noexcept
Invalidate the cache, forcing re-evaluation on the next call to evaluate().
Definition tensor.hpp:309
static CachedMetricTensor make_minkowski(double time_scale=1.0, double spatial_scale=1.0)
Flat Minkowski-like metric: g = diag(−time_scale², σ², σ², σ²).
Definition tensor.hpp:314
static CachedMetricTensor make_diagonal(double time_scale, const std::array< double, 3 > &vol)
Diagonal metric from per-asset volatilities.
Definition tensor.hpp:321
std::optional< MetricMatrix > inverse(const SpacetimePoint &x) const
Delegate: compute g^μν (inverse metric) at x.
Definition tensor.hpp:293
FourVelocity contract(const ChristoffelArray &gamma, const FourVelocity &u) const
ChristoffelArray compute(const SpacetimePoint &x) const
FourVelocity contract(const ChristoffelArray &gamma, const FourVelocity &u) const
ChristoffelArray compute(const SpacetimePoint &x) const
double norm_squared(const SpacetimePoint &x, const FourVelocity &u) const
Definition geodesic.cpp:75
std::vector< GeodesicState > integrate(const SpacetimePoint &x0, const FourVelocity &u0, int steps) const
Definition geodesic.cpp:54
double spacetime_interval(const SpacetimePoint &x, const FourVelocity &dx) const
std::optional< MetricMatrix > inverse(const SpacetimePoint &x) const
bool is_lorentzian(const SpacetimePoint &x) const
static MetricTensor make_from_covariance(double time_scale, const Eigen::Matrix3d &cov)
static MetricTensor make_diagonal(double time_scale, const std::array< double, 3 > &vol)
void evaluate_into(const SpacetimePoint &x, MetricMatrix &out) const
Definition tensor.hpp:189
static MetricTensor make_minkowski(double time_scale=1.0, double spatial_scale=1.0)
MetricMatrix evaluate(const SpacetimePoint &x) const
Physical and financial constants for the SRFM system.
static constexpr double DEFAULT_GEODESIC_STEP
Default proper-time step for geodesic integration.
Definition constants.hpp:40
static constexpr double DEFAULT_FD_STEP
Default finite-difference step for numerical metric derivatives.
Definition constants.hpp:43
Eigen::Matrix< DualNumber, SPACETIME_DIM, 1 > DualSpacetimePoint
Definition tensor.hpp:131
std::function< MetricMatrix(const SpacetimePoint &)> MetricFunction
Definition tensor.hpp:144
std::vector< std::vector< GeodesicState > > integrate_batch(const GeodesicSolver &solver, const std::vector< std::pair< SpacetimePoint, FourVelocity > > &initial_conditions, int steps)
Definition tensor.hpp:629
std::function< DualMetricMatrix(const DualSpacetimePoint &)> DualMetricFunction
Definition tensor.hpp:138
Eigen::Matrix< DualNumber, SPACETIME_DIM, SPACETIME_DIM > DualMetricMatrix
A 4×4 matrix of dual numbers — the metric evaluated at a dual-number point.
Definition tensor.hpp:134
std::array< MetricMatrix, SPACETIME_DIM > ChristoffelArray
Definition tensor.hpp:148
Eigen::Vector< double, SPACETIME_DIM > FourVelocity
A tangent vector at a spacetime point (four-velocity: dx^μ/dτ).
Definition types.hpp:44
Eigen::Matrix< double, SPACETIME_DIM, SPACETIME_DIM > MetricMatrix
The covariant metric tensor g_μν: a 4×4 symmetric matrix.
Definition types.hpp:47
Eigen::Vector< double, SPACETIME_DIM > SpacetimePoint
Definition types.hpp:41
constexpr DualNumber operator+(double s) const noexcept
Definition tensor.hpp:105
constexpr DualNumber operator/(double s) const noexcept
Definition tensor.hpp:114
constexpr DualNumber operator-(double s) const noexcept
Definition tensor.hpp:108
friend constexpr DualNumber operator*(double s, const DualNumber &d) noexcept
Definition tensor.hpp:124
constexpr DualNumber operator-() const noexcept
Definition tensor.hpp:117
constexpr DualNumber operator+(const DualNumber &o) const noexcept
Definition tensor.hpp:86
constexpr DualNumber operator*(double s) const noexcept
Definition tensor.hpp:111
double deriv
Infinitesimal (ε) part: the directional derivative.
Definition tensor.hpp:82
friend constexpr DualNumber operator+(double s, const DualNumber &d) noexcept
Definition tensor.hpp:121
constexpr DualNumber operator-(const DualNumber &o) const noexcept
Definition tensor.hpp:89
constexpr DualNumber operator/(const DualNumber &o) const noexcept
Definition tensor.hpp:96
constexpr DualNumber operator*(const DualNumber &o) const noexcept
Definition tensor.hpp:92
double value
Real part.
Definition tensor.hpp:81
Position and 4-velocity state on the manifold.
Definition tensor.hpp:521
FourVelocity velocity
u^μ = dx^μ/dτ: four-velocity tangent vector
Definition tensor.hpp:523
SpacetimePoint position
x^μ: position in financial spacetime
Definition tensor.hpp:522
friend GeodesicState operator*(double s, const GeodesicState &g) noexcept
Scalar multiplication (used internally by RK4).
Definition tensor.hpp:531
GeodesicState operator+(const GeodesicState &o) const noexcept
Pointwise addition of two states (used internally by RK4).
Definition tensor.hpp:526
Shared primitive types for the Special Relativity in Financial Modeling (SRFM) system.