use crate::ComplexScalar;
use crate::QuditSystem;
use crate::Radices;
use crate::RealScalar;
use faer::{Col, Mat, Row};
use num_complex::ComplexFloat;
use num_traits::One;
use std::ops::Index;
pub struct Ket<R: RealScalar> {
radices: Radices,
vector: Col<R::C>,
}
impl<R: RealScalar> Ket<R> {
#[inline(always)]
#[track_caller]
pub fn new<T: Into<Radices>, V: Into<Self>>(radices: T, vector: V) -> Self {
let guessed_radices_ket = vector.into();
let radices = radices.into();
Self {
radices,
vector: guessed_radices_ket.vector,
}
}
pub fn zero<T: Into<Radices>>(radices: T) -> Self {
Self::basis(radices, 0)
}
pub fn basis<T: Into<Radices>>(radices: T, index: usize) -> Self {
let radices = radices.into();
let dimension = radices.dimension();
assert!(
index < dimension,
"Index {} out of bounds for dimension {}",
index,
dimension
);
let mut vector = Col::zeros(dimension);
vector[index] = R::C::one();
Self { radices, vector }
}
pub fn uniform<T: Into<Radices>>(radices: T) -> Self {
let radices = radices.into();
let dimension = radices.dimension();
let amplitude = R::C::from_real(R::one() / R::from(dimension).unwrap().sqrt());
Self {
radices,
vector: Col::from_fn(dimension, |_| amplitude),
}
}
pub fn is_pure_state(&self) -> bool {
let norm_squared = self.vector.squared_norm_l2();
R::ONE.is_close(norm_squared)
}
pub fn get_distance_from(&self, other: &Self) -> R {
assert_eq!(
self.vector.nrows(),
other.vector.nrows(),
"Cannot compute distance between states of different dimensions"
);
let inner_product = self.inner_product(other);
let fidelity = inner_product.norm_squared();
R::one() - fidelity
}
fn inner_product(&self, other: &Self) -> R::C {
self.vector
.iter()
.zip(other.vector.iter())
.map(|(&a, &b)| a.conj() * b)
.sum()
}
pub fn probabilities(&self) -> Vec<R> {
self.vector.iter().map(|&z| z.norm_squared()).collect()
}
pub fn probability_at(&self, index: usize) -> R {
assert!(index < self.vector.nrows(), "Index {} out of bounds", index);
self.vector[index].norm_squared()
}
pub fn tensor_product(&self, other: &Self) -> Self {
let new_radices = self.radices.concat(&other.radices);
let self_dim = self.vector.nrows();
let other_dim = other.vector.nrows();
let new_dim = self_dim * other_dim;
let new_vector = Col::from_fn(new_dim, |idx| {
let i = idx / other_dim;
let j = idx % other_dim;
self.vector[i] * other.vector[j]
});
Self {
radices: new_radices,
vector: new_vector,
}
}
}
impl<R: RealScalar> QuditSystem for Ket<R> {
fn radices(&self) -> Radices {
self.radices.clone()
}
fn dimension(&self) -> usize {
self.radices.dimension()
}
}
impl<R: RealScalar> Index<usize> for Ket<R> {
type Output = R::C;
fn index(&self, index: usize) -> &Self::Output {
&self.vector[index]
}
}
impl<R: RealScalar, T: Into<R::C> + Copy> TryFrom<Mat<T>> for Ket<R> {
type Error = String;
fn try_from(value: Mat<T>) -> Result<Self, Self::Error> {
if value.ncols() != 1 {
return Err(format!(
"Matrix has {} columns, expected 1 column for conversion to Ket",
value.ncols()
));
}
let vec: Vec<R::C> = value.col(0).iter().copied().map(|x| x.into()).collect();
Ok(vec.into())
}
}
impl<C: ComplexScalar> From<Col<C>> for Ket<C::R> {
fn from(value: Col<C>) -> Self {
let radices = Radices::guess(value.nrows());
Self {
radices,
vector: value,
}
}
}
impl<R: RealScalar, T: Into<R::C> + Copy> From<Row<T>> for Ket<R> {
fn from(value: Row<T>) -> Self {
let vec: Vec<R::C> = value.iter().copied().map(|x| x.into()).collect();
vec.into()
}
}
impl<R: RealScalar, T: Into<R::C> + Copy> From<Vec<T>> for Ket<R> {
fn from(value: Vec<T>) -> Self {
let radices = Radices::guess(value.len());
Self {
radices,
vector: Col::from_fn(value.len(), |i| value[i].into()),
}
}
}
impl<R: RealScalar, T: Into<R::C>> FromIterator<T> for Ket<R> {
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
let vec: Vec<R::C> = iter.into_iter().map(|x| x.into()).collect();
vec.into()
}
}
impl<R: RealScalar, T: Into<R::C> + Copy> From<&[T]> for Ket<R> {
fn from(value: &[T]) -> Self {
let radices = Radices::guess(value.len());
Self {
radices,
vector: Col::from_fn(value.len(), |i| value[i].into()),
}
}
}
impl<R: RealScalar, T: Into<R::C> + Copy, const N: usize> From<&[T; N]> for Ket<R> {
fn from(value: &[T; N]) -> Self {
let radices = Radices::guess(N);
Self {
radices,
vector: Col::from_fn(N, |i| value[i].into()),
}
}
}
impl<R: RealScalar, T: Into<R::C> + Copy, const N: usize> From<[T; N]> for Ket<R> {
fn from(value: [T; N]) -> Self {
let radices = Radices::guess(N);
Self {
radices,
vector: Col::from_fn(N, |i| value[i].into()),
}
}
}