Development Guide

Note

This Development Guide page is still actively updated.

Before reading this page, it is required to first read User Guide for some basic information. Note that this topic is mainly for advanced developers, the end-users can skip this page freely.

For docstring conventions, PEP 257 is used in this library. Since this library is built on the NumPy ecosystem, we further use the docstring conventions from numpydoc.

A Unified API

For PyPop7, we use the popular Object-Oriented Programming (OOP) to structure all optimizers, which can provide consistency, flexibility, and simplicity. We did not adopt another popular Procedure-Oriented Programming. However, in the future versions, we may provide such an interface only at the end-user level (rather than the developer level).

For all optimizers, the abstract class called Optimizer needs to be inherited implicitly or explicitly, in order to provide a unified API.

  • All members shared by all optimizers (e.g., fitness_function, ndim_problem, etc.) should be defined in this class.

  • All functions public to end-users should be defined in this class except special cases.

  • All settings related to fair benchmarking comparisons (e.g., max_function_evaluations, max_runtime, and fitness_threshold) should be defined in this class.

Initialization of Optimizer Options

For initialization of optimizer options, the following function __init__ of Optimizer should be inherited:

def __init__(self, problem, options):
    # here all members will be inherited by any subclass of `Optimizer`

The exclusive members of the subclass will be defined after inheriting the above function of Optimizer.

Initialization of Population

We separate the initialization of optimizer options with that of population (a set of individuals), in order to obtain flexibility. To achieve this, the following function initialize should be modified:

def initialize(self):  # for population initialization
    raise NotImplementedError  # need to be implemented in any subclass of `Optimizer`

Its another goal is to minimize the number of class members, to make it easy to set for end-users, but at a slight cost of more variables control for developers.

Computation of Each Generation

Update each one generation (iteration) via modifying the following function iterate:

def iterate(self):  # for one generation (iteration)
    raise NotImplementedError  # need to be implemented in any subclass of `Optimizer`

Control of Entire Optimization Process

Control the entire search process via modifying the following function optimize:

def optimize(self, fitness_function=None):  # entire optimization process
    return None  # `None` should be replaced in any subclass of `Optimizer`

Typically, common auxiliary tasks (e.g., printing verbose information, restarting) are conducted inside this function.