Skip to content

Commit

Permalink
Minor modifications
Browse files Browse the repository at this point in the history
  • Loading branch information
blankjul committed Dec 22, 2017
1 parent 6c5cd25 commit d5976b1
Show file tree
Hide file tree
Showing 33 changed files with 567 additions and 238 deletions.
Binary file modified .DS_Store
Binary file not shown.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
.DS_Store

# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
Expand Down
1 change: 1 addition & 0 deletions pymoo/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
__all__ = ["algorithms", "model", "operators", "performance", "postprocess", "problems", "rand", "util"]
50 changes: 47 additions & 3 deletions pymoo/algorithms/NSGAII.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from pymoo.algorithms.genetic_algorithm import GeneticAlgorithm
from pymoo.model import random
from pymoo.operators.crossover.bin_uniform_crossover import BinaryUniformCrossover
from pymoo.operators.crossover.real_simulated_binary_crossover import SimulatedBinaryCrossover
from pymoo.operators.mutation.bin_bitflip_mutation import BinaryBitflipMutation
Expand All @@ -7,16 +8,17 @@
from pymoo.operators.sampling.real_random_sampling import RealRandomSampling
from pymoo.operators.selection.tournament_selection import TournamentSelection
from pymoo.operators.survival.rank_and_crowding import RankAndCrowdingSurvival
from pymoo.util.dominator import Dominator


class NSGAII(GeneticAlgorithm):

def __init__(self, var_type, pop_size=100, verbose=1):

if var_type == "real":
super().__init__(
pop_size=pop_size,
sampling=RealRandomSampling(),
selection=TournamentSelection(),
selection=TournamentSelection(f_comp=comp_by_rank_and_crowding),
crossover=SimulatedBinaryCrossover(),
mutation=PolynomialMutation(),
survival=RankAndCrowdingSurvival(),
Expand All @@ -25,10 +27,52 @@ def __init__(self, var_type, pop_size=100, verbose=1):
elif var_type == "binary":
super().__init__(
sampling=BinaryRandomSampling(),
selection=TournamentSelection(),
selection=TournamentSelection(f_comp=comp_by_rank_and_crowding),
crossover=BinaryUniformCrossover(),
mutation=BinaryBitflipMutation(),
survival=RankAndCrowdingSurvival(),
verbose=verbose,
eliminate_duplicates=True
)


def comp_by_rank_and_crowding(pop, indices, data):
if len(indices) != 2:
raise ValueError("Only implemented for binary tournament!")

first = indices[0]
second = indices[1]

if data.rank[first] < data.rank[second]:
return first
elif data.rank[second] < data.rank[first]:
return second
else:
if data.crowding[first] > data.crowding[second]:
return first
elif data.crowding[second] > data.crowding[first]:
return second
else:
return indices[random.randint(0, 2)]


def comp_by_dom_and_crowding(pop, indices, data):
if len(indices) != 2:
raise ValueError("Only implemented for binary tournament!")

first = indices[0]
second = indices[1]

rel = Dominator.get_relation(pop.F[first, :], pop.F[second, :])

if rel == 1:
return first
elif rel == -1:
return second
else:
if data.crowding[first] > data.crowding[second]:
return first
elif data.crowding[second] > data.crowding[first]:
return second
else:
return indices[random.randint(0, 2)]
33 changes: 24 additions & 9 deletions pymoo/algorithms/NSGAIII.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,40 @@
import numpy as np

from pymoo.algorithms.genetic_algorithm import GeneticAlgorithm
from pymoo.operators.crossover.bin_uniform_crossover import BinaryUniformCrossover
from pymoo.operators.crossover.real_simulated_binary_crossover import SimulatedBinaryCrossover
from pymoo.operators.mutation.bin_bitflip_mutation import BinaryBitflipMutation
from pymoo.operators.mutation.real_polynomial_mutation import PolynomialMutation
from pymoo.operators.sampling.bin_random_sampling import BinaryRandomSampling
from pymoo.operators.sampling.real_random_sampling import RealRandomSampling
from pymoo.operators.selection.tournament_selection import TournamentSelection
from pymoo.operators.survival.reference_line_survival import ReferenceLineSurvival


class NSGAIII(GeneticAlgorithm):
def __init__(self, pop_size=100, verbose=1):
def __init__(self, var_type, pop_size=100, verbose=1):

self.ref_lines = self.create_ref_lines()
super().__init__(
pop_size=pop_size,
sampling=RealRandomSampling(),
selection=TournamentSelection(),
crossover=SimulatedBinaryCrossover(),
mutation=PolynomialMutation(),
survival=ReferenceLineSurvival(self.ref_lines),
verbose=verbose)
if var_type == "real":
super().__init__(
pop_size=pop_size,
sampling=RealRandomSampling(),
selection=TournamentSelection(),
crossover=SimulatedBinaryCrossover(),
mutation=PolynomialMutation(),
survival=ReferenceLineSurvival(self.ref_lines),
verbose=verbose
)
elif var_type == "binary":
super().__init__(
sampling=BinaryRandomSampling(),
selection=TournamentSelection(),
crossover=BinaryUniformCrossover(),
mutation=BinaryBitflipMutation(),
survival=ReferenceLineSurvival(self.ref_lines),
verbose=verbose,
eliminate_duplicates=True
)

def create_ref_lines(self):
n_refs = 30
Expand Down
67 changes: 31 additions & 36 deletions pymoo/algorithms/genetic_algorithm.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,22 +15,31 @@ class GeneticAlgorithm(Algorithm):
Attributes
----------
pop_size: int
The population size to be used for the genetic algorithm.
sampling : class
The sampling implementation to create the initial population.
selection : class
A class to select the parents for the crossover
crossover : class
The crossover to be performed on parents.
mutation : class
The mutation that will be performed for each child after the crossover
survival : class
This class selects the individuals to survive for the next generation
eliminate_duplicates : bool
If this flag is set no duplicates are allowed in the population (mostly likely only used for binary or discrete)
verbose : int
If larger than zero output is provided. (verbose=1 means some output, verbose=2 details for debugging)
callback : func
A callback function can be passed that is executed every generation. The parameters for the function
are the algorithm itself, the number of evaluations so far and the current population.
Expand Down Expand Up @@ -62,19 +71,13 @@ def __init__(self,
self.verbose = verbose
self.callback = callback

def _initialize(self):
pass

def _solve(self, problem, evaluator):

# initialize stuff before the initial population is created
self._initialize()

# create the population according to the factoring strategy
pop = Population()
pop.X = self.sampling.sample(problem, self.pop_size)
pop.X = self.sampling.sample(problem, self.pop_size, self)
pop.F, pop.G = evaluator.eval(problem, pop.X)
pop = self.survival.do(pop, self.pop_size)
pop = self.survival.do(pop, self.pop_size, self)

# setup initial generation
n_gen = 0
Expand All @@ -86,46 +89,38 @@ def _solve(self, problem, evaluator):
self._do_each_generation(n_gen, evaluator, pop)
n_gen += 1

# create the offspring generation
offsprings = self._get_offsprings(problem, evaluator, pop)
# initialize selection and offspring methods
off = Population()
off.X = np.full((self.pop_size, problem.n_var), np.inf)
self.selection.set_population(pop, self)

n_off = 0
n_parents = self.crossover.n_parents
n_children = self.crossover.n_children

while n_off < self.pop_size:
parents = self.selection.next(n_parents)
X = self.crossover.do(problem, pop.X[parents, :], self)

off.X[n_off:min(n_off + n_children, self.pop_size)] = X
n_off = n_off + X.shape[0]

off.X = self.mutation.do(problem, off.X)
off.F, off.G = evaluator.eval(problem, off.X)

# merge the population
pop.merge(offsprings)
pop.merge(off)

# eliminate all duplicates in the population
if self.eliminate_duplicates:
pop.filter(unique_rows(pop.X))

# truncate the population
pop = self.survival.do(pop, self.pop_size)
pop = self.survival.do(pop, self.pop_size, self)

self._do_each_generation(n_gen, evaluator, pop)

return pop.X, pop.F, pop.G

def _get_offsprings(self, problem, evaluator, pop):

# initialize selection and offspring methods
off = Population()
off.X = np.zeros((self.pop_size, problem.n_var))
self.selection._initialize(pop)

n_off = 0
n_parents = self.crossover.n_parents
n_children = self.crossover.n_children

while n_off < self.pop_size:
parents = self.selection.next(n_parents)
X = self.crossover.do(problem, pop.X[parents, :])

off.X[n_off:min(n_off + n_children, self.pop_size)] = X
n_off = n_off + len(X)

off.X = self.mutation.do(problem, off.X)
off.F, off.G = evaluator.eval(problem, off.X)

return off

def _do_each_generation(self, n_gen, evaluator, pop):
if self.verbose > 0:
print('gen = %d' % (n_gen + 1))
Expand Down
2 changes: 1 addition & 1 deletion pymoo/configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@


class Configuration:
EPS = 1e-20
EPS = 1e-30
BENCHMARK_DIR = '/Users/julesy/benchmark/'
rand = MyRandomGenerator()
#rand = NumpyRandomGenerator()
Expand Down
72 changes: 57 additions & 15 deletions pymoo/model/algorithm.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,57 @@
from abc import abstractmethod

import numpy

from pymoo.model import random
from pymoo.model.evaluator import Evaluator
from pymoo.util.misc import calc_constraint_violation
from pymoo.util.non_dominated_rank import NonDominatedRank


class Algorithm:
"""
This class represents the abstract class for any algorithm to be implemented. Most importantly it
provides the solve method that is used to optimize a given problem.
"""

def solve(self, problem, evaluator, seed=1, return_only_feasible=True, return_only_non_dominated=True):
"""
Solve a given problem by a given evaluator. The evaluator determines the termination condition and
can either have a maximum budget, hypervolume or whatever. The problem can be any problem the algorithm
is able to solve.
Parameters
----------
problem: class
Problem to be solved by the algorithm
evaluator: class
object that evaluates and saves the number of evaluations and determines the stopping condition
seed: int
Random seed for this run. Before the algorithm starts this seed is set.
return_only_feasible:
If true, only feasible solutions are returned.
return_only_non_dominated
If true, only the non dominated solutions are returned. Otherwise, it might be - dependend on the
algorithm - the final population
Returns
-------
X: matrix
Design space
F: matrix
Objective space
G: matrix
Constraint space
"""

# set the random seed
random.seed(seed)
Expand All @@ -18,24 +60,24 @@ def solve(self, problem, evaluator, seed=1, return_only_feasible=True, return_on
evaluator = Evaluator(evaluator)

# call the algorithm to solve the problem
x, f, g = self._solve(problem, evaluator)
X, F, G = self._solve(problem, evaluator)

if return_only_feasible:
if g is not None and g.shape[0] == len(f) and g.shape[1] > 0:
b = numpy.array(numpy.where(numpy.sum(g, axis=1) <= 0))[0]
x = x[b, :]
f = f[b, :]
if g is not None:
g = g[b, :]
if G is not None and G.shape[0] == len(F) and G.shape[1] > 0:
cv = calc_constraint_violation(G)
X = X[cv, :]
F = F[cv, :]
if G is not None:
G = G[cv, :]

if return_only_non_dominated:
idx_non_dom = NonDominatedRank.calc_as_fronts(f,g)[0]
x = x[idx_non_dom, :]
f = f[idx_non_dom, :]
if g is not None:
g = g[idx_non_dom, :]
idx_non_dom = NonDominatedRank.calc_as_fronts(F,G)[0]
X = X[idx_non_dom, :]
F = F[idx_non_dom, :]
if G is not None:
G = G[idx_non_dom, :]

return x, f, g
return X, F, G

@abstractmethod
def _solve(self, problem, evaluator):
Expand Down
Loading

0 comments on commit d5976b1

Please sign in to comment.