matrix_expression.hpp

Back to Matrix class

pastel/math/matrix/

#ifndef PASTELMATH_MATRIX_EXPRESSION_HPP
#define PASTELMATH_MATRIX_EXPRESSION_HPP

#include "pastel/math/matrix/matrix_expression.h"

#include "pastel/sys/vector.h"

namespace Pastel
{

    template <typename Real, typename Expression>
    class MatrixExpression
    {
    protected:
        MatrixExpression()
        {
        }

        MatrixExpression(const MatrixExpression& that)
        {
        }

        MatrixExpression& operator=(const MatrixExpression& that)
        {
            return *this;
        }

    public:
        Real operator()(integer y, integer x) const
        {
            return ((const Expression&)*this)(y, x);
        }

        integer width() const
        {
            return ((const Expression&)*this).width();
        }

        integer height() const
        {
            return ((const Expression&)*this).height();
        }

        bool involves(
            const void* memoryBegin, const void* memoryEnd) const
        {
            return ((const Expression&)*this).involves(memoryBegin, memoryEnd);
        }

        bool evaluateBeforeAssignment(
            const void* memoryBegin, const void* memoryEnd) const
        {
            return ((const Expression&)*this).evaluateBeforeAssignment(memoryBegin, memoryEnd);
        }

        template <typename RightExpression>
        bool operator==(
            const MatrixExpression<Real, RightExpression>& right) const
        {
            const Expression& left = (const Expression&)*this;

            PENSURE_OP(left.width(), ==, right.width());
            PENSURE_OP(left.height(), ==, right.height());

            integer leftWidth = width();
            integer leftHeight = height();

            for (integer y = 0;y < leftHeight;++y)
            {
                for (integer x = 0;x < leftWidth;++x)
                {
                    if (!(left(y, x) == right(y, x)))
                    {
                        return false;
                    }
                }
            }

            return true;
        }

        template <typename RightExpression>
        bool operator!=(
            const MatrixExpression<Real, RightExpression>& right) const
        {
            return !(*this == right);
        }

        // Negation

        MatrixNegation<Real, Expression> operator-() const
        {

            return MatrixNegation<Real, Expression>((const Expression&)*this);
        }

        // Summation

        template <typename RightExpression>
        MatrixAddition<Real, Expression, 
            RightExpression>
            operator+(const MatrixExpression

            <Real, RightExpression>& right) const
        {
            return MatrixAddition
                <Real, Expression, 
                RightExpression >
                ((const Expression&)*this,
                (const RightExpression&)right);
        }

        // Subtraction

        template <typename RightExpression>
        MatrixSubtraction<Real, Expression, 
            RightExpression>
            operator-(const MatrixExpression

            <Real, RightExpression>& right) const
        {
            return MatrixSubtraction
                <Real, Expression, 
                RightExpression>
                ((const Expression&)*this,
                (const RightExpression&)right);
        }

        // Multiplication

        template <typename RightExpression>
        MatrixMultiplication<Real, Expression, RightExpression>

            operator*(const MatrixExpression<Real, RightExpression>& right) const
        {
            return MatrixMultiplication<Real, Expression, RightExpression>
                ((const Expression&)*this,
                (const RightExpression&)right);
        }

        MatrixScalarAddition<Real, Expression>
            operator+(Real right) const
        {
            return MatrixScalarAddition
                <Real, Expression>

                ((const Expression&)*this, right);
        }

        friend const MatrixScalarAddition<Real, Expression>
            operator+(Real left, const MatrixExpression& right)
        {
            // Scalar addition is commutative.
            return MatrixScalarAddition
                <Real, Expression>
                ((const Expression&)right, left);
        }

        MatrixScalarAddition<Real, Expression>
            operator-(Real right) const
        {
            return MatrixScalarAddition<Real, Expression>

                ((const Expression&)*this, -right);
        }

        friend const MatrixScalarAddition<Real, Expression>
            operator-(Real left, const MatrixExpression& right)
        {
            // Scalar addition is commutative.
            return MatrixScalarAddition<Real, Expression>
                ((const Expression&)right, -left);
        }

        MatrixScalarMultiplication<Real, Expression>

            operator*(Real right) const
        {
            return MatrixScalarMultiplication<Real, Expression>
                ((const Expression&)*this, right);
        }

        friend const MatrixScalarMultiplication<Real, Expression>
            operator*(Real left, const MatrixExpression& right)
        {
            // Scalar multiplication is commutative.
            return MatrixScalarMultiplication<Real, Expression>
                ((const Expression&)right, left);
        }

        MatrixScalarMultiplication<Real, Expression>
            operator/(Real right) const
        {
            return MatrixScalarMultiplication<Real, Expression>

                ((const Expression&)*this, inverse(right));
        }

        // Division of a scalar by a matrix is not defined.
    };

    // Concrete expressions

    template <typename Real, typename Expression>
    class MatrixNegation
        : public MatrixExpression<Real, 
        MatrixNegation<Real, Expression> >
    {
    public:
        using StorageType = const MatrixNegation&;

        explicit MatrixNegation(
            const Expression& data)
            : data_(data)
        {
        }

        Real operator()(integer y, integer x) const
        {
            return -data_(y, x);
        }

        integer width() const
        {
            return data_.width();
        }

        integer height() const
        {
            return data_.height();
        }

        bool involves(
            const void* memoryBegin,
            const void* memoryEnd) const
        {
            return data_.involves(memoryBegin, memoryEnd);
        }

        bool evaluateBeforeAssignment(
            const void* memoryBegin,
            const void* memoryEnd) const
        {
            return data_.evaluateBeforeAssignment(memoryBegin, memoryEnd);
        }

    private:
        typename Expression::StorageType data_;
    };

    template <
        typename Real,
        typename LeftExpression,
        typename RightExpression>
    class MatrixAddition
        : public MatrixExpression<Real, 
        MatrixAddition<Real, 
        LeftExpression, RightExpression> >
    {
    public:
        using StorageType = const MatrixAddition&;

        MatrixAddition(
            const LeftExpression& left,
            const RightExpression& right)
            : left_(left)
            , right_(right)
        {
            PENSURE_OP(left.width(), ==, right.width());
            PENSURE_OP(left.height(), ==, right.height());
        }

        Real operator()(integer y, integer x) const
        {
            return left_(y, x) + right_(y, x);
        }

        integer width() const
        {
            return left_.width();
        }

        integer height() const
        {
            return left_.height();
        }

        bool involves(
            const void* memoryBegin, const void* memoryEnd) const
        {
            return left_.involves(memoryBegin, memoryEnd) ||
                right_.involves(memoryBegin, memoryEnd);
        }

        bool evaluateBeforeAssignment(
            const void* memoryBegin, const void* memoryEnd) const
        {
            return left_.evaluateBeforeAssignment(memoryBegin, memoryEnd) ||
                right_.evaluateBeforeAssignment(memoryBegin, memoryEnd);
        }

    private:
        typename LeftExpression::StorageType left_;
        typename RightExpression::StorageType right_;
    };

    template <
        typename Real,
        typename LeftExpression,
        typename RightExpression>
    class MatrixSubtraction
        : public MatrixExpression<Real, 
        MatrixSubtraction<Real, 
        LeftExpression, RightExpression> >
    {
    public:
        using StorageType = const MatrixSubtraction&;

        MatrixSubtraction(
            const LeftExpression& left,
            const RightExpression& right)
            : left_(left)
            , right_(right)
        {
            PENSURE_OP(left.width(), ==, right.width());
            PENSURE_OP(left.height(), ==, right.height());
        }

        Real operator()(integer y, integer x) const
        {
            return left_(y, x) - right_(y, x);
        }

        integer width() const
        {
            return left_.width();
        }

        integer height() const
        {
            return left_.height();
        }

        bool involves(
            const void* memoryBegin, const void* memoryEnd) const
        {
            return left_.involves(memoryBegin, memoryEnd) ||
                right_.involves(memoryBegin, memoryEnd);
        }

        bool evaluateBeforeAssignment(
            const void* memoryBegin, const void* memoryEnd) const
        {
            return left_.evaluateBeforeAssignment(memoryBegin, memoryEnd) ||
                right_.evaluateBeforeAssignment(memoryBegin, memoryEnd);
        }

    private:
        typename LeftExpression::StorageType left_;
        typename RightExpression::StorageType right_;
    };

    template <
        typename Real,
        typename LeftExpression,
        typename RightExpression>
    class MatrixMultiplication
        : public MatrixExpression<Real, 
        MatrixMultiplication<Real, 
        LeftExpression, RightExpression> >
    {
    public:
        using StorageType = const MatrixMultiplication&;

        MatrixMultiplication(
            const LeftExpression& left,
            const RightExpression& right)
            : left_(left)
            , right_(right)
        {
            PENSURE_OP(left.width(), ==, right.height());
        }

        Real operator()(integer y, integer x) const
        {
            Real sum = 0;
            integer n = left_.width();

            for (integer i = 0;i < n;++i)
            {

                sum += left_(y, i) * right_(i, x);
            }

            return sum;
        }

        integer width() const
        {
            return right_.width();
        }

        integer height() const
        {
            return left_.height();
        }

        bool involves(
            const void* memoryBegin, const void* memoryEnd) const
        {
            return left_.involves(memoryBegin, memoryEnd) ||
                right_.involves(memoryBegin, memoryEnd);
        }

        bool evaluateBeforeAssignment(
            const void* memoryBegin, const void* memoryEnd) const
        {
            // With multiplication, the involvement
            // of a given subexpression becomes
            // non-trivial. From now on we accept
            // any occurence.

            return left_.involves(memoryBegin, memoryEnd) ||
                right_.involves(memoryBegin, memoryEnd);
        }

    private:
        typename LeftExpression::StorageType left_;
        typename RightExpression::StorageType right_;
    };

    template <typename Real, typename Expression>
    class MatrixScalarMultiplication
        : public MatrixExpression<Real, 
        MatrixScalarMultiplication<Real, Expression> >
    {
    public:
        // Since this expression contains data,
        // it must be stored by value.
        using StorageType = const MatrixScalarMultiplication;

        MatrixScalarMultiplication(
            const Expression& data,
            Real factor)
            : data_(data)
            , factor_(std::move(factor))
        {
        }

        Real operator()(integer y, integer x) const
        {
            return data_(y, x) * factor_;
        }

        integer width() const
        {
            return data_.width();
        }

        integer height() const
        {
            return data_.height();
        }

        bool involves(
            const void* memoryBegin,
            const void* memoryEnd) const
        {
            return data_.involves(memoryBegin, memoryEnd);
        }

        bool evaluateBeforeAssignment(
            const void* memoryBegin,
            const void* memoryEnd) const
        {
            return data_.evaluateBeforeAssignment(memoryBegin, memoryEnd);
        }

    private:
        typename Expression::StorageType data_;
        Real factor_;
    };

    template <typename Real, typename Expression>
    class MatrixScalarAddition
        : public MatrixExpression<Real, 
        MatrixScalarAddition<Real, Expression> >
    {
    public:
        // Since this expression contains data,
        // it must be stored by value.
        using StorageType = const MatrixScalarAddition;

        MatrixScalarAddition(
            const Expression& data,
            Real term)
            : data_(data)
            , term_(term)
        {
        }

        Real operator()(integer y, integer x) const
        {
            return data_(y, x) + term_;
        }

        integer width() const
        {
            return data_.width();
        }

        integer height() const
        {
            return data_.height();
        }

        bool involves(
            const void* memoryBegin,
            const void* memoryEnd) const
        {
            return data_.involves(memoryBegin, memoryEnd);
        }

        bool evaluateBeforeAssignment(
            const void* memoryBegin,
            const void* memoryEnd) const
        {
            return data_.evaluateBeforeAssignment(memoryBegin, memoryEnd);
        }

    private:
        typename Expression::StorageType data_;
        Real term_;
    };

    template <typename Real, typename Expression>
    class MatrixDiagonal
        : public MatrixExpression<Real, MatrixDiagonal<Real, Expression>>
    {
    public:
        // Since this expression contains data,
        // it must be stored by value.
        using StorageType = const MatrixDiagonal;

        explicit MatrixDiagonal(
            integer m, integer n,

            const Expression& diagonal)
            : m_(m)
            , n_(n)
            , diagonal_(diagonal)
        {
        }

        Real operator()(integer y, integer x) const
        {
            if (y == x && x >= 0 && x < diagonal_.size())
            {
                return diagonal_[x];
            }

            return 0;
        }

        integer width() const
        {
            return n_;
        }

        integer height() const
        {
            return m_;
        }

        bool involves(
            const void* memoryBegin, const void* memoryEnd) const
        {
            return diagonal_.involves(memoryBegin, memoryEnd);
        }

        bool evaluateBeforeAssignment(
            const void* memoryBegin, const void* memoryEnd) const
        {
            return diagonal_.involves(memoryBegin, memoryEnd);
        }

    private:
        integer m_;
        integer n_;
        typename Expression::StorageType diagonal_;
    };

    template <typename Real>
    class MatrixConstant
        : public MatrixExpression<Real, 
        MatrixConstant<Real> >
    {
    public:
        // Since this expression contains data,
        // it must be stored by value.
        using StorageType = const MatrixConstant;

        MatrixConstant(
            integer height,
            integer width,
            Real value = 0)
            : width_(width)
            , height_(height)
            , value_(std::move(value))
        {
            PENSURE_OP(height, >=, 0);
            PENSURE_OP(width, >=, 0);
        }

        Real operator()(integer y, integer x) const
        {
            return value_;
        }

        integer width() const
        {
            return width_;
        }

        integer height() const
        {
            return height_;
        }

        bool involves(
            const void* memoryBegin, const void* memoryEnd) const
        {
            return false;
        }

        bool evaluateBeforeAssignment(
            const void* memoryBegin, const void* memoryEnd) const
        {
            return false;
        }

    private:
        integer width_;
        integer height_;
        Real value_;
    };

    template <
        typename Real,
        typename Expression>
    class MatrixTranspose
        : public MatrixExpression<Real,
        MatrixTranspose<Real, Expression> >
    {
    public:

        using StorageType = const MatrixTranspose&;

        explicit MatrixTranspose(
            const Expression& data)
            : data_(data)
        {
        }

        Real operator()(integer y, integer x) const
        {
            return data_(x, y);
        }

        integer width() const
        {
            return data_.height();
        }

        integer height() const
        {
            return data_.width();
        }

        bool involves(
            const void* memoryBegin,
            const void* memoryEnd) const
        {
            return data_.involves(memoryBegin, memoryEnd);
        }

        bool evaluateBeforeAssignment(
            const void* memoryBegin,
            const void* memoryEnd) const
        {
            // With transpose the involvement
            // of a subexpression becomes non-trivial,
            // so any occurence will do.

            return data_.involves(memoryBegin, memoryEnd);
        }

    private:
        typename Expression::StorageType data_;
    };

    template <typename Real>
    class ArrayMatrix
        : public MatrixExpression<Real, ArrayMatrix<Real>>
    {
    public:
        using StorageType = const ArrayMatrix&;

        ArrayMatrix(
            integer height, 
            integer width,
            const Real* data)
            : data_(data)
            , width_(width)
            , height_(height)
        {
        }

        Real operator()(integer y, integer x) const
        {
            PENSURE_OP(x, >=, 0);
            PENSURE_OP(x, <, width_);
            PENSURE_OP(y, >=, 0);
            PENSURE_OP(y, <, height_);
            return data_[(y * width_) + x];
        }

        integer width() const
        {
            return width_;
        }

        integer height() const
        {
            return height_;
        }

        bool involves(
            const void* memoryBegin,
            const void* memoryEnd) const
        {
            return memoryOverlaps(
                data_, data_ + height_ * width_, 
                memoryBegin, memoryEnd);
        }

        bool evaluateBeforeAssignment(
            const void* memoryBegin,
            const void* memoryEnd) const
        {
            return memoryOverlaps(
                data_, data_ + height_ * width_, 
                memoryBegin, memoryEnd);
        }

    private:
        const Real* data_;
        integer width_;
        integer height_;
    };

    // Matrices vs vectors

    template <
        typename Real,
        integer N,
        typename LeftExpression,
        typename RightExpression>
    class MatrixVectorMultiplication
        : public VectorExpression<Real, N, MatrixVectorMultiplication<Real, N,
        LeftExpression, RightExpression> >
    {
    public:
        using StorageType = const MatrixVectorMultiplication&;

        MatrixVectorMultiplication(
            const LeftExpression& left,
            const RightExpression& right)
            : left_(left)
            , right_(right)
        {
            PENSURE_OP(left.width(), ==, right.size());
        }

        Real operator[](integer index) const
        {
            integer n = left_.width();

            Real sum = 0;
            for (integer x = 0;x < n;++x)
            {

                sum += left_(index, x) * right_[x];
            }

            return sum;
        }

        integer size() const
        {
            return left_.height();
        }

        bool involves(
            const void* memoryBegin, const void* memoryEnd) const
        {
            return left_.involves(memoryBegin, memoryEnd) ||
                right_.involves(memoryBegin, memoryEnd);
        }

        bool evaluateBeforeAssignment(
            const void* memoryBegin, const void* memoryEnd) const
        {
            // This is a non-trivial expression.
            return left_.involves(memoryBegin, memoryEnd) ||
                right_.involves(memoryBegin, memoryEnd);
        }

    private:
        typename LeftExpression::StorageType left_;
        typename RightExpression::StorageType right_;
    };

    template <
        typename Real,
        integer N,
        typename LeftExpression,
        typename RightExpression>
    class VectorMatrixMultiplication
        : public VectorExpression<Real, N, VectorMatrixMultiplication<Real, N, 
        LeftExpression, RightExpression> >
    {
    public:
        using StorageType = const VectorMatrixMultiplication&;

        VectorMatrixMultiplication(
            const LeftExpression& left,
            const RightExpression& right)
            : left_(left)
            , right_(right)
        {
            PENSURE_OP(left.size(), ==, right.height());
        }

        Real operator[](integer index) const
        {
            integer n = right_.height();

            Real sum = 0;
            for (integer y = 0;y < n;++y)
            {

                sum += left_[y] * right_(y, index);
            }

            return sum;
        }

        integer size() const
        {
            return right_.width();
        }

        bool involves(
            const void* memoryBegin, const void* memoryEnd) const
        {
            return left_.involves(memoryBegin, memoryEnd) ||
                right_.involves(memoryBegin, memoryEnd);
        }

        bool evaluateBeforeAssignment(
            const void* memoryBegin, const void* memoryEnd) const
        {
            // This is a non-trivial expression.
            return left_.involves(memoryBegin, memoryEnd) ||
                right_.involves(memoryBegin, memoryEnd);
        }

    private:
        typename LeftExpression::StorageType left_;
        typename RightExpression::StorageType right_;
    };

}

#endif