import numpy as np
from . import optimiser
# Tests and example of solving optimisation problem
#
# Objects being evolved are lists of coefficients,
# and the optimiser is minimising a polynomial (a quadratic).
#
# Run this script to show animation of the solution, or run
# tests using pytest.
[docs]
class ControlIndex:
"""
Modify an object (e.g. list of coefficients) using an index
"""
[docs]
def __init__(self, index):
self.index = index
[docs]
def set(self, coefs, value):
coefs[self.index] = value
[docs]
def get(self, coefs):
return coefs[self.index]
[docs]
def calculate_score(coefs):
"""
Calculates (x-1)^2 + (y-2)^2
so minimum is at x=1, y=2
"""
return (coefs[0] - 1.0) ** 2 + (coefs[1] - 2.0) ** 2
[docs]
def test_quadratic():
# Check that a quadratic can be solved
# Note that the optimiser uses random numbers so
# there is a small chance of this test failing even if correct
start_values = [0.1, 0.1] # Starting guess
# The optimiser can control two coefficients,
# and should use calculate_score to evaluate solutions
result = optimiser.optimise(
start_values, [ControlIndex(0), ControlIndex(1)], calculate_score, maxgen=300
)
# Answer should be close to (1,2)
expected_point = np.array((1, 2))
start_distance = np.sqrt(np.sum((expected_point - start_values) ** 2))
result_distance = np.sqrt(np.sum((expected_point - result) ** 2))
# It should always get _closer_, even if it doesn't get particularly close
assert result_distance < start_distance
assert np.isclose(result_distance, 0, atol=1e-1)
[docs]
def test_reducing():
# Test that the best score never goes up
start_values = [0.1, 0.1] # Starting guess
best_score = calculate_score(start_values)
monitor_called = 0
def monitor(gen, best, pop):
nonlocal best_score
nonlocal monitor_called
score = best[0]
assert score <= best_score
best_score = score
monitor_called += 1
# Call monitor at each generation
result = optimiser.optimise(
start_values,
[ControlIndex(0), ControlIndex(1)],
calculate_score,
maxgen=10,
monitor=monitor,
)
assert monitor_called > 0
assert result[0] <= best_score
# Internal components of optimiser algorithm
[docs]
def test_pick_all():
vals = optimiser.pickUnique(10, 10, [])
# Should be a scrambled list of [0...10]
assert vals != list(range(10)) # Probably not sorted
assert sorted(vals) == list(range(10))
[docs]
def test_pick_different():
v1 = optimiser.pickUnique(20, 3, [])
v2 = optimiser.pickUnique(20, 3, [])
assert v1 != v2
[docs]
def test_pick_excludes():
for _i in range(20):
vals = optimiser.pickUnique(10, 3, [1, 8])
assert 1 not in vals
assert 8 not in vals
# Example optimiser with animation
if __name__ == "__main__":
start_values = [0.1, 0.1] # Starting guess
import matplotlib.pyplot as plt
fig = plt.figure()
axis = fig.add_subplot(111)
# Plot the exact solution
axis.scatter([1.0], [2.0], c="blue", s=0.1)
axis.axvline(1.0, ls="--")
axis.axhline(2.0, ls="--")
# A PathCollection representing the currently best point
best_point = axis.scatter([start_values[0]], [start_values[1]], c="red")
# Points representing the population
pop_points = None
def monitor(generation, best, pop):
global best_point
global pop_points
# Change the color of the previous points
# to grey and light red
best_point.set_color([1.0, 0.7, 0.7])
if pop_points:
pop_points.set_color([0.7, 0.7, 0.7])
# Get all population values
xs = [agent[1][0] for agent in pop]
ys = [agent[1][1] for agent in pop]
# Plot new points in black and red
pop_points = axis.scatter(xs, ys, c="black")
best_point = axis.scatter([best[1][0]], [best[1][1]], c="red")
axis.figure.canvas.draw()
axis.set_title(f"Generation: {generation}, Best score: {best[0]}")
plt.pause(0.5)
result = optimiser.optimise(
start_values,
[ControlIndex(0), ControlIndex(1)],
calculate_score,
maxgen=100,
monitor=monitor,
)
plt.show()