use crate::entanglement::DensityMatrix;
use serde::{Deserialize, Serialize};
use crate::error::{KanaError, Result};
use crate::operator::Operator;
use crate::state::StateVector;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Hamiltonian {
operator: Operator,
dissipators: Vec<(f64, Operator)>,
}
impl Hamiltonian {
pub fn new(operator: Operator) -> Self {
Self {
operator,
dissipators: Vec::new(),
}
}
pub fn with_dissipators(operator: Operator, dissipators: Vec<(f64, Operator)>) -> Self {
Self {
operator,
dissipators,
}
}
pub fn add_dissipator(&mut self, rate: f64, collapse_op: Operator) {
self.dissipators.push((rate, collapse_op));
}
#[must_use]
pub fn is_open(&self) -> bool {
!self.dissipators.is_empty()
}
#[must_use]
pub fn dim(&self) -> usize {
self.operator.dim()
}
pub fn evolve_state(&self, state: &StateVector, dt: f64) -> Result<StateVector> {
if self.is_open() {
return Err(KanaError::InvalidParameter {
reason: "use evolve_density for open systems with dissipators".into(),
});
}
if self.operator.dim() != state.dimension() {
return Err(KanaError::DimensionMismatch {
expected: self.operator.dim(),
got: state.dimension(),
});
}
let u = self.evolution_operator(dt)?;
u.apply(state)
}
pub fn evolution_operator(&self, dt: f64) -> Result<Operator> {
let dim = self.operator.dim();
let h_elems = self.operator.elements();
let mut m = vec![(0.0, 0.0); dim * dim];
for (idx, &(h_re, h_im)) in h_elems.iter().enumerate() {
m[idx] = (h_im * dt, -h_re * dt);
}
let mut result = vec![(0.0, 0.0); dim * dim];
for i in 0..dim {
result[i * dim + i] = (1.0, 0.0);
}
let mut term = result.clone(); for n in 1..30 {
let mut new_term = vec![(0.0, 0.0); dim * dim];
for i in 0..dim {
for j in 0..dim {
let (mut re, mut im) = (0.0, 0.0);
for k in 0..dim {
let (t_re, t_im) = term[i * dim + k];
let (m_re, m_im) = m[k * dim + j];
re += t_re * m_re - t_im * m_im;
im += t_re * m_im + t_im * m_re;
}
new_term[i * dim + j] = (re / n as f64, im / n as f64);
}
}
term = new_term;
for i in 0..dim * dim {
result[i].0 += term[i].0;
result[i].1 += term[i].1;
}
let norm: f64 = term.iter().map(|(re, im)| re * re + im * im).sum();
if norm < 1e-30 {
break;
}
}
Operator::new(dim, result)
}
pub fn evolve_density(
&self,
rho: &DensityMatrix,
dt: f64,
steps: usize,
) -> Result<DensityMatrix> {
let dim = self.operator.dim();
if rho.dim() != dim {
return Err(KanaError::DimensionMismatch {
expected: dim,
got: rho.dim(),
});
}
let mut current = rho.elements().to_vec();
let step_dt = dt / steps as f64;
let nn = dim * dim;
for _ in 0..steps {
let k1 = self.lindblad_rhs(¤t, dim);
let mut tmp = vec![(0.0, 0.0); nn];
for i in 0..nn {
tmp[i] = (
current[i].0 + 0.5 * step_dt * k1[i].0,
current[i].1 + 0.5 * step_dt * k1[i].1,
);
}
let k2 = self.lindblad_rhs(&tmp, dim);
for i in 0..nn {
tmp[i] = (
current[i].0 + 0.5 * step_dt * k2[i].0,
current[i].1 + 0.5 * step_dt * k2[i].1,
);
}
let k3 = self.lindblad_rhs(&tmp, dim);
for i in 0..nn {
tmp[i] = (
current[i].0 + step_dt * k3[i].0,
current[i].1 + step_dt * k3[i].1,
);
}
let k4 = self.lindblad_rhs(&tmp, dim);
for i in 0..nn {
current[i].0 += step_dt / 6.0 * (k1[i].0 + 2.0 * k2[i].0 + 2.0 * k3[i].0 + k4[i].0);
current[i].1 += step_dt / 6.0 * (k1[i].1 + 2.0 * k2[i].1 + 2.0 * k3[i].1 + k4[i].1);
}
}
DensityMatrix::new(dim, current)
}
fn lindblad_rhs(&self, rho: &[(f64, f64)], dim: usize) -> Vec<(f64, f64)> {
let h = self.operator.elements();
let mut result = vec![(0.0, 0.0); dim * dim];
for i in 0..dim {
for j in 0..dim {
let (mut re, mut im) = (0.0, 0.0);
for k in 0..dim {
let (h_re, h_im) = h[i * dim + k];
let (r_re, r_im) = rho[k * dim + j];
re += h_re * r_re - h_im * r_im;
im += h_re * r_im + h_im * r_re;
let (r2_re, r2_im) = rho[i * dim + k];
let (h2_re, h2_im) = h[k * dim + j];
re -= r2_re * h2_re - r2_im * h2_im;
im -= r2_re * h2_im + r2_im * h2_re;
}
result[i * dim + j].0 += im;
result[i * dim + j].1 += -re;
}
}
for (gamma, l_op) in &self.dissipators {
let l = l_op.elements();
let mut ldl = vec![(0.0, 0.0); dim * dim];
for i in 0..dim {
for j in 0..dim {
for k in 0..dim {
let (l_ki_re, l_ki_im) = l[k * dim + i];
let (l_kj_re, l_kj_im) = l[k * dim + j];
ldl[i * dim + j].0 += l_ki_re * l_kj_re + l_ki_im * l_kj_im;
ldl[i * dim + j].1 += l_ki_re * l_kj_im - l_ki_im * l_kj_re;
}
}
}
for i in 0..dim {
for j in 0..dim {
let (mut re, mut im) = (0.0, 0.0);
for k in 0..dim {
for m in 0..dim {
let (l_ik_re, l_ik_im) = l[i * dim + k];
let (r_km_re, r_km_im) = rho[k * dim + m];
let (l_jm_re, l_jm_im) = l[j * dim + m];
let temp_re = l_ik_re * r_km_re - l_ik_im * r_km_im;
let temp_im = l_ik_re * r_km_im + l_ik_im * r_km_re;
re += temp_re * l_jm_re + temp_im * l_jm_im;
im += temp_im * l_jm_re - temp_re * l_jm_im;
}
let (ldl_re, ldl_im) = ldl[i * dim + k];
let (r_re, r_im) = rho[k * dim + j];
re -= 0.5 * (ldl_re * r_re - ldl_im * r_im);
im -= 0.5 * (ldl_re * r_im + ldl_im * r_re);
let (r2_re, r2_im) = rho[i * dim + k];
let (ldl2_re, ldl2_im) = ldl[k * dim + j];
re -= 0.5 * (r2_re * ldl2_re - r2_im * ldl2_im);
im -= 0.5 * (r2_re * ldl2_im + r2_im * ldl2_re);
}
result[i * dim + j].0 += gamma * re;
result[i * dim + j].1 += gamma * im;
}
}
}
result
}
}
pub fn expectation_value(observable: &Operator, rho: &DensityMatrix) -> Result<(f64, f64)> {
if observable.dim() != rho.dim() {
return Err(KanaError::DimensionMismatch {
expected: observable.dim(),
got: rho.dim(),
});
}
let dim = observable.dim();
let o = observable.elements();
let (mut re, mut im) = (0.0, 0.0);
for i in 0..dim {
for j in 0..dim {
let (o_re, o_im) = o[i * dim + j];
let (r_re, r_im) = rho.element(j, i).unwrap();
re += o_re * r_re - o_im * r_im;
im += o_re * r_im + o_im * r_re;
}
}
Ok((re, im))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_schrodinger_identity_hamiltonian() {
let h = Hamiltonian::new(Operator::new(2, vec![(0.0, 0.0); 4]).unwrap());
let state = StateVector::zero(1);
let evolved = h.evolve_state(&state, 1.0).unwrap();
assert!((evolved.probability(0).unwrap() - 1.0).abs() < 1e-10);
}
#[test]
fn test_schrodinger_pauli_z() {
let h = Hamiltonian::new(Operator::pauli_z());
let state = StateVector::plus();
let evolved = h.evolve_state(&state, std::f64::consts::FRAC_PI_2).unwrap();
let p0 = evolved.probability(0).unwrap();
let p1 = evolved.probability(1).unwrap();
assert!((p0 - 0.5).abs() < 1e-5);
assert!((p1 - 0.5).abs() < 1e-5);
}
#[test]
fn test_evolution_operator_unitarity() {
let h = Hamiltonian::new(Operator::pauli_x());
let u = h.evolution_operator(0.5).unwrap();
let udu = u.dagger().multiply(&u).unwrap();
let id = Operator::identity(2);
for i in 0..2 {
for j in 0..2 {
let (a_re, a_im) = udu.element(i, j).unwrap();
let (b_re, b_im) = id.element(i, j).unwrap();
assert!((a_re - b_re).abs() < 1e-8);
assert!((a_im - b_im).abs() < 1e-8);
}
}
}
#[test]
fn test_lindblad_pure_dephasing() {
let mut h = Hamiltonian::new(Operator::new(2, vec![(0.0, 0.0); 4]).unwrap());
h.add_dissipator(0.1, Operator::pauli_z());
let s = std::f64::consts::FRAC_1_SQRT_2;
let rho = DensityMatrix::from_pure_state(&[(s, 0.0), (s, 0.0)]);
let evolved = h.evolve_density(&rho, 10.0, 1000).unwrap();
let (re01, _im01) = evolved.element(0, 1).unwrap();
assert!(re01.abs() < 0.2);
let (re00, _) = evolved.element(0, 0).unwrap();
assert!((re00 - 0.5).abs() < 0.05);
}
#[test]
fn test_lindblad_amplitude_damping() {
let mut h = Hamiltonian::new(Operator::new(2, vec![(0.0, 0.0); 4]).unwrap());
let l01 = Operator::new(2, vec![(0.0, 0.0), (1.0, 0.0), (0.0, 0.0), (0.0, 0.0)]).unwrap();
h.add_dissipator(0.5, l01);
let rho = DensityMatrix::from_pure_state(&[(0.0, 0.0), (1.0, 0.0)]);
let evolved = h.evolve_density(&rho, 5.0, 500).unwrap();
let (re00, _) = evolved.element(0, 0).unwrap();
assert!(re00 > 0.8);
}
#[test]
fn test_expectation_value_z() {
let rho = DensityMatrix::from_pure_state(&[(1.0, 0.0), (0.0, 0.0)]);
let (re, im) = expectation_value(&Operator::pauli_z(), &rho).unwrap();
assert!((re - 1.0).abs() < 1e-10);
assert!(im.abs() < 1e-10);
}
#[test]
fn test_expectation_value_x_plus() {
let s = std::f64::consts::FRAC_1_SQRT_2;
let rho = DensityMatrix::from_pure_state(&[(s, 0.0), (s, 0.0)]);
let (re, im) = expectation_value(&Operator::pauli_x(), &rho).unwrap();
assert!((re - 1.0).abs() < 1e-10);
assert!(im.abs() < 1e-10);
}
#[test]
fn test_evolve_state_rejects_open_system() {
let mut h = Hamiltonian::new(Operator::pauli_z());
h.add_dissipator(0.1, Operator::pauli_z());
let state = StateVector::zero(1);
assert!(h.evolve_state(&state, 1.0).is_err());
}
}