Skip to content

Commit

Permalink
Add Incremental method (#536)
Browse files Browse the repository at this point in the history
* Add reference for incremental method.

* Add Incremental method to reference_directions.ipynb

* Create incremental.py

* Add IncrementalReferenceDirectionFactory

* Update test_reference_directions.py

* Update test_reference_directions.py
  • Loading branch information
tomtkg authored Jan 19, 2024
1 parent f49596f commit 28fa1f9
Show file tree
Hide file tree
Showing 5 changed files with 143 additions and 0 deletions.
26 changes: 26 additions & 0 deletions docs/source/misc/reference_directions.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,32 @@
"ref_dirs = get_reference_directions(\"layer-energy\", 3, [9, 5, 2, 1])\n",
"Scatter().add(ref_dirs).show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Incremental method"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Incremental method can be used to create reference directions in increments. \n",
"However, the method relies on a partition number `n_partitions` which determines how many points will be sampled.\n",
"Points can be uniformly create on the unit hyperplane using the Incremental method proposed in <cite data-cite=\"incremental_Lattice\"></cite>."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"ref_dirs = get_reference_directions(\"incremental\", 3, n_partitions=8)\n",
"Scatter().add(ref_dirs).show()"
]
}
],
"metadata": {
Expand Down
17 changes: 17 additions & 0 deletions docs/source/references.bib
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,23 @@ @article{das_dennis
keywords = {Pareto set, multicriteria optimization, multiobjective optimization, trade-off curve},
}

@inproceedings{incremental_lattice,
author = {Takagi, Tomoaki and Takadama, Keiki and Sato, Hiroyuki},
title = {Incremental Lattice Design of Weight Vector Set},
booktitle = {Proceedings of the 2020 Genetic and Evolutionary Computation Conference Companion},
series = {GECCO '20},
year = {2020},
isbn = {9781450371278},
location = {Canc\'{u}n, Mexico},
pages = {1486--1494},
numpages = {9},
url = {https://doi.org/10.1145/3377929.3398082},
doi = {10.1145/3377929.3398082},
publisher = {Association for Computing Machinery},
address = {New York, NY, USA},
keywords = {uniform mixture design, weight vector set, evolutionary algorithm, multi-objective optimization, many-objective optimization},
}
@book{multi_objective_book,
author = {Kalyanmoy, Deb},
title = {Multi-Objective Optimization Using Evolutionary Algorithms},
Expand Down
2 changes: 2 additions & 0 deletions pymoo/util/ref_dirs/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from pymoo.util.ref_dirs.energy import RieszEnergyReferenceDirectionFactory
from pymoo.util.ref_dirs.energy_layer import LayerwiseRieszEnergyReferenceDirectionFactory
from pymoo.util.ref_dirs.reduction import ReductionBasedReferenceDirectionFactory
from pymoo.util.ref_dirs.incremental import IncrementalReferenceDirectionFactory
from pymoo.util.reference_direction import MultiLayerReferenceDirectionFactory


Expand All @@ -14,6 +15,7 @@ def get_reference_directions(name, *args, **kwargs):
"multi-layer": MultiLayerReferenceDirectionFactory,
"layer-energy": LayerwiseRieszEnergyReferenceDirectionFactory,
"reduction": ReductionBasedReferenceDirectionFactory,
"incremental": IncrementalReferenceDirectionFactory,
}

if name not in REF:
Expand Down
68 changes: 68 additions & 0 deletions pymoo/util/ref_dirs/incremental.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import numpy as np

from pymoo.util.reference_direction import ReferenceDirectionFactory

def check_n_points(n_points, n_dim):
"""
Returns n_partitions or a numeric value associated with the exception message.
"""

if n_dim == 1:
return [0]

I = n_dim * np.eye(n_dim)
W = np.zeros((1, n_dim))
edgeW = W
i = 0

while len(W) < n_points:
edgeW = np.tile(edgeW, (n_dim, 1)) + np.repeat(I, edgeW.shape[0], axis=0)
edgeW = np.unique(edgeW, axis=0)
edgeW = edgeW [np.any(edgeW == 0, axis=1)]
W = np.vstack((W + 1, edgeW))
i += 1

if len(W) == n_points:
return [i]

return [len(W) - len(edgeW), i - 1, len(W), i]


def incremental_lattice(n_partitions, n_dim):
I = n_dim * np.eye(n_dim)
W = np.zeros((1, n_dim))
edgeW = W

for _ in range(n_partitions):
edgeW = np.tile(edgeW, (n_dim, 1)) + np.repeat(I, edgeW.shape[0], axis=0)
edgeW = np.unique(edgeW, axis=0)
edgeW = edgeW [np.any(edgeW == 0, axis=1)]
W = np.vstack((W + 1, edgeW))

return W / (n_dim * n_partitions)

class IncrementalReferenceDirectionFactory(ReferenceDirectionFactory):

def __init__(self, n_dim, scaling=None, n_points=None, n_partitions=None, **kwargs) -> None:
super().__init__(n_dim, scaling=scaling, **kwargs)

if n_points is not None:
results = check_n_points(n_points, n_dim)

# the number of points are not matching to any partition number
if len(results) > 1:
raise Exception("The number of points (n_points = %s) can not be created uniformly.\n"
"Either choose n_points = %s (n_partitions = %s) or "
"n_points = %s (n_partitions = %s)." %
(n_points, results[0], results[1], results[2], results[3]))

self.n_partitions = results[0]

elif n_partitions is not None:
self.n_partitions = n_partitions

else:
raise Exception("Either provide number of partitions or number of points.")

def _do(self):
return incremental_lattice(self.n_partitions, self.n_dim)
30 changes: 30 additions & 0 deletions tests/misc/test_reference_directions.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,36 @@ def test_das_dennis_not_achievable_points():
get_reference_directions("das-dennis", 3, n_points=92)


def test_incremental():
ref_dirs = get_reference_directions("incremental", 3, n_partitions=8)
assert len(ref_dirs) == 109

def test_incremental_achievable_points():
ref_dirs = get_reference_directions("incremental", 3, n_points=109)
assert len(ref_dirs) == 109

def test_incremental2():
N = [[], [],
[1, 3, 5, 7, 9, 11, 13, 15, 17, 19],
[1, 4, 10, 19, 31, 46, 64, 85, 109, 136],
[1, 5, 15, 35, 69, 121, 195, 295, 425, 589],
[1, 6, 21, 56, 126, 251, 456, 771, 1231, 1876],
[1, 7, 28, 84, 210, 462, 923, 1709, 2975, 4921],
[1, 8, 36, 120, 330, 792, 1716, 3431, 6427, 11404],
[1, 9, 45, 165, 495, 1287, 3003, 6435, 12869, 24301]]
for i, list in enumerate(N):
for j, x in enumerate(list):
ref_dirs = get_reference_directions("incremental", i, n_partitions=j)
assert len(ref_dirs) == x
ref_dirs = get_reference_directions("incremental", i, n_points=x)
assert len(ref_dirs) == x


@pytest.mark.xfail(raises=Exception)
def test_incremental_not_achievable_points():
get_reference_directions("incremental", 3, n_points=110)


def test_unit_simplex_sampling():
n_points = 1000
n_dim = 3
Expand Down

0 comments on commit 28fa1f9

Please sign in to comment.