Skip to content

Commit

Permalink
Fix bug in topology classes (#176)
Browse files Browse the repository at this point in the history
Resolves #170

Fixed a bug in the topology class where not the best values were used as result when optimizing with the LocalBest class. Made the tests for the topologies less trivial by changing the index of the best cost and best position in the fixture swarm. Added a bigger fixture swarm to test the topology classes. Additionally, I fixed a little bug in the Random topology where the elements were compared to 0 instead of np.inf. Deleted the fixture for the neighbour number and replaced it with a pytest.mark.parametrize().

Signed-off-by: Lester James V. Miranda <[email protected]>
Committed-with: Aaron (@whzup)
  • Loading branch information
whzup authored and ljvmiranda921 committed Jul 23, 2018
1 parent a44443d commit d8c39ef
Show file tree
Hide file tree
Showing 8 changed files with 71 additions and 41 deletions.
2 changes: 1 addition & 1 deletion pyswarms/backend/topology/pyramid.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ def compute_gbest(self, swarm):
# Obtain best cost and position
best_cost = np.min(swarm.pbest_cost[best_neighbor])
best_pos = swarm.pbest_pos[
np.argmin(swarm.pbest_cost[best_neighbor])
best_neighbor[np.argmin(swarm.pbest_cost[best_neighbor])]
]
except AttributeError:
msg = "Please pass a Swarm class. You passed {}".format(
Expand Down
7 changes: 2 additions & 5 deletions pyswarms/backend/topology/random.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ def compute_gbest(self, swarm, k):
# Obtain best cost and position
best_cost = np.min(swarm.pbest_cost[best_neighbor])
best_pos = swarm.pbest_pos[
np.argmin(swarm.pbest_cost[best_neighbor])
best_neighbor[np.argmin(swarm.pbest_cost[best_neighbor])]
]

except AttributeError:
Expand Down Expand Up @@ -169,9 +169,6 @@ def __compute_neighbors(self, swarm, k):
graph needs edges to change it to a connected
graph.
.. note:: If the graph isn't connected, it is possible that the
PSO algorithm does not find the best position within
the swarm.
Parameters
----------
Expand Down Expand Up @@ -206,7 +203,7 @@ def __compute_neighbors(self, swarm, k):
# Generate connected graph.
while connected_components(adj_matrix, directed=False, return_labels=False) != 1:
for i, j in itertools.product(range(swarm.n_particles), repeat=2):
if dist_matrix[i][j] == 0:
if dist_matrix[i][j] == np.inf:
adj_matrix[i][j] = 1

return adj_matrix
4 changes: 2 additions & 2 deletions pyswarms/backend/topology/ring.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,15 +72,15 @@ def compute_gbest(self, swarm, p, k):
if k == 1:
# The minimum index is itself, no mapping needed.
best_neighbor = swarm.pbest_cost[self.neighbor_idx][:, np.newaxis].argmin(
axis=1
axis=0
)
else:
idx_min = swarm.pbest_cost[self.neighbor_idx].argmin(axis=1)
best_neighbor = self.neighbor_idx[np.arange(len(self.neighbor_idx)), idx_min]
# Obtain best cost and position
best_cost = np.min(swarm.pbest_cost[best_neighbor])
best_pos = swarm.pbest_pos[
np.argmin(swarm.pbest_cost[best_neighbor])
best_neighbor[np.argmin(swarm.pbest_cost[best_neighbor])]
]
except AttributeError:
msg = "Please pass a Swarm class. You passed {}".format(
Expand Down
52 changes: 37 additions & 15 deletions tests/backend/topology/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,42 @@
def swarm():
"""A contrived instance of the Swarm class at a certain timestep"""
attrs_at_t = {
"position": np.array([[5, 5, 5], [3, 3, 3], [1, 1, 1]]),
"velocity": np.array([[1, 1, 1], [1, 1, 1], [1, 1, 1]]),
"current_cost": np.array([2, 2, 2]),
"pbest_cost": np.array([1, 2, 3]),
"pbest_pos": np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]),
"best_cost": 1,
"best_pos": np.array([1, 1, 1]),
"options": {"c1": 0.5, "c2": 1, "w": 2},
"position": np.array([[9.95838686e-01, 5.87433429e-04, 6.49113772e-03],
[1.00559609e+00, 3.96477697e-02, 7.67205397e-01],
[2.87990950e-01, -3.64932609e-02, 1.89750725e-02],
[1.11646877e+00, 3.12037361e-03, 1.97885369e-01],
[8.96117216e-01, -9.79602053e-03, -1.66139336e-01],
[9.90423669e-01, 1.99307974e-03, -1.23386797e-02],
[2.06800701e-01, -1.67869387e-02, 1.14268810e-01],
[4.21786494e-01, 2.58755510e-02, 6.62254843e-01],
[9.90350831e-01, 3.81575154e-03, 8.80833545e-01],
[9.94353749e-01, -4.85086205e-02, 9.85313500e-03]]),
"velocity": np.array([[2.09076818e-02, 2.04936403e-03, 1.06761248e-02],
[1.64940497e-03, 5.67924469e-03, 9.74902301e-02],
[1.50445516e-01, 9.11699158e-03, 1.51474794e-02],
[2.94238740e-01, 5.71545680e-04, 1.54122294e-02],
[4.10430034e-02, 6.51847479e-04, 6.25109226e-02],
[6.71076116e-06, 1.89615516e-04, 4.65023770e-03],
[4.76081378e-02, 4.24416089e-03, 7.11856172e-02],
[1.33832808e-01, 1.81818698e-02, 1.16947941e-01],
[1.22849955e-03, 1.55685312e-03, 1.67819003e-02],
[5.60617396e-03, 4.31819608e-02, 2.52217220e-02]]),
"current_cost": np.array([1.07818462, 5.5647911, 19.6046078, 14.05300016, 3.72597614, 1.01169386,
16.51846203, 32.72262829, 3.80274901, 1.05237138]),
"pbest_cost": np.array([1.00362006, 2.39151041, 2.55208424, 5.00176207, 1.04510827, 1.00025284,
6.31216654, 2.53873121, 2.00530884, 1.05237138]),
"pbest_pos": np.array([[9.98033031e-01, 4.97392619e-03, 3.07726256e-03],
[1.00665809e+00, 4.22504014e-02, 9.84334657e-01],
[1.12159389e-02, 1.11429739e-01, 2.86388193e-02],
[1.64059236e-01, 6.85791237e-03, -2.32137604e-02],
[9.93740665e-01, -6.16501403e-03, -1.46096578e-02],
[9.90438476e-01, 2.50379538e-03, 1.87405987e-05],
[1.12301876e-01, 1.77099784e-03, 1.45382457e-01],
[4.41204876e-02, 4.84059652e-02, 1.05454822e+00],
[9.89348409e-01, -1.31692358e-03, 9.88291764e-01],
[9.99959923e-01, -5.32665972e-03, -1.53685870e-02]]),
"best_cost": 1.0002528364353296,
"best_pos": np.array([9.90438476e-01, 2.50379538e-03, 1.87405987e-05]),
"options": {'c1': 0.5, 'c2': 0.3, 'w': 0.9},
}
return Swarm(**attrs_at_t)


@pytest.fixture(scope="module")
def k():
"""Default neighbor number"""
_k = 1
return _k
8 changes: 4 additions & 4 deletions tests/backend/topology/test_pyramid.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@
def test_compute_gbest_return_values(swarm, static):
"""Test if compute_gbest() gives the expected return values"""
topology = Pyramid(static=static)
expected_cost = 1
expected_pos = np.array([1, 2, 3])
expected_cost = 1.0002528364353296
expected_pos = np.array([9.90438476e-01, 2.50379538e-03, 1.87405987e-05])
pos, cost = topology.compute_gbest(swarm)
assert cost == expected_cost
assert (pos == expected_pos).all()
assert cost == pytest.approx(expected_cost)
assert (pos == pytest.approx(expected_pos))


@pytest.mark.parametrize("static", [True, False])
Expand Down
21 changes: 16 additions & 5 deletions tests/backend/topology/test_random.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ def test_update_gbest_neighborhood(swarm, k, static):
"""Test if update_gbest_neighborhood gives the expected return values"""
topology = Random(static=static)
pos, cost = topology.compute_gbest(swarm, k=k)
expected_pos = np.array([1, 2, 3])
expected_cost = 1
assert (pos == expected_pos).all()
assert cost == expected_cost
expected_pos = np.array([9.90438476e-01, 2.50379538e-03, 1.87405987e-05])
expected_cost = 1.0002528364353296
assert cost == pytest.approx(expected_cost)
assert (pos == pytest.approx(expected_pos))


@pytest.mark.parametrize("static", [True, False])
Expand Down Expand Up @@ -57,16 +57,27 @@ def test_compute_neighbors_return_values(swarm, k, static):


@pytest.mark.parametrize("static", [True, False])
@pytest.mark.parametrize("k", [1])
def test_compute_neighbors_adjacency_matrix(swarm, k, static):
"""Test if __compute_neighbors() gives the expected matrix"""
np.random.seed(1)
topology = Random(static=static)
adj_matrix = topology._Random__compute_neighbors(swarm, k)
comparison_matrix = np.array([[1, 1, 0], [1, 1, 1], [0, 1, 1]])
comparison_matrix = np.array([[1, 1, 1, 0, 1, 0, 0, 0, 0, 1],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[0, 1, 1, 1, 1, 0, 0, 0, 1, 0],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[0, 1, 1, 0, 1, 1, 0, 1, 0, 1],
[0, 1, 1, 0, 1, 0, 1, 0, 1, 1],
[0, 1, 1, 0, 1, 1, 0, 1, 1, 0],
[0, 1, 1, 1, 1, 0, 1, 1, 1, 0],
[1, 1, 1, 0, 1, 1, 1, 0, 0, 1]])
assert np.allclose(adj_matrix, comparison_matrix, atol=1e-8)


@pytest.mark.parametrize("static", [True, False])
@pytest.mark.parametrize("k", [1])
def test_neighbor_idx(swarm, k, static):
"""Test if the neighbor_idx attribute is assigned"""
topology = Random(static=static)
Expand Down
10 changes: 5 additions & 5 deletions tests/backend/topology/test_ring.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ def test_update_gbest_neighborhood(swarm, p, k, static):
"""Test if update_gbest_neighborhood gives the expected return values"""
topology = Ring(static=static)
pos, cost = topology.compute_gbest(swarm, p=p, k=k)
expected_pos = np.array([1, 2, 3])
expected_cost = 1
assert (pos == expected_pos).all()
assert cost == expected_cost
expected_pos = np.array([9.90438476e-01, 2.50379538e-03, 1.87405987e-05])
expected_cost = 1.0002528364353296
assert cost == pytest.approx(expected_cost)
assert (pos == pytest.approx(expected_pos))


@pytest.mark.parametrize("static", [True, False])
Expand Down Expand Up @@ -50,7 +50,7 @@ def test_compute_position_return_values(swarm, bounds, static):
@pytest.mark.parametrize("static", [True, False])
@pytest.mark.parametrize("k", [1, 2, 3])
@pytest.mark.parametrize("p", [1, 2])
def test_neighbor_idx(swarm, static, p , k):
def test_neighbor_idx(swarm, static, p, k):
"""Test if the neighbor_idx attribute is assigned"""
topology = Ring(static=static)
p = topology.compute_gbest(swarm, p=p, k=k)
Expand Down
8 changes: 4 additions & 4 deletions tests/backend/topology/test_star.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@
def test_compute_gbest_return_values(swarm):
"""Test if compute_gbest() gives the expected return values"""
topology = Star()
expected_cost = 1
expected_pos = np.array([1, 2, 3])
expected_cost = 1.0002528364353296
expected_pos = np.array([9.90438476e-01, 2.50379538e-03, 1.87405987e-05])
pos, cost = topology.compute_gbest(swarm)
assert cost == expected_cost
assert (pos == expected_pos).all()
assert cost == pytest.approx(expected_cost)
assert (pos == pytest.approx(expected_pos))


@pytest.mark.parametrize("clamp", [None, (0, 1), (-1, 1)])
Expand Down

0 comments on commit d8c39ef

Please sign in to comment.