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 <math_PSO.hxx>
#include <math_PSOParticlesPool.hxx>
#include <math_MultipleVarFunction.hxx>
#include <math_Vector.hxx>
#include <gtest/gtest.h>
#include <Standard_Real.hxx>
#include <Standard_Integer.hxx>
#include <cmath>
namespace
{
// Simple quadratic function: f(x,y) = (x-1)^2 + (y-2)^2, minimum at (1, 2)
class QuadraticFunction : public math_MultipleVarFunction
{
public:
int NbVariables() const override { return 2; }
bool Value(const math_Vector& theX, double& theF) override
{
double dx = theX(1) - 1.0;
double dy = theX(2) - 2.0;
theF = dx * dx + dy * dy;
return true;
}
};
// 1D quadratic function: f(x) = (x-3)^2, minimum at x = 3
class Quadratic1DFunction : public math_MultipleVarFunction
{
public:
int NbVariables() const override { return 1; }
bool Value(const math_Vector& theX, double& theF) override
{
double dx = theX(1) - 3.0;
theF = dx * dx;
return true;
}
};
// Rosenbrock function: f(x,y) = (1-x)^2 + 100*(y-x^2)^2
class RosenbrockFunction : public math_MultipleVarFunction
{
public:
int NbVariables() const override { return 2; }
bool Value(const math_Vector& theX, double& theF) override
{
double x = theX(1);
double y = theX(2);
double dx = 1.0 - x;
double dy = y - x * x;
theF = dx * dx + 100.0 * dy * dy;
return true;
}
};
// Multi-modal function: f(x,y) = -cos(x)*cos(y)*exp(-((x-PI)^2+(y-PI)^2))
class MultiModalFunction : public math_MultipleVarFunction
{
public:
int NbVariables() const override { return 2; }
bool Value(const math_Vector& theX, double& theF) override
{
double x = theX(1);
double y = theX(2);
double dx = x - M_PI;
double dy = y - M_PI;
theF = -cos(x) * cos(y) * exp(-(dx * dx + dy * dy));
return true;
}
};
// 3D function: f(x,y,z) = x^2 + 2*y^2 + 3*z^2, minimum at (0,0,0)
class Quadratic3DFunction : public math_MultipleVarFunction
{
public:
int NbVariables() const override { return 3; }
bool Value(const math_Vector& theX, double& theF) override
{
double x = theX(1);
double y = theX(2);
double z = theX(3);
theF = x * x + 2.0 * y * y + 3.0 * z * z;
return true;
}
};
} // anonymous namespace
TEST(MathPSOTest, QuadraticFunctionOptimization)
{
// Test PSO on simple quadratic function
QuadraticFunction aFunc;
math_Vector aLowerBorder(1, 2);
aLowerBorder(1) = -2.0;
aLowerBorder(2) = -1.0;
math_Vector aUpperBorder(1, 2);
aUpperBorder(1) = 4.0;
aUpperBorder(2) = 5.0;
math_Vector aSteps(1, 2);
aSteps(1) = 0.1;
aSteps(2) = 0.1;
math_PSO aSolver(&aFunc, aLowerBorder, aUpperBorder, aSteps, 20, 50);
double aValue;
math_Vector aSolution(1, 2);
aSolver.Perform(aSteps, aValue, aSolution);
EXPECT_NEAR(aSolution(1), 1.0, 0.5) << "PSO should find solution near x = 1";
EXPECT_NEAR(aSolution(2), 2.0, 0.5) << "PSO should find solution near y = 2";
EXPECT_LT(aValue, 1.0) << "Function value should be small near minimum";
}
TEST(MathPSOTest, OneDimensionalOptimization)
{
// Test PSO on 1D function
Quadratic1DFunction aFunc;
math_Vector aLowerBorder(1, 1);
aLowerBorder(1) = 0.0;
math_Vector aUpperBorder(1, 1);
aUpperBorder(1) = 6.0;
math_Vector aSteps(1, 1);
aSteps(1) = 0.1;
math_PSO aSolver(&aFunc, aLowerBorder, aUpperBorder, aSteps, 15, 30);
double aValue;
math_Vector aSolution(1, 1);
aSolver.Perform(aSteps, aValue, aSolution);
EXPECT_NEAR(aSolution(1), 3.0, 0.5) << "PSO should find solution near x = 3";
EXPECT_LT(aValue, 0.5) << "Function value should be small near minimum";
}
TEST(MathPSOTest, ThreeDimensionalOptimization)
{
// Test PSO on 3D function
Quadratic3DFunction aFunc;
math_Vector aLowerBorder(1, 3);
aLowerBorder(1) = -2.0;
aLowerBorder(2) = -2.0;
aLowerBorder(3) = -2.0;
math_Vector aUpperBorder(1, 3);
aUpperBorder(1) = 2.0;
aUpperBorder(2) = 2.0;
aUpperBorder(3) = 2.0;
math_Vector aSteps(1, 3);
aSteps(1) = 0.1;
aSteps(2) = 0.1;
aSteps(3) = 0.1;
math_PSO aSolver(&aFunc, aLowerBorder, aUpperBorder, aSteps, 25, 40);
double aValue;
math_Vector aSolution(1, 3);
aSolver.Perform(aSteps, aValue, aSolution);
EXPECT_NEAR(aSolution(1), 0.0, 0.5) << "PSO should find solution near x = 0";
EXPECT_NEAR(aSolution(2), 0.0, 0.5) << "PSO should find solution near y = 0";
EXPECT_NEAR(aSolution(3), 0.0, 0.5) << "PSO should find solution near z = 0";
EXPECT_LT(aValue, 1.0) << "Function value should be small near minimum";
}
TEST(MathPSOTest, CustomParticleCount)
{
// Test PSO with different particle counts
QuadraticFunction aFunc;
math_Vector aLowerBorder(1, 2);
aLowerBorder(1) = -2.0;
aLowerBorder(2) = -1.0;
math_Vector aUpperBorder(1, 2);
aUpperBorder(1) = 4.0;
aUpperBorder(2) = 5.0;
math_Vector aSteps(1, 2);
aSteps(1) = 0.2;
aSteps(2) = 0.2;
// Few particles
math_PSO aSolver1(&aFunc, aLowerBorder, aUpperBorder, aSteps, 5, 20);
double aValue1;
math_Vector aSolution1(1, 2);
aSolver1.Perform(aSteps, aValue1, aSolution1);
EXPECT_TRUE(aSolution1(1) >= -2.0 && aSolution1(1) <= 4.0) << "Solution should be within bounds";
EXPECT_TRUE(aSolution1(2) >= -1.0 && aSolution1(2) <= 5.0) << "Solution should be within bounds";
// Many particles
math_PSO aSolver2(&aFunc, aLowerBorder, aUpperBorder, aSteps, 50, 30);
double aValue2;
math_Vector aSolution2(1, 2);
aSolver2.Perform(aSteps, aValue2, aSolution2);
EXPECT_TRUE(aSolution2(1) >= -2.0 && aSolution2(1) <= 4.0) << "Solution should be within bounds";
EXPECT_TRUE(aSolution2(2) >= -1.0 && aSolution2(2) <= 5.0) << "Solution should be within bounds";
}
TEST(MathPSOTest, CustomIterationCount)
{
// Test PSO with different iteration counts
QuadraticFunction aFunc;
math_Vector aLowerBorder(1, 2);
aLowerBorder(1) = -2.0;
aLowerBorder(2) = -1.0;
math_Vector aUpperBorder(1, 2);
aUpperBorder(1) = 4.0;
aUpperBorder(2) = 5.0;
math_Vector aSteps(1, 2);
aSteps(1) = 0.1;
aSteps(2) = 0.1;
math_PSO aSolver(&aFunc, aLowerBorder, aUpperBorder, aSteps, 20, 10);
double aValue;
math_Vector aSolution(1, 2);
// Test with fewer iterations
aSolver.Perform(aSteps, aValue, aSolution, 5);
EXPECT_TRUE(aSolution(1) >= -2.0 && aSolution(1) <= 4.0) << "Solution should be within bounds";
EXPECT_TRUE(aSolution(2) >= -1.0 && aSolution(2) <= 5.0) << "Solution should be within bounds";
// Test with more iterations
aSolver.Perform(aSteps, aValue, aSolution, 100);
EXPECT_TRUE(aSolution(1) >= -2.0 && aSolution(1) <= 4.0) << "Solution should be within bounds";
EXPECT_TRUE(aSolution(2) >= -1.0 && aSolution(2) <= 5.0) << "Solution should be within bounds";
}
TEST(MathPSOTest, RosenbrockOptimization)
{
// Test PSO on challenging Rosenbrock function
RosenbrockFunction aFunc;
math_Vector aLowerBorder(1, 2);
aLowerBorder(1) = -2.0;
aLowerBorder(2) = -1.0;
math_Vector aUpperBorder(1, 2);
aUpperBorder(1) = 2.0;
aUpperBorder(2) = 3.0;
math_Vector aSteps(1, 2);
aSteps(1) = 0.1;
aSteps(2) = 0.1;
math_PSO aSolver(&aFunc, aLowerBorder, aUpperBorder, aSteps, 40, 100);
double aValue;
math_Vector aSolution(1, 2);
aSolver.Perform(aSteps, aValue, aSolution);
// PSO may not find exact minimum due to stochastic nature, but should be reasonably close
EXPECT_TRUE(aSolution(1) >= -2.0 && aSolution(1) <= 2.0) << "Solution should be within bounds";
EXPECT_TRUE(aSolution(2) >= -1.0 && aSolution(2) <= 3.0) << "Solution should be within bounds";
EXPECT_LT(aValue, 100.0) << "Function value should improve from random start";
}
TEST(MathPSOTest, MultiModalOptimization)
{
// Test PSO on multi-modal function
MultiModalFunction aFunc;
math_Vector aLowerBorder(1, 2);
aLowerBorder(1) = 0.0;
aLowerBorder(2) = 0.0;
math_Vector aUpperBorder(1, 2);
aUpperBorder(1) = 2.0 * M_PI;
aUpperBorder(2) = 2.0 * M_PI;
math_Vector aSteps(1, 2);
aSteps(1) = 0.2;
aSteps(2) = 0.2;
math_PSO aSolver(&aFunc, aLowerBorder, aUpperBorder, aSteps, 30, 50);
double aValue;
math_Vector aSolution(1, 2);
aSolver.Perform(aSteps, aValue, aSolution);
EXPECT_TRUE(aSolution(1) >= 0.0 && aSolution(1) <= 2.0 * M_PI)
<< "Solution should be within bounds";
EXPECT_TRUE(aSolution(2) >= 0.0 && aSolution(2) <= 2.0 * M_PI)
<< "Solution should be within bounds";
EXPECT_LT(aValue, 0.0) << "Should find negative value (local/global minimum)";
}
TEST(MathPSOTest, DifferentStepSizes)
{
// Test PSO with different step sizes
QuadraticFunction aFunc;
math_Vector aLowerBorder(1, 2);
aLowerBorder(1) = -2.0;
aLowerBorder(2) = -1.0;
math_Vector aUpperBorder(1, 2);
aUpperBorder(1) = 4.0;
aUpperBorder(2) = 5.0;
// Large steps
math_Vector aLargeSteps(1, 2);
aLargeSteps(1) = 0.5;
aLargeSteps(2) = 0.5;
math_PSO aSolver1(&aFunc, aLowerBorder, aUpperBorder, aLargeSteps, 15, 20);
double aValue1;
math_Vector aSolution1(1, 2);
aSolver1.Perform(aLargeSteps, aValue1, aSolution1);
EXPECT_TRUE(aSolution1(1) >= -2.0 && aSolution1(1) <= 4.0) << "Solution should be within bounds";
EXPECT_TRUE(aSolution1(2) >= -1.0 && aSolution1(2) <= 5.0) << "Solution should be within bounds";
// Small steps
math_Vector aSmallSteps(1, 2);
aSmallSteps(1) = 0.05;
aSmallSteps(2) = 0.05;
math_PSO aSolver2(&aFunc, aLowerBorder, aUpperBorder, aSmallSteps, 15, 20);
double aValue2;
math_Vector aSolution2(1, 2);
aSolver2.Perform(aSmallSteps, aValue2, aSolution2);
EXPECT_TRUE(aSolution2(1) >= -2.0 && aSolution2(1) <= 4.0) << "Solution should be within bounds";
EXPECT_TRUE(aSolution2(2) >= -1.0 && aSolution2(2) <= 5.0) << "Solution should be within bounds";
}
TEST(MathPSOTest, PSOParticlesPoolIntegration)
{
// Test PSO with explicit particles pool
QuadraticFunction aFunc;
math_Vector aLowerBorder(1, 2);
aLowerBorder(1) = -2.0;
aLowerBorder(2) = -1.0;
math_Vector aUpperBorder(1, 2);
aUpperBorder(1) = 4.0;
aUpperBorder(2) = 5.0;
math_Vector aSteps(1, 2);
aSteps(1) = 0.1;
aSteps(2) = 0.1;
math_PSO aSolver(&aFunc, aLowerBorder, aUpperBorder, aSteps, 20, 30);
// Create particles pool
int aNbParticles = 20;
math_PSOParticlesPool aParticlesPool(aNbParticles, 2);
double aValue;
math_Vector aSolution(1, 2);
aSolver.Perform(aParticlesPool, aNbParticles, aValue, aSolution);
EXPECT_TRUE(aSolution(1) >= -2.0 && aSolution(1) <= 4.0) << "Solution should be within bounds";
EXPECT_TRUE(aSolution(2) >= -1.0 && aSolution(2) <= 5.0) << "Solution should be within bounds";
}
TEST(MathPSOTest, SmallSearchSpace)
{
// Test PSO with very small search space
QuadraticFunction aFunc;
math_Vector aLowerBorder(1, 2);
aLowerBorder(1) = 0.8;
aLowerBorder(2) = 1.8;
math_Vector aUpperBorder(1, 2);
aUpperBorder(1) = 1.2;
aUpperBorder(2) = 2.2;
math_Vector aSteps(1, 2);
aSteps(1) = 0.05;
aSteps(2) = 0.05;
math_PSO aSolver(&aFunc, aLowerBorder, aUpperBorder, aSteps, 10, 20);
double aValue;
math_Vector aSolution(1, 2);
aSolver.Perform(aSteps, aValue, aSolution);
EXPECT_NEAR(aSolution(1), 1.0, 0.3) << "Should find solution close to minimum in small space";
EXPECT_NEAR(aSolution(2), 2.0, 0.3) << "Should find solution close to minimum in small space";
EXPECT_LT(aValue, 0.5) << "Should find small function value";
}
TEST(MathPSOTest, AsymmetricBounds)
{
// Test PSO with asymmetric bounds
QuadraticFunction aFunc;
math_Vector aLowerBorder(1, 2);
aLowerBorder(1) = -5.0; // Far from minimum
aLowerBorder(2) = 1.5; // Close to minimum
math_Vector aUpperBorder(1, 2);
aUpperBorder(1) = 2.0; // Includes minimum
aUpperBorder(2) = 10.0; // Far from minimum
math_Vector aSteps(1, 2);
aSteps(1) = 0.2;
aSteps(2) = 0.2;
math_PSO aSolver(&aFunc, aLowerBorder, aUpperBorder, aSteps, 25, 40);
double aValue;
math_Vector aSolution(1, 2);
aSolver.Perform(aSteps, aValue, aSolution);
EXPECT_TRUE(aSolution(1) >= -5.0 && aSolution(1) <= 2.0) << "Solution should be within x bounds";
EXPECT_TRUE(aSolution(2) >= 1.5 && aSolution(2) <= 10.0) << "Solution should be within y bounds";
EXPECT_NEAR(aSolution(1), 1.0, 1.5) << "Should find solution reasonably close to minimum";
EXPECT_NEAR(aSolution(2), 2.0, 2.0) << "Should find solution reasonably close to minimum";
}
TEST(MathPSOTest, MinimalConfiguration)
{
// Test PSO with minimal configuration (few particles, few iterations)
Quadratic1DFunction aFunc;
math_Vector aLowerBorder(1, 1);
aLowerBorder(1) = 0.0;
math_Vector aUpperBorder(1, 1);
aUpperBorder(1) = 6.0;
math_Vector aSteps(1, 1);
aSteps(1) = 0.5;
math_PSO aSolver(&aFunc, aLowerBorder, aUpperBorder, aSteps, 3, 5); // Minimal config
double aValue;
math_Vector aSolution(1, 1);
aSolver.Perform(aSteps, aValue, aSolution, 3);
EXPECT_TRUE(aSolution(1) >= 0.0 && aSolution(1) <= 6.0) << "Solution should be within bounds";
// With minimal configuration, we just check it doesn't crash and produces valid output
}
TEST(MathPSOTest, RepeatedPerformCalls)
{
// Test multiple calls to Perform method
QuadraticFunction aFunc;
math_Vector aLowerBorder(1, 2);
aLowerBorder(1) = -2.0;
aLowerBorder(2) = -1.0;
math_Vector aUpperBorder(1, 2);
aUpperBorder(1) = 4.0;
aUpperBorder(2) = 5.0;
math_Vector aSteps(1, 2);
aSteps(1) = 0.1;
aSteps(2) = 0.1;
math_PSO aSolver(&aFunc, aLowerBorder, aUpperBorder, aSteps, 15, 20);
double aValue1, aValue2;
math_Vector aSolution1(1, 2), aSolution2(1, 2);
// First call
aSolver.Perform(aSteps, aValue1, aSolution1);
EXPECT_TRUE(aSolution1(1) >= -2.0 && aSolution1(1) <= 4.0)
<< "First solution should be within bounds";
EXPECT_TRUE(aSolution1(2) >= -1.0 && aSolution1(2) <= 5.0)
<< "First solution should be within bounds";
// Second call
aSolver.Perform(aSteps, aValue2, aSolution2);
EXPECT_TRUE(aSolution2(1) >= -2.0 && aSolution2(1) <= 4.0)
<< "Second solution should be within bounds";
EXPECT_TRUE(aSolution2(2) >= -1.0 && aSolution2(2) <= 5.0)
<< "Second solution should be within bounds";
// Results may vary due to stochastic nature, but both should be valid
}3d097a032This file is pinned to the selected release. Download the original to open it in your design tool.