use ndarray::{Array1, Array2};
use ndarray_linalg::{Lapack, QR};
use ndarray_rand::{
RandomExt,
rand::distributions::Distribution,
rand::thread_rng,
rand_distr::{Normal, StandardNormal},
};
use num_complex::Complex;
use num_traits::Float;
fn ginibre<T>(m: usize, n: usize, is_real: bool) -> Array2<Complex<T>>
where
T: Float + ndarray_linalg::Scalar,
StandardNormal: Distribution<T>,
{
let mut rng = thread_rng();
let normal = Normal::new(T::zero(), T::one()).unwrap();
if is_real {
Array2::random_using((m, n), normal, &mut rng).mapv(|x| Complex::new(x, T::zero()))
} else {
let re = Array2::random_using((m, n), normal, &mut rng);
let im = Array2::random_using((m, n), normal, &mut rng);
re.mapv(|x| Complex::new(x, T::zero())) + im.mapv(|y| Complex::new(T::zero(), y))
}
}
pub struct RandomUnitary<T> {
dim_col: usize,
dim_row: Option<usize>,
is_real: Option<bool>,
_marker: std::marker::PhantomData<T>,
}
impl<T> RandomUnitary<T>
where
T: Float + ndarray_linalg::Scalar,
StandardNormal: Distribution<T>,
Complex<T>: Lapack,
{
pub fn new(dim_col: usize) -> Self {
Self {
dim_col,
dim_row: None,
is_real: None,
_marker: std::marker::PhantomData,
}
}
pub fn dim(mut self, dim_row: usize) -> Self {
self.dim_row = Some(dim_row);
self
}
pub fn is_real(mut self, flag: bool) -> Self {
self.is_real = Some(flag);
self
}
pub fn build(self) -> Array2<Complex<T>> {
let dim_row = self.dim_row.unwrap_or(self.dim_col);
let is_real = self.is_real.unwrap_or(false);
let gin: Array2<Complex<T>> = ginibre::<T>(self.dim_col, dim_row, is_real);
let (mut q, r) = gin.qr().expect("QR decomposition failed");
let r_diag_signs: Array1<T> = r.diag().mapv(|c| {
let re = c.re.signum();
if re == T::zero() { T::one() } else { re }
});
for (mut col, &sign) in q.columns_mut().into_iter().zip(&r_diag_signs) {
col.mapv_inplace(|z| z * Complex::new(sign, T::zero()));
}
q
}
}
pub struct RandomDensityMat<T: Float + ndarray_linalg::Scalar> {
dim_col: usize,
dim_row: Option<usize>,
is_real: Option<bool>,
is_bures: bool,
_marker: std::marker::PhantomData<T>,
}
impl<T> RandomDensityMat<T>
where
T: Float + ndarray_linalg::Scalar,
StandardNormal: Distribution<T>,
Complex<T>: Lapack,
{
pub fn new(dim_col: usize) -> Self {
Self {
dim_col,
dim_row: None,
is_real: None,
is_bures: false,
_marker: std::marker::PhantomData,
}
}
pub fn dim(mut self, dim_row: usize) -> Self {
self.dim_row = Some(dim_row);
self
}
pub fn is_real(mut self, flag: bool) -> Self {
self.is_real = Some(flag);
self
}
pub fn bures(mut self) -> Self {
self.is_bures = true;
self
}
pub fn build(self) -> Array2<Complex<T>> {
let dim_row = self.dim_row.unwrap_or(self.dim_col);
let is_real = self.is_real.unwrap_or(false);
let mut gin = ginibre::<T>(self.dim_col, dim_row, is_real);
if self.is_bures {
let u = RandomUnitary::<T>::new(self.dim_col)
.is_real(is_real)
.build();
let eye = Array2::<Complex<T>>::eye(self.dim_col);
gin = (u + eye).dot(&gin);
}
let mut rho = gin.dot(&gin.t().mapv(|c| c.conj()));
let trace = rho.diag().sum();
rho.mapv_inplace(|x| x / trace);
rho
}
}