// 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 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 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 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 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