use crate::{OperateOnDensityMatrix, SpinIndex, StruqtureError};
use num_complex::{Complex, Complex64};
use qoqo_calculator::{CalculatorComplex, CalculatorFloat};
use std::collections::HashMap;
use std::convert::TryInto;
use std::iter::IntoIterator;
use std::ops::{Add, Mul, Sub};
mod decoherence_product;
pub use decoherence_product::*;
mod pauli_product;
pub use pauli_product::*;
mod decoherence_operator;
pub use decoherence_operator::*;
mod pauli_operator;
pub use pauli_operator::*;
mod pauli_hamiltonian;
pub use pauli_hamiltonian::*;
mod pauli_noise_operator;
pub use pauli_noise_operator::*;
mod pauli_open_system;
pub use pauli_open_system::*;
mod plus_minus_product;
pub use plus_minus_product::*;
mod plus_minus_operator;
pub use plus_minus_operator::*;
mod plus_minus_noise_operator;
pub use plus_minus_noise_operator::*;
use crate::CooSparseMatrix;
pub trait OperateOnSpins<'a>: PartialEq + Clone + Mul<CalculatorFloat> + Add + Sub {
fn current_number_spins(&self) -> usize;
}
pub trait ToSparseMatrixOperator<'a>:
ToSparseMatrixSuperOperator<'a>
+ OperateOnSpins<'a>
+ OperateOnDensityMatrix<'a>
+ IntoIterator<Item = (Self::Index, Self::Value)>
+ PartialEq
+ Clone
where
SinglePauliOperator:
From<<<Self as OperateOnDensityMatrix<'a>>::Index as SpinIndex>::SingleSpinType>,
CalculatorComplex: From<<Self as OperateOnDensityMatrix<'a>>::Value>,
&'a Self: IntoIterator<Item = (&'a Self::Index, &'a Self::Value)>,
Self::Index: SpinIndex,
Self::Value: Into<CalculatorComplex>,
{
fn sparse_matrix(
&'a self,
number_spins: usize,
) -> Result<HashMap<(usize, usize), Complex64>, StruqtureError> {
let dimension = 2usize.pow(number_spins as u32);
let mut matrix: HashMap<(usize, usize), Complex64> = HashMap::new();
for row in 0..dimension {
for (column, val) in self.sparse_matrix_entries_on_row(row)?.into_iter() {
matrix.insert((row, column), val);
}
}
Ok(matrix)
}
fn sparse_matrix_coo(&'a self, number_spins: usize) -> Result<CooSparseMatrix, StruqtureError> {
let dimension = 2usize.pow(number_spins as u32);
let capacity = dimension;
let mut values: Vec<Complex64> = Vec::with_capacity(capacity);
let mut rows: Vec<usize> = Vec::with_capacity(capacity);
let mut columns: Vec<usize> = Vec::with_capacity(capacity);
for row in 0..dimension {
for (col, val) in self.sparse_matrix_entries_on_row(row)?.into_iter() {
rows.push(row);
columns.push(col);
values.push(val);
}
}
Ok((values, (rows, columns)))
}
fn sparse_matrix_entries_on_row(
&'a self,
row: usize,
) -> Result<HashMap<usize, Complex<f64>>, StruqtureError> {
let mut entries: HashMap<usize, Complex<f64>> = HashMap::with_capacity(self.len());
for (index, value) in self.iter() {
let mut column = row;
let mut prefac: Complex<f64> = 1.0.into();
for (spin_op_index, pauliop) in index.iter() {
match SinglePauliOperator::from(*pauliop) {
SinglePauliOperator::X => {
match row.div_euclid(2usize.pow(*spin_op_index as u32)) % 2 {
0 => column += 2usize.pow(*spin_op_index as u32),
1 => column -= 2usize.pow(*spin_op_index as u32),
_ => panic!("Internal error in constructing matrix"),
}
}
SinglePauliOperator::Y => {
match row.div_euclid(2usize.pow(*spin_op_index as u32)) % 2 {
0 => {
column += 2usize.pow(*spin_op_index as u32);
prefac *= Complex::<f64>::new(0.0, -1.0);
}
1 => {
column -= 2usize.pow(*spin_op_index as u32);
prefac *= Complex::<f64>::new(0.0, 1.0);
}
_ => panic!("Internal error in constructing matrix"),
};
}
SinglePauliOperator::Z => {
match row.div_euclid(2usize.pow(*spin_op_index as u32)) % 2 {
0 => {
prefac *= Complex::<f64>::new(1.0, 0.0);
}
1 => {
prefac *= Complex::<f64>::new(-1.0, 0.0);
}
_ => panic!("Internal error in constructing matrix"),
};
}
SinglePauliOperator::Identity => (),
}
}
let mut_value = entries.get_mut(&column);
let ri_value = CalculatorComplex::from(value.clone());
let real_value: f64 = ri_value.re.try_into()?;
let imag_value: f64 = ri_value.im.try_into()?;
let complex_value = Complex::<f64>::new(real_value, imag_value);
match mut_value {
Some(x) => *x += prefac * complex_value,
None => {
entries.insert(column, prefac * complex_value);
}
}
}
Ok(entries)
}
fn sparse_matrix_superoperator_entries_on_row(
&'a self,
row: usize,
number_spins: usize,
) -> Result<HashMap<usize, Complex<f64>>, StruqtureError> {
let mut entries: HashMap<usize, Complex<f64>> = HashMap::new();
let dimension = 2_usize.pow(number_spins as u32);
let constant_prefactor = Complex64::new(0.0, -1.0);
for (index, value) in self.iter() {
for (row_adjusted, commutator_prefactor, shift) in [
(row.div_euclid(dimension), 1.0, number_spins),
(row % dimension, -1.0, 0),
] {
let mut column = row;
let mut prefac: Complex<f64> = 1.0.into();
for (spin_op_index, pauliop) in index.iter() {
match SinglePauliOperator::from(*pauliop) {
SinglePauliOperator::X => {
match row_adjusted.div_euclid(2usize.pow(*spin_op_index as u32)) % 2 {
0 => column += 2usize.pow((*spin_op_index + shift) as u32),
1 => column -= 2usize.pow((*spin_op_index + shift) as u32),
_ => panic!("Internal error in constructing matrix"),
}
}
SinglePauliOperator::Y => {
match row_adjusted.div_euclid(2usize.pow(*spin_op_index as u32)) % 2 {
0 => {
column += 2usize.pow((*spin_op_index + shift) as u32);
prefac *= Complex::<f64>::new(0.0, -1.0) * commutator_prefactor;
}
1 => {
column -= 2usize.pow((*spin_op_index + shift) as u32);
prefac *= Complex::<f64>::new(0.0, 1.0) * commutator_prefactor;
}
_ => panic!("Internal error in constructing matrix"),
};
}
SinglePauliOperator::Z => {
match row_adjusted.div_euclid(2usize.pow(*spin_op_index as u32)) % 2 {
0 => {
prefac *= Complex::<f64>::new(1.0, 0.0);
}
1 => {
prefac *= Complex::<f64>::new(-1.0, 0.0);
}
_ => panic!("Internal error in constructing matrix"),
};
}
SinglePauliOperator::Identity => (),
}
}
prefac *= commutator_prefactor * constant_prefactor;
let mut_value = entries.get_mut(&column);
let ri_value = CalculatorComplex::from(value.clone());
let real_value: f64 = ri_value.re.try_into()?;
let imag_value: f64 = ri_value.im.try_into()?;
let complex_value = Complex::<f64>::new(real_value, imag_value);
if complex_value != Complex64::new(0.0, 0.0) {
match mut_value {
Some(x) => {
if *x + prefac * complex_value == Complex64::new(0.0, 0.0) {
entries.remove(&column);
} else {
*x += prefac * complex_value;
}
}
None => {
entries.insert(column, prefac * complex_value);
}
}
}
}
}
Ok(entries)
}
}
pub trait ToSparseMatrixSuperOperator<'a>: OperateOnSpins<'a> + PartialEq + Clone {
fn sparse_matrix_superoperator(
&'a self,
number_spins: usize,
) -> Result<HashMap<(usize, usize), Complex64>, StruqtureError> {
let dimension = 2usize.pow(number_spins as u32);
let mut matrix: HashMap<(usize, usize), Complex64> = HashMap::new();
for row in 0..dimension.pow(2) {
for (column, val) in self
.sparse_matrix_superoperator_entries_on_row(row, number_spins)?
.into_iter()
{
matrix.insert((row, column), val);
}
}
Ok(matrix)
}
fn sparse_matrix_superoperator_coo(
&'a self,
number_spins: usize,
) -> Result<CooSparseMatrix, StruqtureError> {
let dimension = 2usize.pow(number_spins as u32);
let capacity = dimension;
let mut values: Vec<Complex64> = Vec::with_capacity(capacity);
let mut rows: Vec<usize> = Vec::with_capacity(capacity);
let mut columns: Vec<usize> = Vec::with_capacity(capacity);
for row in 0..dimension.pow(2) {
for (col, val) in self
.sparse_matrix_superoperator_entries_on_row(row, number_spins)?
.into_iter()
{
rows.push(row);
columns.push(col);
values.push(val);
}
}
Ok((values, (rows, columns)))
}
fn sparse_matrix_superoperator_entries_on_row(
&'a self,
row: usize,
number_spins: usize,
) -> Result<HashMap<usize, Complex<f64>>, StruqtureError>;
}
pub trait HermitianOperateOnSpins<'a>:
OperateOnSpins<'a>
+ OperateOnDensityMatrix<'a>
+ IntoIterator<Item = (Self::Index, Self::Value)>
+ PartialEq
+ Clone
where
&'a Self: IntoIterator<Item = (&'a Self::Index, &'a Self::Value)>,
SinglePauliOperator:
From<<<Self as OperateOnDensityMatrix<'a>>::Index as SpinIndex>::SingleSpinType>,
<Self as OperateOnDensityMatrix<'a>>::Value: Into<CalculatorFloat>,
Self::Index: SpinIndex,
{
}