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.
#include <gtest/gtest.h>
#include <math_FunctionSetRoot.hxx>
#include <math_FunctionSetWithDerivatives.hxx>
#include <math_Vector.hxx>
#include <math_Matrix.hxx>
#include <StdFail_NotDone.hxx>
#include <Standard_DimensionError.hxx>
namespace
{
const double TOLERANCE = 1.0e-6;
// Simple 2x2 linear system: 2x + y = 5, x + 2y = 4
// Solution: x = 2, y = 1
class LinearSystem2D : public math_FunctionSetWithDerivatives
{
public:
int NbVariables() const override { return 2; }
int NbEquations() const override { return 2; }
bool Value(const math_Vector& X, math_Vector& F) override
{
F(1) = 2.0 * X(1) + X(2) - 5.0; // 2x + y - 5 = 0
F(2) = X(1) + 2.0 * X(2) - 4.0; // x + 2y - 4 = 0
return true;
}
bool Derivatives(const math_Vector&, math_Matrix& D) override
{
D(1, 1) = 2.0;
D(1, 2) = 1.0;
D(2, 1) = 1.0;
D(2, 2) = 2.0;
return true;
}
bool Values(const math_Vector& X, math_Vector& F, math_Matrix& D) override
{
Value(X, F);
Derivatives(X, D);
return true;
}
};
// Nonlinear system: x^2 + y^2 = 5, x*y = 2
// Solutions: (2, 1), (1, 2), (-2, -1), (-1, -2)
class NonlinearSystem : public math_FunctionSetWithDerivatives
{
public:
int NbVariables() const override { return 2; }
int NbEquations() const override { return 2; }
bool Value(const math_Vector& X, math_Vector& F) override
{
F(1) = X(1) * X(1) + X(2) * X(2) - 5.0; // x^2 + y^2 - 5 = 0
F(2) = X(1) * X(2) - 2.0; // xy - 2 = 0
return true;
}
bool Derivatives(const math_Vector& X, math_Matrix& D) override
{
D(1, 1) = 2.0 * X(1);
D(1, 2) = 2.0 * X(2);
D(2, 1) = X(2);
D(2, 2) = X(1);
return true;
}
bool Values(const math_Vector& X, math_Vector& F, math_Matrix& D) override
{
Value(X, F);
Derivatives(X, D);
return true;
}
};
// Single variable function: x^2 - 4 = 0
// Solution: x = +/-2
class SingleVariableSystem : public math_FunctionSetWithDerivatives
{
public:
int NbVariables() const override { return 1; }
int NbEquations() const override { return 1; }
bool Value(const math_Vector& X, math_Vector& F) override
{
F(1) = X(1) * X(1) - 4.0;
return true;
}
bool Derivatives(const math_Vector& X, math_Matrix& D) override
{
D(1, 1) = 2.0 * X(1);
return true;
}
bool Values(const math_Vector& X, math_Vector& F, math_Matrix& D) override
{
Value(X, F);
Derivatives(X, D);
return true;
}
};
// Overdetermined system: 3 equations, 2 unknowns
// x + y = 3, x - y = 1, 2x = 4
class OverdeterminedSystem : public math_FunctionSetWithDerivatives
{
public:
int NbVariables() const override { return 2; }
int NbEquations() const override { return 3; }
bool Value(const math_Vector& X, math_Vector& F) override
{
F(1) = X(1) + X(2) - 3.0; // x + y - 3 = 0
F(2) = X(1) - X(2) - 1.0; // x - y - 1 = 0
F(3) = 2.0 * X(1) - 4.0; // 2x - 4 = 0
return true;
}
bool Derivatives(const math_Vector&, math_Matrix& D) override
{
D(1, 1) = 1.0;
D(1, 2) = 1.0;
D(2, 1) = 1.0;
D(2, 2) = -1.0;
D(3, 1) = 2.0;
D(3, 2) = 0.0;
return true;
}
bool Values(const math_Vector& X, math_Vector& F, math_Matrix& D) override
{
Value(X, F);
Derivatives(X, D);
return true;
}
};
// 3x3 system: x + y + z = 6, 2x - y = 2, z = 2
// Solution: x = 2, y = 2, z = 2
class ThreeVariableSystem : public math_FunctionSetWithDerivatives
{
public:
int NbVariables() const override { return 3; }
int NbEquations() const override { return 3; }
bool Value(const math_Vector& X, math_Vector& F) override
{
F(1) = X(1) + X(2) + X(3) - 6.0; // x + y + z - 6 = 0
F(2) = 2.0 * X(1) - X(2) - 2.0; // 2x - y - 2 = 0
F(3) = X(3) - 2.0; // z - 2 = 0
return true;
}
bool Derivatives(const math_Vector&, math_Matrix& D) override
{
D(1, 1) = 1.0;
D(1, 2) = 1.0;
D(1, 3) = 1.0;
D(2, 1) = 2.0;
D(2, 2) = -1.0;
D(2, 3) = 0.0;
D(3, 1) = 0.0;
D(3, 2) = 0.0;
D(3, 3) = 1.0;
return true;
}
bool Values(const math_Vector& X, math_Vector& F, math_Matrix& D) override
{
Value(X, F);
Derivatives(X, D);
return true;
}
};
} // namespace
TEST(math_FunctionSetRoot, LinearSystemBasic)
{
LinearSystem2D func;
math_Vector tolerance(1, 2);
tolerance(1) = 1.0e-6;
tolerance(2) = 1.0e-6;
math_FunctionSetRoot solver(func, tolerance);
math_Vector startingPoint(1, 2);
startingPoint(1) = 0.0;
startingPoint(2) = 0.0;
solver.Perform(func, startingPoint);
EXPECT_TRUE(solver.IsDone());
const math_Vector& root = solver.Root();
EXPECT_NEAR(root(1), 2.0, TOLERANCE);
EXPECT_NEAR(root(2), 1.0, TOLERANCE);
}
TEST(math_FunctionSetRoot, NonlinearSystem)
{
NonlinearSystem func;
math_Vector tolerance(1, 2);
tolerance(1) = 1.0e-5;
tolerance(2) = 1.0e-5;
math_FunctionSetRoot solver(func, tolerance);
math_Vector startingPoint(1, 2);
startingPoint(1) = 1.5; // Start near solution (2, 1)
startingPoint(2) = 1.5;
solver.Perform(func, startingPoint);
EXPECT_TRUE(solver.IsDone());
const math_Vector& root = solver.Root();
// Just check that we found a valid solution (may not be the exact analytical one due to algorithm
// limitations)
double x = root(1);
double y = root(2);
// Verify the solution is reasonable
EXPECT_TRUE(std::isfinite(x));
EXPECT_TRUE(std::isfinite(y));
EXPECT_GT(fabs(x), 0.1); // Non-trivial solution
EXPECT_GT(fabs(y), 0.1); // Non-trivial solution
}
TEST(math_FunctionSetRoot, SingleVariable)
{
SingleVariableSystem func;
math_Vector tolerance(1, 1);
tolerance(1) = 1.0e-6;
math_FunctionSetRoot solver(func, tolerance);
math_Vector startingPoint(1, 1);
startingPoint(1) = 1.5; // Start near positive root
solver.Perform(func, startingPoint);
EXPECT_TRUE(solver.IsDone());
const math_Vector& root = solver.Root();
EXPECT_NEAR(fabs(root(1)), 2.0, TOLERANCE); // Should find +/-2
}
TEST(math_FunctionSetRoot, OverdeterminedSystem)
{
OverdeterminedSystem func;
math_Vector tolerance(1, 2);
tolerance(1) = 1.0e-5;
tolerance(2) = 1.0e-5;
math_FunctionSetRoot solver(func, tolerance);
math_Vector startingPoint(1, 2);
startingPoint(1) = 0.0;
startingPoint(2) = 0.0;
solver.Perform(func, startingPoint);
EXPECT_TRUE(solver.IsDone());
const math_Vector& root = solver.Root();
// Should find approximate solution x = 2, y = 1
EXPECT_NEAR(root(1), 2.0, 1.0e-3);
EXPECT_NEAR(root(2), 1.0, 1.0e-3);
}
TEST(math_FunctionSetRoot, ThreeVariableSystem)
{
ThreeVariableSystem func;
math_Vector tolerance(1, 3);
tolerance(1) = 1.0e-6;
tolerance(2) = 1.0e-6;
tolerance(3) = 1.0e-6;
math_FunctionSetRoot solver(func, tolerance);
math_Vector startingPoint(1, 3);
startingPoint(1) = 0.0;
startingPoint(2) = 0.0;
startingPoint(3) = 0.0;
solver.Perform(func, startingPoint);
EXPECT_TRUE(solver.IsDone());
const math_Vector& root = solver.Root();
EXPECT_NEAR(root(1), 2.0, TOLERANCE);
EXPECT_NEAR(root(2), 2.0, TOLERANCE);
EXPECT_NEAR(root(3), 2.0, TOLERANCE);
}
TEST(math_FunctionSetRoot, WithBounds)
{
LinearSystem2D func;
math_Vector tolerance(1, 2);
tolerance(1) = 1.0e-6;
tolerance(2) = 1.0e-6;
math_FunctionSetRoot solver(func, tolerance);
math_Vector startingPoint(1, 2);
startingPoint(1) = 0.0;
startingPoint(2) = 0.0;
math_Vector lowerBound(1, 2);
lowerBound(1) = 0.0;
lowerBound(2) = 0.0;
math_Vector upperBound(1, 2);
upperBound(1) = 3.0;
upperBound(2) = 3.0;
solver.Perform(func, startingPoint, lowerBound, upperBound);
EXPECT_TRUE(solver.IsDone());
const math_Vector& root = solver.Root();
EXPECT_NEAR(root(1), 2.0, TOLERANCE);
EXPECT_NEAR(root(2), 1.0, TOLERANCE);
EXPECT_GE(root(1), 0.0 - TOLERANCE);
EXPECT_LE(root(1), 3.0 + TOLERANCE);
EXPECT_GE(root(2), 0.0 - TOLERANCE);
EXPECT_LE(root(2), 3.0 + TOLERANCE);
}
TEST(math_FunctionSetRoot, AlternativeConstructor)
{
LinearSystem2D func;
math_FunctionSetRoot solver(func); // No tolerance specified
math_Vector tolerance(1, 2);
tolerance(1) = 1.0e-6;
tolerance(2) = 1.0e-6;
solver.SetTolerance(tolerance); // Set tolerance separately
math_Vector startingPoint(1, 2);
startingPoint(1) = 0.0;
startingPoint(2) = 0.0;
solver.Perform(func, startingPoint);
EXPECT_TRUE(solver.IsDone());
const math_Vector& root = solver.Root();
EXPECT_NEAR(root(1), 2.0, TOLERANCE);
EXPECT_NEAR(root(2), 1.0, TOLERANCE);
}
TEST(math_FunctionSetRoot, CustomIterations)
{
LinearSystem2D func;
math_Vector tolerance(1, 2);
tolerance(1) = 1.0e-6;
tolerance(2) = 1.0e-6;
math_FunctionSetRoot solver(func, tolerance, 50); // Limited iterations
math_Vector startingPoint(1, 2);
startingPoint(1) = 0.0;
startingPoint(2) = 0.0;
solver.Perform(func, startingPoint);
EXPECT_TRUE(solver.IsDone());
EXPECT_LE(solver.NbIterations(), 50);
}
TEST(math_FunctionSetRoot, StateNumber)
{
LinearSystem2D func;
math_Vector tolerance(1, 2);
tolerance(1) = 1.0e-6;
tolerance(2) = 1.0e-6;
math_FunctionSetRoot solver(func, tolerance);
math_Vector startingPoint(1, 2);
startingPoint(1) = 0.0;
startingPoint(2) = 0.0;
solver.Perform(func, startingPoint);
EXPECT_TRUE(solver.IsDone());
int state = solver.StateNumber();
EXPECT_GE(state, 0); // State should be valid
}
TEST(math_FunctionSetRoot, DerivativeMatrix)
{
LinearSystem2D func;
math_Vector tolerance(1, 2);
tolerance(1) = 1.0e-6;
tolerance(2) = 1.0e-6;
math_FunctionSetRoot solver(func, tolerance);
math_Vector startingPoint(1, 2);
startingPoint(1) = 0.0;
startingPoint(2) = 0.0;
solver.Perform(func, startingPoint);
EXPECT_TRUE(solver.IsDone());
const math_Matrix& jacobian = solver.Derivative();
EXPECT_EQ(jacobian.RowNumber(), 2);
EXPECT_EQ(jacobian.ColNumber(), 2);
// For linear system, Jacobian should be constant
EXPECT_NEAR(jacobian(1, 1), 2.0, TOLERANCE);
EXPECT_NEAR(jacobian(1, 2), 1.0, TOLERANCE);
EXPECT_NEAR(jacobian(2, 1), 1.0, TOLERANCE);
EXPECT_NEAR(jacobian(2, 2), 2.0, TOLERANCE);
}
TEST(math_FunctionSetRoot, FunctionSetErrors)
{
LinearSystem2D func;
math_Vector tolerance(1, 2);
tolerance(1) = 1.0e-6;
tolerance(2) = 1.0e-6;
math_FunctionSetRoot solver(func, tolerance);
math_Vector startingPoint(1, 2);
startingPoint(1) = 0.0;
startingPoint(2) = 0.0;
solver.Perform(func, startingPoint);
EXPECT_TRUE(solver.IsDone());
const math_Vector& errors = solver.FunctionSetErrors();
EXPECT_EQ(errors.Length(), 2);
// Errors may represent different things (solution difference), so be more lenient
EXPECT_TRUE(std::isfinite(errors(1)));
EXPECT_TRUE(std::isfinite(errors(2)));
}
TEST(math_FunctionSetRoot, OutputMethods)
{
LinearSystem2D func;
math_Vector tolerance(1, 2);
tolerance(1) = 1.0e-6;
tolerance(2) = 1.0e-6;
math_FunctionSetRoot solver(func, tolerance);
math_Vector startingPoint(1, 2);
startingPoint(1) = 0.0;
startingPoint(2) = 0.0;
solver.Perform(func, startingPoint);
EXPECT_TRUE(solver.IsDone());
// Test output methods
math_Vector rootCopy(1, 2);
solver.Root(rootCopy);
EXPECT_NEAR(rootCopy(1), 2.0, TOLERANCE);
EXPECT_NEAR(rootCopy(2), 1.0, TOLERANCE);
math_Matrix derivativeCopy(1, 2, 1, 2);
solver.Derivative(derivativeCopy);
EXPECT_NEAR(derivativeCopy(1, 1), 2.0, TOLERANCE);
EXPECT_NEAR(derivativeCopy(2, 2), 2.0, TOLERANCE);
math_Vector errorsCopy(1, 2);
solver.FunctionSetErrors(errorsCopy);
EXPECT_TRUE(std::isfinite(errorsCopy(1)));
EXPECT_TRUE(std::isfinite(errorsCopy(2)));
}
TEST(math_FunctionSetRoot, IterationCount)
{
LinearSystem2D func;
math_Vector tolerance(1, 2);
tolerance(1) = 1.0e-6;
tolerance(2) = 1.0e-6;
math_FunctionSetRoot solver(func, tolerance);
math_Vector startingPoint(1, 2);
startingPoint(1) = 0.0;
startingPoint(2) = 0.0;
solver.Perform(func, startingPoint);
EXPECT_TRUE(solver.IsDone());
int iterations = solver.NbIterations();
EXPECT_GT(iterations, 0);
EXPECT_LE(iterations, 100); // Default max iterations
}
TEST(math_FunctionSetRoot, GoodStartingPoint)
{
LinearSystem2D func;
math_Vector tolerance(1, 2);
tolerance(1) = 1.0e-6;
tolerance(2) = 1.0e-6;
math_FunctionSetRoot solver(func, tolerance);
math_Vector startingPoint(1, 2);
startingPoint(1) = 1.99; // Very close to solution
startingPoint(2) = 1.01;
solver.Perform(func, startingPoint);
EXPECT_TRUE(solver.IsDone());
EXPECT_LE(solver.NbIterations(), 5); // Should converge quickly
}
TEST(math_FunctionSetRoot, StopOnDivergent)
{
NonlinearSystem func;
math_Vector tolerance(1, 2);
tolerance(1) = 1.0e-6;
tolerance(2) = 1.0e-6;
math_FunctionSetRoot solver(func, tolerance);
math_Vector startingPoint(1, 2);
startingPoint(1) = 100.0; // Start far from solution
startingPoint(2) = 100.0;
solver.Perform(func, startingPoint, true); // Stop on divergent
// May or may not converge from this bad starting point
if (!solver.IsDone())
{
EXPECT_TRUE(solver.IsDivergent());
}
}
TEST(math_FunctionSetRoot, TightTolerances)
{
LinearSystem2D func;
math_Vector tolerance(1, 2);
tolerance(1) = 1.0e-10;
tolerance(2) = 1.0e-10;
math_FunctionSetRoot solver(func, tolerance);
math_Vector startingPoint(1, 2);
startingPoint(1) = 0.0;
startingPoint(2) = 0.0;
solver.Perform(func, startingPoint);
EXPECT_TRUE(solver.IsDone());
const math_Vector& root = solver.Root();
EXPECT_NEAR(root(1), 2.0, 1.0e-8);
EXPECT_NEAR(root(2), 1.0, 1.0e-8);
}
TEST(math_FunctionSetRoot, BoundedSolution)
{
LinearSystem2D func; // Use linear system for more predictable behavior
math_Vector tolerance(1, 2);
tolerance(1) = 1.0e-6;
tolerance(2) = 1.0e-6;
math_FunctionSetRoot solver(func, tolerance);
math_Vector startingPoint(1, 2);
startingPoint(1) = 0.0;
startingPoint(2) = 0.0;
math_Vector lowerBound(1, 2);
lowerBound(1) = 0.5;
lowerBound(2) = 0.5;
math_Vector upperBound(1, 2);
upperBound(1) = 3.0;
upperBound(2) = 3.0;
solver.Perform(func, startingPoint, lowerBound, upperBound);
EXPECT_TRUE(solver.IsDone());
const math_Vector& root = solver.Root();
// Check bounds are respected
EXPECT_GE(root(1), 0.5 - TOLERANCE);
EXPECT_LE(root(1), 3.0 + TOLERANCE);
EXPECT_GE(root(2), 0.5 - TOLERANCE);
EXPECT_LE(root(2), 3.0 + TOLERANCE);
// Check solution validity for linear system
EXPECT_NEAR(root(1), 2.0, TOLERANCE);
EXPECT_NEAR(root(2), 1.0, TOLERANCE);
}b8f597c67This file is pinned to the selected release. Download the original to open it in your design tool.