use crate::error::PhysicsError;
use deep_causality_num::{Field, Float};
use deep_causality_tensor::CausalTensor;
pub fn einstein_tensor_kernel<T>(
ricci: &CausalTensor<T>,
scalar: T,
metric: &CausalTensor<T>,
) -> Result<CausalTensor<T>, PhysicsError>
where
T: Field + Float + From<f64>,
{
if ricci.num_dim() != 2 || metric.num_dim() != 2 {
return Err(PhysicsError::DimensionMismatch(format!(
"Einstein tensor requires rank-2 tensors. Got ranks: ricci={}, metric={}",
ricci.num_dim(),
metric.num_dim()
)));
}
if ricci.shape() != metric.shape() {
return Err(PhysicsError::DimensionMismatch(format!(
"Ricci and metric shapes must match. Got {:?} vs {:?}",
ricci.shape(),
metric.shape()
)));
}
if ricci.shape().len() == 2 && ricci.shape()[0] != ricci.shape()[1] {
return Err(PhysicsError::DimensionMismatch(format!(
"Ricci tensor must be square. Got {:?}",
ricci.shape()
)));
}
let half_scalar = scalar * <T as From<f64>>::from(0.5);
let term2_data: Vec<T> = metric.as_slice().iter().map(|&x| x * half_scalar).collect();
let term2 = CausalTensor::from_vec(term2_data, metric.shape());
let result = ricci - term2;
Ok(result)
}
pub fn geodesic_deviation_kernel<T>(
riemann: &CausalTensor<T>,
u: &[T],
n: &[T],
) -> Result<Vec<T>, PhysicsError>
where
T: Field + Float + From<f64>,
{
let dim = 4usize;
if riemann.num_dim() != 4 {
return Err(PhysicsError::DimensionMismatch(format!(
"Riemann tensor must be rank-4, got {}",
riemann.num_dim()
)));
}
if u.len() != dim || n.len() != dim {
return Err(PhysicsError::DimensionMismatch(format!(
"Vectors must have length 4, got u={}, n={}",
u.len(),
n.len()
)));
}
let r_data = riemann.as_slice();
let mut result = vec![T::zero(); dim];
for (mu, res_mu) in result.iter_mut().enumerate() {
let mut acc = T::zero();
for v in 0..dim {
for (r, n_r) in n.iter().enumerate() {
for s in 0..dim {
let idx = mu * dim * dim * dim + v * dim * dim + r * dim + s;
let r_component = r_data[idx];
acc = acc + r_component * u[v] * *n_r * u[s];
}
}
}
*res_mu = T::zero() - acc; }
Ok(result)
}
#[allow(clippy::type_complexity)]
pub fn geodesic_integrator_kernel<T>(
initial_position: &[T],
initial_velocity: &[T],
christoffel: &CausalTensor<T>,
proper_time_step: T,
num_steps: usize,
) -> Result<Vec<(Vec<T>, Vec<T>)>, PhysicsError>
where
T: Field + Float + From<f64> + Copy,
{
if initial_position.len() != initial_velocity.len() {
return Err(PhysicsError::DimensionMismatch(format!(
"Position and velocity must have same dimension: {} vs {}",
initial_position.len(),
initial_velocity.len()
)));
}
let dim = initial_position.len();
if christoffel.num_dim() != 3 {
return Err(PhysicsError::DimensionMismatch(format!(
"Christoffel symbols must be rank 3, got {}",
christoffel.num_dim()
)));
}
let shape = christoffel.shape();
if shape[0] != dim || shape[1] != dim || shape[2] != dim {
return Err(PhysicsError::DimensionMismatch(format!(
"Christoffel shape {:?} incompatible with dimension {}",
shape, dim
)));
}
if !proper_time_step.is_finite() || proper_time_step == T::zero() {
return Err(PhysicsError::NumericalInstability(
"Invalid proper time step".into(),
));
}
let dt = proper_time_step;
let christoffel_data = christoffel.as_slice();
let compute_acceleration = |u: &[T]| -> Vec<T> {
let mut a = vec![T::zero(); dim];
for mu in 0..dim {
let mut acc = T::zero();
for nu in 0..dim {
for rho in 0..dim {
let gamma = christoffel_data[mu * dim * dim + nu * dim + rho];
acc = acc - (gamma * u[nu] * u[rho]);
}
}
a[mu] = acc;
}
a
};
let mut trajectory = Vec::with_capacity(num_steps + 1);
let mut x: Vec<T> = initial_position.to_vec();
let mut u: Vec<T> = initial_velocity.to_vec();
trajectory.push((x.clone(), u.clone()));
for _ in 0..num_steps {
let a1 = compute_acceleration(&u);
let k1_x: Vec<T> = u.iter().map(|&v| v * dt).collect();
let k1_u: Vec<T> = a1.iter().map(|&a| a * dt).collect();
let u_mid1: Vec<T> = u
.iter()
.zip(k1_u.iter())
.map(|(v, k)| *v + <T as From<f64>>::from(0.5) * *k)
.collect();
let a2 = compute_acceleration(&u_mid1);
let k2_x: Vec<T> = u_mid1.iter().map(|&v| v * dt).collect();
let k2_u: Vec<T> = a2.iter().map(|&a| a * dt).collect();
let u_mid2: Vec<T> = u
.iter()
.zip(k2_u.iter())
.map(|(v, k)| *v + <T as From<f64>>::from(0.5) * *k)
.collect();
let a3 = compute_acceleration(&u_mid2);
let k3_x: Vec<T> = u_mid2.iter().map(|&v| v * dt).collect();
let k3_u: Vec<T> = a3.iter().map(|&a| a * dt).collect();
let u_end: Vec<T> = u.iter().zip(k3_u.iter()).map(|(v, k)| *v + *k).collect();
let a4 = compute_acceleration(&u_end);
let k4_x: Vec<T> = u_end.iter().map(|&v| v * dt).collect();
let k4_u: Vec<T> = a4.iter().map(|&a| a * dt).collect();
for i in 0..dim {
x[i] = x[i]
+ (k1_x[i]
+ <T as From<f64>>::from(2.0) * k2_x[i]
+ <T as From<f64>>::from(2.0) * k3_x[i]
+ k4_x[i])
/ <T as From<f64>>::from(6.0);
u[i] = u[i]
+ (k1_u[i]
+ <T as From<f64>>::from(2.0) * k2_u[i]
+ <T as From<f64>>::from(2.0) * k3_u[i]
+ k4_u[i])
/ <T as From<f64>>::from(6.0);
}
if x.iter().any(|v| !v.is_finite()) || u.iter().any(|v| !v.is_finite()) {
return Err(PhysicsError::NumericalInstability(format!(
"Geodesic integration diverged at step {}",
trajectory.len()
)));
}
trajectory.push((x.clone(), u.clone()));
}
Ok(trajectory)
}