Skip to main content

Metaheuristic global optimization algorithm

Project description

rt_opt: Run-and-tumble global optimizer

Metaheuristic global optimization algorithm combining a bacterial run-and-tumble chemotactic search (see, e.g., here) with a local, gradient-based search around the best minimum candidate points. The algorithm's goal is to find the global minimum of an objective function f over a (possibly bounded) region Ω, that is, to find

min f(x), x ∈ Ω,
f: Ω ⊂ ℝn → ℝ.

For the local, gradient-based search, a bounded BFGS algorithm is used.

Since the chemotactic search becomes more and more ineffective with increasing problem dimensionality, Sequential Random Embeddings are used to solve the optimization problem once its dimensionality exceeds a given threshold. The idea of Sequential Random Embeddings is basically to reduce high-dimensional problems to low-dimensional ones by embedding the original, high-dimensional search space ℝn into a low dimensional one, ℝm, by sequentially applying the random linear transformation

x0 = 0
xk+1 = αk+1xk + A • yk+1,
x ∈ ℝn,    y ∈ ℝm,     A ∈ N(0, 1)n×m,    α ∈ ℝ,

and minimizing the objective function f(αk+1xk + A • yk+1) w.r.t. (αk+1, yk+1) for each k in a given range.

Installation

rt_opt can be most conveniently installed via pip:

pip install rt_opt

Usage

For a quick start, try to find the global minimum of the Eggholder function within the default square xi ∈ [-512, 512] ∀ i = 1, 2:

import time

import numpy as np

from rt_opt.optimizer import optimize
from rt_opt.testproblems import Eggholder


problem = Eggholder()
box_bounds = np.vstack((problem.bounds.lower, problem.bounds.upper)).T
start = time.time()
ret = optimize(problem.f, bounds=box_bounds)
end = time.time()

print(f'Function minimum found at x = {ret.x}, yielding f(x) = {ret.fun}.')
print(f'Optimization took {end - start:.3f} seconds.')
print(f'Optimization error is {np.abs(ret.fun - problem.min.f)}.')



>>> Function minimum found at x = [512.         404.23180623], yielding f(x) = -959.6406627208495.
>>> Optimization took 0.097 seconds.
>>> Optimization error is 1.3642420526593924e-12.

Non-rectangular bounds

If your optimization problem involves an arbitrary, non-rectangular bounded domain, you may as well provide a custom bounds callback. Every such callback must return a tuple (x_projected, bounds_hit), where x_projected is the input variable x projected to the defined search region. That is, if x is within this region, it is returned unchanged, whereas if it is outside this region, it is projected to the region's boundaries. The second output, bounds_hit, indicates whether the boundary has been hit for each component of x. If, for example, x is three-dimensional and has hit the search region's boundaries in x1 and x2, but not in x3, bounds_hit = [True, True, False]. Here, we define a "boundary hit" in any component of x in the following way:
bounds_hit[i] = True iff either x + δêi or x - δêi is outside the defined search domain ∀ δ ∈ ℝ⁺, where êi is the i-th unit vector.

In the following example, we try to find the global minimum of the Eggholder function within a circle with radius 512 around the origin:

import numpy as np
import matplotlib.pyplot as plt

from rt_opt.optimizer import optimize
from rt_opt.testproblems import Eggholder


RADIUS = 512


def bounds_circle(x):
    """
    Callback function for defining a circular bounded domain with a given radius around the origin.
    """

    bounds_hit = np.zeros(len(x), dtype=bool)
    if np.sqrt(np.sum(np.square(x))) > RADIUS:  # x outside of the circle
        x = x / (np.sqrt(np.sum(np.square(x)))) * RADIUS
        bounds_hit = np.ones(len(x), dtype=bool)

    return x, bounds_hit


def gridmap2d(fun, x_specs, y_specs):
    """
    Helper function for plotting the objective function.
    """

    grid_x = np.linspace(*x_specs)
    grid_y = np.linspace(*y_specs)
    arr_z = np.empty(len(grid_x) * len(grid_y))
    i = 0
    for y in grid_y:
        for x in grid_x:
            arr_z[i] = fun(np.array([x, y]))
            i += 1
    arr_x, arr_y = np.meshgrid(grid_x, grid_y)
    arr_z.shape = arr_x.shape
    return arr_x, arr_y, arr_z


problem = Eggholder()
ret = optimize(problem.f, x0=[0, 0], bounds=bounds_circle, domain_scale=RADIUS)

print(f'Function minimum found at x = {ret.x}, yielding f(x) = {ret.fun}.')

# Plot objective function and optimizer traces
fig, ax = plt.subplots()
X, Y, Z = gridmap2d(problem.f, (-512, 512, 100), (-512, 512, 100))
cp = ax.contourf(X, Y, Z, levels=20)
ax.set_xlim([-512, 512])
ax.set_ylim([-512, 512])
fig.colorbar(cp)
for single_trace in ret.trace.transpose(1, 0, 2):
    ax.plot(single_trace[:, 0], single_trace[:, 1], 'o', c='white', ms=0.4)
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_title(f'Problem: Eggholder', fontsize=10)
plt.show()



>>> Function minimum found at x = [418.56055019 171.04305027], yielding f(x) = -629.6336812770477.

Example Trace

Performance

For a performance comparison of rt_opt with other global optimization algorithms, namely differential_evolution and dual_annealing from scipy, as well as dlib's LIPO-based find_min_global, see here. The comparison results were obtained by running demo.py, where each of these four algorithms was evaluated 100 times with default parameters on a couple of 2D and 15D test functions (details on these functions can be found here).

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

rt_opt-0.1.1.tar.gz (21.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

rt_opt-0.1.1-py3-none-any.whl (21.9 kB view details)

Uploaded Python 3

File details

Details for the file rt_opt-0.1.1.tar.gz.

File metadata

  • Download URL: rt_opt-0.1.1.tar.gz
  • Upload date:
  • Size: 21.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.4.1 importlib_metadata/3.7.3 pkginfo/1.7.0 requests/2.25.1 requests-toolbelt/0.9.1 tqdm/4.56.0 CPython/3.7.9

File hashes

Hashes for rt_opt-0.1.1.tar.gz
Algorithm Hash digest
SHA256 61157a81df0b2e68d8d5cee5a0ffd981f3b325fe8cf9c53e1afa783375d02a4c
MD5 51e0452aba4d02ee2add34df443bfc13
BLAKE2b-256 eed8e34dfc92179d3492ffd7da8dcdc0979af60f8845dc895ad3b2de158b7e70

See more details on using hashes here.

File details

Details for the file rt_opt-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: rt_opt-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 21.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.4.1 importlib_metadata/3.7.3 pkginfo/1.7.0 requests/2.25.1 requests-toolbelt/0.9.1 tqdm/4.56.0 CPython/3.7.9

File hashes

Hashes for rt_opt-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 319389b651181b1bfe432a9b91dbdd4b25cd6542bc2f3a7c939f364914a90815
MD5 e2ed64474357253baa870ccdbb1984fc
BLAKE2b-256 7454ebe5bc27fd449e5b22ec16f18c1b22798eae0e54e1f2b693942f8bd6f091

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page