Skip to content

Commit

Permalink
Improved Documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
Julian Blank committed May 4, 2020
1 parent ee25f26 commit 03942cb
Show file tree
Hide file tree
Showing 23 changed files with 585 additions and 143 deletions.
26 changes: 9 additions & 17 deletions pymoo/algorithms/moead.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,12 @@
from scipy.spatial.distance import cdist

from pymoo.algorithms.genetic_algorithm import GeneticAlgorithm
from pymoo.docs import parse_doc_string
from pymoo.factory import get_decomposition
from pymoo.operators.crossover.simulated_binary_crossover import SimulatedBinaryCrossover
from pymoo.util.display import MultiObjectiveDisplay
from pymoo.util.misc import set_if_none
from pymoo.operators.mutation.polynomial_mutation import PolynomialMutation
from pymoo.operators.sampling.random_sampling import FloatRandomSampling
from pymoo.util.display import MultiObjectiveDisplay
from pymoo.util.misc import set_if_none


# =========================================================================================================
Expand All @@ -28,19 +27,12 @@ def __init__(self,
Parameters
----------
ref_dirs : {ref_dirs}
decomposition : {{ 'auto', 'tchebi', 'pbi' }}
The decomposition approach that should be used. If set to `auto` for two objectives `tchebi` and for more than
two `pbi` will be used.
n_neighbors : int
Number of neighboring reference lines to be used for selection.
prob_neighbor_mating : float
Probability of selecting the parents in the neighborhood.
ref_dirs
n_neighbors
decomposition
prob_neighbor_mating
display
kwargs
"""

self.n_neighbors = n_neighbors
Expand Down Expand Up @@ -130,4 +122,4 @@ def _next(self):
pop[N[I]] = off


parse_doc_string(MOEAD.__init__)
# parse_doc_string(MOEAD.__init__)
3 changes: 2 additions & 1 deletion pymoo/algorithms/so_cmaes.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ def _do(self, problem, evaluator, algorithm):
class CMAES(LocalSearch):

def __init__(self,
x0=None,
sigma=0.5,
parallelize=True,
maxfevals=np.inf,
Expand Down Expand Up @@ -333,7 +334,7 @@ def __init__(self,
for a list of available options.
"""
super().__init__(display=display, **kwargs)
super().__init__(x0=x0, display=display, **kwargs)

self.es = None
self.cma = None
Expand Down
175 changes: 175 additions & 0 deletions pymoo/algorithms/so_direct.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
import numpy as np

from pymoo.algorithms.so_local_search import LocalSearch
from pymoo.model.individual import Individual
from pymoo.model.population import Population
from pymoo.model.problem import Problem
from pymoo.optimize import minimize
from pymoo.problems.single import Himmelblau, Sphere, Rastrigin
from pymoo.util.display import SingleObjectiveDisplay
from pymoo.util.nds.non_dominated_sorting import NonDominatedSorting
from pymoo.util.normalization import normalize, denormalize


def norm_bounds(pop, problem):
nxl = normalize(pop.get("xl"), problem.xl, problem.xu)
nxu = normalize(pop.get("xu"), problem.xl, problem.xu)
return nxl, nxu


def update_bounds(ind, xl, xu, k, delta):
_xl = np.copy(xl)
_xl[k] = ind.X[k] - delta
ind.set("xl", _xl)

_xu = np.copy(xu)
_xu[k] = ind.X[k] + delta
ind.set("xu", _xu)


class DIRECT(LocalSearch):

def __init__(self,
eps=1e-2,
penalty=0.1,
display=SingleObjectiveDisplay(),
**kwargs):
super().__init__(display=display, **kwargs)
self.eps = eps
self.penalty = penalty

def initialize(self, problem, **kwargs):
super().initialize(problem, **kwargs)

xl, xu = problem.bounds()
X = denormalize(0.5 * np.ones(problem.n_var), xl, xu)
x0 = Individual(X=X)
x0.set("xl", xl)
x0.set("xu", xu)
x0.set("depth", 0)
self.x0 = x0

def _initialize(self, **kwargs):
super()._initialize(**kwargs)

def _potential_optimal(self):
pop = self.pop

if len(pop) == 1:
return pop

# get the intervals of each individual
_F, _CV, xl, xu = pop.get("F", "CV", "xl", "xu")
nF = normalize(_F)
F = nF + self.penalty * _CV

# get the length of the interval of each solution
nxl, nxu = norm_bounds(pop, problem)
length = (nxu - nxl) / 2

val = length.max(axis=1)

# (a) non-dominated with respect to interval
obj = np.column_stack([-val, F])
I = NonDominatedSorting().do(obj, only_non_dominated_front=True)
candidates, F, xl, xu, val = pop[I], F[I], xl[I], xu[I], val[I]

# import matplotlib.pyplot as plt
# plt.scatter(obj[:, 0], obj[:, 1])
# plt.scatter(obj[I, 0], obj[I, 1], color="red")
# plt.show()

if len(candidates) == 1:
return candidates

else:
# TODO: The second condition needs to be implemented here. Exact implementation still unclear.

n_max_candidates = 10

if len(candidates) > n_max_candidates:
I = list(np.random.choice(np.arange(len(candidates)), n_max_candidates - 1))
k = np.argmin(F[:, 0])
if k not in I:
I.append(k)
candidates = candidates[I]

return candidates

def _next(self):
# the offspring population to finally evaluate and attach to the population
off = Population()

# find the potential optimal solution in the current population
potential_optimal = self._potential_optimal()

# for each of those solutions execute the division move
for current in potential_optimal:

# find the largest dimension the solution has not been evaluated yet
nxl, nxu = norm_bounds(current, problem)
k = np.argmax(nxu - nxl)

# the delta value to be used to get left and right - this is one sixth of the range
xl, xu = current.get("xl"), current.get("xu")

delta = (xu[k] - xl[k]) / 6

# print(current.X, delta, k, xl, xu)

# create the left individual
left_x = np.copy(current.X)
left_x[k] = xl[k] + delta
left = Individual(X=left_x)

# create the right individual
right_x = np.copy(current.X)
right_x[k] = xu[k] - delta
right = Individual(X=right_x)

# update the boundaries for all the points accordingly
for ind in [current, left, right]:
update_bounds(ind, xl, xu, k, delta)

# create the offspring population, evaluate and attach to current population
_off = Population.create(left, right)
_off.set("depth", current.get("depth") + 1)

off = Population.merge(off, _off)

# evaluate the offsprings
self.evaluator.eval(self.problem, off, algorithm=self)

# print(off.get("X"))

# add the offsprings to the population
self.pop = Population.merge(self.pop, off)


class ExSwarm(Problem):

def __init__(self):
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]))

def _evaluate(self, x, out, *args, **kwargs):
v1 = 20 * x[:, 0]
v2 = 20 * x[:, 1]
out["F"] = -np.sin(v1 / np.pi) ** 2. * np.sin(v2 / np.pi) ** 2. * (abs(v1) + abs(v2) + 0.1 * (v1 + v2)) + (
v1 * v1 + v2 * v2) / 30


if __name__ == '__main__':
problem = ExSwarm()
problem = Himmelblau()
# problem = Sphere(n_var=100, opt=0.2 * np.ones(100))
problem = Rastrigin(n_var=10)
problem.xl *= 1.5

# problem = get_problem("g02")
algorithm = DIRECT()
# algorithm = GA()

ret = minimize(problem,
algorithm,
("n_iter", 1000),
verbose=True)
20 changes: 18 additions & 2 deletions pymoo/algorithms/so_pattern_search.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,20 +43,30 @@ def do_continue(self, algorithm):


class PatternSearch(LocalSearch):

def __init__(self,
explr_delta=0.25,
explr_rho=0.5,
pattern_step=2,
eps=1e-5,
display=PatternSearchDisplay(),
**kwargs):
"""
Parameters
----------
explr_delta
explr_rho
pattern_step
eps
display
kwargs
"""

super().__init__(display=display, **kwargs)
self.explr_rho = explr_rho
self.pattern_step = pattern_step
self.explr_delta = explr_delta
self.default_termination = PatternSearchTermination(eps=eps, x_tol=1e-6, f_tol=1e-6, nth_gen=1, n_last=2)
self.default_termination = PatternSearchTermination(eps=eps, x_tol=1e-6, f_tol=1e-6, nth_gen=1, n_last=30)

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

# we can break if we did not improve
if not is_better(explr, self._current):
break

# else also check if we are terminating - otherwise this loop might run far too long
self._set_optimum()
if self.termination.has_terminated(self):
break

self._previous, self._current = self._current, explr

self.explr_delta *= self.explr_rho
Expand Down
14 changes: 7 additions & 7 deletions pymoo/docs.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,40 +100,40 @@

"tight_layout": """bool
Whether tight layout should be used.
""",
""",

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

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

"axis_style": """dict
Most of the plots consists of an axis. The style of the axis, e.g. color, alpha, ..., can be changed to
further modify the plot appealing.
""",
""",

"cmap": """colormap
For some plots different kind of colors are used. The colormap can be changed to modify the color sequence
for the plots.
""",
""",

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

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

}

Expand Down
6 changes: 5 additions & 1 deletion pymoo/factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
import re

from pymoo.configuration import Configuration

from pymoo.problems.many import *
from pymoo.problems.multi import *
from pymoo.problems.single import *
Expand All @@ -19,6 +18,7 @@
# =========================================================================================================



def get_from_list(l, name, args, kwargs):
i = None

Expand Down Expand Up @@ -67,12 +67,14 @@ def get_algorithm_options():
from pymoo.algorithms.so_nelder_mead import NelderMead
from pymoo.algorithms.so_cmaes import CMAES
from pymoo.algorithms.so_brkga import BRKGA
from pymoo.algorithms.so_pattern_search import PatternSearch

ALGORITHMS = [
("ga", GA),
("brkga", BRKGA),
("de", DE),
("nelder-mead", NelderMead),
("pattern-search", PatternSearch),
("cmaes", CMAES),
("nsga2", NSGA2),
("rnsga2", RNSGA2),
Expand Down Expand Up @@ -317,11 +319,13 @@ def get_reference_direction_options():
from pymoo.util.reference_direction import MultiLayerReferenceDirectionFactory
from pymoo.util.ref_dirs.reduction import ReductionBasedReferenceDirectionFactory
from pymoo.util.ref_dirs.energy import RieszEnergyReferenceDirectionFactory
from pymoo.util.ref_dirs.energy_layer import LayerwiseRieszEnergyReferenceDirectionFactory

REFERENCE_DIRECTIONS = [
("(das-dennis|uniform)", UniformReferenceDirectionFactory),
("multi-layer", MultiLayerReferenceDirectionFactory),
("(energy|riesz)", RieszEnergyReferenceDirectionFactory),
("(layer-energy|layer-riesz)", LayerwiseRieszEnergyReferenceDirectionFactory),
("red", ReductionBasedReferenceDirectionFactory)
]

Expand Down
Loading

0 comments on commit 03942cb

Please sign in to comment.