use quantrs2_sim::fermionic_simulation::{
FermionicHamiltonian, FermionicOperator, FermionicSimulator, FermionicString,
};
use quantrs2_sim::pauli::{PauliOperator, PauliString};
use scirs2_core::Complex64;
#[test]
fn test_number_operator_mode0_occupied() {
let mut sim = FermionicSimulator::new(2).expect("create simulator");
sim.set_initial_state(&[true, false])
.expect("set initial state");
let n0 = sim
.expectation_value(&FermionicOperator::Number(0))
.expect("n0 expectation");
let n1 = sim
.expectation_value(&FermionicOperator::Number(1))
.expect("n1 expectation");
assert!(
(n0.re - 1.0).abs() < 1e-10,
"mode 0 occupied: expected 1.0, got {:.6}",
n0.re
);
assert!(
n1.re.abs() < 1e-10,
"mode 1 empty: expected 0.0, got {:.6}",
n1.re
);
}
#[test]
fn test_number_operator_mode1_occupied() {
let mut sim = FermionicSimulator::new(2).expect("create simulator");
sim.set_initial_state(&[false, true])
.expect("set initial state");
let n0 = sim
.expectation_value(&FermionicOperator::Number(0))
.expect("n0 expectation");
let n1 = sim
.expectation_value(&FermionicOperator::Number(1))
.expect("n1 expectation");
assert!(
n0.re.abs() < 1e-10,
"mode 0 empty: expected 0.0, got {:.6}",
n0.re
);
assert!(
(n1.re - 1.0).abs() < 1e-10,
"mode 1 occupied: expected 1.0, got {:.6}",
n1.re
);
}
#[test]
fn test_pauli_z_expectation_both_occupied() {
let mut sim = FermionicSimulator::new(2).expect("create simulator");
sim.set_initial_state(&[true, true])
.expect("set initial state");
let zi_string =
PauliString::from_string("ZI", Complex64::new(1.0, 0.0)).expect("create ZI string");
let mut sim1 = FermionicSimulator::new(1).expect("create 1-mode simulator");
sim1.set_initial_state(&[true]).expect("set occupied");
let z_string =
PauliString::from_string("Z", Complex64::new(1.0, 0.0)).expect("create Z string");
let n0 = sim1
.expectation_value(&FermionicOperator::Number(0))
.expect("n0 expectation");
let z_exp = 2.0_f64.mul_add(-n0.re, 1.0);
assert!(
(z_exp - (-1.0)).abs() < 1e-10,
"⟨Z⟩ for occupied mode: expected -1.0, got {z_exp:.6}"
);
let n0_2mode = sim
.expectation_value(&FermionicOperator::Number(0))
.expect("n0 2-mode");
let z0_2mode = 2.0_f64.mul_add(-n0_2mode.re, 1.0);
assert!(
(z0_2mode - (-1.0)).abs() < 1e-10,
"⟨Z_0⟩ in |11⟩: expected -1.0, got {z0_2mode:.6}"
);
let _ = zi_string; let _ = z_string;
}
#[test]
fn test_norm_preserved_after_evolution() {
let hamiltonian =
FermionicHamiltonian::hubbard_model(2, 1.0, 1.0, 0.0).expect("create Hubbard model");
let mut sim = FermionicSimulator::new(4).expect("create simulator");
sim.set_initial_state(&[true, false, true, false])
.expect("set initial state");
for step in 1..=5 {
let t = 0.1 * f64::from(step);
sim.evolve_hamiltonian(&hamiltonian, t)
.expect("evolve Hamiltonian");
let norm_sq: f64 = sim.get_state().iter().map(|a| a.norm_sqr()).sum();
assert!(
(norm_sq - 1.0).abs() < 1e-8,
"norm² after t={t:.1}: expected 1.0, got {norm_sq:.10}"
);
}
}
#[test]
fn test_hopping_moves_particle() {
let mut hamiltonian = FermionicHamiltonian::new(2);
let hop_fwd = FermionicString::hopping(0, 1, Complex64::new(-1.0, 0.0), 2);
hamiltonian.add_term(hop_fwd).expect("add fwd hop");
let hop_bwd = FermionicString::hopping(1, 0, Complex64::new(-1.0, 0.0), 2);
hamiltonian.add_term(hop_bwd).expect("add bwd hop");
let mut sim = FermionicSimulator::new(2).expect("create simulator");
sim.set_initial_state(&[true, false])
.expect("set initial state");
let n0_init = sim
.expectation_value(&FermionicOperator::Number(0))
.expect("n0 init")
.re;
let n1_init = sim
.expectation_value(&FermionicOperator::Number(1))
.expect("n1 init")
.re;
assert!(
(n0_init - 1.0).abs() < 1e-10,
"initial n0 expected 1.0, got {n0_init:.6}"
);
assert!(
n1_init.abs() < 1e-10,
"initial n1 expected 0.0, got {n1_init:.6}"
);
sim.evolve_hamiltonian(&hamiltonian, 0.3).expect("evolve");
let n0_after = sim
.expectation_value(&FermionicOperator::Number(0))
.expect("n0 after")
.re;
let n1_after = sim
.expectation_value(&FermionicOperator::Number(1))
.expect("n1 after")
.re;
assert!(
n0_after < n0_init - 1e-4,
"mode 0 should decrease after hopping: {n0_init:.6} → {n0_after:.6}"
);
assert!(
n1_after > n1_init + 1e-4,
"mode 1 should increase after hopping: {n1_init:.6} → {n1_after:.6}"
);
assert!(
(n0_after + n1_after - 1.0).abs() < 1e-8,
"total particle number should be conserved: {:.6}",
n0_after + n1_after
);
}
#[test]
fn test_jw_number_operator() {
let mut sim_vac = FermionicSimulator::new(2).expect("create simulator");
let n0_vac = sim_vac
.expectation_value(&FermionicOperator::Number(0))
.expect("vacuum n0")
.re;
assert!(
n0_vac.abs() < 1e-10,
"vacuum ⟨n_0⟩ should be 0, got {n0_vac:.6}"
);
let mut sim_occ = FermionicSimulator::new(2).expect("create simulator");
sim_occ
.set_initial_state(&[true, false])
.expect("set occupied");
let n0_occ = sim_occ
.expectation_value(&FermionicOperator::Number(0))
.expect("occupied n0")
.re;
assert!(
(n0_occ - 1.0).abs() < 1e-10,
"occupied ⟨n_0⟩ should be 1, got {n0_occ:.6}"
);
use quantrs2_sim::fermionic_simulation::JordanWignerTransform;
let mut jw = JordanWignerTransform::new(2);
let pauli = jw
.transform_operator(&FermionicOperator::Number(0))
.expect("JW transform number op");
assert_eq!(
pauli.operators[0],
PauliOperator::Z,
"number operator site 0 should have Z at position 0"
);
assert!(
(pauli.coefficient.re - (-0.5)).abs() < 1e-10,
"number operator JW coefficient should be -0.5, got {:.6}",
pauli.coefficient.re
);
}
#[test]
fn test_particle_correlation_product_state() {
let mut sim = FermionicSimulator::new(2).expect("create simulator");
sim.set_initial_state(&[true, false])
.expect("set initial state");
let corr = sim.particle_correlation(0, 1).expect("correlation");
assert!(
corr.abs() < 1e-10,
"connected correlation in product state should be 0, got {corr:.10}"
);
}
#[test]
fn test_vacuum_state_zero_occupation() {
let mut sim = FermionicSimulator::new(3).expect("create simulator");
for mode in 0..3 {
let n = sim
.expectation_value(&FermionicOperator::Number(mode))
.expect("number op");
assert!(
n.re.abs() < 1e-10,
"vacuum ⟨n_{mode}⟩ should be 0, got {:.6}",
n.re
);
}
}