PyPop7

repository·main·Indexed 43 days ago

https://github.com/evolutionary-intelligence/pypop

A pure-Python library for population-based randomized optimization algorithms, specifically targeting single-objective, real-parameter, unconstrained black-box optimization (BBO) problems. PyPop7 focuses on research repeatability and large-scale BBO to address the curse of dimensionality, providing support for various optimizers including Evolution Strategies, Natural Evolution Strategies, Estimation of Distribution Algorithms, and Differential Evolution.

Tokens
45.7K
Snippets
147
Records
194
Agent score
53%

What's inside PyPop

  1. Overview of PyPop7 Optimization Algorithm Families

    main

    PyPop7 provides a wide range of Black-Box Optimization (BBO) algorithms for continuous optimization problems, categorized into several major families:

    • Evolutionary Programming (EP): Includes Lévy distribution based EP (LEP), Fast EP (FEP), and Classical EP (CEP).
    • Direct Search (DS): Includes Powell's search method (POWELL), Generalized Pattern Search (GPS), Nelder-Mead simplex method (NM), Hooke-Jeeves direct search method (HJ), and Coordinate/Compass Search (CS).
    • Random (stochastic) Search (RS): Includes Bernoulli Smoothing (BES), Gaussian Smoothing (GS), Simple Random Search (SRS), Annealed Random Hill Climber (ARHC), Random Hill Climber (RHC), and Pure Random Search (PRS).
    • Evolution Strategies (ES): A large collection of variants including LMCMA, LMCMAES, R1ES, OPOA2015, OPOA2010, CCMAES2009, OPOC2009, OPOC2006, MAES, DDCMA, SAMAES, SAES, CSAES, DSAES, SSAES, and RES.
    • Natural ES (NES): Includes Separable NES (SNES), Original NES (ONES), and Search Gradient ES (SGES).
    • Estimation of Distribution Algorithms (EDA): Includes Adaptive Estimation of Multivariate Normal Algorithm (AEMNA) and EMNA.
    • Cross-Entropy Method (CEM): Includes Model Reference Adaptive Search (MRAS).
  2. Use Estimation of Distribution Algorithms (EDA) in PyPop7

    main

    Estimation of Distribution Algorithms (EDA) are a class of population-based optimization algorithms that build a probabilistic model of the best solutions found so far to guide the search. In PyPop7, these are implemented under the pypop7.optimizers.eda.eda module.

    Available EDA implementations in PyPop7 include:

    • RPEDA
    • AEMNA
    • EMNA
    • UMDA

    To use an EDA, you typically instantiate the EDA class (or its specific subclasses) with your problem settings and algorithm options.

  3. Explore Python Black-Box Optimization (BBO) Libraries

    main

    The following Python libraries are recommended for various black-box optimization tasks:

    Active & Well-Maintained

    • pygmo: A well-designed library for parallel optimization. Supports algorithms like de, sade, de1220, pso, pso_gen, sea, sga, simulated_annealing, cmaes, and xnes.
    • pycma: Implementation of CMA-ES.
    • optuna: Used for hyperparameter optimization and supports CMA-ES.
    • evosax: A JAX-based library for Evolutionary Algorithms (EAs) and NeuroEvolution (NE) that runs on GPU.
    • EvoJAX: A scalable, hardware-accelerated neuroevolution toolkit based on JAX.
    • pymoo: Focuses on multi-objective black-box optimization (supports cmaes, de, es, ga, g3pcx, isres, nelder, pattern, pso, sres).
    • BayesianOptimization: For Bayesian Optimization (BO).
    • hyperopt: For Random Search (RS).
    • vizier: For Hyperparameter Optimization and AutoML.
    • deephyper: For Bayesian Optimization (BO).
    • evotorch: Neuroevolution toolkit.

    Specialized Tools

    • FairEMOL: For mitigating unfairness via Evolutionary Multiobjective Optimisation Learning.
    • IOHanalyzer: A performance analyzer for Iterative Optimization Heuristics (IOHs).
  4. How to implement a custom Optimizer

    main

    To create a new black-box optimizer in PyPop7, you must subclass the Optimizer base class. A complete implementation requires overriding specific lifecycle methods that the Optimizer.optimize() method calls.

    Required Lifecycle Methods

    • initialize(self): Handles the population initialization stage. It should return the initial point(s) or population.
    • iterate(self): Handles the iteration stage (generating new candidates/individuals).
    • _collect(self, fitness, y=None): Collects necessary output information and metadata (like the number of generations) to be returned by the optimize() method.

    Implementation Pattern

    The Optimizer.optimize() method follows a standard lifecycle:

    1. initialize() is called to set up the starting state.
    2. The initial fitness is evaluated.
    3. A loop runs calling iterate() and evaluating fitness until _check_terminations() returns true.
    4. _collect() is called to package the results.
    from pypop7.optimizers.core.optimizer import Optimizer
    
    class MyCustomOptimizer(Optimizer):
        def initialize(self):
            # Return initial population/point
            return ...
    
        def iterate(self):
            # Return next candidate/population
            return ...
    
        def _collect(self, fitness, y=None):
            # Package results
            results = super()._collect(fitness, y)
            results['my_custom_metric'] = ...
            return results
  5. Choosing General-Purpose Optimization Algorithms

    main

    Because of the No Free Lunch (NFL) Theorems, no single optimization algorithm performs better than all others across every possible problem class. An algorithm that excels in one domain may perform poorly in another.

    PyPop focuses on providing general-purpose optimization algorithms. When selecting an algorithm for a real-world problem where the underlying structure is unknown, you should look for optimizers that satisfy these criteria:

    • Effectiveness and Efficiency: Ability to find good solutions quickly.
    • Flexibility (Versatility): Ability to handle different problem types.
    • Robustness (Reliability): Consistent performance across varying conditions.
    • Scalability: Ability to handle increasing dimensions (mitigating the 'Curse of Dimensionality').
    • Simplicity: Ease of use and implementation.
  6. Use Cooperative Coevolution (CC) for optimization

    main

    Cooperative Coevolution (CC) is a framework in PyPop7 used to decompose a large-scale optimization problem into several smaller sub-problems. Each sub-problem is optimized by a separate individual (or group of individuals) in a population, and the fitness of an individual is evaluated based on how well its components perform when combined with the current components of other individuals.

    In PyPop7, the pypop7.optimizers.cc.cc.CC class serves as the base implementation for CC-based optimization. This framework is particularly useful for high-dimensional black-box optimization where standard population-based methods struggle with the curse of dimensionality.

    from pypop7.optimizers.cc.cc import CC
    
    # CC is typically used to wrap or manage sub-optimizers for decomposed problems
    # Specific implementations include HCC, CoCMA, CoSyNE, and CoEA.
    optimizer = CC(...)
  7. Algorithm classification by dimensionality

    main

    PyPop7 categorizes its Black-Box Optimization (BBO) algorithms based on the dimensionality of the objective function. This helps in selecting an appropriate algorithm for your problem:

    • Large-Scale Optimization (LSO): Indicated by an orange badge (lso). These are designed for high-dimensional problems (e.g., dimension $\gg$ 100).
    • Competitive/De Facto: Indicated by a blue badge (c). These are standard versions for low- or medium-dimensional problems.
    • Baseline: Indicated by a light grey badge (b). These are simpler algorithms primarily used for theoretical or educational purposes.
  8. When to use (and when not to use) Black-Box Optimization

    main

    Black-Box Optimization (BBO) is a derivative-free approach. While powerful, it has specific use cases and limitations:

    When to use BBO

    • When you cannot obtain clean derivatives (gradients) for your problem.
    • When the functions defining your problem are noisy or non-smooth.
    • When you need to explore a diverse solution space.

    When to avoid BBO

    • If derivatives are available: If you can obtain clean derivatives and the functions are smooth and noise-free, you should use gradient-based (first-order) methods instead.
    • High-dimensional problems on serial machines: Traditionally, it is often not reasonable to optimize problems with more than a few tens of variables on a serial machine using derivative-free methods, though some modern techniques can handle hundreds of variables in unconstrained problems.
  9. Understanding Black-Box Optimization (BBO) in PyPop

    main

    Black-Box Optimization (BBO) refers to optimization problems where the only accessible information is obtained through sampling evaluations. In these scenarios, the algorithm does not have access to the internal mathematical model or gradient information of the objective function.

    Common characteristics of black-box problems that PyPop is designed to handle include:

    • Unavailability of gradients: Due to complex modeling, legacy code, or noisy evaluations.
    • Lack of precise mathematical models: Often caused by complex simulations.
    • Non-differentiability: Where automatic differentiation is impossible or non-informative.
    • Non-linearity and Non-convexity: Including multi-modal landscapes.
    • Ill-conditioning and Discontinuity: Difficult mathematical landscapes.
    • Noisiness/Stochasticity: Function evaluations that are not deterministic.
    • Needle-in-a-haystack: Extremely sparse optimal regions.

    PyPop provides various algorithms (also known as Zeroth-Order, Derivative-Free, or Gradient-Free Optimization) to navigate these landscapes using only function samples.

  10. Computational dependencies and performance

    main

    PyPop7 is optimized for large-scale optimization (LSO) and high-performance computation by leveraging several key numerical libraries:

    • NumPy: Used as the primary base for all numerical computations.
    • SciPy and scikit-learn: Used for scientific and machine learning operations.
    • Numba: Utilized in certain modules to accelerate wall-clock execution time via JIT compilation.
  11. Handle fitness functions with extra input arguments

    main

    If your fitness function requires arguments other than the sampling point x, use one of these two methods:

    1. Class Wrapper

    Create a class with an __init__ method to store the extra arguments and a __call__ method to perform the evaluation.

    2. Unified API Interface

    Use the unified API provided by PyPop7 optimizers. When calling optimizer.optimize(args=...), the args passed to optimize will be forwarded to your fitness_function as the second argument.

    # Method 1: Class Wrapper
    class Rosenbrock(object):
        def __init__(self, arg):
            self.arg = arg
        def __call__(self, x):
            return rosenbrock(x, self.arg)
    
    problem = {'fitness_function': Rosenbrock(100.0), ...}
    
    # Method 2: Unified API
    def rosenbrock(x, args):
        return args*np.sum(np.square(x[1:] - np.square(x[:-1]))) + np.sum(np.square(x[:-1] - 1.0))
    
    problem = {'fitness_function': rosenbrock, ...}
    from pypop7.optimizers.es.maes import MAES
    maes = MAES(problem, options)
    results = maes.optimize(args=100.0)  # args are passed to rosenbrock