src/direct_product.rs

Fri, 13 Dec 2024 22:37:12 -0500

author
Tuomo Valkonen <tuomov@iki.fi>
date
Fri, 13 Dec 2024 22:37:12 -0500
branch
dev
changeset 57
1b3b1687b9ed
child 59
9226980e45a7
permissions
-rw-r--r--

Add direct products (Pair, RowOp, ColOp, DiagOp)

/*!
Direct products of the form $A \times B$.

TODO: This could be easily much more generic if `derive_more` could derive arithmetic
operations on references.
*/

use core::ops::{Mul,MulAssign,Div,DivAssign,Add,AddAssign,Sub,SubAssign,Neg};
use std::clone::Clone;
use serde::{Serialize, Deserialize};
use crate::types::{Num, Float};
use crate::{maybe_lifetime, maybe_ref, impl_vectorspace_ops};
use crate::euclidean::{Dot, Euclidean};

#[derive(Debug,Clone,PartialEq,Eq,Serialize,Deserialize)]
pub struct Pair<A, B> (pub A, pub B);

impl<A, B> Pair<A,B> {
    pub fn new(a : A, b : B) -> Pair<A,B> { Pair{ 0 : a, 1 : b } }
}

impl<A, B> From<(A,B)> for Pair<A,B> {
    #[inline]
    fn from((a, b) : (A, B)) -> Pair<A,B> { Pair{ 0 : a, 1 : b } }
}

macro_rules! impl_binop {
    ($trait : ident, $fn : ident, $refl:ident, $refr:ident) => {
        impl_binop!(@doit: $trait, $fn;
                           maybe_lifetime!($refl, &'l Pair<A,B>),
                               (maybe_lifetime!($refl, &'l A), maybe_lifetime!($refl, &'l B));
                           maybe_lifetime!($refr, &'r Pair<Ai,Bi>),
                               (maybe_lifetime!($refr, &'r Ai), maybe_lifetime!($refr, &'r Bi));
                           $refl, $refr);
    };

    (@doit: $trait:ident, $fn:ident;
            $self:ty, ($aself:ty, $bself:ty);
            $in:ty, ($ain:ty, $bin:ty);
            $refl:ident, $refr:ident) => {
        impl<'l, 'r, A, B, Ai, Bi> $trait<$in>
        for $self
        where $aself: $trait<$ain>,
              $bself: $trait<$bin> {
            type Output = Pair<<$aself as $trait<$ain>>::Output,
                                       <$bself as $trait<$bin>>::Output>;

            #[inline]
            fn $fn(self, y : $in) -> Self::Output {
                Pair { 0 : maybe_ref!($refl, self.0).$fn(maybe_ref!($refr, y.0)),
                               1 : maybe_ref!($refl, self.1).$fn(maybe_ref!($refr, y.1)) }
            }
        }
    };
}

macro_rules! impl_assignop {
    ($trait : ident, $fn : ident, $refr:ident) => {
        impl_assignop!(@doit: $trait, $fn;
                              maybe_lifetime!($refr, &'r Pair<Ai,Bi>),
                                  (maybe_lifetime!($refr, &'r Ai), maybe_lifetime!($refr, &'r Bi));
                              $refr);
    };
    (@doit: $trait:ident, $fn:ident;
            $in:ty, ($ain:ty, $bin:ty);
            $refr:ident) => {
        impl<'r, A, B, Ai, Bi> $trait<$in>
        for Pair<A,B>
        where A: $trait<$ain>,
              B: $trait<$bin> {
            #[inline]
            fn $fn(&mut self, y : $in) -> () {
                self.0.$fn(maybe_ref!($refr, y.0));
                self.1.$fn(maybe_ref!($refr, y.1));
            }
        }
    }
}

macro_rules! impl_scalarop {
    ($trait : ident, $fn : ident, $refl:ident) => {
        impl_scalarop!(@doit: $trait, $fn;
                              maybe_lifetime!($refl, &'l Pair<A,B>),
                                  (maybe_lifetime!($refl, &'l A), maybe_lifetime!($refl, &'l B));
                              $refl);
    };
    (@doit: $trait:ident, $fn:ident;
            $self:ty, ($aself:ty, $bself:ty);
            $refl:ident) => {
        // Scalar as Rhs
        impl<'l, F : Num, A, B> $trait<F>
        for $self
        where $aself: $trait<F>,
              $bself: $trait<F> {
            type Output = Pair<<$aself as $trait<F>>::Output,
                                       <$bself as $trait<F>>::Output>;
            #[inline]
            fn $fn(self, a : F) -> Self::Output {
                Pair{ 0 : maybe_ref!($refl, self.0).$fn(a),
                              1 : maybe_ref!($refl, self.1).$fn(a)}
            }
        }
    }
}

// Not used due to compiler overflow
#[allow(unused_macros)]
macro_rules! impl_scalarlhs_op {
    ($trait:ident, $fn:ident, $refr:ident, $field:ty) => {
        impl_scalarlhs_op!(@doit: $trait, $fn, 
                                  maybe_lifetime!($refr, &'r Pair<Ai,Bi>),
                                  (maybe_lifetime!($refr, &'r Ai), maybe_lifetime!($refr, &'r Bi));
                                  $refr, $field);
    };
    (@doit: $trait:ident, $fn:ident, 
            $in:ty, ($ain:ty, $bin:ty);
            $refr:ident, $field:ty) => {
        impl<'r, Ai, Bi> $trait<$in>
        for $field
        where $field : $trait<$ain>
                     + $trait<$bin> {
            type Output = Pair<<$field as $trait<$ain>>::Output,
                                       <$field as $trait<$bin>>::Output>;
            #[inline]
            fn $fn(self, x : $in) -> Self::Output {
                Pair{ 0 : self.$fn(maybe_ref!($refr, x.0)),
                              1 : self.$fn(maybe_ref!($refr, x.1))}
            }
        }
    };
}

macro_rules! impl_scalar_assignop {
    ($trait : ident, $fn : ident) => {
        impl<'r, F : Num, A, B> $trait<F>
        for Pair<A,B>
        where A: $trait<F>, B: $trait<F> {
            #[inline]
            fn $fn(&mut self, a : F) -> () {
                self.0.$fn(a);
                self.1.$fn(a);
            }
        }
    }
}

macro_rules! impl_unaryop {
    ($trait:ident, $fn:ident, $refl:ident) => {
        impl_unaryop!(@doit: $trait, $fn;
                             maybe_lifetime!($refl, &'l Pair<A,B>),
                                 (maybe_lifetime!($refl, &'l A), maybe_lifetime!($refl, &'l B));
                             $refl);
    };
    (@doit: $trait:ident, $fn:ident;
            $self:ty, ($aself:ty, $bself:ty);
            $refl : ident) => {
        impl<'l, A, B> $trait
        for $self
        where $aself: $trait,
              $bself: $trait {
            type Output = Pair<<$aself as $trait>::Output,
                                       <$bself as $trait>::Output>;
            #[inline]
            fn $fn(self) -> Self::Output {
                Pair{ 0 : maybe_ref!($refl, self.0).$fn(),
                              1 : maybe_ref!($refl, self.1).$fn()}
            }
        }
    }
}


impl_vectorspace_ops!(impl_binop, impl_assignop, impl_scalarop, impl_scalarlhs_op,
                      impl_scalar_assignop, impl_unaryop);


impl<A, B, U, V, F> Dot<Pair<U, V>, F> for Pair<A, B>
where
    A : Dot<U, F>,
    B : Dot<V, F>,
    F : Num
{

    fn dot(&self, Pair(ref u, ref v) : &Pair<U, V>) -> F {
        self.0.dot(u) + self.1.dot(v)
    }
}

impl<A, B, F> Euclidean<F>  for Pair<A, B>
where
    A : Euclidean<F>,
    B : Euclidean<F>,
    F : Float
{
    type Output = Pair<<A as Euclidean<F>>::Output, <B as Euclidean<F>>::Output>;

    fn similar_origin(&self) -> <Self as Euclidean<F>>::Output {
        Pair(self.0.similar_origin(), self.1.similar_origin())
    }

    fn dist2_squared(&self, Pair(ref u, ref v) : &Self) -> F {
        self.0.dist2_squared(u) + self.1.dist2_squared(v)
    }
}

use crate::linops::AXPY;

impl<F, A, B, U, V> AXPY<F, Pair<U, V>> for Pair<A, B>
where
    A : AXPY<F, U>,
    B : AXPY<F, V>,
    F : Num
{
    fn axpy(&mut self, α : F, x : &Pair<U, V>, β : F) {
        self.0.axpy(α, &x.0, β);
        self.1.axpy(α, &x.1, β);
    }

    fn copy_from(&mut self, x : &Pair<U, V>,) {
        self.0.copy_from(&x.0);
        self.1.copy_from(&x.1);
    }

    fn scale_from(&mut self, α : F, x : &Pair<U, V>) {
        self.0.scale_from(α, &x.0);
        self.1.scale_from(α, &x.1);
    }
}

mercurial