Skip to content

Wrapper types for transpose and adjoint #5

@fre-hu

Description

@fre-hu

In linear algebra for example matrix multiplications, one would like to be able to take the transpose or adjoint of input matrices. One idea is to use wrapper types, similar to how it is implemented in Julia.

With wrapper types, the layout of input matrices are preserved avoiding having to create more layout types. It also supports more cases like adjoint for complex matrices and maybe more types for triangular and symmetric/hermitian matrices.

With more than two dimensions, one can either let transpose and adjoint affect all dimensions or swap the last two. The latter is maybe more useful for linear algebra. Or do as in Julia and not define these for rank >2.

Below are some examples how it can be implemented. The idea with the transpose and adjoint functions is that they can take any array that is convertible to a view as input, for example transpose(&array) for an owned array or just transpose(view).

If these would be added, one could maybe reconsider the tranpose methods in mdarray if they should be renamed.

Of course it has to be tried out first if it is at all useful.

struct Adjoint<T> {
    inner: T,
}

struct Transpose<T> {
    inner: T,
}

trait IntoAdjointOrTranspose {
    type Inner;

    const IS_ADJOINT: bool;
    const IS_TRANSPOSE: bool;

    fn into_inner(self) -> Self::Inner;
}

fn adjoint<'a, T, S: Shape, L: Layout, I>(expr: I) -> Adjoint<View<'a, T, S, L>>
where
    I: IntoExpression<IntoExpr = View<'a, T, S, L>>,
{
    Adjoint { inner: expr.into_expr() }
}

fn transpose<'a, T, S: Shape, L: Layout, I>(expr: I) -> Transpose<View<'a, T, S, L>>
where
    I: IntoExpression<IntoExpr = View<'a, T, S, L>>,
{
    Transpose { inner: expr.into_expr() }
}

impl<'a, T: 'a, S: Shape, L: Layout, I> IntoAdjointOrTranspose for I
where
    I: IntoExpression<IntoExpr = View<'a, T, S, L>>,
{
    type Inner = View<'a, T, S, L>;

    const IS_ADJOINT: bool = false;
    const IS_TRANSPOSE: bool = false;

    fn into_inner(self) -> Self::Inner {
        self.into_expr()
    }
}

impl<'a, T, S: Shape, L: Layout> IntoAdjointOrTranspose for Adjoint<View<'a, T, S, L>> {
    type Inner = View<'a, T, S, L>;

    const IS_ADJOINT: bool = true;
    const IS_TRANSPOSE: bool = false;

    fn into_inner(self) -> Self::Inner {
        self.inner
    }
}

impl<'a, T, S: Shape, L: Layout> IntoAdjointOrTranspose for Transpose<View<'a, T, S, L>> {
    type Inner = View<'a, T, S, L>;

    const IS_ADJOINT: bool = false;
    const IS_TRANSPOSE: bool = true;

    fn into_inner(self) -> Self::Inner {
        self.inner
    }
}

fn test<'a, T: 'a, S: Shape, L: Layout>(_: impl IntoAdjointOrTranspose<Inner = View<'a, T, S, L>>) {
    // ...
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions