Skip to content

Commit 6d8cb13

Browse files
author
Julian Blank
committed
BUGFIX: Exception when no feasible solution was found
1 parent d6f35c9 commit 6d8cb13

File tree

6 files changed

+110
-103
lines changed

6 files changed

+110
-103
lines changed

pymoo/model/algorithm.py

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import numpy as np
44

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

5660
def __init__(self,
5761
callback=None,
62+
return_least_infeasible=False,
5863
**kwargs):
5964

6065
# !
@@ -72,6 +77,7 @@ def __init__(self,
7277
# other attributes of the algorithm
7378
self.callback = callback
7479
self.func_display_attrs = None
80+
self.return_least_infeasible = return_least_infeasible
7581

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

159165
# if the algorithm already set the optimum just return it, else filter it by default
160166
if self.opt is None:
161-
opt = filter_optimum(res.pop.copy())
167+
opt = filter_optimum(res.pop.copy(), least_infeasible=self.return_least_infeasible)
162168
else:
163169
opt = self.opt
164170

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

168174
if isinstance(opt, Population):
169175
X, F, CV, G = opt.get("X", "F", "CV", "G")
170-
else:
176+
elif isinstance(opt, Individual):
171177
X, F, CV, G = opt.X, opt.F, opt.CV, opt.G
178+
else:
179+
X, F, CV, G = None, None, None, None
172180

173-
if opt is not None:
174-
res.X, res.F, res.CV, res.G = X, F, CV, G
181+
# set all the individual values
182+
res.X, res.F, res.CV, res.G = X, F, CV, G
175183

176184
# create the result object
177185
res.problem, res.pf = self.problem, self.pf
@@ -247,25 +255,31 @@ def _finalize(self):
247255
pass
248256

249257

250-
def filter_optimum(pop):
258+
def filter_optimum(pop, least_infeasible=False):
251259

252260
# first only choose feasible solutions
253-
pop = pop[pop.collect(lambda ind: ind.feasible)[:, 0]]
261+
ret = pop[pop.collect(lambda ind: ind.feasible)[:, 0]]
254262

255263
# if at least one feasible solution was found
256-
if len(pop) > 0:
264+
if len(ret) > 0:
257265

258266
# then check the objective values
259-
F = pop.get("F")
267+
F = ret.get("F")
260268

261269
if F.shape[1] > 1:
262-
I = NonDominatedSorting().do(pop.get("F"), only_non_dominated_front=True)
263-
pop = pop[I]
270+
I = NonDominatedSorting().do(ret.get("F"), only_non_dominated_front=True)
271+
ret = ret[I]
264272

265273
else:
266-
pop = pop[np.argmin(F)]
274+
ret = ret[np.argmin(F)]
267275

276+
# no feasible solution was found
268277
else:
269-
pop = None
278+
# if flag enable report the least infeasible
279+
if least_infeasible:
280+
ret = pop[np.argmin(pop.get("CV"))]
281+
# otherwise just return none
282+
else:
283+
ret = None
270284

271-
return pop
285+
return ret
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
from pymoo.algorithms.nsga2 import NSGA2
2+
from pymoo.factory import get_problem
3+
from pymoo.optimize import minimize
4+
from pymoo.visualization.scatter import Scatter
5+
6+
7+
def my_callback(algorithm):
8+
disp = algorithm.func_display_attrs(algorithm.problem, algorithm.evaluator, algorithm, algorithm.pf)
9+
print("My Custom Output: ", end='')
10+
algorithm._display(disp)
11+
12+
13+
problem = get_problem("zdt2")
14+
15+
algorithm = NSGA2(pop_size=100, elimate_duplicates=True, callback=my_callback)
16+
17+
res = minimize(problem,
18+
algorithm,
19+
('n_gen', 200),
20+
seed=1,
21+
verbose=False)
22+
23+
plot = Scatter()
24+
plot.add(problem.pareto_front(), plot_type="line", color="black", alpha=0.7)
25+
plot.add(res.F, color="red")
26+
plot.show()
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
from pymoo.algorithms.genetic_algorithm import GeneticAlgorithm
2+
3+
4+
class MemeticAlgorithm(GeneticAlgorithm):
5+
6+
def _next(self):
7+
# do the mating using the current population
8+
self.off = self._mating(self.pop)
9+
10+
################################################################
11+
# Add a local optimization here
12+
################################################################
13+
14+
# evaluate the offspring
15+
self.evaluator.eval(self.problem, self.off, algorithm=self)
16+
17+
# merge the offsprings with the current population
18+
self.pop = self.pop.merge(self.off)
19+
20+
# the do survival selection
21+
self.pop = self.survival.do(self.problem, self.pop, self.pop_size, algorithm=self)

tests/algorithms/test_algorithms.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,11 @@
22

33
import numpy as np
44

5+
from pymoo.algorithms.nsga2 import NSGA2
56
from pymoo.algorithms.nsga2 import nsga2
67
from pymoo.factory import get_problem, Problem, ZDT
8+
from pymoo.factory import get_sampling, get_crossover, get_mutation
9+
from pymoo.factory import get_termination
710
from pymoo.optimize import minimize
811

912

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

36+
def test_no_feasible_solution_found(self):
37+
class MyProblem(Problem):
38+
39+
def __init__(self):
40+
super().__init__(n_var=2,
41+
n_obj=1,
42+
n_constr=36,
43+
xl=np.array([0, 0]),
44+
xu=np.array([100, 100]))
45+
46+
def _evaluate(self, x, out, *args, **kwargs):
47+
f1 = x[:, 0] + x[:, 1]
48+
out["F"] = np.column_stack([f1])
49+
out["G"] = np.ones(len(x))
50+
51+
res = minimize(MyProblem(),
52+
NSGA2(),
53+
("n_gen", 10),
54+
seed=1,
55+
save_history=True)
56+
57+
self.assertEqual(res.X, None)
58+
self.assertEqual(res.F, None)
59+
self.assertEqual(res.G, None)
60+
61+
res = minimize(MyProblem(),
62+
NSGA2(return_least_infeasible=True),
63+
("n_gen", 10),
64+
seed=1,
65+
save_history=True)
66+
67+
self.assertEqual(res.CV, 1)
68+
3369

3470
if __name__ == '__main__':
3571
unittest.main()

tests/scratch/issue.py

Lines changed: 0 additions & 80 deletions
This file was deleted.

tests/scratch/scratch1.py

Lines changed: 0 additions & 10 deletions
This file was deleted.

0 commit comments

Comments
 (0)