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 are

  • res.F: Objective spaces values

  • res.G: Constraint values

  • res.CV: Aggregated constraint violation

  • res.algorithm: Algorithm object which has been iterated over

  • res.opt: The solutions as a Population object.

  • res.pop: The final Population

  • res.history: The history of the algorithm. (only if save_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]])