/*========================================================================= * * Copyright NumFOCUS * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #include #include "itkInitializationBiasedParticleSwarmOptimizer.h" #include "itkParticleSwarmOptimizerTestFunctions.h" #include "itkTestingMacros.h" using OptimizerType = itk::InitializationBiasedParticleSwarmOptimizer; static OptimizerType::RandomVariateGeneratorType::IntegerType seedOffset = 0; /** * Test using a 1D function with two minima, two parabolas. Check that the * optimizer converges to the global minimum if it is initialized inside the * domain of either parabolas (runs the optimizer once with initial guess in * each of the domains). */ int IBPSOTest1(typename OptimizerType::CoefficientType, typename OptimizerType::CoefficientType, typename OptimizerType::CoefficientType, typename OptimizerType::CoefficientType); /** * Test using a 2D quadratic function (single minimum), check that converges * correctly. */ int IBPSOTest2(typename OptimizerType::CoefficientType, typename OptimizerType::CoefficientType, typename OptimizerType::CoefficientType, typename OptimizerType::CoefficientType); /** * Test using the 2D Rosenbrock function. */ int IBPSOTest3(typename OptimizerType::CoefficientType, typename OptimizerType::CoefficientType, typename OptimizerType::CoefficientType, typename OptimizerType::CoefficientType); bool initalizationBasedTestVerboseFlag = false; /** * The particle swarm optimizer is a stochastic algorithm. Consequentially, we run * the same test multiple times and deem it a success if the number of successful * runs is above a threshold. */ int itkInitializationBiasedParticleSwarmOptimizerTest(int argc, char * argv[]) { if (argc < 5) { std::cerr << "Missing parameters." << std::endl; std::cerr << "Usage: " << itkNameOfTestExecutableMacro(argv) << " inertiaCoefficient" << " personalCoefficient" << " globalCoefficient" << " initializationCoefficient" << " [initalizationBasedTestVerboseFlag]" << std::endl; return EXIT_FAILURE; } if (argc > 5) { initalizationBasedTestVerboseFlag = std::stoi(argv[5]) ? true : false; } unsigned int i, allIterations = 10; double threshold = 0.8; unsigned int success1, success2, success3; std::cout << "Initialization Biased Particle Swarm Optimizer Test \n \n"; auto inertiaCoefficient = static_cast(std::stod(argv[1])); auto personalCoefficient = static_cast(std::stod(argv[2])); auto globalCoefficient = static_cast(std::stod(argv[3])); auto initializationCoefficient = static_cast(std::stod(argv[4])); success1 = success2 = success3 = 0; for (i = 0; i < allIterations; ++i) { if (EXIT_SUCCESS == IBPSOTest1(inertiaCoefficient, personalCoefficient, globalCoefficient, initializationCoefficient)) { success1++; } if (EXIT_SUCCESS == IBPSOTest2(inertiaCoefficient, personalCoefficient, globalCoefficient, initializationCoefficient)) { success2++; } if (EXIT_SUCCESS == IBPSOTest3(inertiaCoefficient, personalCoefficient, globalCoefficient, initializationCoefficient)) { success3++; } } std::cout << "All Tests Completed." << std::endl; if (static_cast(success1) / static_cast(allIterations) <= threshold || static_cast(success2) / static_cast(allIterations) <= threshold || static_cast(success3) / static_cast(allIterations) <= threshold) { std::cout << "[FAILURE]\n"; return EXIT_FAILURE; } std::cout << "[SUCCESS]" << std::endl; return EXIT_SUCCESS; } int IBPSOTest1(typename OptimizerType::CoefficientType inertiaCoefficient, typename OptimizerType::CoefficientType personalCoefficient, typename OptimizerType::CoefficientType globalCoefficient, typename OptimizerType::CoefficientType initializationCoefficient) { std::cout << "Particle Swarm Optimizer Test 1 [f(x) = if(x<0) x^2+4x; else 2x^2-8x]\n"; std::cout << "-------------------------------\n"; double knownParameters = 2.0; // the function we want to optimize itk::ParticleSwarmTestF1::Pointer costFunction = itk::ParticleSwarmTestF1::New(); auto itkOptimizer = OptimizerType::New(); ITK_EXERCISE_BASIC_OBJECT_METHODS( itkOptimizer, InitializationBiasedParticleSwarmOptimizer, ParticleSwarmOptimizerBase); itkOptimizer->SetInertiaCoefficient(inertiaCoefficient); ITK_TEST_SET_GET_VALUE(inertiaCoefficient, itkOptimizer->GetInertiaCoefficient()); itkOptimizer->SetPersonalCoefficient(personalCoefficient); ITK_TEST_SET_GET_VALUE(personalCoefficient, itkOptimizer->GetPersonalCoefficient()); itkOptimizer->SetGlobalCoefficient(globalCoefficient); ITK_TEST_SET_GET_VALUE(globalCoefficient, itkOptimizer->GetGlobalCoefficient()); itkOptimizer->SetInitializationCoefficient(globalCoefficient); ITK_TEST_SET_GET_VALUE(initializationCoefficient, itkOptimizer->GetInitializationCoefficient()); itkOptimizer->UseSeedOn(); itkOptimizer->SetSeed(8775070 + seedOffset++); // set optimizer parameters OptimizerType::ParameterBoundsType bounds; bounds.push_back(std::make_pair(-10, 10)); unsigned int numberOfParticles = 10; unsigned int maxIterations = 200; double xTolerance = 0.1; double fTolerance = 0.001; OptimizerType::ParametersType initialParameters(1), finalParameters; itkOptimizer->SetParameterBounds(bounds); itkOptimizer->SetNumberOfParticles(numberOfParticles); itkOptimizer->SetMaximalNumberOfIterations(maxIterations); itkOptimizer->SetParametersConvergenceTolerance(xTolerance, costFunction->GetNumberOfParameters()); itkOptimizer->SetFunctionConvergenceTolerance(fTolerance); itkOptimizer->SetCostFunction(costFunction); // observe the iterations itk::CommandIterationUpdateParticleSwarm::Pointer observer = itk::CommandIterationUpdateParticleSwarm::New(); if (initalizationBasedTestVerboseFlag) { itkOptimizer->AddObserver(itk::IterationEvent(), observer); itkOptimizer->AddObserver(itk::StartEvent(), observer); } try { initialParameters[0] = 3; itkOptimizer->SetInitialPosition(initialParameters); itkOptimizer->StartOptimization(); finalParameters = itkOptimizer->GetCurrentPosition(); // show why we stopped and see if the optimization succeeded std::cout << "Reason for stopping optimization:\n"; std::cout << '\t' << itkOptimizer->GetStopConditionDescription() << '\n'; finalParameters = itkOptimizer->GetCurrentPosition(); std::cout << "Known parameters = " << knownParameters << " "; std::cout << "Estimated parameters = " << finalParameters << std::endl; if (itk::Math::abs(finalParameters[0] - knownParameters) > xTolerance) { std::cout << "[Test 1 FAILURE]" << std::endl; return EXIT_FAILURE; } // run optimization again with a different initial value initialParameters[0] = 2.5; itkOptimizer->ClearSwarm(); itkOptimizer->SetInitialPosition(initialParameters); if (initalizationBasedTestVerboseFlag) { observer->Reset(); } itkOptimizer->StartOptimization(); finalParameters = itkOptimizer->GetCurrentPosition(); // show why we stopped and see if the optimization succeeded std::cout << "Reason for stopping optimization:\n"; std::cout << '\t' << itkOptimizer->GetStopConditionDescription() << '\n'; finalParameters = itkOptimizer->GetCurrentPosition(); std::cout << "Known parameters = " << knownParameters << " "; std::cout << "Estimated parameters = " << finalParameters << std::endl; if (itk::Math::abs(finalParameters[0] - knownParameters) > xTolerance) { std::cout << "[Test 1 FAILURE]" << std::endl; return EXIT_FAILURE; } } catch (const itk::ExceptionObject & e) { std::cout << "[Test 1 FAILURE]" << std::endl; std::cout << "Exception thrown ! " << std::endl; std::cout << "An error occurred during Optimization" << std::endl; std::cout << "Location = " << e.GetLocation() << std::endl; std::cout << "Description = " << e.GetDescription() << std::endl; return EXIT_FAILURE; } std::cout << "[Test 1 SUCCESS]" << std::endl; return EXIT_SUCCESS; } int IBPSOTest2(typename OptimizerType::CoefficientType inertiaCoefficient, typename OptimizerType::CoefficientType personalCoefficient, typename OptimizerType::CoefficientType globalCoefficient, typename OptimizerType::CoefficientType initializationCoefficient) { std::cout << "Particle Swarm Optimizer Test 2 [f(x) = 1/2 x^T A x - b^T x]\n"; std::cout << "----------------------------------\n"; itk::Array knownParameters(2); knownParameters[0] = 2.0; knownParameters[1] = -2.0; // the function we want to optimize itk::ParticleSwarmTestF2::Pointer costFunction = itk::ParticleSwarmTestF2::New(); auto itkOptimizer = OptimizerType::New(); ITK_EXERCISE_BASIC_OBJECT_METHODS( itkOptimizer, InitializationBiasedParticleSwarmOptimizer, ParticleSwarmOptimizerBase); itkOptimizer->SetInertiaCoefficient(inertiaCoefficient); ITK_TEST_SET_GET_VALUE(inertiaCoefficient, itkOptimizer->GetInertiaCoefficient()); itkOptimizer->SetPersonalCoefficient(personalCoefficient); ITK_TEST_SET_GET_VALUE(personalCoefficient, itkOptimizer->GetPersonalCoefficient()); itkOptimizer->SetGlobalCoefficient(globalCoefficient); ITK_TEST_SET_GET_VALUE(globalCoefficient, itkOptimizer->GetGlobalCoefficient()); itkOptimizer->SetInitializationCoefficient(globalCoefficient); ITK_TEST_SET_GET_VALUE(initializationCoefficient, itkOptimizer->GetInitializationCoefficient()); itkOptimizer->UseSeedOn(); itkOptimizer->SetSeed(8775070 + seedOffset++); // set optimizer parameters OptimizerType::ParameterBoundsType bounds; bounds.push_back(std::make_pair(-10, 10)); bounds.push_back(std::make_pair(-10, 10)); unsigned int numberOfParticles = 10; unsigned int maxIterations = 200; double xTolerance = 0.1; double fTolerance = 0.001; OptimizerType::ParametersType initialParameters(2), finalParameters; itkOptimizer->SetParameterBounds(bounds); itkOptimizer->SetNumberOfParticles(numberOfParticles); itkOptimizer->SetMaximalNumberOfIterations(maxIterations); itkOptimizer->SetParametersConvergenceTolerance(xTolerance, costFunction->GetNumberOfParameters()); itkOptimizer->SetFunctionConvergenceTolerance(fTolerance); itkOptimizer->SetCostFunction(costFunction); // observe the iterations itk::CommandIterationUpdateParticleSwarm::Pointer observer = itk::CommandIterationUpdateParticleSwarm::New(); if (initalizationBasedTestVerboseFlag) { itkOptimizer->AddObserver(itk::IterationEvent(), observer); } try { initialParameters[0] = 9; initialParameters[1] = -9; itkOptimizer->SetInitialPosition(initialParameters); itkOptimizer->StartOptimization(); finalParameters = itkOptimizer->GetCurrentPosition(); // show why we stopped and see if the optimization succeeded std::cout << "Reason for stopping optimization:\n"; std::cout << '\t' << itkOptimizer->GetStopConditionDescription() << '\n'; finalParameters = itkOptimizer->GetCurrentPosition(); std::cout << "Known parameters = " << knownParameters << " "; std::cout << "Estimated parameters = " << finalParameters << std::endl; if (itk::Math::abs(finalParameters[0] - knownParameters[0]) > xTolerance || itk::Math::abs(finalParameters[1] - knownParameters[1]) > xTolerance) { std::cout << "[Test 2 FAILURE]" << std::endl; return EXIT_FAILURE; } } catch (const itk::ExceptionObject & e) { std::cout << "[Test 2 FAILURE]" << std::endl; std::cout << "Exception thrown ! " << std::endl; std::cout << "An error occurred during Optimization" << std::endl; std::cout << "Location = " << e.GetLocation() << std::endl; std::cout << "Description = " << e.GetDescription() << std::endl; return EXIT_FAILURE; } std::cout << "[Test 2 SUCCESS]" << std::endl; return EXIT_SUCCESS; } int IBPSOTest3(typename OptimizerType::CoefficientType inertiaCoefficient, typename OptimizerType::CoefficientType personalCoefficient, typename OptimizerType::CoefficientType globalCoefficient, typename OptimizerType::CoefficientType initializationCoefficient) { std::cout << "Particle Swarm Optimizer Test 3 [f(x,y) = (1-x)^2 + 100(y-x^2)^2]\n"; std::cout << "----------------------------------\n"; itk::Array knownParameters(2); knownParameters[0] = 1.0; knownParameters[1] = 1.0; // the function we want to optimize itk::ParticleSwarmTestF3::Pointer costFunction = itk::ParticleSwarmTestF3::New(); auto itkOptimizer = OptimizerType::New(); ITK_EXERCISE_BASIC_OBJECT_METHODS( itkOptimizer, InitializationBiasedParticleSwarmOptimizer, ParticleSwarmOptimizerBase); itkOptimizer->SetInertiaCoefficient(inertiaCoefficient); ITK_TEST_SET_GET_VALUE(inertiaCoefficient, itkOptimizer->GetInertiaCoefficient()); itkOptimizer->SetPersonalCoefficient(personalCoefficient); ITK_TEST_SET_GET_VALUE(personalCoefficient, itkOptimizer->GetPersonalCoefficient()); itkOptimizer->SetGlobalCoefficient(globalCoefficient); ITK_TEST_SET_GET_VALUE(globalCoefficient, itkOptimizer->GetGlobalCoefficient()); itkOptimizer->SetInitializationCoefficient(globalCoefficient); ITK_TEST_SET_GET_VALUE(initializationCoefficient, itkOptimizer->GetInitializationCoefficient()); itkOptimizer->UseSeedOn(); itkOptimizer->SetSeed(8775070 + seedOffset++); // set optimizer parameters OptimizerType::ParameterBoundsType bounds; bounds.push_back(std::make_pair(-100, 100)); bounds.push_back(std::make_pair(-100, 100)); unsigned int numberOfParticles = 100; unsigned int maxIterations = 1000; double xTolerance = 0.1; double fTolerance = 0.01; OptimizerType::ParametersType initialParameters(2), finalParameters; itkOptimizer->SetParameterBounds(bounds); itkOptimizer->SetNumberOfParticles(numberOfParticles); itkOptimizer->SetMaximalNumberOfIterations(maxIterations); itkOptimizer->SetParametersConvergenceTolerance(xTolerance, costFunction->GetNumberOfParameters()); itkOptimizer->SetFunctionConvergenceTolerance(fTolerance); itkOptimizer->SetCostFunction(costFunction); // observe the iterations itk::CommandIterationUpdateParticleSwarm::Pointer observer = itk::CommandIterationUpdateParticleSwarm::New(); if (initalizationBasedTestVerboseFlag) { itkOptimizer->AddObserver(itk::IterationEvent(), observer); itkOptimizer->AddObserver(itk::StartEvent(), observer); } try { initialParameters[0] = 3; initialParameters[1] = 3; itkOptimizer->SetInitialPosition(initialParameters); itkOptimizer->StartOptimization(); finalParameters = itkOptimizer->GetCurrentPosition(); // show why we stopped and see if the optimization succeeded std::cout << "Reason for stopping optimization:\n"; std::cout << '\t' << itkOptimizer->GetStopConditionDescription() << '\n'; finalParameters = itkOptimizer->GetCurrentPosition(); std::cout << "Known parameters = " << knownParameters << " "; std::cout << "Estimated parameters = " << finalParameters << std::endl; if (itk::Math::abs(finalParameters[0] - knownParameters[0]) > xTolerance || itk::Math::abs(finalParameters[1] - knownParameters[1]) > xTolerance) { std::cout << "[Test 3 FAILURE]" << std::endl; return EXIT_FAILURE; } // initial position near known minimum (1,1) - for PSO this // makes no difference when the initial swarm is generated using // a uniform distribution initialParameters[0] = 10; initialParameters[1] = 10; itkOptimizer->SetInitialPosition(initialParameters); // reset the iteration count done by the observer if (initalizationBasedTestVerboseFlag) { observer->Reset(); } itkOptimizer->ClearSwarm(); itkOptimizer->StartOptimization(); finalParameters = itkOptimizer->GetCurrentPosition(); // show why we stopped and see if the optimization succeeded std::cout << "Reason for stopping optimization:\n"; std::cout << '\t' << itkOptimizer->GetStopConditionDescription() << '\n'; finalParameters = itkOptimizer->GetCurrentPosition(); std::cout << "Known parameters = " << knownParameters << " "; std::cout << "Estimated parameters = " << finalParameters << std::endl; if (itk::Math::abs(finalParameters[0] - knownParameters[0]) > xTolerance || itk::Math::abs(finalParameters[1] - knownParameters[1]) > xTolerance) { std::cout << "[Test 3 FAILURE]" << std::endl; return EXIT_FAILURE; } // initial position near known minimum (1,1) - potentially reduce // the number of iterations by initializing particles using a normal // distribution centered on initial parameter values initialParameters[0] = 10; initialParameters[1] = 10; itkOptimizer->SetInitialPosition(initialParameters); itkOptimizer->InitializeNormalDistributionOn(); // reset the iteration count done by the observer if (initalizationBasedTestVerboseFlag) { observer->Reset(); } itkOptimizer->ClearSwarm(); itkOptimizer->StartOptimization(); finalParameters = itkOptimizer->GetCurrentPosition(); // show why we stopped and see if the optimization succeeded std::cout << "Reason for stopping optimization:\n"; std::cout << '\t' << itkOptimizer->GetStopConditionDescription() << '\n'; finalParameters = itkOptimizer->GetCurrentPosition(); std::cout << "Known parameters = " << knownParameters << " "; std::cout << "Estimated parameters = " << finalParameters << std::endl; if (itk::Math::abs(finalParameters[0] - knownParameters[0]) > xTolerance || itk::Math::abs(finalParameters[1] - knownParameters[1]) > xTolerance) { std::cout << "[Test 3 FAILURE]" << std::endl; return EXIT_FAILURE; } } catch (const itk::ExceptionObject & e) { std::cout << "[Test 3 FAILURE]" << std::endl; std::cout << "Exception thrown ! " << std::endl; std::cout << "An error occurred during Optimization" << std::endl; std::cout << "Location = " << e.GetLocation() << std::endl; std::cout << "Description = " << e.GetDescription() << std::endl; return EXIT_FAILURE; } std::cout << "[Test 3 SUCCESS]" << std::endl; return EXIT_SUCCESS; }