Loading files
// 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.
#ifndef _MathLin_Gauss_HeaderFile
#define _MathLin_Gauss_HeaderFile
#include <MathUtils_Types.hxx>
#include <MathUtils_Config.hxx>
#include <MathUtils_Core.hxx>
#include <math_Vector.hxx>
#include <math_Matrix.hxx>
#include <math_IntegerVector.hxx>
#include <cmath>
namespace MathLin
{
using namespace MathUtils;
//! Result for LU decomposition.
struct LUResult
{
MathUtils::Status Status = MathUtils::Status::NotConverged;
std::optional<math_Matrix> LU; //!< Combined L and U matrices
std::optional<math_IntegerVector> Pivot; //!< Pivot indices
std::optional<double> Determinant;
int Sign = 1; //!< Sign from row interchanges
bool IsDone() const { return Status == MathUtils::Status::OK; }
explicit operator bool() const { return IsDone(); }
};
//! Perform LU decomposition of matrix A with partial pivoting.
//! Decomposes A into L*U where L is lower triangular with unit diagonal
//! and U is upper triangular. The result stores L and U in a combined matrix.
//!
//! @param theA input square matrix
//! @param theMinPivot minimum pivot value (smaller treated as singular)
//! @return LU decomposition result
inline LUResult LU(const math_Matrix& theA, double theMinPivot = 1.0e-20)
{
LUResult aResult;
const int aRowLower = theA.LowerRow();
const int aRowUpper = theA.UpperRow();
const int aColLower = theA.LowerCol();
const int aColUpper = theA.UpperCol();
const int aRowCount = aRowUpper - aRowLower + 1;
const int aColCount = aColUpper - aColLower + 1;
// Check for square matrix
if (aRowCount != aColCount)
{
aResult.Status = Status::InvalidInput;
return aResult;
}
// Initialize LU matrix as copy of A
aResult.LU = theA;
aResult.Pivot = math_IntegerVector(aRowLower, aRowUpper);
aResult.Sign = 1;
math_Matrix& aLU = *aResult.LU;
math_IntegerVector& aPivot = *aResult.Pivot;
// Scaling factors for implicit pivoting
math_Vector aScale(aRowLower, aRowUpper);
for (int i = aRowLower; i <= aRowUpper; ++i)
{
double aMax = 0.0;
for (int j = aColLower; j <= aColUpper; ++j)
{
const double aAbs = std::abs(aLU(i, j));
if (aAbs > aMax)
{
aMax = aAbs;
}
}
if (aMax < theMinPivot)
{
aResult.Status = Status::NumericalError; // Singular matrix
return aResult;
}
aScale(i) = 1.0 / aMax;
}
// Crout's algorithm with partial pivoting
for (int k = aRowLower; k <= aRowUpper; ++k)
{
// Find pivot
double aMaxScaled = 0.0;
int aPivotRow = k;
for (int i = k; i <= aRowUpper; ++i)
{
const double aScaled = aScale(i) * std::abs(aLU(i, k));
if (aScaled > aMaxScaled)
{
aMaxScaled = aScaled;
aPivotRow = i;
}
}
// Swap rows if needed
if (aPivotRow != k)
{
for (int j = aColLower; j <= aColUpper; ++j)
{
std::swap(aLU(aPivotRow, j), aLU(k, j));
}
aResult.Sign = -aResult.Sign;
std::swap(aScale(aPivotRow), aScale(k));
}
aPivot(k) = aPivotRow;
// Check for singular matrix
if (std::abs(aLU(k, k)) < theMinPivot)
{
aResult.Status = Status::NumericalError;
return aResult;
}
// Eliminate below diagonal
for (int i = k + 1; i <= aRowUpper; ++i)
{
aLU(i, k) /= aLU(k, k);
const double aFactor = aLU(i, k);
for (int j = k + 1; j <= aColUpper; ++j)
{
aLU(i, j) -= aFactor * aLU(k, j);
}
}
}
// Compute determinant
aResult.Determinant = static_cast<double>(aResult.Sign);
for (int i = aRowLower; i <= aRowUpper; ++i)
{
*aResult.Determinant *= aLU(i, i);
}
aResult.Status = Status::OK;
return aResult;
}
//! Solve linear system AX = B using LU decomposition.
//!
//! @param theA coefficient matrix (square)
//! @param theB right-hand side vector
//! @param theMinPivot minimum pivot value
//! @return result containing solution vector
inline LinearResult Solve(const math_Matrix& theA,
const math_Vector& theB,
double theMinPivot = 1.0e-20)
{
LinearResult aResult;
// Perform LU decomposition
LUResult aLURes = LU(theA, theMinPivot);
if (!aLURes.IsDone())
{
aResult.Status = aLURes.Status;
return aResult;
}
const int aRowLower = theA.LowerRow();
const int aRowUpper = theA.UpperRow();
// Check dimensions
if (theB.Lower() != aRowLower || theB.Upper() != aRowUpper)
{
aResult.Status = Status::InvalidInput;
return aResult;
}
const math_Matrix& aLU = *aLURes.LU;
const math_IntegerVector& aPivot = *aLURes.Pivot;
// Copy B to working vector (we modify it in-place like the legacy algorithm)
math_Vector aX = theB;
// Forward substitution with in-place permutation (matches legacy LU_Solve)
int aFirstNonZero = 0;
for (int i = aRowLower; i <= aRowUpper; ++i)
{
const int aPivotIdx = aPivot(i);
double aSum = aX(aPivotIdx);
aX(aPivotIdx) = aX(i); // Swap elements in-place
if (aFirstNonZero != 0)
{
for (int j = aFirstNonZero; j < i; ++j)
{
aSum -= aLU(i, j) * aX(j);
}
}
else if (!MathUtils::IsZero(aSum))
{
aFirstNonZero = i;
}
aX(i) = aSum;
}
// Back substitution (solve Ux = y)
for (int i = aRowUpper; i >= aRowLower; --i)
{
double aSum = aX(i);
for (int j = i + 1; j <= aRowUpper; ++j)
{
aSum -= aLU(i, j) * aX(j);
}
aX(i) = aSum / aLU(i, i);
}
aResult.Status = Status::OK;
aResult.Solution = aX;
aResult.Determinant = aLURes.Determinant;
return aResult;
}
//! Solve multiple linear systems AX = B where B is a matrix.
//! Each column of B is a separate right-hand side.
//!
//! @param theA coefficient matrix (square)
//! @param theB right-hand side matrix
//! @param theMinPivot minimum pivot value
//! @return result containing solution matrix
inline LinearMultipleResult SolveMultiple(const math_Matrix& theA,
const math_Matrix& theB,
double theMinPivot = 1.0e-20)
{
LinearMultipleResult aResult;
// Perform LU decomposition
LUResult aLURes = LU(theA, theMinPivot);
if (!aLURes.IsDone())
{
aResult.Status = aLURes.Status;
return aResult;
}
const int aRowLower = theA.LowerRow();
const int aRowUpper = theA.UpperRow();
// Check dimensions
if (theB.LowerRow() != aRowLower || theB.UpperRow() != aRowUpper)
{
aResult.Status = Status::InvalidInput;
return aResult;
}
const math_Matrix& aLU = *aLURes.LU;
const math_IntegerVector& aPivot = *aLURes.Pivot;
// Solve for each column of B
math_Matrix aX(aRowLower, aRowUpper, theB.LowerCol(), theB.UpperCol());
for (int col = theB.LowerCol(); col <= theB.UpperCol(); ++col)
{
// Extract column as working vector
math_Vector aWork(aRowLower, aRowUpper);
for (int i = aRowLower; i <= aRowUpper; ++i)
{
aWork(i) = theB(i, col);
}
// Forward substitution with in-place permutation
int aFirstNonZero = 0;
for (int i = aRowLower; i <= aRowUpper; ++i)
{
const int aPivotIdx = aPivot(i);
double aSum = aWork(aPivotIdx);
aWork(aPivotIdx) = aWork(i); // Swap elements in-place
if (aFirstNonZero != 0)
{
for (int j = aFirstNonZero; j < i; ++j)
{
aSum -= aLU(i, j) * aWork(j);
}
}
else if (!MathUtils::IsZero(aSum))
{
aFirstNonZero = i;
}
aWork(i) = aSum;
}
// Back substitution
for (int i = aRowUpper; i >= aRowLower; --i)
{
double aSum = aWork(i);
for (int j = i + 1; j <= aRowUpper; ++j)
{
aSum -= aLU(i, j) * aWork(j);
}
aWork(i) = aSum / aLU(i, i);
}
// Copy result to output matrix
for (int i = aRowLower; i <= aRowUpper; ++i)
{
aX(i, col) = aWork(i);
}
}
aResult.Status = Status::OK;
aResult.Determinant = aLURes.Determinant;
aResult.Solutions = aX;
return aResult;
}
//! Compute determinant of matrix A.
//!
//! @param theA input square matrix
//! @param theMinPivot minimum pivot value
//! @return result containing determinant value
inline LinearResult Determinant(const math_Matrix& theA, double theMinPivot = 1.0e-20)
{
LinearResult aResult;
LUResult aLURes = LU(theA, theMinPivot);
if (!aLURes.IsDone())
{
aResult.Status = aLURes.Status;
aResult.Determinant = 0.0;
return aResult;
}
aResult.Status = Status::OK;
aResult.Determinant = aLURes.Determinant;
return aResult;
}
//! Compute inverse of matrix A.
//!
//! @param theA input square matrix
//! @param theMinPivot minimum pivot value
//! @return result containing inverse matrix
inline InverseResult Invert(const math_Matrix& theA, double theMinPivot = 1.0e-20)
{
InverseResult aResult;
// Perform LU decomposition
LUResult aLURes = LU(theA, theMinPivot);
if (!aLURes.IsDone())
{
aResult.Status = aLURes.Status;
return aResult;
}
const int aRowLower = theA.LowerRow();
const int aRowUpper = theA.UpperRow();
const int aColLower = theA.LowerCol();
const int aColUpper = theA.UpperCol();
const math_Matrix& aLU = *aLURes.LU;
const math_IntegerVector& aPivot = *aLURes.Pivot;
// Compute inverse column by column using LU_Solve approach
math_Matrix aInv(aRowLower, aRowUpper, aColLower, aColUpper, 0.0);
math_Vector aCol(aRowLower, aRowUpper);
for (int col = aColLower; col <= aColUpper; ++col)
{
// Initialize column as unit vector
for (int i = aRowLower; i <= aRowUpper; ++i)
{
aCol(i) = 0.0;
}
aCol(col - aColLower + aRowLower) = 1.0;
// Forward substitution with in-place permutation
int aFirstNonZero = 0;
for (int i = aRowLower; i <= aRowUpper; ++i)
{
const int aPivotIdx = aPivot(i);
double aSum = aCol(aPivotIdx);
aCol(aPivotIdx) = aCol(i); // Swap elements in-place
if (aFirstNonZero != 0)
{
for (int j = aFirstNonZero; j < i; ++j)
{
aSum -= aLU(i, j) * aCol(j);
}
}
else if (!MathUtils::IsZero(aSum))
{
aFirstNonZero = i;
}
aCol(i) = aSum;
}
// Back substitution
for (int i = aRowUpper; i >= aRowLower; --i)
{
double aSum = aCol(i);
for (int j = i + 1; j <= aRowUpper; ++j)
{
aSum -= aLU(i, j) * aCol(j);
}
aCol(i) = aSum / aLU(i, i);
}
// Copy result to inverse matrix
for (int i = aRowLower; i <= aRowUpper; ++i)
{
aInv(i, col) = aCol(i);
}
}
aResult.Status = Status::OK;
aResult.Inverse = aInv;
aResult.Determinant = aLURes.Determinant;
return aResult;
}
} // namespace MathLin
#endif // _MathLin_Gauss_HeaderFile
b8f597c67This file is pinned to the selected release. Download the original to open it in your design tool.