ResultΒΆ
After an algorithm has been executed a result object is returned. In the following, single- and multi-objective runs with and without constraints are shown and the corresponding Result
object is explained:
[1]:
from pymoo.algorithms.soo.nonconvex.ga import GA
from pymoo.problems import get_problem
from pymoo.optimize import minimize
problem = get_problem("sphere")
algorithm = GA(pop_size=5)
res = minimize(problem,
algorithm,
('n_gen', 30),
seed=1)
After an algorithm has been executed, a result object is returned. In the following, single- and multi-objective runs with and without constraints are shown, and the corresponding Result
object is explained:
In this single-objective optimization problem, there exists a single best solution that was found. The result directly contains the best-found values in the corresponding spaces.
res.X
: Design space values areres.F
: Objective spaces valuesres.G
: Constraint valuesres.CV
: Aggregated constraint violationres.algorithm
: Algorithm object which has been iterated overres.opt
: The solutions as aPopulation
object.res.pop
: The final Populationres.history
: The history of the algorithm. (only ifsave_history
has been enabled during the algorithm initialization)res.exec_time
: The time required to run the algorithm
[2]:
res.X
[2]:
array([0.5340465 , 0.50205636, 0.34165826, 0.41981651, 0.46178424,
0.5639494 , 0.40332476, 0.55896734, 0.39803397, 0.33708973])
[3]:
res.F
[3]:
array([0.08797494])
[4]:
res.G
[4]:
array([], dtype=float64)
[5]:
res.CV
[5]:
array([0.])
[6]:
res.algorithm
[6]:
<pymoo.algorithms.soo.nonconvex.ga.GA at 0x110275fa0>
[7]:
pop = res.pop
The values from the final population can be extracted by using the get
method. The population object is used internally and store information for each individual. The get
method allows returning vectors or matrices based on the provided properties.
[8]:
pop.get("X")
[8]:
array([[0.5340465 , 0.50205636, 0.34165826, 0.41981651, 0.46178424,
0.5639494 , 0.40332476, 0.55896734, 0.39803397, 0.33708973],
[0.5340465 , 0.50205636, 0.34165826, 0.41981651, 0.46302044,
0.5639494 , 0.40332476, 0.55896734, 0.40619667, 0.3168352 ],
[0.5340465 , 0.50205636, 0.34165826, 0.41981651, 0.46412638,
0.5639494 , 0.40332476, 0.55896734, 0.39803397, 0.31733429],
[0.5340465 , 0.50205636, 0.34165826, 0.41981651, 0.46304329,
0.5639494 , 0.40332476, 0.55896734, 0.39803397, 0.3168352 ],
[0.5340465 , 0.50205636, 0.34165826, 0.41981651, 0.46302044,
0.5639494 , 0.40332476, 0.55896734, 0.39803397, 0.3168352 ]])
[9]:
pop.get("F")
[9]:
array([[0.08797494],
[0.09329356],
[0.09462842],
[0.09488988],
[0.09489157]])
In this run, the problem did not have any constraints, and res.G
evaluated to None
. Also, note that res.CV
will always be set to 0
, no matter if the problem has constraints or not.
Let us consider a problem that has, in fact, constraints:
[10]:
problem = get_problem("g1")
algorithm = GA(pop_size=5)
res = minimize(problem,
algorithm,
('n_gen', 5),
verbose=True,
seed=1)
=================================================================================================
n_gen | n_eval | cv_min | cv_avg | f_avg | f_min | f_gap
=================================================================================================
1 | 5 | 1.350271E+02 | 5.475700E+02 | - | - | -
2 | 10 | 1.350271E+02 | 3.326996E+02 | - | - | -
3 | 15 | 1.340021E+02 | 2.054140E+02 | - | - | -
4 | 20 | 1.340021E+02 | 1.344295E+02 | - | - | -
5 | 25 | 1.148974E+02 | 1.301816E+02 | - | - | -
[11]:
res.X, res.F, res.G, res.CV
[11]:
(None, None, None, None)
Here, the algorithm was not able to find any feasible solution in 5 generations. Therefore, all values contained in the results are equals to None
. If the least feasible solution should be returned when no feasible solution was found, the flag return_least_infeasible
can be enabled:
[12]:
problem = get_problem("g1")
algorithm = GA(pop_size=5)
res = minimize(problem,
algorithm,
('n_gen', 5),
verbose=True,
return_least_infeasible=True,
seed=1)
=================================================================================================
n_gen | n_eval | cv_min | cv_avg | f_avg | f_min | f_gap
=================================================================================================
1 | 5 | 1.350271E+02 | 5.475700E+02 | - | - | -
2 | 10 | 1.350271E+02 | 3.326996E+02 | - | - | -
3 | 15 | 1.340021E+02 | 2.054140E+02 | - | - | -
4 | 20 | 1.340021E+02 | 1.344295E+02 | - | - | -
5 | 25 | 1.148974E+02 | 1.301816E+02 | - | - | -
[13]:
res.X, res.F, res.G, res.CV
[13]:
(array([ 0.75014431, 0.9351402 , 0.74816565, 0.26852842, 0.72881453,
0.11094439, 0.44789353, 0.9085955 , 0.29361415, 23.65973322,
13.00285721, 1.9656022 , 0.67883553]),
array([-38.63231253]),
array([30.03315945, 18.62195536, 8.33507111, 17.6585787 , 5.52173564,
-4.01972304, 22.39386185, 12.33307491, -0.14520295]),
array([114.89743704]))
We have made this design decision, because an infeasible solution can often not be considered as a solution of the optimization problem. Therefore, having a solution equals to None
indicates the fact no feasible solution has been found.
If the problem has multiple objectives, the result object has the same structure but res.X
, res.F
, res .G
, res.CV
is a set of non-dominated solutions instead of a single one.
[14]:
from pymoo.algorithms.moo.nsga2 import NSGA2
problem = get_problem("zdt2")
algorithm = NSGA2()
res = minimize(problem,
algorithm,
('n_gen', 10),
seed=1)
[15]:
res.F
[15]:
array([[1.63139466e-04, 3.32756054e+00],
[9.28340571e-01, 2.55735495e+00],
[1.94284053e-02, 3.21756760e+00],
[5.98649978e-02, 2.89294302e+00]])