use num_complex::Complex;
use num_traits::Num;
pub trait ElementOpApply: Copy {
#[inline(always)]
fn conj(self) -> Self {
self
}
#[inline(always)]
fn transpose(self) -> Self {
self
}
#[inline(always)]
fn adjoint(self) -> Self {
self
}
}
macro_rules! impl_element_op_apply_real {
($($t:ty),*) => {
$(impl ElementOpApply for $t {})*
};
}
impl_element_op_apply_real!(
f32, f64, i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize
);
impl<T: Num + Copy + Clone + std::ops::Neg<Output = T>> ElementOpApply for Complex<T> {
#[inline(always)]
fn conj(self) -> Self {
Complex::conj(&self)
}
#[inline(always)]
fn transpose(self) -> Self {
self
}
#[inline(always)]
fn adjoint(self) -> Self {
Complex::conj(&self)
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct Identity;
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct Conj;
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct Transpose;
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct Adjoint;
pub trait ElementOp<T>: Copy + Default + 'static {
const IS_IDENTITY: bool = false;
fn apply(value: T) -> T;
}
impl<T: Copy> ElementOp<T> for Identity {
const IS_IDENTITY: bool = true;
#[inline(always)]
fn apply(value: T) -> T {
value
}
}
impl<T: ElementOpApply> ElementOp<T> for Conj {
#[inline(always)]
fn apply(value: T) -> T {
value.conj()
}
}
impl<T: ElementOpApply> ElementOp<T> for Transpose {
#[inline(always)]
fn apply(value: T) -> T {
value.transpose()
}
}
impl<T: ElementOpApply> ElementOp<T> for Adjoint {
#[inline(always)]
fn apply(value: T) -> T {
value.adjoint()
}
}
pub trait ComposableElementOp<T: ElementOpApply>: ElementOp<T> {
type Inverse: ComposableElementOp<T>;
type ComposeConj: ComposableElementOp<T>;
type ComposeTranspose: ComposableElementOp<T>;
type ComposeAdjoint: ComposableElementOp<T>;
}
impl<T: ElementOpApply> ComposableElementOp<T> for Identity {
type Inverse = Identity;
type ComposeConj = Conj;
type ComposeTranspose = Transpose;
type ComposeAdjoint = Adjoint;
}
impl<T: ElementOpApply> ComposableElementOp<T> for Conj {
type Inverse = Conj;
type ComposeConj = Identity;
type ComposeTranspose = Adjoint;
type ComposeAdjoint = Transpose;
}
impl<T: ElementOpApply> ComposableElementOp<T> for Transpose {
type Inverse = Transpose;
type ComposeConj = Adjoint;
type ComposeTranspose = Identity;
type ComposeAdjoint = Conj;
}
impl<T: ElementOpApply> ComposableElementOp<T> for Adjoint {
type Inverse = Adjoint;
type ComposeConj = Transpose;
type ComposeTranspose = Conj;
type ComposeAdjoint = Identity;
}
pub trait Compose<T: ElementOpApply, Other: ComposableElementOp<T>>:
ComposableElementOp<T>
{
type Result: ComposableElementOp<T>;
}
impl<T: ElementOpApply, Op: ComposableElementOp<T>> Compose<T, Identity> for Op {
type Result = Op;
}
impl<T: ElementOpApply> Compose<T, Conj> for Identity {
type Result = Conj;
}
impl<T: ElementOpApply> Compose<T, Conj> for Conj {
type Result = Identity;
}
impl<T: ElementOpApply> Compose<T, Conj> for Transpose {
type Result = Adjoint;
}
impl<T: ElementOpApply> Compose<T, Conj> for Adjoint {
type Result = Transpose;
}
impl<T: ElementOpApply> Compose<T, Transpose> for Identity {
type Result = Transpose;
}
impl<T: ElementOpApply> Compose<T, Transpose> for Conj {
type Result = Adjoint;
}
impl<T: ElementOpApply> Compose<T, Transpose> for Transpose {
type Result = Identity;
}
impl<T: ElementOpApply> Compose<T, Transpose> for Adjoint {
type Result = Conj;
}
impl<T: ElementOpApply> Compose<T, Adjoint> for Identity {
type Result = Adjoint;
}
impl<T: ElementOpApply> Compose<T, Adjoint> for Conj {
type Result = Transpose;
}
impl<T: ElementOpApply> Compose<T, Adjoint> for Transpose {
type Result = Conj;
}
impl<T: ElementOpApply> Compose<T, Adjoint> for Adjoint {
type Result = Identity;
}
#[cfg(test)]
mod tests {
use super::*;
use num_complex::Complex64;
#[test]
fn test_identity() {
let x = Complex64::new(3.0, 4.0);
assert_eq!(<Identity as ElementOp<Complex64>>::apply(x), x);
}
#[test]
fn test_identity_custom_type() {
#[derive(Debug, Clone, Copy, PartialEq)]
struct MyCustom(f64);
let x = MyCustom(42.0);
assert_eq!(<Identity as ElementOp<MyCustom>>::apply(x), x);
}
#[test]
fn test_conj() {
let x = Complex64::new(3.0, 4.0);
assert_eq!(
<Conj as ElementOp<Complex64>>::apply(x),
Complex64::new(3.0, -4.0)
);
}
#[test]
fn test_conj_real() {
let x = 3.0f64;
assert_eq!(<Conj as ElementOp<f64>>::apply(x), 3.0);
}
#[test]
fn test_adjoint_complex() {
let x = Complex64::new(3.0, 4.0);
assert_eq!(
<Adjoint as ElementOp<Complex64>>::apply(x),
Complex64::new(3.0, -4.0)
);
}
#[test]
fn test_composition_conj_conj() {
let x = Complex64::new(3.0, 4.0);
let result =
<Conj as ElementOp<Complex64>>::apply(<Conj as ElementOp<Complex64>>::apply(x));
assert_eq!(result, x);
}
#[test]
fn test_composable_types() {
fn assert_same<A: 'static, B: 'static>() {
assert_eq!(
std::any::TypeId::of::<A>(),
std::any::TypeId::of::<B>(),
"types should be the same"
);
}
assert_same::<<Identity as Compose<f64, Conj>>::Result, Conj>();
assert_same::<<Conj as Compose<f64, Conj>>::Result, Identity>();
assert_same::<<Transpose as Compose<f64, Conj>>::Result, Adjoint>();
assert_same::<<Adjoint as Compose<f64, Adjoint>>::Result, Identity>();
}
#[test]
fn test_element_op_apply_defaults() {
#[derive(Debug, Clone, Copy, PartialEq)]
struct Real(f64);
impl ElementOpApply for Real {}
let x = Real(3.0);
assert_eq!(x.conj(), x);
assert_eq!(x.transpose(), x);
assert_eq!(x.adjoint(), x);
assert_eq!(<Conj as ElementOp<Real>>::apply(x), x);
assert_eq!(<Transpose as ElementOp<Real>>::apply(x), x);
assert_eq!(<Adjoint as ElementOp<Real>>::apply(x), x);
}
}