mirror of
https://git.dev.opencascade.org/repos/occt.git
synced 2025-06-30 12:14:08 +03:00
Refactor multiplication to avoid self-correlation issues by using a temporary copy. Add a test file (math_Matrix_Test.cxx) to verify proper behavior.
597 lines
17 KiB
C++
597 lines
17 KiB
C++
// Copyright (c) 2025 OPEN CASCADE SAS
|
|
//
|
|
// This file is part of Open CASCADE Technology software library.
|
|
//
|
|
// This library is free software; you can redistribute it and/or modify it under
|
|
// the terms of the GNU Lesser General Public License version 2.1 as published
|
|
// by the Free Software Foundation, with special exception defined in the file
|
|
// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
|
|
// distribution for complete text of the license and disclaimer of any warranty.
|
|
//
|
|
// Alternatively, this file may be used under the terms of Open CASCADE
|
|
// commercial license or contractual agreement.
|
|
|
|
#include <math_Matrix.hxx>
|
|
|
|
#include <gtest/gtest.h>
|
|
|
|
#include <Standard_Real.hxx>
|
|
#include <Standard_Integer.hxx>
|
|
#include <math_Vector.hxx>
|
|
#include <Precision.hxx>
|
|
|
|
namespace
|
|
{
|
|
// Helper function for comparing matrices with tolerance
|
|
void checkMatricesEqual(const math_Matrix& theM1,
|
|
const math_Matrix& theM2,
|
|
const Standard_Real theTolerance = Precision::Confusion())
|
|
{
|
|
ASSERT_EQ(theM1.RowNumber(), theM2.RowNumber());
|
|
ASSERT_EQ(theM1.ColNumber(), theM2.ColNumber());
|
|
ASSERT_EQ(theM1.LowerRow(), theM2.LowerRow());
|
|
ASSERT_EQ(theM1.LowerCol(), theM2.LowerCol());
|
|
|
|
for (Standard_Integer anI = theM1.LowerRow(); anI <= theM1.UpperRow(); anI++)
|
|
{
|
|
for (Standard_Integer aJ = theM1.LowerCol(); aJ <= theM1.UpperCol(); aJ++)
|
|
{
|
|
EXPECT_NEAR(theM1(anI, aJ), theM2(anI, aJ), theTolerance);
|
|
}
|
|
}
|
|
}
|
|
} // namespace
|
|
|
|
// Tests for constructors
|
|
TEST(MathMatrixTest, Constructors)
|
|
{
|
|
// Test standard constructor
|
|
math_Matrix aMatrix1(1, 3, 1, 4);
|
|
EXPECT_EQ(aMatrix1.RowNumber(), 3);
|
|
EXPECT_EQ(aMatrix1.ColNumber(), 4);
|
|
EXPECT_EQ(aMatrix1.LowerRow(), 1);
|
|
EXPECT_EQ(aMatrix1.UpperRow(), 3);
|
|
EXPECT_EQ(aMatrix1.LowerCol(), 1);
|
|
EXPECT_EQ(aMatrix1.UpperCol(), 4);
|
|
|
|
// Test constructor with initial value
|
|
math_Matrix aMatrix2(2, 4, 3, 5, 2.5);
|
|
EXPECT_EQ(aMatrix2.RowNumber(), 3);
|
|
EXPECT_EQ(aMatrix2.ColNumber(), 3);
|
|
|
|
for (Standard_Integer anI = 2; anI <= 4; anI++)
|
|
{
|
|
for (Standard_Integer aJ = 3; aJ <= 5; aJ++)
|
|
{
|
|
EXPECT_EQ(aMatrix2(anI, aJ), 2.5);
|
|
}
|
|
}
|
|
|
|
// Test copy constructor
|
|
math_Matrix aMatrix3(aMatrix2);
|
|
checkMatricesEqual(aMatrix2, aMatrix3);
|
|
}
|
|
|
|
// Tests for initialization and access
|
|
TEST(MathMatrixTest, InitAndAccess)
|
|
{
|
|
math_Matrix aMatrix(1, 3, 1, 3);
|
|
|
|
// Test Init
|
|
aMatrix.Init(5.0);
|
|
|
|
for (Standard_Integer anI = 1; anI <= 3; anI++)
|
|
{
|
|
for (Standard_Integer aJ = 1; aJ <= 3; aJ++)
|
|
{
|
|
EXPECT_EQ(aMatrix(anI, aJ), 5.0);
|
|
}
|
|
}
|
|
|
|
// Test direct value access and modification
|
|
aMatrix(2, 2) = 10.0;
|
|
EXPECT_EQ(aMatrix(2, 2), 10.0);
|
|
|
|
// Test Value method
|
|
aMatrix.Value(3, 3) = 15.0;
|
|
EXPECT_EQ(aMatrix(3, 3), 15.0);
|
|
}
|
|
|
|
// Tests for matrix operations
|
|
TEST(MathMatrixTest, MatrixOperations)
|
|
{
|
|
math_Matrix aMatrix1(1, 3, 1, 3);
|
|
math_Matrix aMatrix2(1, 3, 1, 3);
|
|
|
|
// Initialize matrices
|
|
aMatrix1.Init(2.0);
|
|
aMatrix2.Init(3.0);
|
|
|
|
// Test addition
|
|
math_Matrix anAddResult = aMatrix1.Added(aMatrix2);
|
|
for (Standard_Integer anI = 1; anI <= 3; anI++)
|
|
{
|
|
for (Standard_Integer aJ = 1; aJ <= 3; aJ++)
|
|
{
|
|
EXPECT_EQ(anAddResult(anI, aJ), 5.0);
|
|
}
|
|
}
|
|
|
|
// Test subtraction
|
|
math_Matrix aSubResult = aMatrix1.Subtracted(aMatrix2);
|
|
for (Standard_Integer anI = 1; anI <= 3; anI++)
|
|
{
|
|
for (Standard_Integer aJ = 1; aJ <= 3; aJ++)
|
|
{
|
|
EXPECT_EQ(aSubResult(anI, aJ), -1.0);
|
|
}
|
|
}
|
|
|
|
// Test multiplication by scalar
|
|
math_Matrix aMulResult = aMatrix1.Multiplied(2.5);
|
|
for (Standard_Integer anI = 1; anI <= 3; anI++)
|
|
{
|
|
for (Standard_Integer aJ = 1; aJ <= 3; aJ++)
|
|
{
|
|
EXPECT_EQ(aMulResult(anI, aJ), 5.0);
|
|
}
|
|
}
|
|
// Test division by scalar
|
|
math_Matrix aDivResult = aMatrix1.Divided(0.5);
|
|
for (Standard_Integer anI = 1; anI <= 3; anI++)
|
|
{
|
|
for (Standard_Integer aJ = 1; aJ <= 3; aJ++)
|
|
{
|
|
EXPECT_EQ(aDivResult(anI, aJ), 4.0);
|
|
}
|
|
}
|
|
|
|
// Test in-place multiplication by scalar using Multiply method
|
|
math_Matrix aInPlaceMul(1, 3, 1, 3);
|
|
aInPlaceMul.Init(3.0);
|
|
aInPlaceMul.Multiply(2.0);
|
|
|
|
for (Standard_Integer anI = 1; anI <= 3; anI++)
|
|
{
|
|
for (Standard_Integer aJ = 1; aJ <= 3; aJ++)
|
|
{
|
|
EXPECT_EQ(aInPlaceMul(anI, aJ), 6.0);
|
|
}
|
|
}
|
|
|
|
// Test in-place multiplication by scalar using operator*=
|
|
math_Matrix aInPlaceOp(1, 3, 1, 3);
|
|
aInPlaceOp.Init(2.0);
|
|
aInPlaceOp *= 3.0;
|
|
|
|
for (Standard_Integer anI = 1; anI <= 3; anI++)
|
|
{
|
|
for (Standard_Integer aJ = 1; aJ <= 3; aJ++)
|
|
{
|
|
EXPECT_EQ(aInPlaceOp(anI, aJ), 6.0);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Tests for matrix multiplication
|
|
TEST(MathMatrixTest, MatrixMultiplication)
|
|
{
|
|
// Create identity matrix
|
|
math_Matrix anIdentity(1, 3, 1, 3);
|
|
anIdentity.Init(0.0);
|
|
anIdentity(1, 1) = 1.0;
|
|
anIdentity(2, 2) = 1.0;
|
|
anIdentity(3, 3) = 1.0;
|
|
|
|
// Create test matrix
|
|
math_Matrix aMatrix(1, 3, 1, 3);
|
|
for (Standard_Integer anI = 1; anI <= 3; anI++)
|
|
{
|
|
for (Standard_Integer aJ = 1; aJ <= 3; aJ++)
|
|
{
|
|
aMatrix(anI, aJ) = anI + aJ;
|
|
}
|
|
}
|
|
|
|
// Test matrix multiplication with identity
|
|
math_Matrix aResult = aMatrix.Multiplied(anIdentity);
|
|
checkMatricesEqual(aResult, aMatrix);
|
|
|
|
// Test matrix multiplication
|
|
math_Matrix aMatA(1, 2, 1, 3);
|
|
math_Matrix aMatB(1, 3, 1, 2);
|
|
|
|
aMatA(1, 1) = 1.0;
|
|
aMatA(1, 2) = 2.0;
|
|
aMatA(1, 3) = 3.0;
|
|
aMatA(2, 1) = 4.0;
|
|
aMatA(2, 2) = 5.0;
|
|
aMatA(2, 3) = 6.0;
|
|
|
|
aMatB(1, 1) = 7.0;
|
|
aMatB(1, 2) = 8.0;
|
|
aMatB(2, 1) = 9.0;
|
|
aMatB(2, 2) = 10.0;
|
|
aMatB(3, 1) = 11.0;
|
|
aMatB(3, 2) = 12.0;
|
|
|
|
math_Matrix aMatC = aMatA.Multiplied(aMatB);
|
|
|
|
EXPECT_EQ(aMatC(1, 1), 1 * 7 + 2 * 9 + 3 * 11);
|
|
EXPECT_EQ(aMatC(1, 2), 1 * 8 + 2 * 10 + 3 * 12);
|
|
EXPECT_EQ(aMatC(2, 1), 4 * 7 + 5 * 9 + 6 * 11);
|
|
EXPECT_EQ(aMatC(2, 2), 4 * 8 + 5 * 10 + 6 * 12);
|
|
|
|
// Test in-place matrix multiplication using Multiply method
|
|
math_Matrix aInPlaceMulMat1(1, 2, 1, 2);
|
|
math_Matrix aInPlaceMulMat2(1, 2, 1, 2);
|
|
|
|
// Set matrix values
|
|
aInPlaceMulMat1(1, 1) = 1.0;
|
|
aInPlaceMulMat1(1, 2) = 2.0;
|
|
aInPlaceMulMat1(2, 1) = 3.0;
|
|
aInPlaceMulMat1(2, 2) = 4.0;
|
|
|
|
aInPlaceMulMat2(1, 1) = 2.0;
|
|
aInPlaceMulMat2(1, 2) = 0.0;
|
|
aInPlaceMulMat2(2, 1) = 1.0;
|
|
aInPlaceMulMat2(2, 2) = 3.0;
|
|
|
|
// Store original matrix for comparison
|
|
math_Matrix aOrigMat(aInPlaceMulMat1);
|
|
|
|
// Perform in-place multiplication
|
|
aInPlaceMulMat1.Multiply(aInPlaceMulMat2);
|
|
|
|
// Expected result: [1 2; 3 4] * [2 0; 1 3] = [4 6; 10 12]
|
|
EXPECT_EQ(aInPlaceMulMat1(1, 1), 1 * 2 + 2 * 1); // 4
|
|
EXPECT_EQ(aInPlaceMulMat1(1, 2), 1 * 0 + 2 * 3); // 6
|
|
EXPECT_EQ(aInPlaceMulMat1(2, 1), 3 * 2 + 4 * 1); // 10
|
|
EXPECT_EQ(aInPlaceMulMat1(2, 2), 3 * 0 + 4 * 3); // 12
|
|
|
|
// Test in-place matrix multiplication using operator*=
|
|
math_Matrix aInPlaceOpMat(aOrigMat);
|
|
aInPlaceOpMat *= aInPlaceMulMat2;
|
|
|
|
// Check same result as with direct Multiply method
|
|
EXPECT_EQ(aInPlaceOpMat(1, 1), 4);
|
|
EXPECT_EQ(aInPlaceOpMat(1, 2), 6);
|
|
EXPECT_EQ(aInPlaceOpMat(2, 1), 10);
|
|
EXPECT_EQ(aInPlaceOpMat(2, 2), 12);
|
|
}
|
|
|
|
// Tests for transpose and inverse operations
|
|
TEST(MathMatrixTest, TransposeAndInverse)
|
|
{
|
|
// Test transpose
|
|
math_Matrix aMatrix(1, 2, 1, 3);
|
|
aMatrix(1, 1) = 1.0;
|
|
aMatrix(1, 2) = 2.0;
|
|
aMatrix(1, 3) = 3.0;
|
|
aMatrix(2, 1) = 4.0;
|
|
aMatrix(2, 2) = 5.0;
|
|
aMatrix(2, 3) = 6.0;
|
|
|
|
math_Matrix aTransposed = aMatrix.Transposed();
|
|
|
|
EXPECT_EQ(aTransposed.RowNumber(), aMatrix.ColNumber());
|
|
EXPECT_EQ(aTransposed.ColNumber(), aMatrix.RowNumber());
|
|
EXPECT_EQ(aTransposed(1, 1), aMatrix(1, 1));
|
|
EXPECT_EQ(aTransposed(2, 1), aMatrix(1, 2));
|
|
EXPECT_EQ(aTransposed(3, 1), aMatrix(1, 3));
|
|
EXPECT_EQ(aTransposed(1, 2), aMatrix(2, 1));
|
|
EXPECT_EQ(aTransposed(2, 2), aMatrix(2, 2));
|
|
EXPECT_EQ(aTransposed(3, 2), aMatrix(2, 3));
|
|
|
|
// Test inverse of a matrix
|
|
math_Matrix anInvertible(1, 3, 1, 3);
|
|
anInvertible(1, 1) = 1.0;
|
|
anInvertible(1, 2) = 0.0;
|
|
anInvertible(1, 3) = 0.0;
|
|
anInvertible(2, 1) = 0.0;
|
|
anInvertible(2, 2) = 2.0;
|
|
anInvertible(2, 3) = 0.0;
|
|
anInvertible(3, 1) = 0.0;
|
|
anInvertible(3, 2) = 0.0;
|
|
anInvertible(3, 3) = 4.0;
|
|
|
|
math_Matrix anInverse = anInvertible.Inverse();
|
|
|
|
EXPECT_EQ(anInverse(1, 1), 1.0);
|
|
EXPECT_EQ(anInverse(2, 2), 0.5);
|
|
EXPECT_EQ(anInverse(3, 3), 0.25);
|
|
|
|
// Test identity after multiplication
|
|
math_Matrix anIdentity = anInvertible.Multiplied(anInverse);
|
|
|
|
EXPECT_NEAR(anIdentity(1, 1), 1.0, Precision::Confusion());
|
|
EXPECT_NEAR(anIdentity(2, 2), 1.0, Precision::Confusion());
|
|
EXPECT_NEAR(anIdentity(3, 3), 1.0, Precision::Confusion());
|
|
EXPECT_NEAR(anIdentity(1, 2), 0.0, Precision::Confusion());
|
|
EXPECT_NEAR(anIdentity(1, 3), 0.0, Precision::Confusion());
|
|
EXPECT_NEAR(anIdentity(2, 1), 0.0, Precision::Confusion());
|
|
EXPECT_NEAR(anIdentity(2, 3), 0.0, Precision::Confusion());
|
|
EXPECT_NEAR(anIdentity(3, 1), 0.0, Precision::Confusion());
|
|
EXPECT_NEAR(anIdentity(3, 2), 0.0, Precision::Confusion());
|
|
}
|
|
|
|
// Tests for determinant calculation
|
|
TEST(MathMatrixTest, Determinant)
|
|
{
|
|
// Test determinant of identity matrix
|
|
math_Matrix anIdentity(1, 3, 1, 3);
|
|
anIdentity.Init(0.0);
|
|
anIdentity(1, 1) = 1.0;
|
|
anIdentity(2, 2) = 1.0;
|
|
anIdentity(3, 3) = 1.0;
|
|
|
|
EXPECT_NEAR(anIdentity.Determinant(), 1.0, Precision::Confusion());
|
|
|
|
// Test determinant of diagonal matrix
|
|
math_Matrix aDiagonal(1, 3, 1, 3);
|
|
aDiagonal.Init(0.0);
|
|
aDiagonal(1, 1) = 2.0;
|
|
aDiagonal(2, 2) = 3.0;
|
|
aDiagonal(3, 3) = 4.0;
|
|
|
|
EXPECT_NEAR(aDiagonal.Determinant(), 24.0, Precision::Confusion());
|
|
|
|
// Test determinant of a non-singular matrix
|
|
math_Matrix aMatrix(1, 3, 1, 3);
|
|
aMatrix(1, 1) = 3.0;
|
|
aMatrix(1, 2) = 8.0;
|
|
aMatrix(1, 3) = 5.0;
|
|
aMatrix(2, 1) = 6.0;
|
|
aMatrix(2, 2) = 2.0;
|
|
aMatrix(2, 3) = 7.0;
|
|
aMatrix(3, 1) = 4.0;
|
|
aMatrix(3, 2) = 1.0;
|
|
aMatrix(3, 3) = 9.0;
|
|
|
|
// det = 3*(2*9-7*1) - 8*(6*9-7*4) + 5*(6*1-2*4) = 3*11 - 8*26 + 5*-2 = 33 - 208 - 10 = -185
|
|
EXPECT_NEAR(aMatrix.Determinant(), -185.0, Precision::Confusion());
|
|
}
|
|
|
|
// Tests for Row and Column operations
|
|
TEST(MathMatrixTest, RowAndColumnOperations)
|
|
{
|
|
math_Matrix aMatrix(1, 3, 1, 3);
|
|
aMatrix.Init(0.0);
|
|
|
|
// Setup matrix rows
|
|
aMatrix(1, 1) = 1.0;
|
|
aMatrix(1, 2) = 2.0;
|
|
aMatrix(1, 3) = 3.0;
|
|
aMatrix(2, 1) = 4.0;
|
|
aMatrix(2, 2) = 5.0;
|
|
aMatrix(2, 3) = 6.0;
|
|
aMatrix(3, 1) = 7.0;
|
|
aMatrix(3, 2) = 8.0;
|
|
aMatrix(3, 3) = 9.0;
|
|
|
|
// Test Row method
|
|
math_Vector aRow = aMatrix.Row(2);
|
|
EXPECT_EQ(aRow.Length(), 3);
|
|
EXPECT_EQ(aRow(1), 4.0);
|
|
EXPECT_EQ(aRow(2), 5.0);
|
|
EXPECT_EQ(aRow(3), 6.0);
|
|
|
|
// Test Col method
|
|
math_Vector aCol = aMatrix.Col(3);
|
|
EXPECT_EQ(aCol.Length(), 3);
|
|
EXPECT_EQ(aCol(1), 3.0);
|
|
EXPECT_EQ(aCol(2), 6.0);
|
|
EXPECT_EQ(aCol(3), 9.0);
|
|
|
|
// Test SetRow
|
|
math_Vector aNewRow(1, 3);
|
|
aNewRow(1) = 10.0;
|
|
aNewRow(2) = 11.0;
|
|
aNewRow(3) = 12.0;
|
|
aMatrix.SetRow(2, aNewRow);
|
|
|
|
EXPECT_EQ(aMatrix(2, 1), 10.0);
|
|
EXPECT_EQ(aMatrix(2, 2), 11.0);
|
|
EXPECT_EQ(aMatrix(2, 3), 12.0);
|
|
|
|
// Test SetCol
|
|
math_Vector aNewCol(1, 3);
|
|
aNewCol(1) = 13.0;
|
|
aNewCol(2) = 14.0;
|
|
aNewCol(3) = 15.0;
|
|
aMatrix.SetCol(1, aNewCol);
|
|
|
|
EXPECT_EQ(aMatrix(1, 1), 13.0);
|
|
EXPECT_EQ(aMatrix(2, 1), 14.0);
|
|
EXPECT_EQ(aMatrix(3, 1), 15.0);
|
|
|
|
// Test SwapRow
|
|
aMatrix.SwapRow(1, 3);
|
|
EXPECT_EQ(aMatrix(1, 1), 15.0);
|
|
EXPECT_EQ(aMatrix(1, 2), 8.0);
|
|
EXPECT_EQ(aMatrix(1, 3), 9.0);
|
|
EXPECT_EQ(aMatrix(3, 1), 13.0);
|
|
EXPECT_EQ(aMatrix(3, 2), 2.0);
|
|
EXPECT_EQ(aMatrix(3, 3), 3.0);
|
|
|
|
// Test SwapCol
|
|
aMatrix.SwapCol(2, 3);
|
|
EXPECT_EQ(aMatrix(1, 2), 9.0);
|
|
EXPECT_EQ(aMatrix(1, 3), 8.0);
|
|
EXPECT_EQ(aMatrix(2, 2), 12.0);
|
|
EXPECT_EQ(aMatrix(2, 3), 11.0);
|
|
EXPECT_EQ(aMatrix(3, 2), 3.0);
|
|
EXPECT_EQ(aMatrix(3, 3), 2.0);
|
|
}
|
|
|
|
// Tests for SetDiag method
|
|
TEST(MathMatrixTest, SetDiag)
|
|
{
|
|
math_Matrix aMatrix(1, 3, 1, 3);
|
|
aMatrix.Init(0.0);
|
|
|
|
// Set diagonal elements to 5.0
|
|
aMatrix.SetDiag(5.0);
|
|
|
|
EXPECT_EQ(aMatrix(1, 1), 5.0);
|
|
EXPECT_EQ(aMatrix(2, 2), 5.0);
|
|
EXPECT_EQ(aMatrix(3, 3), 5.0);
|
|
|
|
// Check non-diagonal elements remain 0
|
|
EXPECT_EQ(aMatrix(1, 2), 0.0);
|
|
EXPECT_EQ(aMatrix(1, 3), 0.0);
|
|
EXPECT_EQ(aMatrix(2, 1), 0.0);
|
|
EXPECT_EQ(aMatrix(2, 3), 0.0);
|
|
EXPECT_EQ(aMatrix(3, 1), 0.0);
|
|
EXPECT_EQ(aMatrix(3, 2), 0.0);
|
|
}
|
|
|
|
// Tests for vector-matrix operations
|
|
TEST(MathMatrixTest, VectorMatrixOperations)
|
|
{
|
|
// Create test matrix
|
|
math_Matrix aMatrix(1, 3, 1, 3);
|
|
aMatrix(1, 1) = 1;
|
|
aMatrix(1, 2) = 2;
|
|
aMatrix(1, 3) = 3;
|
|
aMatrix(2, 1) = 4;
|
|
aMatrix(2, 2) = 5;
|
|
aMatrix(2, 3) = 6;
|
|
aMatrix(3, 1) = 7;
|
|
aMatrix(3, 2) = 8;
|
|
aMatrix(3, 3) = 9;
|
|
|
|
// Create test vector
|
|
math_Vector aVector(1, 3);
|
|
aVector(1) = 2;
|
|
aVector(2) = 3;
|
|
aVector(3) = 4;
|
|
|
|
// Test matrix * vector
|
|
math_Vector aResult = aMatrix * aVector;
|
|
|
|
EXPECT_EQ(aResult.Length(), 3);
|
|
// For row 1: 1*2 + 2*3 + 3*4 = 2 + 6 + 12 = 20
|
|
EXPECT_EQ(aResult(1), 20);
|
|
// For row 2: 4*2 + 5*3 + 6*4 = 8 + 15 + 24 = 47
|
|
EXPECT_EQ(aResult(2), 47);
|
|
// For row 3: 7*2 + 8*3 + 9*4 = 14 + 24 + 36 = 74
|
|
EXPECT_EQ(aResult(3), 74);
|
|
|
|
// Test Multiply method with two vectors
|
|
math_Vector aVec1(1, 2), aVec2(1, 3);
|
|
aVec1(1) = 1;
|
|
aVec1(2) = 2;
|
|
aVec2(1) = 3;
|
|
aVec2(2) = 4;
|
|
aVec2(3) = 5;
|
|
|
|
math_Matrix aMat(1, 2, 1, 3);
|
|
aMat.Multiply(aVec1, aVec2);
|
|
|
|
// The result should be the outer product of v1 and v2
|
|
EXPECT_EQ(aMat(1, 1), 1 * 3); // aVec1(1) * aVec2(1)
|
|
EXPECT_EQ(aMat(1, 2), 1 * 4); // aVec1(1) * aVec2(2)
|
|
EXPECT_EQ(aMat(1, 3), 1 * 5); // aVec1(1) * aVec2(3)
|
|
EXPECT_EQ(aMat(2, 1), 2 * 3); // aVec1(2) * aVec2(1)
|
|
EXPECT_EQ(aMat(2, 2), 2 * 4); // aVec1(2) * aVec2(2)
|
|
EXPECT_EQ(aMat(2, 3), 2 * 5); // aVec1(2) * aVec2(3)
|
|
}
|
|
|
|
// Special test to ensure that the matrix multiplication bug is fixed
|
|
// Bug description: In-place matrix multiplication modifies matrix elements
|
|
// that are still needed for the computation of remaining elements
|
|
TEST(MathMatrixTest, InPlaceMatrixMultiplication)
|
|
{
|
|
// Create a test matrix with specific values to reveal the bug
|
|
math_Matrix aMatrix(1, 2, 1, 2);
|
|
aMatrix(1, 1) = 1.0;
|
|
aMatrix(1, 2) = 2.0;
|
|
aMatrix(2, 1) = 3.0;
|
|
aMatrix(2, 2) = 4.0;
|
|
|
|
// Create a copy of the matrix for comparison
|
|
math_Matrix aMatrixCopy(aMatrix);
|
|
|
|
// Expected result of matrix * matrix (calculated by hand)
|
|
// [1 2] * [1 2] = [7 10]
|
|
// [3 4] [3 4] [15 22]
|
|
math_Matrix aExpectedResult(1, 2, 1, 2);
|
|
aExpectedResult(1, 1) = 7.0;
|
|
aExpectedResult(1, 2) = 10.0;
|
|
aExpectedResult(2, 1) = 15.0;
|
|
aExpectedResult(2, 2) = 22.0;
|
|
|
|
// Non-in-place multiplication result (known to be correct)
|
|
math_Matrix aCorrectResult = aMatrixCopy.Multiplied(aMatrixCopy);
|
|
|
|
// Verify that the non-in-place multiplication gives the correct result
|
|
EXPECT_EQ(aCorrectResult(1, 1), 7.0);
|
|
EXPECT_EQ(aCorrectResult(1, 2), 10.0);
|
|
EXPECT_EQ(aCorrectResult(2, 1), 15.0);
|
|
EXPECT_EQ(aCorrectResult(2, 2), 22.0);
|
|
|
|
// Perform in-place multiplication with self
|
|
aMatrix.Multiply(aMatrix);
|
|
|
|
// If the bug is fixed, both results should be identical
|
|
EXPECT_NEAR(aMatrix(1, 1), aCorrectResult(1, 1), Precision::Confusion());
|
|
EXPECT_NEAR(aMatrix(1, 2), aCorrectResult(1, 2), Precision::Confusion());
|
|
EXPECT_NEAR(aMatrix(2, 1), aCorrectResult(2, 1), Precision::Confusion());
|
|
EXPECT_NEAR(aMatrix(2, 2), aCorrectResult(2, 2), Precision::Confusion());
|
|
|
|
// Also test the operator*= version with self
|
|
aMatrixCopy *= aMatrixCopy;
|
|
|
|
// Verify the operator*= with self gives correct results
|
|
EXPECT_NEAR(aMatrixCopy(1, 1), aCorrectResult(1, 1), Precision::Confusion());
|
|
EXPECT_NEAR(aMatrixCopy(1, 2), aCorrectResult(1, 2), Precision::Confusion());
|
|
EXPECT_NEAR(aMatrixCopy(2, 1), aCorrectResult(2, 1), Precision::Confusion());
|
|
EXPECT_NEAR(aMatrixCopy(2, 2), aCorrectResult(2, 2), Precision::Confusion());
|
|
|
|
// Create two different matrices
|
|
math_Matrix aMatrixA(1, 2, 1, 2);
|
|
aMatrixA(1, 1) = 1.0;
|
|
aMatrixA(1, 2) = 2.0;
|
|
aMatrixA(2, 1) = 3.0;
|
|
aMatrixA(2, 2) = 4.0;
|
|
|
|
math_Matrix aMatrixB(1, 2, 1, 2);
|
|
aMatrixB(1, 1) = 5.0;
|
|
aMatrixB(1, 2) = 6.0;
|
|
aMatrixB(2, 1) = 7.0;
|
|
aMatrixB(2, 2) = 8.0;
|
|
|
|
// Create copies for non-in-place calculation
|
|
math_Matrix aMatrixACopy(aMatrixA);
|
|
math_Matrix aMatrixBCopy(aMatrixB);
|
|
|
|
// Calculate expected result using non-in-place multiplication
|
|
math_Matrix aExpectedAB = aMatrixACopy.Multiplied(aMatrixBCopy);
|
|
|
|
// Expected result: [1 2] * [5 6] = [19 22]
|
|
// [3 4] [7 8] [43 50]
|
|
EXPECT_EQ(aExpectedAB(1, 1), 19.0);
|
|
EXPECT_EQ(aExpectedAB(1, 2), 22.0);
|
|
EXPECT_EQ(aExpectedAB(2, 1), 43.0);
|
|
EXPECT_EQ(aExpectedAB(2, 2), 50.0);
|
|
|
|
// Test in-place multiplication with different matrix
|
|
aMatrixA.Multiply(aMatrixB);
|
|
|
|
// Check results match expected values
|
|
EXPECT_NEAR(aMatrixA(1, 1), aExpectedAB(1, 1), Precision::Confusion());
|
|
EXPECT_NEAR(aMatrixA(1, 2), aExpectedAB(1, 2), Precision::Confusion());
|
|
EXPECT_NEAR(aMatrixA(2, 1), aExpectedAB(2, 1), Precision::Confusion());
|
|
EXPECT_NEAR(aMatrixA(2, 2), aExpectedAB(2, 2), Precision::Confusion());
|
|
|
|
// Test operator*= with different matrices
|
|
aMatrixACopy *= aMatrixBCopy;
|
|
|
|
// Check results match expected values
|
|
EXPECT_NEAR(aMatrixACopy(1, 1), aExpectedAB(1, 1), Precision::Confusion());
|
|
EXPECT_NEAR(aMatrixACopy(1, 2), aExpectedAB(1, 2), Precision::Confusion());
|
|
EXPECT_NEAR(aMatrixACopy(2, 1), aExpectedAB(2, 1), Precision::Confusion());
|
|
EXPECT_NEAR(aMatrixACopy(2, 2), aExpectedAB(2, 2), Precision::Confusion());
|
|
}
|