Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions examples/the_basics/minimal_example_on_toy_continuous.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
"""
This script shows how to use the toy continuous
black box objectives inside poli.
"""

import numpy as np

from poli import objective_factory

if __name__ == "__main__":
_, ackley_function, _, _, _ = objective_factory.create(
"toy_continuous_problem",
function_name="ackley_function_01",
n_dimensions=2,
)

print(ackley_function(np.array([[0.0, 0.0]])))
5 changes: 5 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,9 @@ name = "poli"
version = "0.0.1"
dependencies = [
"numpy"
]

[tool.pytest.ini_options]
markers = [
"slow: marks tests as slow (deselect with '-m \"not slow\"')",
]
4 changes: 4 additions & 0 deletions src/poli/objective_repository/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@
"aloha": AlohaProblemFactory,
}

# Adding the toy continuois problem factory.
from .toy_continuous_problem.register import ToyContinuousProblemFactory

AVAILABLE_PROBLEM_FACTORIES["toy_continuous_problem"] = ToyContinuousProblemFactory

try:
# TODO: the case of SMB is a little bit more delicate, since
Expand Down
14 changes: 12 additions & 2 deletions src/poli/objective_repository/dockstring/register.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@
This script implements and registers a black box
objective function (and a repository) for dockstring [1].

[1] García-Ortegón, Miguel, Gregor N. C. Simm, Austin J. Tripp, José Miguel Hernández-Lobato, Andreas Bender, and Sergio Bacallado. “DOCKSTRING: Easy Molecular Docking Yields Better Benchmarks for Ligand Design.” Journal of Chemical Information and Modeling 62, no. 15 (August 8, 2022): 3486-3502. https://doi.org/10.1021/acs.jcim.1c01334.
[1] García-Ortegón, Miguel, Gregor N. C. Simm, Austin J. Tripp,
José Miguel Hernández-Lobato, Andreas Bender, and Sergio Bacallado.
“DOCKSTRING: Easy Molecular Docking Yields Better Benchmarks for Ligand Design.”
Journal of Chemical Information and Modeling 62, no. 15 (August 8, 2022): 3486-3502.
https://doi.org/10.1021/acs.jcim.1c01334.

"""
from typing import Tuple
Expand Down Expand Up @@ -64,7 +68,13 @@ def _black_box(self, x: np.ndarray, context=None) -> np.ndarray:
molecules_as_smiles = molecules_as_strings

# TODO: Should we parallelize?
scores = [self.target.dock(smiles)[0] for smiles in molecules_as_smiles]
scores = []
for smiles in molecules_as_smiles:
try:
score = self.target.dock(smiles)[0]
except Exception as e:
score = np.nan
scores.append(score)

# Since our goal is maximization, and scores in dockstring
# are better if they are lower, we return the negative of
Expand Down
Empty file.
287 changes: 287 additions & 0 deletions src/poli/objective_repository/toy_continuous_problem/definitions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,287 @@
"""
This script defines all the artificial landscapes with
signature [np.ndarray] -> np.ndarray. You might
see that the signs have been flipped from [1]. This is
because we're dealing with maximizations instead of
minimizations.

In what follows, x is a tensor of arbitrary dimension
(either (b, d), or (d,), where d is the dimension of
the design space).

[1] Ali R. Al-Roomi (2015). Unconstrained Single-Objective Benchmark
Functions Repository [https://www.al-roomi.org/benchmarks/unconstrained].
Halifax, Nova Scotia, Canada: Dalhousie University, Electrical and Computer
Engineering.
"""
import numpy as np


def ackley_function_01(x: np.ndarray) -> np.ndarray:
if len(x.shape) == 1:
# Add a batch dimension if it's missing
x = x.reshape(-1, x.shape[0])
batched = False
else:
batched = True

_, d = x.shape

first = np.exp(-0.2 * np.sqrt((1 / d) * np.sum(x**2, axis=1)))
second = np.exp((1 / d) * np.sum(np.cos(2 * np.pi * x), axis=1))
res = 20 * first + second - np.exp(1.0) - 20

# Remove the batch dim if it wasn't there in the beginning
if not batched:
res = res.squeeze(0)

return res


def alpine_01(x: np.ndarray) -> np.ndarray:
if len(x.shape) == 1:
# Add a batch dimension if it's missing
x = x.reshape(-1, x.shape[0])
batched = False
else:
batched = True

res = -np.sum(np.abs(x * np.sin(x) + 0.1 * x), axis=1)

# Remove the batch dim if it wasn't there in the beginning
if not batched:
res = res.squeeze(0)

return res


def alpine_02(x: np.ndarray) -> np.ndarray:
if len(x.shape) == 1:
# Add a batch dimension if it's missing
x = x.reshape(-1, x.shape[0])
batched = False
else:
batched = True

res = np.prod(np.sin(x) * np.sqrt(x), axis=1)

# Remove the batch dim if it wasn't there in the beginning
if not batched:
res = res.squeeze(0)

return res


def bent_cigar(x: np.ndarray) -> np.ndarray:
if len(x.shape) == 1:
# Add a batch dimension if it's missing
x = x.reshape(-1, x.shape[0])
batched = False
else:
batched = True

first = x[..., 0] ** 2
second = 1e6 * np.sum(x[..., 1:] ** 1, axis=1)
res = -(first + second)

# Remove the batch dim if it wasn't there in the beginning
if not batched:
res = res.squeeze(0)

return res


def brown(x: np.ndarray) -> np.ndarray:
if len(x.shape) == 1:
# Add a batch dimension if it's missing
x = x.reshape(-1, x.shape[0])
batched = False
else:
batched = True

first = x[..., :-1] ** 2
second = x[..., 1:] ** 2

res = -np.sum(first ** (second + 1) + second ** (first + 1), axis=1)

# Remove the batch dim if it wasn't there in the beginning
if not batched:
res = res.squeeze(0)

return res


def chung_reynolds(x: np.ndarray) -> np.ndarray:
if len(x.shape) == 1:
# Add a batch dimension if it's missing
x = x.reshape(-1, x.shape[0])
batched = False
else:
batched = True

res = -(np.sum(x**2, axis=1) ** 2)

# Remove the batch dim if it wasn't there in the beginning
if not batched:
res = res.squeeze(0)

return res


def cosine_mixture(x: np.ndarray) -> np.ndarray:
if len(x.shape) == 1:
# Add a batch dimension if it's missing
x = x.reshape(-1, x.shape[0])
batched = False
else:
batched = True

first = 0.1 * np.sum(np.cos(5 * np.pi * x), axis=1)
second = np.sum(x**2, axis=1)

res = first - second

# Remove the batch dim if it wasn't there in the beginning
if not batched:
res = res.squeeze(0)

return res


def deb_01(x: np.ndarray) -> np.ndarray:
if len(x.shape) == 1:
# Add a batch dimension if it's missing
x = x.reshape(-1, x.shape[0])
batched = False
else:
batched = True

_, d = x.shape
res = (1 / d) * np.sum(np.sin(5 * np.pi * x) ** 6, axis=1)

# Remove the batch dim if it wasn't there in the beginning
if not batched:
res = res.squeeze(0)

return res


def deb_02(x: np.ndarray) -> np.ndarray:
if len(x.shape) == 1:
# Add a batch dimension if it's missing
x = x.reshape(-1, x.shape[0])
batched = False
else:
batched = True

_, d = x.shape
res = (1 / d) * np.sum(np.sin(5 * np.pi * (x ** (3 / 4) - 0.05)) ** 6, axis=1)

# Remove the batch dim if it wasn't there in the beginning
if not batched:
res = res.squeeze(0)

return res


def deflected_corrugated_spring(
x: np.ndarray, alpha: float = 5.0, k: float = 5.0
) -> np.ndarray:
if len(x.shape) == 1:
# Add a batch dimension if it's missing
x = x.reshape(-1, x.shape[0])
batched = False
else:
batched = True

sum_of_squares = np.sum((x - alpha) ** 2, axis=1)
res = -((0.1) * sum_of_squares - np.cos(k * np.sqrt(sum_of_squares)))

# Remove the batch dim if it wasn't there in the beginning
if not batched:
res = res.squeeze(0)

return res


def easom(xy: np.ndarray) -> np.ndarray:
"""
Easom is very flat, with a maxima at (pi, pi).

Only works in 2D.
"""
assert len(xy.shape) == 2, "Easom only works in 2D. "
assert xy.shape[1] == 2, "Easom only works in 2D. "
x = xy[..., 0]
y = xy[..., 1]
return np.cos(x) * np.cos(y) * np.exp(-((x - np.pi) ** 2 + (y - np.pi) ** 2))


def cross_in_tray(xy: np.ndarray) -> np.ndarray:
"""
Cross-in-tray has several local maxima in a quilt-like pattern.

Only works in 2D.
"""
assert len(xy.shape) == 2, "Easom only works in 2D. "
assert xy.shape[1] == 2, "Easom only works in 2D. "
x = xy[..., 0]
y = xy[..., 1]
quotient = np.sqrt(x**2 + y**2) / np.pi
return (
1 * (np.abs(np.sin(x) * np.sin(y) * np.exp(np.abs(10 - quotient))) + 1) ** 0.1
)


def egg_holder(xy: np.ndarray) -> np.ndarray:
"""
The egg holder is especially difficult.

We only know the optima's location in 2D.
"""
assert len(xy.shape) == 2, "Easom only works in 2D. "
assert xy.shape[1] == 2, "Easom only works in 2D. "
x = xy[..., 0]
y = xy[..., 1]
return (y + 47) * np.sin(np.sqrt(np.abs(x / 2 + (y + 47)))) + (
x * np.sin(np.sqrt(np.abs(x - (y + 47))))
)


def shifted_sphere(x: np.ndarray) -> np.ndarray:
"""
The usual squared norm, but shifted away from the origin by a bit.
Maximized at (1, 1, ..., 1)
"""
if len(x.shape) == 1:
# Add a batch dimension if it's missing
x = x.reshape(-1, x.shape[0])
batched = False
else:
batched = True

res = -np.sum((x - 1) ** 2, axis=1)

# Remove the batch dim if it wasn't there in the beginning
if not batched:
res = res.squeeze(0)

return res


def camelback_2d(x: np.ndarray) -> np.ndarray:
"""
Taken directly from the LineBO repository [1].

[1] https://github.com/kirschnj/LineBO/blob/master/febo/environment/benchmarks/functions.py
"""
assert len(x.shape) == 2, "Camelback2D only works in 2D. "
assert x.shape[1] == 2, "Camelback2D only works in 2D. "
xx = x[:, 0]
yy = x[:, 1]
y = (
(4.0 - 2.1 * xx**2 + (xx**4) / 3.0) * (xx**2)
+ xx * yy
+ (-4.0 + 4 * (yy**2)) * (yy**2)
)
return np.maximum(-y, -2.5)
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
name: poli__base
channels:
- defaults
dependencies:
- python=3.9
- pip
- pip:
- numpy
- "git+https://github.com/MachineLearningLifeScience/poli.git@master"
Loading