use crate::{DenseSimulator, Error, Matrix, Result, StateVector, Unitary};
use burn::tensor::Tensor;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]
struct ObservableData {
wires: Vec<usize>,
matrix: Matrix,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(try_from = "ObservableData", into = "ObservableData")]
pub struct Observable {
wires: Vec<usize>,
matrix: Matrix,
}
impl Observable {
pub fn new(wires: Vec<usize>, matrix: Matrix) -> Result<Self> {
if wires.is_empty()
|| wires
.iter()
.enumerate()
.any(|(i, w)| wires[..i].contains(w))
{
return Err(Error::Wires(
"observable targets empty or duplicated".into(),
));
}
matrix.check_hermitian(1e-10)?;
Ok(Self { wires, matrix })
}
pub fn z(wire: usize) -> Self {
Self::new(vec![wire], Unitary::z().matrix().clone()).unwrap()
}
pub fn x(wire: usize) -> Self {
Self::new(vec![wire], Unitary::x().matrix().clone()).unwrap()
}
pub fn y(wire: usize) -> Self {
Self::new(vec![wire], Unitary::y().matrix().clone()).unwrap()
}
pub fn wires(&self) -> &[usize] {
&self.wires
}
pub fn matrix(&self) -> &Matrix {
&self.matrix
}
pub fn expectation(&self, engine: &DenseSimulator, state: &StateVector) -> Result<Tensor<1>> {
engine.limits.check_matrix(1, self.matrix.dimension())?;
let op = self
.matrix
.to_tensor(&state.device(), state.amplitudes().dtype())?;
let out = engine.apply_tensor(state, &self.wires, &op)?;
let a = state.amplitudes();
let b = out.amplitudes();
Ok((a.real() * b.real() + a.imag() * b.imag())
.sum_dim(1)
.reshape([state.batch_size()]))
}
pub fn variance(&self, engine: &DenseSimulator, state: &StateVector) -> Result<Tensor<1>> {
engine.limits.check_matrix(1, self.matrix.dimension())?;
let op = self
.matrix
.to_tensor(&state.device(), state.amplitudes().dtype())?;
let out = engine.apply_tensor(state, &self.wires, &op)?;
Ok(out.norm_squared() - self.expectation(engine, state)?.square())
}
}
impl TryFrom<ObservableData> for Observable {
type Error = Error;
fn try_from(v: ObservableData) -> Result<Self> {
Self::new(v.wires, v.matrix)
}
}
impl From<Observable> for ObservableData {
fn from(v: Observable) -> Self {
Self {
wires: v.wires,
matrix: v.matrix,
}
}
}
#[derive(Clone, Debug)]
pub struct Hamiltonian {
terms: Vec<(f64, Observable)>,
}
impl Hamiltonian {
pub fn new(terms: Vec<(f64, Observable)>) -> Result<Self> {
if terms.iter().any(|(weight, _)| !weight.is_finite()) {
return Err(Error::InvalidValue(
"nonfinite Hamiltonian coefficient".into(),
));
}
Ok(Self { terms })
}
pub fn terms(&self) -> &[(f64, Observable)] {
&self.terms
}
pub fn expectation(&self, engine: &DenseSimulator, state: &StateVector) -> Result<Tensor<1>> {
let mut result = state.norm_squared() * 0.;
for (weight, observable) in &self.terms {
result = result + observable.expectation(engine, state)? * *weight;
}
Ok(result)
}
}