Skip to content

Commit

Permalink
BUGFIX: Exception when no feasible solution was found
Browse files Browse the repository at this point in the history
  • Loading branch information
Julian Blank committed Oct 4, 2019
1 parent d6f35c9 commit 6d8cb13
Show file tree
Hide file tree
Showing 6 changed files with 110 additions and 103 deletions.
40 changes: 27 additions & 13 deletions pymoo/model/algorithm.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import numpy as np

from pymoo.model.evaluator import Evaluator
from pymoo.model.individual import Individual
from pymoo.model.population import Population
from pymoo.model.result import Result
from pymoo.util.function_loader import FunctionLoader
Expand Down Expand Up @@ -47,6 +48,9 @@ def callback(algorithm):
pf : np.array
The Pareto-front for the given problem. If provided performance metrics are printed during execution.
return_least_infeasible : bool
Whether the algorithm should return the least infeasible solution, if no solution was found.
evaluator : class
The evaluator which can be used to make modifications before calling the evaluate function of a problem.
Expand All @@ -55,6 +59,7 @@ def callback(algorithm):

def __init__(self,
callback=None,
return_least_infeasible=False,
**kwargs):

# !
Expand All @@ -72,6 +77,7 @@ def __init__(self,
# other attributes of the algorithm
self.callback = callback
self.func_display_attrs = None
self.return_least_infeasible = return_least_infeasible

# !
# Attributes to be set later on for each problem run
Expand Down Expand Up @@ -158,7 +164,7 @@ def solve(self):

# if the algorithm already set the optimum just return it, else filter it by default
if self.opt is None:
opt = filter_optimum(res.pop.copy())
opt = filter_optimum(res.pop.copy(), least_infeasible=self.return_least_infeasible)
else:
opt = self.opt

Expand All @@ -167,11 +173,13 @@ def solve(self):

if isinstance(opt, Population):
X, F, CV, G = opt.get("X", "F", "CV", "G")
else:
elif isinstance(opt, Individual):
X, F, CV, G = opt.X, opt.F, opt.CV, opt.G
else:
X, F, CV, G = None, None, None, None

if opt is not None:
res.X, res.F, res.CV, res.G = X, F, CV, G
# set all the individual values
res.X, res.F, res.CV, res.G = X, F, CV, G

# create the result object
res.problem, res.pf = self.problem, self.pf
Expand Down Expand Up @@ -247,25 +255,31 @@ def _finalize(self):
pass


def filter_optimum(pop):
def filter_optimum(pop, least_infeasible=False):

# first only choose feasible solutions
pop = pop[pop.collect(lambda ind: ind.feasible)[:, 0]]
ret = pop[pop.collect(lambda ind: ind.feasible)[:, 0]]

# if at least one feasible solution was found
if len(pop) > 0:
if len(ret) > 0:

# then check the objective values
F = pop.get("F")
F = ret.get("F")

if F.shape[1] > 1:
I = NonDominatedSorting().do(pop.get("F"), only_non_dominated_front=True)
pop = pop[I]
I = NonDominatedSorting().do(ret.get("F"), only_non_dominated_front=True)
ret = ret[I]

else:
pop = pop[np.argmin(F)]
ret = ret[np.argmin(F)]

# no feasible solution was found
else:
pop = None
# if flag enable report the least infeasible
if least_infeasible:
ret = pop[np.argmin(pop.get("CV"))]
# otherwise just return none
else:
ret = None

return pop
return ret
26 changes: 26 additions & 0 deletions pymoo/usage/algorithms/usage_custom_output.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
from pymoo.algorithms.nsga2 import NSGA2
from pymoo.factory import get_problem
from pymoo.optimize import minimize
from pymoo.visualization.scatter import Scatter


def my_callback(algorithm):
disp = algorithm.func_display_attrs(algorithm.problem, algorithm.evaluator, algorithm, algorithm.pf)
print("My Custom Output: ", end='')
algorithm._display(disp)


problem = get_problem("zdt2")

algorithm = NSGA2(pop_size=100, elimate_duplicates=True, callback=my_callback)

res = minimize(problem,
algorithm,
('n_gen', 200),
seed=1,
verbose=False)

plot = Scatter()
plot.add(problem.pareto_front(), plot_type="line", color="black", alpha=0.7)
plot.add(res.F, color="red")
plot.show()
21 changes: 21 additions & 0 deletions pymoo/usage/algorithms/usage_memetic_algorithm.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from pymoo.algorithms.genetic_algorithm import GeneticAlgorithm


class MemeticAlgorithm(GeneticAlgorithm):

def _next(self):
# do the mating using the current population
self.off = self._mating(self.pop)

################################################################
# Add a local optimization here
################################################################

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

# merge the offsprings with the current population
self.pop = self.pop.merge(self.off)

# the do survival selection
self.pop = self.survival.do(self.problem, self.pop, self.pop_size, algorithm=self)
36 changes: 36 additions & 0 deletions tests/algorithms/test_algorithms.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@

import numpy as np

from pymoo.algorithms.nsga2 import NSGA2
from pymoo.algorithms.nsga2 import nsga2
from pymoo.factory import get_problem, Problem, ZDT
from pymoo.factory import get_sampling, get_crossover, get_mutation
from pymoo.factory import get_termination
from pymoo.optimize import minimize


Expand All @@ -30,6 +33,39 @@ def _evaluate(self, x, out, *args, **kwargs):
algorithm = nsga2(pop_size=100, elimate_duplicates=True)
minimize(ZDT1NoPF(), algorithm, ('n_gen', 20), seed=1, verbose=True)

def test_no_feasible_solution_found(self):
class MyProblem(Problem):

def __init__(self):
super().__init__(n_var=2,
n_obj=1,
n_constr=36,
xl=np.array([0, 0]),
xu=np.array([100, 100]))

def _evaluate(self, x, out, *args, **kwargs):
f1 = x[:, 0] + x[:, 1]
out["F"] = np.column_stack([f1])
out["G"] = np.ones(len(x))

res = minimize(MyProblem(),
NSGA2(),
("n_gen", 10),
seed=1,
save_history=True)

self.assertEqual(res.X, None)
self.assertEqual(res.F, None)
self.assertEqual(res.G, None)

res = minimize(MyProblem(),
NSGA2(return_least_infeasible=True),
("n_gen", 10),
seed=1,
save_history=True)

self.assertEqual(res.CV, 1)


if __name__ == '__main__':
unittest.main()
80 changes: 0 additions & 80 deletions tests/scratch/issue.py

This file was deleted.

10 changes: 0 additions & 10 deletions tests/scratch/scratch1.py

This file was deleted.

0 comments on commit 6d8cb13

Please sign in to comment.