use super::{FermionToQubitMapper, OccupationStateMapper};
use crate::qis::fermion::{FermionicLadder, FermionicOp};
use crate::qis::{Hamiltonian, Pauli, PauliString, QisError};
use num_complex::Complex64;
use std::collections::BTreeMap;
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct JordanWignerMapper;
impl JordanWignerMapper {
pub fn new() -> Self {
Self
}
}
impl FermionToQubitMapper for JordanWignerMapper {
fn num_qubits(&self, num_modes: usize) -> usize {
num_modes
}
fn map_operator(&self, op: &FermionicOp) -> Result<Hamiltonian, QisError> {
let n = self.num_qubits(op.num_modes());
let mut result = Hamiltonian::new(n);
let mut components = BTreeMap::new();
for (term, coefficient) in op.terms() {
let mut product = Hamiltonian::new(n);
product.add_term(PauliString::new(n), Complex64::new(1.0, 0.0))?;
for ladder in term {
let p = ladder.mode();
let (x, y) = components.entry(p).or_insert_with(|| {
let mut x = PauliString::new(n);
let mut y = PauliString::new(n);
for j in 0..p {
x.set_pauli(j, Pauli::Z);
y.set_pauli(j, Pauli::Z);
}
x.set_pauli(p, Pauli::X);
y.set_pauli(p, Pauli::Y);
(x, y)
});
let imaginary = match ladder {
FermionicLadder::Create(_) => -0.5,
FermionicLadder::Annihilate(_) => 0.5,
};
let mut next = Hamiltonian::new(n);
for (pauli, c) in &product.terms {
next.add_term(pauli * &*x, *c * 0.5)?;
next.add_term(pauli * &*y, *c * Complex64::new(0.0, imaginary))?;
}
product = next.normalized()?;
if product.terms.is_empty() {
break;
}
}
result.terms.extend(
product
.terms
.into_iter()
.map(|(pauli, c)| (pauli, c * coefficient)),
);
}
result.normalized()
}
}
impl OccupationStateMapper for JordanWignerMapper {
fn map_occupation(&self, occupation: &[u8]) -> Result<Vec<bool>, QisError> {
occupation
.iter()
.enumerate()
.map(|(index, value)| match value {
0 => Ok(false),
1 => Ok(true),
value => Err(QisError::InvalidOccupation {
index,
value: *value,
}),
})
.collect()
}
}