Skip to content

Commit 72880e4

Browse files
author
Julian Blank
committed
Improved Documentation
1 parent 255b12d commit 72880e4

23 files changed

+585
-143
lines changed

pymoo/algorithms/moead.py

Lines changed: 9 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,12 @@
22
from scipy.spatial.distance import cdist
33

44
from pymoo.algorithms.genetic_algorithm import GeneticAlgorithm
5-
from pymoo.docs import parse_doc_string
65
from pymoo.factory import get_decomposition
76
from pymoo.operators.crossover.simulated_binary_crossover import SimulatedBinaryCrossover
8-
from pymoo.util.display import MultiObjectiveDisplay
9-
from pymoo.util.misc import set_if_none
107
from pymoo.operators.mutation.polynomial_mutation import PolynomialMutation
118
from pymoo.operators.sampling.random_sampling import FloatRandomSampling
9+
from pymoo.util.display import MultiObjectiveDisplay
10+
from pymoo.util.misc import set_if_none
1211

1312

1413
# =========================================================================================================
@@ -28,19 +27,12 @@ def __init__(self,
2827
2928
Parameters
3029
----------
31-
ref_dirs : {ref_dirs}
32-
33-
decomposition : {{ 'auto', 'tchebi', 'pbi' }}
34-
The decomposition approach that should be used. If set to `auto` for two objectives `tchebi` and for more than
35-
two `pbi` will be used.
36-
37-
n_neighbors : int
38-
Number of neighboring reference lines to be used for selection.
39-
40-
prob_neighbor_mating : float
41-
Probability of selecting the parents in the neighborhood.
42-
43-
30+
ref_dirs
31+
n_neighbors
32+
decomposition
33+
prob_neighbor_mating
34+
display
35+
kwargs
4436
"""
4537

4638
self.n_neighbors = n_neighbors
@@ -130,4 +122,4 @@ def _next(self):
130122
pop[N[I]] = off
131123

132124

133-
parse_doc_string(MOEAD.__init__)
125+
# parse_doc_string(MOEAD.__init__)

pymoo/algorithms/so_cmaes.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ def _do(self, problem, evaluator, algorithm):
5050
class CMAES(LocalSearch):
5151

5252
def __init__(self,
53+
x0=None,
5354
sigma=0.5,
5455
parallelize=True,
5556
maxfevals=np.inf,
@@ -333,7 +334,7 @@ def __init__(self,
333334
for a list of available options.
334335
335336
"""
336-
super().__init__(display=display, **kwargs)
337+
super().__init__(x0=x0, display=display, **kwargs)
337338

338339
self.es = None
339340
self.cma = None

pymoo/algorithms/so_direct.py

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
import numpy as np
2+
3+
from pymoo.algorithms.so_local_search import LocalSearch
4+
from pymoo.model.individual import Individual
5+
from pymoo.model.population import Population
6+
from pymoo.model.problem import Problem
7+
from pymoo.optimize import minimize
8+
from pymoo.problems.single import Himmelblau, Sphere, Rastrigin
9+
from pymoo.util.display import SingleObjectiveDisplay
10+
from pymoo.util.nds.non_dominated_sorting import NonDominatedSorting
11+
from pymoo.util.normalization import normalize, denormalize
12+
13+
14+
def norm_bounds(pop, problem):
15+
nxl = normalize(pop.get("xl"), problem.xl, problem.xu)
16+
nxu = normalize(pop.get("xu"), problem.xl, problem.xu)
17+
return nxl, nxu
18+
19+
20+
def update_bounds(ind, xl, xu, k, delta):
21+
_xl = np.copy(xl)
22+
_xl[k] = ind.X[k] - delta
23+
ind.set("xl", _xl)
24+
25+
_xu = np.copy(xu)
26+
_xu[k] = ind.X[k] + delta
27+
ind.set("xu", _xu)
28+
29+
30+
class DIRECT(LocalSearch):
31+
32+
def __init__(self,
33+
eps=1e-2,
34+
penalty=0.1,
35+
display=SingleObjectiveDisplay(),
36+
**kwargs):
37+
super().__init__(display=display, **kwargs)
38+
self.eps = eps
39+
self.penalty = penalty
40+
41+
def initialize(self, problem, **kwargs):
42+
super().initialize(problem, **kwargs)
43+
44+
xl, xu = problem.bounds()
45+
X = denormalize(0.5 * np.ones(problem.n_var), xl, xu)
46+
x0 = Individual(X=X)
47+
x0.set("xl", xl)
48+
x0.set("xu", xu)
49+
x0.set("depth", 0)
50+
self.x0 = x0
51+
52+
def _initialize(self, **kwargs):
53+
super()._initialize(**kwargs)
54+
55+
def _potential_optimal(self):
56+
pop = self.pop
57+
58+
if len(pop) == 1:
59+
return pop
60+
61+
# get the intervals of each individual
62+
_F, _CV, xl, xu = pop.get("F", "CV", "xl", "xu")
63+
nF = normalize(_F)
64+
F = nF + self.penalty * _CV
65+
66+
# get the length of the interval of each solution
67+
nxl, nxu = norm_bounds(pop, problem)
68+
length = (nxu - nxl) / 2
69+
70+
val = length.max(axis=1)
71+
72+
# (a) non-dominated with respect to interval
73+
obj = np.column_stack([-val, F])
74+
I = NonDominatedSorting().do(obj, only_non_dominated_front=True)
75+
candidates, F, xl, xu, val = pop[I], F[I], xl[I], xu[I], val[I]
76+
77+
# import matplotlib.pyplot as plt
78+
# plt.scatter(obj[:, 0], obj[:, 1])
79+
# plt.scatter(obj[I, 0], obj[I, 1], color="red")
80+
# plt.show()
81+
82+
if len(candidates) == 1:
83+
return candidates
84+
85+
else:
86+
# TODO: The second condition needs to be implemented here. Exact implementation still unclear.
87+
88+
n_max_candidates = 10
89+
90+
if len(candidates) > n_max_candidates:
91+
I = list(np.random.choice(np.arange(len(candidates)), n_max_candidates - 1))
92+
k = np.argmin(F[:, 0])
93+
if k not in I:
94+
I.append(k)
95+
candidates = candidates[I]
96+
97+
return candidates
98+
99+
def _next(self):
100+
# the offspring population to finally evaluate and attach to the population
101+
off = Population()
102+
103+
# find the potential optimal solution in the current population
104+
potential_optimal = self._potential_optimal()
105+
106+
# for each of those solutions execute the division move
107+
for current in potential_optimal:
108+
109+
# find the largest dimension the solution has not been evaluated yet
110+
nxl, nxu = norm_bounds(current, problem)
111+
k = np.argmax(nxu - nxl)
112+
113+
# the delta value to be used to get left and right - this is one sixth of the range
114+
xl, xu = current.get("xl"), current.get("xu")
115+
116+
delta = (xu[k] - xl[k]) / 6
117+
118+
# print(current.X, delta, k, xl, xu)
119+
120+
# create the left individual
121+
left_x = np.copy(current.X)
122+
left_x[k] = xl[k] + delta
123+
left = Individual(X=left_x)
124+
125+
# create the right individual
126+
right_x = np.copy(current.X)
127+
right_x[k] = xu[k] - delta
128+
right = Individual(X=right_x)
129+
130+
# update the boundaries for all the points accordingly
131+
for ind in [current, left, right]:
132+
update_bounds(ind, xl, xu, k, delta)
133+
134+
# create the offspring population, evaluate and attach to current population
135+
_off = Population.create(left, right)
136+
_off.set("depth", current.get("depth") + 1)
137+
138+
off = Population.merge(off, _off)
139+
140+
# evaluate the offsprings
141+
self.evaluator.eval(self.problem, off, algorithm=self)
142+
143+
# print(off.get("X"))
144+
145+
# add the offsprings to the population
146+
self.pop = Population.merge(self.pop, off)
147+
148+
149+
class ExSwarm(Problem):
150+
151+
def __init__(self):
152+
super().__init__(n_var=2, n_obj=1, n_constr=0, xl=np.array([-1.0, -1.0]), xu=np.array([1.0, 1.0]))
153+
154+
def _evaluate(self, x, out, *args, **kwargs):
155+
v1 = 20 * x[:, 0]
156+
v2 = 20 * x[:, 1]
157+
out["F"] = -np.sin(v1 / np.pi) ** 2. * np.sin(v2 / np.pi) ** 2. * (abs(v1) + abs(v2) + 0.1 * (v1 + v2)) + (
158+
v1 * v1 + v2 * v2) / 30
159+
160+
161+
if __name__ == '__main__':
162+
problem = ExSwarm()
163+
problem = Himmelblau()
164+
# problem = Sphere(n_var=100, opt=0.2 * np.ones(100))
165+
problem = Rastrigin(n_var=10)
166+
problem.xl *= 1.5
167+
168+
# problem = get_problem("g02")
169+
algorithm = DIRECT()
170+
# algorithm = GA()
171+
172+
ret = minimize(problem,
173+
algorithm,
174+
("n_iter", 1000),
175+
verbose=True)

pymoo/algorithms/so_pattern_search.py

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,20 +43,30 @@ def do_continue(self, algorithm):
4343

4444

4545
class PatternSearch(LocalSearch):
46-
4746
def __init__(self,
4847
explr_delta=0.25,
4948
explr_rho=0.5,
5049
pattern_step=2,
5150
eps=1e-5,
5251
display=PatternSearchDisplay(),
5352
**kwargs):
53+
"""
54+
55+
Parameters
56+
----------
57+
explr_delta
58+
explr_rho
59+
pattern_step
60+
eps
61+
display
62+
kwargs
63+
"""
5464

5565
super().__init__(display=display, **kwargs)
5666
self.explr_rho = explr_rho
5767
self.pattern_step = pattern_step
5868
self.explr_delta = explr_delta
59-
self.default_termination = PatternSearchTermination(eps=eps, x_tol=1e-6, f_tol=1e-6, nth_gen=1, n_last=2)
69+
self.default_termination = PatternSearchTermination(eps=eps, x_tol=1e-6, f_tol=1e-6, nth_gen=1, n_last=30)
6070

6171
def _initialize(self, **kwargs):
6272
super()._initialize(**kwargs)
@@ -80,9 +90,15 @@ def _next(self):
8090
# perform an exploration move around the trial vector - the best known solution is always stored in _current
8191
explr = self._exploration_move(trial, opt=self._current)
8292

93+
# we can break if we did not improve
8394
if not is_better(explr, self._current):
8495
break
8596

97+
# else also check if we are terminating - otherwise this loop might run far too long
98+
self._set_optimum()
99+
if self.termination.has_terminated(self):
100+
break
101+
86102
self._previous, self._current = self._current, explr
87103

88104
self.explr_delta *= self.explr_rho

pymoo/docs.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -100,40 +100,40 @@
100100

101101
"tight_layout": """bool
102102
Whether tight layout should be used.
103-
""",
103+
""",
104104

105105
"bounds": """tuple
106106
If plot requires normalization, it might be necessary to supply the boundaries. (Otherwise they might be
107107
approximate by the minimum and maximum of the provided data). The boundaries should be provided as a list/tuple or
108108
2D numpy array, where the first element represents the minimum, second the second the maximum values.
109109
If only an integer or float is supplied, the boundaries apply for each variable.
110-
""",
110+
""",
111111

112112
"reverse": """bool
113113
If plot requires normalization, then the reverse values can be plotted (1 - Input). For some plots
114114
it can be useful to interpret a larger area as better regarding a value. If minimization applies, a smaller
115115
area means better, which can be misleading.
116-
""",
116+
""",
117117

118118
"axis_style": """dict
119119
Most of the plots consists of an axis. The style of the axis, e.g. color, alpha, ..., can be changed to
120120
further modify the plot appealing.
121-
""",
121+
""",
122122

123123
"cmap": """colormap
124124
For some plots different kind of colors are used. The colormap can be changed to modify the color sequence
125125
for the plots.
126-
""",
126+
""",
127127

128128
"labels": """str or list
129129
The labels to be used for each variable provided in the plot. If a string is used, then they will
130130
be enumerated. Otherwise, a list equal to the number of variables can be provided directly.
131-
""",
131+
""",
132132

133133
"func_number_to_text": """func
134134
A function which defines how numerical values should be represented if present in the plot
135135
for instance scientific notation, rounding and so on.
136-
""",
136+
""",
137137

138138
}
139139

pymoo/factory.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
import re
99

1010
from pymoo.configuration import Configuration
11-
1211
from pymoo.problems.many import *
1312
from pymoo.problems.multi import *
1413
from pymoo.problems.single import *
@@ -19,6 +18,7 @@
1918
# =========================================================================================================
2019

2120

21+
2222
def get_from_list(l, name, args, kwargs):
2323
i = None
2424

@@ -67,12 +67,14 @@ def get_algorithm_options():
6767
from pymoo.algorithms.so_nelder_mead import NelderMead
6868
from pymoo.algorithms.so_cmaes import CMAES
6969
from pymoo.algorithms.so_brkga import BRKGA
70+
from pymoo.algorithms.so_pattern_search import PatternSearch
7071

7172
ALGORITHMS = [
7273
("ga", GA),
7374
("brkga", BRKGA),
7475
("de", DE),
7576
("nelder-mead", NelderMead),
77+
("pattern-search", PatternSearch),
7678
("cmaes", CMAES),
7779
("nsga2", NSGA2),
7880
("rnsga2", RNSGA2),
@@ -317,11 +319,13 @@ def get_reference_direction_options():
317319
from pymoo.util.reference_direction import MultiLayerReferenceDirectionFactory
318320
from pymoo.util.ref_dirs.reduction import ReductionBasedReferenceDirectionFactory
319321
from pymoo.util.ref_dirs.energy import RieszEnergyReferenceDirectionFactory
322+
from pymoo.util.ref_dirs.energy_layer import LayerwiseRieszEnergyReferenceDirectionFactory
320323

321324
REFERENCE_DIRECTIONS = [
322325
("(das-dennis|uniform)", UniformReferenceDirectionFactory),
323326
("multi-layer", MultiLayerReferenceDirectionFactory),
324327
("(energy|riesz)", RieszEnergyReferenceDirectionFactory),
328+
("(layer-energy|layer-riesz)", LayerwiseRieszEnergyReferenceDirectionFactory),
325329
("red", ReductionBasedReferenceDirectionFactory)
326330
]
327331

0 commit comments

Comments
 (0)