pymoo
Latest Version: pymoo==0.3.2

ResultsΒΆ

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.so_genetic_algorithm import GA
from pymoo.factory 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)

In this single-objective optimization problem, there exists a single best solution that was found. The result contains directly 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

  • res.pop final population object

  • res.history history of algorithm object. (only if save_history has been enabled during the algorithm initialization)

Note, when the minimize function is called a deep copy of the algorithm object is created. This ensures that two independent runs with the same algorithm and same random seed have the same results without any side effects.

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 to return vectors or matrices based on the provided properties.

[2]:
res.X, res.F, res.G, res.CV
[2]:
(array([0.64014154, 0.48263145, 0.49898381, 0.56769556, 0.47794749,
        0.67105917, 0.49758836, 0.47342642, 0.48633132, 0.51630811]),
 array([0.05543735]),
 None,
 array([0.]))
[3]:
res.algorithm, res.pop
[3]:
(<pymoo.algorithms.so_genetic_algorithm.GA at 0x11e673e90>,
 Population([<pymoo.model.individual.Individual object at 0x11e7c2050>,
             <pymoo.model.individual.Individual object at 0x11e7c23d0>,
             <pymoo.model.individual.Individual object at 0x11e67da10>,
             <pymoo.model.individual.Individual object at 0x11e7c2210>,
             <pymoo.model.individual.Individual object at 0x11e7c2350>],
            dtype=object))

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 to return vectors or matrices based on the provided properties.

[4]:
res.pop.get("X"), res.pop.get("F")
[4]:
(array([[0.64014154, 0.48263145, 0.49898381, 0.56769556, 0.47794749,
         0.67105917, 0.49758836, 0.47342642, 0.48633132, 0.51630811],
        [0.64014154, 0.47726981, 0.53101528, 0.56769556, 0.47312045,
         0.67105917, 0.49758836, 0.46866063, 0.48633132, 0.51630811],
        [0.64014154, 0.42882383, 0.54200361, 0.56769556, 0.47312045,
         0.67105917, 0.49758836, 0.46866063, 0.48633132, 0.51630811],
        [0.64014154, 0.48263145, 0.58416635, 0.56769556, 0.47819705,
         0.67105917, 0.49758836, 0.46866063, 0.48633132, 0.51630811],
        [0.64014154, 0.48263145, 0.58416635, 0.56769556, 0.47788436,
         0.67105917, 0.49758836, 0.4685275 , 0.48633132, 0.51630811]]),
 array([[0.05543735],
        [0.05712546],
        [0.0624772 ],
        [0.06278535],
        [0.06280745]]))

In this run, the problem did not have any constraints and therefore 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:

[5]:
problem = get_problem("g01")
algorithm = GA(pop_size=5)
res = minimize(problem,
               algorithm,
               ('n_gen', 5),
               verbose=True,
               seed=1)
======================================================================
n_gen | n_eval  | cv (min/avg)                | favg  | fopt
======================================================================
1     | 5       | 1.350271E+02 / 5.475700E+02 | -     | -
2     | 10      | 1.350271E+02 / 3.607562E+02 | -     | -
3     | 15      | 1.350271E+02 / 2.259881E+02 | -     | -
4     | 20      | 1.350271E+02 / 1.565811E+02 | -     | -
5     | 25      | 3.310368E+01 / 1.096916E+02 | -     | -
[6]:
res.X, res.F, res.G, res.CV
[6]:
(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 in this case the least feasible solution should be returned, the flag return_least_infeasible needs to be enabled:

[7]:
problem = get_problem("g01")
algorithm = GA(pop_size=5, return_least_infeasible=True)
res = minimize(problem,
               algorithm,
               ('n_gen', 5),
               verbose=True,
               seed=1)
======================================================================
n_gen | n_eval  | cv (min/avg)                | favg  | fopt
======================================================================
1     | 5       | 1.350271E+02 / 5.475700E+02 | -     | -
2     | 10      | 1.350271E+02 / 3.607562E+02 | -     | -
3     | 15      | 1.350271E+02 / 2.259881E+02 | -     | -
4     | 20      | 1.350271E+02 / 1.565811E+02 | -     | -
5     | 25      | 3.310368E+01 / 1.096916E+02 | -     | -
[8]:
res.X, res.F, res.G, res.CV
[8]:
(array([ 0.79663211,  0.90276502,  0.74816565,  0.43310131,  0.78930018,
         0.0717483 ,  0.44789353,  0.9028263 ,  0.10182177,  0.23918845,
        13.00285721,  1.93669579,  0.67883553]),
 array([-14.75252652]),
 array([ 6.64083991, -4.73452024,  8.24141434, -6.13386842,  5.78073708,
        -4.04862945, -1.41631436, 12.41146709,  0.02922141]),
 array([33.10367983]))

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 objective, the result object has the same structure but res.X, res.F, res.G, res.CV are a set of non-dominated solutions instead of a single one.

[9]:
from pymoo.algorithms.nsga2 import NSGA2

problem = get_problem("zdt2")
algorithm = NSGA2()
res = minimize(problem,
               algorithm,
               ('n_gen', 10),
               seed=1)
[10]:
res.F
[10]:
array([[6.59972937e-04, 3.46313095e+00],
       [6.70781056e-02, 2.87596946e+00],
       [4.42856882e-05, 3.95330114e+00],
       [1.31990677e-02, 3.11337727e+00],
       [2.50016243e-04, 3.56762952e+00],
       [1.53940423e-03, 3.41822087e+00],
       [6.26189621e-04, 3.56293085e+00],
       [1.81927730e-03, 3.13899781e+00],
       [1.78852173e-04, 3.84385603e+00]])