Loading files
b8f597c67This file is pinned to the selected release. Download the original to open it in your design tool.
// 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 _MathSys_Newton_HeaderFile
#define _MathSys_Newton_HeaderFile
#include <MathUtils_Types.hxx>
#include <MathUtils_Config.hxx>
#include <MathLin_Gauss.hxx>
#include <math_FunctionSetWithDerivatives.hxx>
#include <cmath>
namespace MathSys
{
using namespace MathUtils;
//! Newton-Raphson method for solving systems of nonlinear equations.
//!
//! Solves F(X) = 0 where F is a vector function of vector X.
//! The method iteratively improves an initial guess by solving
//! the linear system J(X)*dX = -F(X) where J is the Jacobian matrix.
//!
//! @param theFunc function set with derivatives (Jacobian)
//! @param theStart initial guess vector
//! @param theTolX tolerance for solution change ||X(n+1) - X(n)|| < tolX
//! @param theTolF tolerance for function values ||F(X)|| < tolF
//! @param theMaxIter maximum number of iterations
//! @return result containing solution vector if converged
template <typename FuncSetType>
VectorResult Newton(FuncSetType& theFunc,
const math_Vector& theStart,
const math_Vector& theTolX,
double theTolF,
size_t theMaxIter = 100)
{
VectorResult aResult;
const int aN = theFunc.NbVariables();
const int aM = theFunc.NbEquations();
// Check dimensions
if (aN != aM || theStart.Length() != aN || theTolX.Length() != aN)
{
aResult.Status = Status::InvalidInput;
return aResult;
}
const int aLower = theStart.Lower();
const int aUpper = theStart.Upper();
// Working vectors
math_Vector aSol = theStart;
math_Vector aF(aLower, aUpper);
math_Vector aDeltaX(aLower, aUpper);
math_Matrix aJacobian(aLower, aUpper, aLower, aUpper);
// Newton iteration
for (size_t anIter = 0; anIter < theMaxIter; ++anIter)
{
// Evaluate function and Jacobian
if (!theFunc.Values(aSol, aF, aJacobian))
{
aResult.Status = Status::NumericalError;
aResult.NbIterations = anIter;
return aResult;
}
// Solve J * dX = -F
// Negate F for the right-hand side
math_Vector aNegF(aLower, aUpper);
for (int i = aLower; i <= aUpper; ++i)
{
aNegF(i) = -aF(i);
}
auto aLinResult = MathLin::Solve(aJacobian, aNegF);
if (!aLinResult.IsDone())
{
aResult.Status = Status::Singular;
aResult.NbIterations = anIter;
return aResult;
}
aDeltaX = *aLinResult.Solution;
// Check X convergence before update
bool aXConverged = true;
for (int i = aLower; i <= aUpper; ++i)
{
if (std::abs(aDeltaX(i)) > theTolX(i))
{
aXConverged = false;
break;
}
}
// Update solution
for (int i = aLower; i <= aUpper; ++i)
{
aSol(i) += aDeltaX(i);
}
// Re-evaluate function at new solution to check F convergence
if (!theFunc.Value(aSol, aF))
{
aResult.Status = Status::NumericalError;
aResult.NbIterations = anIter + 1;
return aResult;
}
// Check F convergence with updated function values
bool aFConverged = true;
for (int i = aLower; i <= aUpper; ++i)
{
if (std::abs(aF(i)) > theTolF)
{
aFConverged = false;
break;
}
}
if (aXConverged && aFConverged)
{
aResult.Status = Status::OK;
aResult.NbIterations = anIter + 1;
aResult.Solution = aSol;
aResult.Jacobian = aJacobian;
return aResult;
}
aResult.NbIterations = anIter + 1;
}
// Max iterations reached - return last solution
aResult.Status = Status::MaxIterations;
aResult.Solution = aSol;
aResult.Jacobian = aJacobian;
return aResult;
}
//! Newton-Raphson method with bounds constraints.
//!
//! Solves F(X) = 0 subject to InfBound <= X <= SupBound.
//! If the Newton step would take X outside bounds, the solution
//! is clamped to the boundary.
//!
//! @param theFunc function set with derivatives (Jacobian)
//! @param theStart initial guess vector
//! @param theInfBound lower bounds for solution
//! @param theSupBound upper bounds for solution
//! @param theTolX tolerance for solution change
//! @param theTolF tolerance for function values
//! @param theMaxIter maximum number of iterations
//! @return result containing solution vector if converged
template <typename FuncSetType>
VectorResult NewtonBounded(FuncSetType& theFunc,
const math_Vector& theStart,
const math_Vector& theInfBound,
const math_Vector& theSupBound,
const math_Vector& theTolX,
double theTolF,
size_t theMaxIter = 100)
{
VectorResult aResult;
const int aN = theFunc.NbVariables();
const int aM = theFunc.NbEquations();
// Check dimensions
if (aN != aM || theStart.Length() != aN || theTolX.Length() != aN || theInfBound.Length() != aN
|| theSupBound.Length() != aN)
{
aResult.Status = Status::InvalidInput;
return aResult;
}
const int aLower = theStart.Lower();
const int aUpper = theStart.Upper();
// Working vectors
math_Vector aSol = theStart;
math_Vector aF(aLower, aUpper);
math_Vector aDeltaX(aLower, aUpper);
math_Matrix aJacobian(aLower, aUpper, aLower, aUpper);
// Clamp initial solution to bounds
for (int i = aLower; i <= aUpper; ++i)
{
if (aSol(i) < theInfBound(i))
{
aSol(i) = theInfBound(i);
}
if (aSol(i) > theSupBound(i))
{
aSol(i) = theSupBound(i);
}
}
// Newton iteration
for (size_t anIter = 0; anIter < theMaxIter; ++anIter)
{
// Evaluate function and Jacobian
if (!theFunc.Values(aSol, aF, aJacobian))
{
aResult.Status = Status::NumericalError;
aResult.NbIterations = anIter;
return aResult;
}
// Solve J * dX = -F
math_Vector aNegF(aLower, aUpper);
for (int i = aLower; i <= aUpper; ++i)
{
aNegF(i) = -aF(i);
}
auto aLinResult = MathLin::Solve(aJacobian, aNegF);
if (!aLinResult.IsDone())
{
aResult.Status = Status::Singular;
aResult.NbIterations = anIter;
return aResult;
}
aDeltaX = *aLinResult.Solution;
// Check X convergence before update
bool aXConverged = true;
for (int i = aLower; i <= aUpper; ++i)
{
if (std::abs(aDeltaX(i)) > theTolX(i))
{
aXConverged = false;
break;
}
}
// Update solution with bounds clamping
for (int i = aLower; i <= aUpper; ++i)
{
aSol(i) += aDeltaX(i);
if (aSol(i) < theInfBound(i))
{
aSol(i) = theInfBound(i);
}
if (aSol(i) > theSupBound(i))
{
aSol(i) = theSupBound(i);
}
}
// Re-evaluate function at new solution to check F convergence
if (!theFunc.Value(aSol, aF))
{
aResult.Status = Status::NumericalError;
aResult.NbIterations = anIter + 1;
return aResult;
}
// Check F convergence with updated function values
bool aFConverged = true;
for (int i = aLower; i <= aUpper; ++i)
{
if (std::abs(aF(i)) > theTolF)
{
aFConverged = false;
break;
}
}
if (aXConverged && aFConverged)
{
aResult.Status = Status::OK;
aResult.NbIterations = anIter + 1;
aResult.Solution = aSol;
aResult.Jacobian = aJacobian;
return aResult;
}
aResult.NbIterations = anIter + 1;
}
// Max iterations reached
aResult.Status = Status::MaxIterations;
aResult.Solution = aSol;
aResult.Jacobian = aJacobian;
return aResult;
}
//! Simplified Newton method with uniform tolerances.
//!
//! @param theFunc function set with derivatives
//! @param theStart initial guess vector
//! @param theTolX uniform tolerance for all variables
//! @param theTolF tolerance for function values
//! @param theMaxIter maximum number of iterations
//! @return result containing solution vector if converged
template <typename FuncSetType>
VectorResult Newton(FuncSetType& theFunc,
const math_Vector& theStart,
double theTolX,
double theTolF,
size_t theMaxIter = 100)
{
math_Vector aTolXVec(theStart.Lower(), theStart.Upper(), theTolX);
return Newton(theFunc, theStart, aTolXVec, theTolF, theMaxIter);
}
} // namespace MathSys
#endif // _MathSys_Newton_HeaderFile