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.0.tar.gz (21.5 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.0-py3-none-any.whl (22.0 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: rt_opt-0.1.0.tar.gz
  • Upload date:
  • Size: 21.5 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.0.tar.gz
Algorithm Hash digest
SHA256 16d9ec60fe3e7515c530e0a6e5f924613a13d3dee4842764563291df43e37b65
MD5 63c87695498467d50448658b34529909
BLAKE2b-256 6813d44465711e9b3d82a147e2940e3fd27752b7246302a93a96f4dc39e42a76

See more details on using hashes here.

File details

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

File metadata

  • Download URL: rt_opt-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 22.0 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.0-py3-none-any.whl
Algorithm Hash digest
SHA256 f0eb401699868f5ed7c8e0e1a8c1f8c1e9fb62823db2a069e1ad17c19cb39eac
MD5 0f8b6ac9070f51616b0841175a129b92
BLAKE2b-256 0a8e3365ab98c374fcb977139063348e309476c0d43609ad4e5a5fc6c47dce9a

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