mirror of
https://git.dev.opencascade.org/repos/occt.git
synced 2025-06-30 12:14:08 +03:00
Foundation Classes - Matrix multiplied issue (#522)
Refactor multiplication to avoid self-correlation issues by using a temporary copy. Add a test file (math_Matrix_Test.cxx) to verify proper behavior.
This commit is contained in:
parent
57d6038b26
commit
97920011fa
@ -4,4 +4,5 @@ set(OCCT_TKMath_GTests_FILES_LOCATION "${CMAKE_CURRENT_LIST_DIR}")
|
||||
set(OCCT_TKMath_GTests_FILES
|
||||
Bnd_BoundSortBox_Test.cxx
|
||||
ElCLib_Test.cxx
|
||||
math_Matrix_Test.cxx
|
||||
)
|
||||
|
596
src/FoundationClasses/TKMath/GTests/math_Matrix_Test.cxx
Normal file
596
src/FoundationClasses/TKMath/GTests/math_Matrix_Test.cxx
Normal file
@ -0,0 +1,596 @@
|
||||
// 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());
|
||||
}
|
@ -635,6 +635,14 @@ void math_Matrix::Multiply(const math_Matrix& Right)
|
||||
ColNumber() != Right.RowNumber(),
|
||||
"math_Matrix::Multiply() - input matrix has incompatible dimensions");
|
||||
|
||||
// Create a temporary copy to avoid corrupting our own data during calculation
|
||||
math_Matrix aTemp = *this;
|
||||
if (this == &Right)
|
||||
{
|
||||
Multiply(aTemp, aTemp);
|
||||
return;
|
||||
}
|
||||
|
||||
Standard_Real Som;
|
||||
for (Standard_Integer I = LowerRowIndex; I <= UpperRowIndex; I++)
|
||||
{
|
||||
@ -644,7 +652,7 @@ void math_Matrix::Multiply(const math_Matrix& Right)
|
||||
Standard_Integer I2 = Right.LowerRowIndex;
|
||||
for (Standard_Integer J = LowerColIndex; J <= UpperColIndex; J++)
|
||||
{
|
||||
Som = Som + Array(I, J) * Right.Array(I2, J2);
|
||||
Som += aTemp.Array(I, J) * Right.Array(I2, J2);
|
||||
I2++;
|
||||
}
|
||||
Array(I, J2) = Som;
|
||||
|
Loading…
x
Reference in New Issue
Block a user