use crate::PhysicsError;
use crate::Probability;
use deep_causality_num::RealField;
use deep_causality_tensor::{CausalTensor, EinSumOp, Tensor};
pub fn generalized_master_equation_kernel<R>(
state: &[Probability<R>],
history: &[Vec<Probability<R>>],
markov_operator: Option<&CausalTensor<R>>,
memory_kernel: &[CausalTensor<R>],
) -> Result<Vec<Probability<R>>, PhysicsError>
where
R: RealField + Default,
{
if history.len() != memory_kernel.len() {
return Err(PhysicsError::DimensionMismatch(format!(
"History length {} != Memory kernel length {}",
history.len(),
memory_kernel.len()
)));
}
let state_vec: Vec<R> = state.iter().map(|p| p.value()).collect();
let n = state_vec.len();
let state_tensor = CausalTensor::new(state_vec, vec![n, 1]).map_err(PhysicsError::from)?;
let mut p_new_tensor = if let Some(t) = markov_operator {
t.matmul(&state_tensor).map_err(PhysicsError::from)?
} else {
CausalTensor::new(vec![R::zero(); n], vec![n, 1]).map_err(PhysicsError::from)?
};
for (k, kernel) in memory_kernel.iter().enumerate() {
let hist_vec: Vec<R> = history[k].iter().map(|p| p.value()).collect();
if hist_vec.len() != n {
return Err(PhysicsError::DimensionMismatch(format!(
"History[{}] dimension {} != State dimension {}",
k,
hist_vec.len(),
n
)));
}
let hist_tensor = CausalTensor::new(hist_vec, vec![n, 1]).map_err(PhysicsError::from)?;
let contribution = kernel.matmul(&hist_tensor).map_err(PhysicsError::from)?;
let sum: CausalTensor<R> = p_new_tensor.add(&contribution);
p_new_tensor = sum;
}
let result_data = p_new_tensor.data();
let mut result_probs = Vec::with_capacity(n);
for &val in result_data {
let clamped = val.clamp(R::zero(), R::one());
result_probs.push(Probability::new_unchecked(clamped));
}
Ok(result_probs)
}
pub fn kalman_filter_linear_kernel<R>(
x_pred: &CausalTensor<R>,
p_pred: &CausalTensor<R>,
measurement: &CausalTensor<R>,
measurement_matrix: &CausalTensor<R>,
measurement_noise: &CausalTensor<R>,
_process_noise: &CausalTensor<R>,
) -> Result<(CausalTensor<R>, CausalTensor<R>), PhysicsError>
where
R: RealField + Default + core::iter::Sum,
{
let hx = measurement_matrix
.matmul(x_pred)
.map_err(PhysicsError::from)?;
if measurement.shape() != hx.shape() {
return Err(PhysicsError::DimensionMismatch(format!(
"Measurement shape {:?} != prediction shape {:?}",
measurement.shape(),
hx.shape()
)));
}
let y: CausalTensor<R> = measurement.sub(&hx);
let hp = measurement_matrix
.matmul(p_pred)
.map_err(PhysicsError::from)?;
let ht_op = EinSumOp::<R>::transpose(measurement_matrix.clone(), vec![1, 0]);
let ht = CausalTensor::ein_sum(&ht_op).map_err(PhysicsError::from)?;
let hph_t = hp.matmul(&ht).map_err(PhysicsError::from)?;
if hph_t.shape() != measurement_noise.shape() {
return Err(PhysicsError::DimensionMismatch(format!(
"Innovation covariance shape {:?} != measurement noise shape {:?}",
hph_t.shape(),
measurement_noise.shape()
)));
}
let s: CausalTensor<R> = hph_t.add(measurement_noise);
let s_inv = s.inverse().map_err(PhysicsError::from)?;
let pht = p_pred.matmul(&ht).map_err(PhysicsError::from)?;
let k = pht.matmul(&s_inv).map_err(PhysicsError::from)?;
let ky = k.matmul(&y).map_err(PhysicsError::from)?;
if x_pred.shape() != ky.shape() {
return Err(PhysicsError::DimensionMismatch(format!(
"State shape {:?} != update shape {:?}",
x_pred.shape(),
ky.shape()
)));
}
let x_new: CausalTensor<R> = x_pred.add(&ky);
let kh = k.matmul(measurement_matrix).map_err(PhysicsError::from)?;
let shape = p_pred.shape();
let identity = CausalTensor::identity(shape).map_err(PhysicsError::from)?;
if identity.shape() != kh.shape() {
return Err(PhysicsError::DimensionMismatch(format!(
"Identity shape {:?} != KH shape {:?}",
identity.shape(),
kh.shape()
)));
}
let i_kh: CausalTensor<R> = identity.sub(&kh);
let left = i_kh.matmul(p_pred).map_err(PhysicsError::from)?;
let i_kh_t = {
let op_t = EinSumOp::<R>::transpose(i_kh.clone(), vec![1, 0]);
CausalTensor::ein_sum(&op_t).map_err(PhysicsError::from)?
};
let joseph_main = left.matmul(&i_kh_t).map_err(PhysicsError::from)?;
let kt = {
let op_t = EinSumOp::<R>::transpose(k.clone(), vec![1, 0]);
CausalTensor::ein_sum(&op_t).map_err(PhysicsError::from)?
};
let kr = k.matmul(measurement_noise).map_err(PhysicsError::from)?;
let krkt = kr.matmul(&kt).map_err(PhysicsError::from)?;
let p_new: CausalTensor<R> = joseph_main.add(&krkt);
Ok((x_new, p_new))
}