/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. By using JUCE, you agree to the terms of both the JUCE 7 End-User License Agreement and JUCE Privacy Policy. End User License Agreement: www.juce.com/juce-7-licence Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE DISCLAIMED. ============================================================================== */ namespace juce { namespace dsp { /** General matrix and vectors class, meant for classic math manipulation such as additions, multiplications, and linear systems of equations solving. @see LinearAlgebra @tags{DSP} */ template class Matrix { public: //============================================================================== /** Creates a new matrix with a given number of rows and columns. */ Matrix (size_t numRows, size_t numColumns) : rows (numRows), columns (numColumns) { resize(); clear(); } /** Creates a new matrix with a given number of rows and columns, with initial data coming from an array, stored in row-major order. */ Matrix (size_t numRows, size_t numColumns, const ElementType* dataPointer) : rows (numRows), columns (numColumns) { resize(); memcpy (data.getRawDataPointer(), dataPointer, rows * columns * sizeof (ElementType)); } /** Creates a copy of another matrix. */ Matrix (const Matrix&) = default; /** Moves a copy of another matrix. */ Matrix (Matrix&&) noexcept = default; /** Creates a copy of another matrix. */ Matrix& operator= (const Matrix&) = default; /** Moves another matrix into this one */ Matrix& operator= (Matrix&&) noexcept = default; //============================================================================== /** Creates the identity matrix */ static Matrix identity (size_t size); /** Creates a Toeplitz Matrix from a vector with a given squared size */ static Matrix toeplitz (const Matrix& vector, size_t size); /** Creates a squared size x size Hankel Matrix from a vector with an optional offset. @param vector The vector from which the Hankel matrix should be generated. Its number of rows should be at least 2 * (size - 1) + 1 @param size The size of resulting square matrix. @param offset An optional offset into the given vector. */ static Matrix hankel (const Matrix& vector, size_t size, size_t offset = 0); //============================================================================== /** Returns the number of rows in the matrix. */ size_t getNumRows() const noexcept { return rows; } /** Returns the number of columns in the matrix. */ size_t getNumColumns() const noexcept { return columns; } /** Returns an Array of 2 integers with the number of rows and columns in the matrix. */ Array getSize() const noexcept { return { rows, columns }; } /** Fills the contents of the matrix with zeroes. */ void clear() noexcept { zeromem (data.begin(), (size_t) data.size() * sizeof (ElementType)); } //============================================================================== /** Swaps the contents of two rows in the matrix and returns a reference to itself. */ Matrix& swapRows (size_t rowOne, size_t rowTwo) noexcept; /** Swaps the contents of two columns in the matrix and returns a reference to itself. */ Matrix& swapColumns (size_t columnOne, size_t columnTwo) noexcept; //============================================================================== /** Returns the value of the matrix at a given row and column (for reading). */ inline ElementType operator() (size_t row, size_t column) const noexcept { jassert (row < rows && column < columns); return data.getReference (static_cast (dataAcceleration.getReference (static_cast (row))) + static_cast (column)); } /** Returns the value of the matrix at a given row and column (for modifying). */ inline ElementType& operator() (size_t row, size_t column) noexcept { jassert (row < rows && column < columns); return data.getReference (static_cast (dataAcceleration.getReference (static_cast (row))) + static_cast (column)); } /** Returns a pointer to the raw data of the matrix object, ordered in row-major order (for modifying). */ inline ElementType* getRawDataPointer() noexcept { return data.getRawDataPointer(); } /** Returns a pointer to the raw data of the matrix object, ordered in row-major order (for reading). */ inline const ElementType* getRawDataPointer() const noexcept { return data.begin(); } //============================================================================== /** Addition of two matrices */ inline Matrix& operator+= (const Matrix& other) noexcept { return apply (other, [] (ElementType a, ElementType b) { return a + b; } ); } /** Subtraction of two matrices */ inline Matrix& operator-= (const Matrix& other) noexcept { return apply (other, [] (ElementType a, ElementType b) { return a - b; } ); } /** Scalar multiplication */ inline Matrix& operator*= (ElementType scalar) noexcept { std::for_each (begin(), end(), [scalar] (ElementType& x) { x *= scalar; }); return *this; } /** Addition of two matrices */ inline Matrix operator+ (const Matrix& other) const { Matrix result (*this); result += other; return result; } /** Addition of two matrices */ inline Matrix operator- (const Matrix& other) const { Matrix result (*this); result -= other; return result; } /** Scalar multiplication */ inline Matrix operator* (ElementType scalar) const { Matrix result (*this); result *= scalar; return result; } /** Matrix multiplication */ Matrix operator* (const Matrix& other) const; /** Does a hadarmard product with the receiver and other and stores the result in the receiver */ inline Matrix& hadarmard (const Matrix& other) noexcept { return apply (other, [] (ElementType a, ElementType b) { return a * b; } ); } /** Does a hadarmard product with a and b returns the result. */ static Matrix hadarmard (const Matrix& a, const Matrix& b) { Matrix result (a); result.hadarmard (b); return result; } //============================================================================== /** Compare to matrices with a given tolerance */ static bool compare (const Matrix& a, const Matrix& b, ElementType tolerance = 0) noexcept; /* Comparison operator */ inline bool operator== (const Matrix& other) const noexcept { return compare (*this, other); } //============================================================================== /** Tells if the matrix is a square matrix */ bool isSquare() const noexcept { return rows == columns; } /** Tells if the matrix is a vector */ bool isVector() const noexcept { return isOneColumnVector() || isOneRowVector(); } /** Tells if the matrix is a one column vector */ bool isOneColumnVector() const noexcept { return columns == 1; } /** Tells if the matrix is a one row vector */ bool isOneRowVector() const noexcept { return rows == 1; } /** Tells if the matrix is a null matrix */ bool isNullMatrix() const noexcept { return rows == 0 || columns == 0; } //============================================================================== /** Solves a linear system of equations represented by this object and the argument b, using various algorithms depending on the size of the arguments. The matrix must be a square matrix N times N, and b must be a vector N times 1, with the coefficients of b. After the execution of the algorithm, the vector b will contain the solution. Returns true if the linear system of equations was successfully solved. */ bool solve (Matrix& b) const noexcept; //============================================================================== /** Returns a String displaying in a convenient way the matrix contents. */ String toString() const; //============================================================================== ElementType* begin() noexcept { return data.begin(); } ElementType* end() noexcept { return data.end(); } const ElementType* begin() const noexcept { return &data.getReference (0); } const ElementType* end() const noexcept { return begin() + data.size(); } private: //============================================================================== /** Resizes the matrix. */ void resize() { data.resize (static_cast (columns * rows)); dataAcceleration.resize (static_cast (rows)); for (size_t i = 0; i < rows; ++i) dataAcceleration.setUnchecked (static_cast (i), i * columns); } template Matrix& apply (const Matrix& other, BinaryOperation binaryOp) { jassert (rows == other.rows && columns == other.columns); auto* dst = getRawDataPointer(); for (auto src : other) { *dst = binaryOp (*dst, src); ++dst; } return *this; } //============================================================================== Array data; Array dataAcceleration; size_t rows, columns; //============================================================================== JUCE_LEAK_DETECTOR (Matrix) }; } // namespace dsp } // namespace juce