From f5aa14bd1dc3dc13fcdd5520838baeb5dd919bb7 Mon Sep 17 00:00:00 2001 From: r3kste <138380708+r3kste@users.noreply.github.com> Date: Mon, 7 Apr 2025 23:29:23 +0530 Subject: [PATCH 1/3] Add wrapper for Nevergrad PSO optimizer --- .tools/envs/testenv-linux.yml | 1 + .tools/envs/testenv-numpy.yml | 1 + .tools/envs/testenv-others.yml | 1 + .tools/envs/testenv-pandas.yml | 1 + docs/source/algorithms.md | 55 +++++++++++ docs/source/refs.bib | 23 +++++ environment.yml | 1 + pyproject.toml | 1 + src/optimagic/algorithms.py | 33 +++++++ src/optimagic/config.py | 8 ++ .../optimizers/nevergrad_optimizers.py | 91 +++++++++++++++++++ 11 files changed, 216 insertions(+) create mode 100644 src/optimagic/optimizers/nevergrad_optimizers.py diff --git a/.tools/envs/testenv-linux.yml b/.tools/envs/testenv-linux.yml index 67fab9017..17f441dee 100644 --- a/.tools/envs/testenv-linux.yml +++ b/.tools/envs/testenv-linux.yml @@ -8,6 +8,7 @@ dependencies: - jax - cyipopt>=1.4.0 # dev, tests - pygmo>=2.19.0 # dev, tests, docs + - nevergrad # dev, tests - nlopt # dev, tests, docs - pip # dev, tests, docs - pytest # dev, tests diff --git a/.tools/envs/testenv-numpy.yml b/.tools/envs/testenv-numpy.yml index 2cd35c4e0..ff0b3fe41 100644 --- a/.tools/envs/testenv-numpy.yml +++ b/.tools/envs/testenv-numpy.yml @@ -8,6 +8,7 @@ dependencies: - numpy<2 - cyipopt>=1.4.0 # dev, tests - pygmo>=2.19.0 # dev, tests, docs + - nevergrad # dev, tests - nlopt # dev, tests, docs - pip # dev, tests, docs - pytest # dev, tests diff --git a/.tools/envs/testenv-others.yml b/.tools/envs/testenv-others.yml index 974cffec1..2aa52b2e9 100644 --- a/.tools/envs/testenv-others.yml +++ b/.tools/envs/testenv-others.yml @@ -6,6 +6,7 @@ channels: dependencies: - cyipopt>=1.4.0 # dev, tests - pygmo>=2.19.0 # dev, tests, docs + - nevergrad # dev, tests - nlopt # dev, tests, docs - pip # dev, tests, docs - pytest # dev, tests diff --git a/.tools/envs/testenv-pandas.yml b/.tools/envs/testenv-pandas.yml index 6d88f1016..19671aa70 100644 --- a/.tools/envs/testenv-pandas.yml +++ b/.tools/envs/testenv-pandas.yml @@ -8,6 +8,7 @@ dependencies: - numpy<2 - cyipopt>=1.4.0 # dev, tests - pygmo>=2.19.0 # dev, tests, docs + - nevergrad # dev, tests - nlopt # dev, tests, docs - pip # dev, tests, docs - pytest # dev, tests diff --git a/docs/source/algorithms.md b/docs/source/algorithms.md index 4ec46b6b5..f1ebc80b6 100644 --- a/docs/source/algorithms.md +++ b/docs/source/algorithms.md @@ -3913,6 +3913,61 @@ addition to optimagic when using an NLOPT algorithm. To install nlopt run 10 * (number of parameters + 1). ``` +(nevergrad-algorithms)= + +## Nevergrad Optimizers + +optimagic supports some algorithms from the +[Nevergrad](https://facebookresearch.github.io/nevergrad/index.html) library. To use +these optimizers, you need to have +[the nevergrad package](https://github.com/facebookresearch/nevergrad) installed. +(`pip install nevergrad`). + +```{eval-rst} +.. dropdown:: nevergrad_pso + + .. code-block:: + + "nevergrad_pso" + + Minimize a scalar function using the Particle Swarm Optimization (PSO) algorithm. + + The Particle Swarm Optimization algorithm was originally proposed by + :cite:`Kennedy1995`. The implementation in Nevergrad is based on + :cite:`Zambrano2013`. + + Particle Swarm Optimization (PSO) solves a problem by having a population of + candidate solutions, here dubbed particles, and moving these particles around in the + search-space according to simple mathematical formulae over the particle's position + and velocity. Each particle's movement is influenced by its local best known + position (termed "cognitive" component), but is also guided toward the best known + positions (termed "social" component) in the search-space, which are updated as + better positions are found by other particles. This is expected to move the swarm + toward the best solutions. + + - **transform** (str): The transform to use to map from PSO optimization space to + R-space. + - **population_size** (int): Population size of the particle swarm. + - **n_cores** (int): Number of cores to use. + - **stopping.maxfun** (int): Maximum number of function evaluations. + - **inertia** (float): Inertia weight. Denoted by :math:`\omega`. + Default is 0.7213475204444817. To prevent divergence, the value must be smaller + than 1. It controls the influence of the particle's previous velocity on its + movement. + - **cognitive** (float): Cognitive coefficient. Denoted by :math:`\phi_p`. + Default is 1.1931471805599454. Typical values range from 1.0 to 3.0. It controls + the influence of its own best known position on the particle's movement. + - **social** (float): Social coefficient. Denoted by :math:`\phi_g`. + Default is 1.1931471805599454. Typical values range from 1.0 to 3.0. It controls + the influence of the swarm's best known position on the particle's movement. + - **quasi_opp_init** (bool): Whether to use quasi-opposition initialization. + Default is False. + - **speed_quasi_opp_init** (bool): Whether to use quasi-opposition initialization + for speed. Default is False. + - **special_speed_quasi_opp_init** (bool): Whether to use special quasi-opposition + initialization for speed. Default is False. +``` + ## References ```{eval-rst} diff --git a/docs/source/refs.bib b/docs/source/refs.bib index bdae3f4f2..a6018f5ef 100644 --- a/docs/source/refs.bib +++ b/docs/source/refs.bib @@ -893,4 +893,27 @@ @book{Conn2009 URL = {https://epubs.siam.org/doi/abs/10.1137/1.9780898718768}, } + +@InProceedings{Kennedy1995, + author={Kennedy, J. and Eberhart, R.}, + booktitle={Proceedings of ICNN'95 - International Conference on Neural Networks}, + title={Particle swarm optimization}, + year={1995}, + volume={4}, + pages={1942-1948 vol.4}, + keywords={Particle swarm optimization;Birds;Educational institutions;Marine animals;Testing;Humans;Genetic algorithms;Optimization methods;Artificial neural networks;Performance evaluation}, + doi={10.1109/ICNN.1995.488968}, +} + + +@InProceedings{Zambrano2013, + author = {Zambrano-Bigiarini, Mauricio and Clerc, Maurice and Rojas, Rodrigo}, + booktitle = {2013 IEEE Congress on Evolutionary Computation}, + title = {Standard Particle Swarm Optimisation 2011 at CEC-2013: A baseline for future PSO improvements}, + year = {2013}, + pages = {2337-2344}, + keywords = {Optimization;Standards;Benchmark testing;Topology;Algorithm design and analysis;Convergence;Equations;particle swarm optimization;SPSO-2011;CEC-2013;random topology;rotational invariance;benchmark testing;evolutionary computation;optimization}, + doi = {10.1109/CEC.2013.6557848}, +} + @Comment{jabref-meta: databaseType:bibtex;} diff --git a/environment.yml b/environment.yml index 83b2a0952..40095bc26 100644 --- a/environment.yml +++ b/environment.yml @@ -7,6 +7,7 @@ dependencies: - python=3.10 # dev - cyipopt>=1.4.0 # dev, tests - pygmo>=2.19.0 # dev, tests, docs + - nevergrad # dev, tests - jupyterlab # dev, docs - nlopt # dev, tests, docs - pip # dev, tests, docs diff --git a/pyproject.toml b/pyproject.toml index 40b93ff8d..5aa2dae78 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -378,5 +378,6 @@ module = [ "optimagic._version", "annotated_types", "pdbp", + "nevergrad", ] ignore_missing_imports = true diff --git a/src/optimagic/algorithms.py b/src/optimagic/algorithms.py index a892f5a51..7e9e49c1c 100644 --- a/src/optimagic/algorithms.py +++ b/src/optimagic/algorithms.py @@ -17,6 +17,7 @@ from optimagic.optimizers.ipopt import Ipopt from optimagic.optimizers.nag_optimizers import NagDFOLS, NagPyBOBYQA from optimagic.optimizers.neldermead import NelderMeadParallel +from optimagic.optimizers.nevergrad_optimizers import NevergradPSO from optimagic.optimizers.nlopt_optimizers import ( NloptBOBYQA, NloptCCSAQ, @@ -170,6 +171,7 @@ def Scalar( @dataclass(frozen=True) class BoundedGlobalGradientFreeParallelScalarAlgorithms(AlgoSelection): + nevergrad_pso: Type[NevergradPSO] = NevergradPSO pygmo_gaco: Type[PygmoGaco] = PygmoGaco pygmo_pso_gen: Type[PygmoPsoGen] = PygmoPsoGen scipy_brute: Type[ScipyBrute] = ScipyBrute @@ -362,6 +364,7 @@ def Scalar(self) -> BoundedGlobalGradientFreeNonlinearConstrainedScalarAlgorithm @dataclass(frozen=True) class BoundedGlobalGradientFreeScalarAlgorithms(AlgoSelection): + nevergrad_pso: Type[NevergradPSO] = NevergradPSO nlopt_crs2_lm: Type[NloptCRS2LM] = NloptCRS2LM nlopt_direct: Type[NloptDirect] = NloptDirect nlopt_esch: Type[NloptESCH] = NloptESCH @@ -401,6 +404,7 @@ def Parallel(self) -> BoundedGlobalGradientFreeParallelScalarAlgorithms: @dataclass(frozen=True) class BoundedGlobalGradientFreeParallelAlgorithms(AlgoSelection): + nevergrad_pso: Type[NevergradPSO] = NevergradPSO pygmo_gaco: Type[PygmoGaco] = PygmoGaco pygmo_pso_gen: Type[PygmoPsoGen] = PygmoPsoGen scipy_brute: Type[ScipyBrute] = ScipyBrute @@ -456,6 +460,7 @@ def Scalar(self) -> GlobalGradientFreeNonlinearConstrainedParallelScalarAlgorith @dataclass(frozen=True) class GlobalGradientFreeParallelScalarAlgorithms(AlgoSelection): + nevergrad_pso: Type[NevergradPSO] = NevergradPSO pygmo_gaco: Type[PygmoGaco] = PygmoGaco pygmo_pso_gen: Type[PygmoPsoGen] = PygmoPsoGen scipy_brute: Type[ScipyBrute] = ScipyBrute @@ -603,6 +608,7 @@ def Scalar(self) -> BoundedGradientFreeNonlinearConstrainedParallelScalarAlgorit @dataclass(frozen=True) class BoundedGradientFreeParallelScalarAlgorithms(AlgoSelection): + nevergrad_pso: Type[NevergradPSO] = NevergradPSO pygmo_gaco: Type[PygmoGaco] = PygmoGaco pygmo_pso_gen: Type[PygmoPsoGen] = PygmoPsoGen scipy_brute: Type[ScipyBrute] = ScipyBrute @@ -697,6 +703,7 @@ def Scalar(self) -> BoundedGlobalNonlinearConstrainedParallelScalarAlgorithms: @dataclass(frozen=True) class BoundedGlobalParallelScalarAlgorithms(AlgoSelection): + nevergrad_pso: Type[NevergradPSO] = NevergradPSO pygmo_gaco: Type[PygmoGaco] = PygmoGaco pygmo_pso_gen: Type[PygmoPsoGen] = PygmoPsoGen scipy_brute: Type[ScipyBrute] = ScipyBrute @@ -1020,6 +1027,7 @@ def Local(self) -> GradientBasedLocalNonlinearConstrainedScalarAlgorithms: @dataclass(frozen=True) class BoundedGlobalGradientFreeAlgorithms(AlgoSelection): + nevergrad_pso: Type[NevergradPSO] = NevergradPSO nlopt_crs2_lm: Type[NloptCRS2LM] = NloptCRS2LM nlopt_direct: Type[NloptDirect] = NloptDirect nlopt_esch: Type[NloptESCH] = NloptESCH @@ -1083,6 +1091,7 @@ def Scalar(self) -> GlobalGradientFreeNonlinearConstrainedScalarAlgorithms: @dataclass(frozen=True) class GlobalGradientFreeScalarAlgorithms(AlgoSelection): + nevergrad_pso: Type[NevergradPSO] = NevergradPSO nlopt_crs2_lm: Type[NloptCRS2LM] = NloptCRS2LM nlopt_direct: Type[NloptDirect] = NloptDirect nlopt_esch: Type[NloptESCH] = NloptESCH @@ -1126,6 +1135,7 @@ def Parallel(self) -> GlobalGradientFreeParallelScalarAlgorithms: @dataclass(frozen=True) class GlobalGradientFreeParallelAlgorithms(AlgoSelection): + nevergrad_pso: Type[NevergradPSO] = NevergradPSO pygmo_gaco: Type[PygmoGaco] = PygmoGaco pygmo_pso_gen: Type[PygmoPsoGen] = PygmoPsoGen scipy_brute: Type[ScipyBrute] = ScipyBrute @@ -1291,6 +1301,7 @@ def Scalar(self) -> BoundedGradientFreeNonlinearConstrainedScalarAlgorithms: @dataclass(frozen=True) class BoundedGradientFreeScalarAlgorithms(AlgoSelection): nag_pybobyqa: Type[NagPyBOBYQA] = NagPyBOBYQA + nevergrad_pso: Type[NevergradPSO] = NevergradPSO nlopt_bobyqa: Type[NloptBOBYQA] = NloptBOBYQA nlopt_cobyla: Type[NloptCOBYLA] = NloptCOBYLA nlopt_crs2_lm: Type[NloptCRS2LM] = NloptCRS2LM @@ -1362,6 +1373,7 @@ def Parallel(self) -> BoundedGradientFreeLeastSquaresParallelAlgorithms: @dataclass(frozen=True) class BoundedGradientFreeParallelAlgorithms(AlgoSelection): + nevergrad_pso: Type[NevergradPSO] = NevergradPSO pounders: Type[Pounders] = Pounders pygmo_gaco: Type[PygmoGaco] = PygmoGaco pygmo_pso_gen: Type[PygmoPsoGen] = PygmoPsoGen @@ -1443,6 +1455,7 @@ def Scalar(self) -> GradientFreeNonlinearConstrainedParallelScalarAlgorithms: @dataclass(frozen=True) class GradientFreeParallelScalarAlgorithms(AlgoSelection): neldermead_parallel: Type[NelderMeadParallel] = NelderMeadParallel + nevergrad_pso: Type[NevergradPSO] = NevergradPSO pygmo_gaco: Type[PygmoGaco] = PygmoGaco pygmo_pso_gen: Type[PygmoPsoGen] = PygmoPsoGen scipy_brute: Type[ScipyBrute] = ScipyBrute @@ -1511,6 +1524,7 @@ def Scalar(self) -> BoundedGlobalNonlinearConstrainedScalarAlgorithms: @dataclass(frozen=True) class BoundedGlobalScalarAlgorithms(AlgoSelection): + nevergrad_pso: Type[NevergradPSO] = NevergradPSO nlopt_crs2_lm: Type[NloptCRS2LM] = NloptCRS2LM nlopt_direct: Type[NloptDirect] = NloptDirect nlopt_esch: Type[NloptESCH] = NloptESCH @@ -1559,6 +1573,7 @@ def Parallel(self) -> BoundedGlobalParallelScalarAlgorithms: @dataclass(frozen=True) class BoundedGlobalParallelAlgorithms(AlgoSelection): + nevergrad_pso: Type[NevergradPSO] = NevergradPSO pygmo_gaco: Type[PygmoGaco] = PygmoGaco pygmo_pso_gen: Type[PygmoPsoGen] = PygmoPsoGen scipy_brute: Type[ScipyBrute] = ScipyBrute @@ -1627,6 +1642,7 @@ def Scalar(self) -> GlobalNonlinearConstrainedParallelScalarAlgorithms: @dataclass(frozen=True) class GlobalParallelScalarAlgorithms(AlgoSelection): + nevergrad_pso: Type[NevergradPSO] = NevergradPSO pygmo_gaco: Type[PygmoGaco] = PygmoGaco pygmo_pso_gen: Type[PygmoPsoGen] = PygmoPsoGen scipy_brute: Type[ScipyBrute] = ScipyBrute @@ -1860,6 +1876,7 @@ def Scalar(self) -> BoundedNonlinearConstrainedParallelScalarAlgorithms: @dataclass(frozen=True) class BoundedParallelScalarAlgorithms(AlgoSelection): + nevergrad_pso: Type[NevergradPSO] = NevergradPSO pygmo_gaco: Type[PygmoGaco] = PygmoGaco pygmo_pso_gen: Type[PygmoPsoGen] = PygmoPsoGen scipy_brute: Type[ScipyBrute] = ScipyBrute @@ -2115,6 +2132,7 @@ def Local(self) -> GradientBasedLikelihoodLocalAlgorithms: @dataclass(frozen=True) class GlobalGradientFreeAlgorithms(AlgoSelection): + nevergrad_pso: Type[NevergradPSO] = NevergradPSO nlopt_crs2_lm: Type[NloptCRS2LM] = NloptCRS2LM nlopt_direct: Type[NloptDirect] = NloptDirect nlopt_esch: Type[NloptESCH] = NloptESCH @@ -2202,6 +2220,7 @@ def Scalar(self) -> GradientFreeLocalScalarAlgorithms: class BoundedGradientFreeAlgorithms(AlgoSelection): nag_dfols: Type[NagDFOLS] = NagDFOLS nag_pybobyqa: Type[NagPyBOBYQA] = NagPyBOBYQA + nevergrad_pso: Type[NevergradPSO] = NevergradPSO nlopt_bobyqa: Type[NloptBOBYQA] = NloptBOBYQA nlopt_cobyla: Type[NloptCOBYLA] = NloptCOBYLA nlopt_crs2_lm: Type[NloptCRS2LM] = NloptCRS2LM @@ -2298,6 +2317,7 @@ def Scalar(self) -> GradientFreeNonlinearConstrainedScalarAlgorithms: class GradientFreeScalarAlgorithms(AlgoSelection): nag_pybobyqa: Type[NagPyBOBYQA] = NagPyBOBYQA neldermead_parallel: Type[NelderMeadParallel] = NelderMeadParallel + nevergrad_pso: Type[NevergradPSO] = NevergradPSO nlopt_bobyqa: Type[NloptBOBYQA] = NloptBOBYQA nlopt_cobyla: Type[NloptCOBYLA] = NloptCOBYLA nlopt_crs2_lm: Type[NloptCRS2LM] = NloptCRS2LM @@ -2378,6 +2398,7 @@ def Parallel(self) -> GradientFreeLeastSquaresParallelAlgorithms: @dataclass(frozen=True) class GradientFreeParallelAlgorithms(AlgoSelection): neldermead_parallel: Type[NelderMeadParallel] = NelderMeadParallel + nevergrad_pso: Type[NevergradPSO] = NevergradPSO pounders: Type[Pounders] = Pounders pygmo_gaco: Type[PygmoGaco] = PygmoGaco pygmo_pso_gen: Type[PygmoPsoGen] = PygmoPsoGen @@ -2417,6 +2438,7 @@ def Scalar(self) -> GradientFreeParallelScalarAlgorithms: @dataclass(frozen=True) class BoundedGlobalAlgorithms(AlgoSelection): + nevergrad_pso: Type[NevergradPSO] = NevergradPSO nlopt_crs2_lm: Type[NloptCRS2LM] = NloptCRS2LM nlopt_direct: Type[NloptDirect] = NloptDirect nlopt_esch: Type[NloptESCH] = NloptESCH @@ -2498,6 +2520,7 @@ def Scalar(self) -> GlobalNonlinearConstrainedScalarAlgorithms: @dataclass(frozen=True) class GlobalScalarAlgorithms(AlgoSelection): + nevergrad_pso: Type[NevergradPSO] = NevergradPSO nlopt_crs2_lm: Type[NloptCRS2LM] = NloptCRS2LM nlopt_direct: Type[NloptDirect] = NloptDirect nlopt_esch: Type[NloptESCH] = NloptESCH @@ -2550,6 +2573,7 @@ def Parallel(self) -> GlobalParallelScalarAlgorithms: @dataclass(frozen=True) class GlobalParallelAlgorithms(AlgoSelection): + nevergrad_pso: Type[NevergradPSO] = NevergradPSO pygmo_gaco: Type[PygmoGaco] = PygmoGaco pygmo_pso_gen: Type[PygmoPsoGen] = PygmoPsoGen scipy_brute: Type[ScipyBrute] = ScipyBrute @@ -2811,6 +2835,7 @@ class BoundedScalarAlgorithms(AlgoSelection): fides: Type[Fides] = Fides ipopt: Type[Ipopt] = Ipopt nag_pybobyqa: Type[NagPyBOBYQA] = NagPyBOBYQA + nevergrad_pso: Type[NevergradPSO] = NevergradPSO nlopt_bobyqa: Type[NloptBOBYQA] = NloptBOBYQA nlopt_ccsaq: Type[NloptCCSAQ] = NloptCCSAQ nlopt_cobyla: Type[NloptCOBYLA] = NloptCOBYLA @@ -2911,6 +2936,7 @@ def Parallel(self) -> BoundedLeastSquaresParallelAlgorithms: @dataclass(frozen=True) class BoundedParallelAlgorithms(AlgoSelection): + nevergrad_pso: Type[NevergradPSO] = NevergradPSO pounders: Type[Pounders] = Pounders pygmo_gaco: Type[PygmoGaco] = PygmoGaco pygmo_pso_gen: Type[PygmoPsoGen] = PygmoPsoGen @@ -3012,6 +3038,7 @@ def Scalar(self) -> NonlinearConstrainedParallelScalarAlgorithms: @dataclass(frozen=True) class ParallelScalarAlgorithms(AlgoSelection): neldermead_parallel: Type[NelderMeadParallel] = NelderMeadParallel + nevergrad_pso: Type[NevergradPSO] = NevergradPSO pygmo_gaco: Type[PygmoGaco] = PygmoGaco pygmo_pso_gen: Type[PygmoPsoGen] = PygmoPsoGen scipy_brute: Type[ScipyBrute] = ScipyBrute @@ -3118,6 +3145,7 @@ class GradientFreeAlgorithms(AlgoSelection): nag_dfols: Type[NagDFOLS] = NagDFOLS nag_pybobyqa: Type[NagPyBOBYQA] = NagPyBOBYQA neldermead_parallel: Type[NelderMeadParallel] = NelderMeadParallel + nevergrad_pso: Type[NevergradPSO] = NevergradPSO nlopt_bobyqa: Type[NloptBOBYQA] = NloptBOBYQA nlopt_cobyla: Type[NloptCOBYLA] = NloptCOBYLA nlopt_crs2_lm: Type[NloptCRS2LM] = NloptCRS2LM @@ -3188,6 +3216,7 @@ def Scalar(self) -> GradientFreeScalarAlgorithms: @dataclass(frozen=True) class GlobalAlgorithms(AlgoSelection): + nevergrad_pso: Type[NevergradPSO] = NevergradPSO nlopt_crs2_lm: Type[NloptCRS2LM] = NloptCRS2LM nlopt_direct: Type[NloptDirect] = NloptDirect nlopt_esch: Type[NloptESCH] = NloptESCH @@ -3319,6 +3348,7 @@ class BoundedAlgorithms(AlgoSelection): ipopt: Type[Ipopt] = Ipopt nag_dfols: Type[NagDFOLS] = NagDFOLS nag_pybobyqa: Type[NagPyBOBYQA] = NagPyBOBYQA + nevergrad_pso: Type[NevergradPSO] = NevergradPSO nlopt_bobyqa: Type[NloptBOBYQA] = NloptBOBYQA nlopt_ccsaq: Type[NloptCCSAQ] = NloptCCSAQ nlopt_cobyla: Type[NloptCOBYLA] = NloptCOBYLA @@ -3454,6 +3484,7 @@ class ScalarAlgorithms(AlgoSelection): ipopt: Type[Ipopt] = Ipopt nag_pybobyqa: Type[NagPyBOBYQA] = NagPyBOBYQA neldermead_parallel: Type[NelderMeadParallel] = NelderMeadParallel + nevergrad_pso: Type[NevergradPSO] = NevergradPSO nlopt_bobyqa: Type[NloptBOBYQA] = NloptBOBYQA nlopt_ccsaq: Type[NloptCCSAQ] = NloptCCSAQ nlopt_cobyla: Type[NloptCOBYLA] = NloptCOBYLA @@ -3582,6 +3613,7 @@ def Local(self) -> LikelihoodLocalAlgorithms: @dataclass(frozen=True) class ParallelAlgorithms(AlgoSelection): neldermead_parallel: Type[NelderMeadParallel] = NelderMeadParallel + nevergrad_pso: Type[NevergradPSO] = NevergradPSO pounders: Type[Pounders] = Pounders pygmo_gaco: Type[PygmoGaco] = PygmoGaco pygmo_pso_gen: Type[PygmoPsoGen] = PygmoPsoGen @@ -3629,6 +3661,7 @@ class Algorithms(AlgoSelection): nag_dfols: Type[NagDFOLS] = NagDFOLS nag_pybobyqa: Type[NagPyBOBYQA] = NagPyBOBYQA neldermead_parallel: Type[NelderMeadParallel] = NelderMeadParallel + nevergrad_pso: Type[NevergradPSO] = NevergradPSO nlopt_bobyqa: Type[NloptBOBYQA] = NloptBOBYQA nlopt_ccsaq: Type[NloptCCSAQ] = NloptCCSAQ nlopt_cobyla: Type[NloptCOBYLA] = NloptCOBYLA diff --git a/src/optimagic/config.py b/src/optimagic/config.py index d63ef54ac..44336fe82 100644 --- a/src/optimagic/config.py +++ b/src/optimagic/config.py @@ -92,6 +92,14 @@ IS_NUMBA_INSTALLED = True +try: + import nevergrad # noqa: F401 +except ImportError: + IS_NEVERGRAD_INSTALLED = False +else: + IS_NEVERGRAD_INSTALLED = True + + # ====================================================================================== # Check if pandas version is newer or equal to version 2.1.0 # ====================================================================================== diff --git a/src/optimagic/optimizers/nevergrad_optimizers.py b/src/optimagic/optimizers/nevergrad_optimizers.py new file mode 100644 index 000000000..6aaaf747e --- /dev/null +++ b/src/optimagic/optimizers/nevergrad_optimizers.py @@ -0,0 +1,91 @@ +"""Implement nevergrad optimizers.""" + +import math +from dataclasses import dataclass +from typing import Literal + +import numpy as np +from numpy.typing import NDArray + +from optimagic import mark +from optimagic.config import IS_NEVERGRAD_INSTALLED +from optimagic.exceptions import NotInstalledError +from optimagic.optimization.algo_options import STOPPING_MAXFUN_GLOBAL +from optimagic.optimization.algorithm import Algorithm, InternalOptimizeResult +from optimagic.optimization.internal_optimization_problem import ( + InternalOptimizationProblem, +) +from optimagic.typing import AggregationLevel, PositiveInt + +if IS_NEVERGRAD_INSTALLED: + import nevergrad as ng + + +@mark.minimizer( + name="nevergrad_pso", + solver_type=AggregationLevel.SCALAR, + is_available=IS_NEVERGRAD_INSTALLED, + is_global=True, + needs_jac=False, + needs_hess=False, + supports_parallelism=True, + supports_bounds=True, + supports_linear_constraints=False, + supports_nonlinear_constraints=False, + disable_history=True, +) +@dataclass(frozen=True) +class NevergradPSO(Algorithm): + transform: Literal["arctan", "gaussian", "identity"] = "arctan" + population_size: int | None = None + n_cores: int = 1 + stopping_maxfun: PositiveInt = STOPPING_MAXFUN_GLOBAL + inertia: float = 0.5 / math.log(2.0) + cognitive: float = 0.5 + math.log(2.0) + social: float = 0.5 + math.log(2.0) + quasi_opp_init: bool = False + speed_quasi_opp_init: bool = False + special_speed_quasi_opp_init: bool = False + + def _solve_internal_problem( + self, problem: InternalOptimizationProblem, x0: NDArray[np.float64] + ) -> InternalOptimizeResult: + if not IS_NEVERGRAD_INSTALLED: + raise NotInstalledError( + "The nevergrad_pso optimizer requires the 'nevergrad' package to be " + "installed. You can install it with `pip install nevergrad`. " + "Visit https://facebookresearch.github.io/nevergrad/getting_started.html" + " for more detailed installation instructions." + ) + + instrum = ng.p.Instrumentation( + ng.p.Array( + shape=x0.shape, lower=problem.bounds.lower, upper=problem.bounds.upper + ) + ) + + optimizer = ng.optimizers.ConfPSO( + transform=self.transform, + popsize=self.population_size, + omega=self.inertia, + phip=self.cognitive, + phig=self.social, + qo=self.quasi_opp_init, + sqo=self.speed_quasi_opp_init, + so=self.special_speed_quasi_opp_init, + )( + parametrization=instrum, + budget=self.stopping_maxfun, + num_workers=self.n_cores, + ) + + recommendation = optimizer.minimize(problem.fun) + + result = InternalOptimizeResult( + x=recommendation.value[0][0], + fun=recommendation.loss, + success=True, + n_fun_evals=optimizer.num_ask, + ) + + return result From 54d3c91c095d167fabd0d2c6b237abf05579872e Mon Sep 17 00:00:00 2001 From: r3kste <138380708+r3kste@users.noreply.github.com> Date: Sun, 13 Apr 2025 20:32:54 +0530 Subject: [PATCH 2/3] Enable history tracking and Use ask - tell interface --- .../optimizers/nevergrad_optimizers.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/optimagic/optimizers/nevergrad_optimizers.py b/src/optimagic/optimizers/nevergrad_optimizers.py index 6aaaf747e..71261afa3 100644 --- a/src/optimagic/optimizers/nevergrad_optimizers.py +++ b/src/optimagic/optimizers/nevergrad_optimizers.py @@ -32,7 +32,7 @@ supports_bounds=True, supports_linear_constraints=False, supports_nonlinear_constraints=False, - disable_history=True, + disable_history=False, ) @dataclass(frozen=True) class NevergradPSO(Algorithm): @@ -79,7 +79,20 @@ def _solve_internal_problem( num_workers=self.n_cores, ) - recommendation = optimizer.minimize(problem.fun) + while optimizer.num_ask < optimizer.budget: + x_list = [ + optimizer.ask() + for _ in range( + min(optimizer.num_workers, optimizer.budget - optimizer.num_ask) + ) + ] + losses = problem.batch_fun( + [x.value[0][0] for x in x_list], n_cores=self.n_cores + ) + for x, loss in zip(x_list, losses, strict=True): + optimizer.tell(x, loss) + + recommendation = optimizer.provide_recommendation() result = InternalOptimizeResult( x=recommendation.value[0][0], From 1b272c4aa75116ba2722453342c869d72911b2c1 Mon Sep 17 00:00:00 2001 From: r3kste <138380708+r3kste@users.noreply.github.com> Date: Tue, 22 Apr 2025 15:37:39 +0530 Subject: [PATCH 3/3] Improve readability and fix issues with build environment --- .tools/envs/testenv-linux.yml | 2 +- .tools/envs/testenv-numpy.yml | 2 +- .tools/envs/testenv-others.yml | 2 +- .tools/envs/testenv-pandas.yml | 2 +- environment.yml | 2 +- src/optimagic/optimizers/nevergrad_optimizers.py | 6 ++++-- 6 files changed, 9 insertions(+), 7 deletions(-) diff --git a/.tools/envs/testenv-linux.yml b/.tools/envs/testenv-linux.yml index 17f441dee..5d2a0ad55 100644 --- a/.tools/envs/testenv-linux.yml +++ b/.tools/envs/testenv-linux.yml @@ -8,7 +8,6 @@ dependencies: - jax - cyipopt>=1.4.0 # dev, tests - pygmo>=2.19.0 # dev, tests, docs - - nevergrad # dev, tests - nlopt # dev, tests, docs - pip # dev, tests, docs - pytest # dev, tests @@ -29,6 +28,7 @@ dependencies: - jinja2 # dev, tests - annotated-types # dev, tests - pip: # dev, tests, docs + - nevergrad # dev, tests - DFO-LS>=1.5.3 # dev, tests - Py-BOBYQA # dev, tests - fides==0.7.4 # dev, tests diff --git a/.tools/envs/testenv-numpy.yml b/.tools/envs/testenv-numpy.yml index ff0b3fe41..674b10e8c 100644 --- a/.tools/envs/testenv-numpy.yml +++ b/.tools/envs/testenv-numpy.yml @@ -8,7 +8,6 @@ dependencies: - numpy<2 - cyipopt>=1.4.0 # dev, tests - pygmo>=2.19.0 # dev, tests, docs - - nevergrad # dev, tests - nlopt # dev, tests, docs - pip # dev, tests, docs - pytest # dev, tests @@ -27,6 +26,7 @@ dependencies: - jinja2 # dev, tests - annotated-types # dev, tests - pip: # dev, tests, docs + - nevergrad # dev, tests - DFO-LS>=1.5.3 # dev, tests - Py-BOBYQA # dev, tests - fides==0.7.4 # dev, tests diff --git a/.tools/envs/testenv-others.yml b/.tools/envs/testenv-others.yml index 2aa52b2e9..1c11e3774 100644 --- a/.tools/envs/testenv-others.yml +++ b/.tools/envs/testenv-others.yml @@ -6,7 +6,6 @@ channels: dependencies: - cyipopt>=1.4.0 # dev, tests - pygmo>=2.19.0 # dev, tests, docs - - nevergrad # dev, tests - nlopt # dev, tests, docs - pip # dev, tests, docs - pytest # dev, tests @@ -27,6 +26,7 @@ dependencies: - jinja2 # dev, tests - annotated-types # dev, tests - pip: # dev, tests, docs + - nevergrad # dev, tests - DFO-LS>=1.5.3 # dev, tests - Py-BOBYQA # dev, tests - fides==0.7.4 # dev, tests diff --git a/.tools/envs/testenv-pandas.yml b/.tools/envs/testenv-pandas.yml index 19671aa70..752238d9c 100644 --- a/.tools/envs/testenv-pandas.yml +++ b/.tools/envs/testenv-pandas.yml @@ -8,7 +8,6 @@ dependencies: - numpy<2 - cyipopt>=1.4.0 # dev, tests - pygmo>=2.19.0 # dev, tests, docs - - nevergrad # dev, tests - nlopt # dev, tests, docs - pip # dev, tests, docs - pytest # dev, tests @@ -27,6 +26,7 @@ dependencies: - jinja2 # dev, tests - annotated-types # dev, tests - pip: # dev, tests, docs + - nevergrad # dev, tests - DFO-LS>=1.5.3 # dev, tests - Py-BOBYQA # dev, tests - fides==0.7.4 # dev, tests diff --git a/environment.yml b/environment.yml index 8edc18dbe..b617f4bed 100644 --- a/environment.yml +++ b/environment.yml @@ -7,7 +7,6 @@ dependencies: - python=3.10 # dev - cyipopt>=1.4.0 # dev, tests - pygmo>=2.19.0 # dev, tests, docs - - nevergrad # dev, tests - jupyterlab # dev, docs - nlopt # dev, tests, docs - pip # dev, tests, docs @@ -39,6 +38,7 @@ dependencies: - furo # dev, docs - annotated-types # dev, tests - pip: # dev, tests, docs + - nevergrad # dev, tests - DFO-LS>=1.5.3 # dev, tests - Py-BOBYQA # dev, tests - fides==0.7.4 # dev, tests diff --git a/src/optimagic/optimizers/nevergrad_optimizers.py b/src/optimagic/optimizers/nevergrad_optimizers.py index 71261afa3..e24e4fc65 100644 --- a/src/optimagic/optimizers/nevergrad_optimizers.py +++ b/src/optimagic/optimizers/nevergrad_optimizers.py @@ -79,11 +79,11 @@ def _solve_internal_problem( num_workers=self.n_cores, ) - while optimizer.num_ask < optimizer.budget: + while optimizer.num_ask < self.stopping_maxfun: x_list = [ optimizer.ask() for _ in range( - min(optimizer.num_workers, optimizer.budget - optimizer.num_ask) + min(self.n_cores, self.stopping_maxfun - optimizer.num_ask) ) ] losses = problem.batch_fun( @@ -99,6 +99,8 @@ def _solve_internal_problem( fun=recommendation.loss, success=True, n_fun_evals=optimizer.num_ask, + n_jac_evals=0, + n_hess_evals=0, ) return result