pymoode: Differential Evolution in Python

Solve single- and multi-objective optimization problems using Differential Evolution algorithms

Bruno Scalia C. F. Leite
Towards Data Science

--

Photo by Brendan Church on Unsplash

Differential evolution (DE) (Storn & Price, 1997) was originally designed for scalar objective optimization. However, because of its simple implementation and efficient problem-solving quality, DE has been modified in different ways to solve multi-objective optimization problems.

Throughout this article, we will see the algorithms and operators available in the Python package pymoode with applications to single-, multi-, and many-objective optimization problems. It is available on PyPI, so any Python user can install it with the simple command line:

pip install pymoode

The package was written using pymoo basic structure (Blank & Deb, 2020), which is effective, easy to use, and gives margin to high customization when desired or needed by the user. I strongly recommend checking its documentation and available features. Julian Blank has also written this great article on Medium with an overview of Genetic Algorithms in which he presents some features of pymoo.

The next sections will cover the following topics.

  1. pymoode: Algorithms and additional operators
  2. DE: Overview and control parameters
  3. Defining a problem in pymoo
  4. Single-objective DE
  5. A brief introduction to multi-objective optimization
  6. Multi-objective algorithms: GDE3 and NSDE
  7. Improving solutions in Bi-objective problems
  8. Many-objective algorithms: NSDE-R and GDE3-MNN
  9. Applications and perspective
  10. Conclusions
  11. Acknowledgements
  12. References

I hope you all can have a good read and be successful applying pymoode to some projects of yours! Those interested can follow all implementation steps along with this example notebook. The package is also available on GitHub.

If you are unfamiliar with Nonlinear Programming or Differential Evolution, I suggest reading my previous articles to better understand the concepts herein presented.

Although previously we were in pursuit of a single objective, in this article we have many directions to follow… Simultaneously!

To provide the readers with great navigation throughout the different sections, a “Back to contents” link is available at the end of each section to scroll the page back to the list of contents.

pymoode: Algorithms and additional operators

The purpose of pymoode is to provide an extension of the algorithms available in pymoo with a focus on Differential Evolution variants. Therefore, the algorithms will share some common features amongst themselves of DE reproduction operators. Additionally, I have implemented some survival operators not yet available in pymoo providing more options to the user. These algorithms and operators might be incorporated by future versions of pymoo.

The package can be summarized by the following tree structure:

pymoode
├───algorithms
│ ├───DE
│ ├───GDE3
│ ├───NSDE
│ └───NSDER
├───survival
│ ├───RankAndCrowding
│ └───ConstrRankAndCrowding
└───performance
└───SpacingIndicator

The algorithms available are:

  • DE: Differential Evolution for single-objective problems proposed by Storn & Price (1997). Other features later implemented are also present, such as dither, jitter, selection variants, and crossover strategies. For details see Price et al. (2005).
  • NSDE: Non-dominated Sorting Differential Evolution, a multi-objective algorithm that combines DE mutation and crossover operators to NSGA-II (Deb et al., 2002) survival.
  • GDE3: Generalized Differential Evolution 3, a multi-objective algorithm that combines DE mutation and crossover operators to NSGA-II survival with a hybrid type survival strategy. In this algorithm, individuals might be removed in a one-to-one comparison before truncating the population by the multi-objective survival operator. It was proposed by Kukkonen, S. & Lampinen, J. (2005). Variants with M-Nearest Neighbors and 2-Nearest Neighbors survival are also available.
  • NSDE-R: Non-dominated Sorting Differential Evolution based on Reference directions (Reddy & Dulikravich, 2019). It is an algorithm for many-objective problems that works as an extension of NSDE using NSGA-III (Deb & Jain, 2014) survival strategy.

And the additional survivor operators are:

  • RandAndCrowding: Flexible structure to implement NSGA-II rank and crowding survival with different options for crowding metric and elimination of individuals.
  • ConstrRankAndCrowding: A survival operator based on rank and crowding with a special constraint handling approach proposed by Kukkonen, S. and Lampinen, J. (2005).

The crowding metrics available are:

  • Crowding Distance (‘cd’): Proposed by Deb et al. (2002) in NSGA-II. Imported from pymoo.
  • Pruning Crowding Distance (‘pcd’): Proposed by Kukkonen & Deb (2006a), it recursively recalculates crowding distances as removes individuals from a population to improve diversity.
  • M-Nearest Neighbors (‘mnn’): Proposed by Kukkonen & Deb (2006b) in an extension of GDE3 to many-objective problems in which M corresponds to the number of objectives.
  • 2-Nearest Neighbors (‘2nn’): Also proposed by Kukkonen & Deb (2006b), it is a variant of M-Nearest Neighbors in which the number of neighbors is two.
  • Crowding Entropy (‘ce’): Proposed by Wang et al. (2009) in MOSADE.

Back to contents.

DE: Overview and control parameters

The basic structure of a DE algorithm is represented by the figure below, and the most important control parameters implemented in pymoode will be described next. For more details, I recommend reading my previous article on DE and the book by Price et al. (2005).

The basic structure of DE algorithms. (Image by the author).
  • pop_size (int, optional): Population size.
  • sampling (Sampling, Population, or array-like, optional): Sampling strategy of pymoo.
  • variant (str, optional): Differential evolution strategy. Must be a string in the format: “DE/selection/n/crossover”. Defaults to “DE/rand/1/bin”.
  • CR (float, optional): Crossover parameter. Defined in the range [0, 1]. To reinforce mutation, use higher values. To control convergence speed, use lower values.
  • F (iterable of float or float, optional): Scale factor or mutation parameter. Defined in the range (0, 2]. To reinforce exploration, use higher values; whereas for exploitation, use lower.
  • gamma (float, optional): Jitter deviation parameter. Should be in the range (0, 2). Defaults to 1e-4.
  • mutation (Mutation, optional): Pymoo’s mutation operators after crossover. Defaults to None.
  • de_repair (callable or str): Repair strategy to mutant vectors outside problem boundaries. The strategies adopted are based on Price et al. (2005). Defaults to “bounce-back”.
  • survival (Survival, optional): Pymoo’s survival strategy. Should be considered in multi-objective algorithms.

When choosing control parameters for DE, I would recommend giving priority to pop_size, variant, CR, and F (in respective order). Jitter has been reported useful in the literature when using very small population sizes (Price et al., 2005), a situation in which using higher values for gamma can be desirable. I have found it useful also for situations of concatenating linear segments in the Pareto front. The parameter mutation refers to a pymoo mutation that might be performed after crossover. When using variants that reinforce exploitation and elitism, such as DE/best, using Polynomial Mutation is extremely helpful.

The de_repair parameter chooses the repair strategy adopted for mutant vectors outside problem boundaries. It might be passed as a callable or a string. The current implementation uses alternatives presented by Price et al. (2005). The default bounce-back strategy sets variables that violate boundaries to a random value between the corresponding base vector of DE mutation and the variable boundary; midway, in a similar strategy, resets the value to the midway point between both. The strategy rand-init randomly samples a new value between the problem boundaries for violated decision variables, which can be useful for enhancing diversity, although it can disrupt progress toward solutions that lie near bounds.

The survival strategy is an argument valid for multi-objective variants, whereas single-objective DE uses the classic one-to-one replacement. It is crucial to choose an adequate strategy considering the problem being solved. For Bi-objective problems, the RankAndCrowding survival of pymoode with the crowding_func = ‘pcd’ is recommended, whereas for many-objective problems crowding_func = ‘mnn’ is preferred, or switching the algorithm to NSDE-R.

Let us, in the following section, put some hands-on and create problems using pymoo’s structures.

Back to contents.

Defining a problem in pymoo

In pymoo, there are three different ways of defining a problem. All three create an instance with inheritance from the Problem class. They are described below, as in the documentation:

  • Problem: Object-oriented definition which implements a method evaluating a set of solutions.
  • ElementwiseProblem: Object-oriented definition which implements a function evaluating a single solution at a time.
  • FunctionalProblem: Define a problem by using a function for each objective and constraint.
from pymoo.core.problem import Problem, ElementwiseProblem
from pymoo.problems.functional import FunctionalProblem

In this section, I will use the three available structures to show different ways of solving the same optimization problem.

This first example will be based on the single-objective optimization problem of the Rastrigin function. This is a usual single-objective test problem for nonconvex optimization, as it contains several local optima. It is described by the equation below.

Rastrigin problem. (Image by the author).

In which, A is a user-specified parameter (usually 10), and n is the number of decision variables used. In our implementation, we will use two decision variables. Both will be bounded by -5.12 and 5.12.

Probably, the most intuitive way of defining a problem is the functional, which in code goes as follows.

# Defining the objective function
def rastrigin(x):
return np.sum(x * x - \
10 * np.cos(2 * np.pi * x)) \
+ 10 * np.size(x)

# Functional
functional_rastrigin = FunctionalProblem(
n_var=2, objs=rastrigin,
xl=np.full(2, -5.12), xu=np.full(2, 5.12),
constr_ieq=[],
)

Alternatively, the same problem could have been defined as an ElementwiseProblem

class ElementwiseRastrigin(ElementwiseProblem):

def __init__(self):
xl = np.full(2, -5.12)
xu = np.full(2, 5.12)
super().__init__(
n_var=2, n_obj=1, n_constr=0,
xl=xl, xu=xu)

def _evaluate(self, x, out, *args, **kwargs):
out["F"] = rastrigin(x)

… or as a Problem, defined for evaluating a set of solutions in a vectorized manner.

class Rastrigin(Problem):

def __init__(self):
xl = np.full(2, -5.12)
xu = np.full(2, 5.12)
super().__init__(
n_var=2, n_obj=1, n_constr=0,
xl=xl, xu=xu)

def _evaluate(self, x, out, *args, **kwargs):
out["F"] = np.sum(x * x - \
10 * np.cos(2 * np.pi * x), axis=1) \
+ 10 * x.shape[1]

Notice x in the Problem structure is an array corresponding to the decision variables in a Population with shape (N, m), in which N corresponds to the population size and m to the number of decision variables.

In the following section, let us solve this problem using the pymoode implementation of single-objective DE.

Back to contents.

Single-objective DE

To solve the Rastrigin problem, we will first import some useful operators. They will be the algorithm (DE), the minimize interface, and the single-objective termination operator (DefaultSingleObjectiveTermination).

from pymoode.algorithms import DE
from pymoo.optimize import minimize
from pymoo.termination.default import DefaultSingleObjectiveTermination

We will instantiate our first implementation of DE as follows.

de = DE(pop_size=30, variant="DE/rand/1/bin", F=(0.3, 1.0), CR=0.5)

This is a low-dimensional problem, although challenging due to the presence of many local optima. Therefore, I have used 30 as the population size N to ensure global convergence. The DE variant was selected as the most usual DE/rand/1/bin, the F parameter as (0.3, 1.0), which uses random uniform distribution dither, and the CR parameter as 0.5, to reinforce the search on the coordinate axis and control convergence.

I haven’t worked much on tuning control parameters for this problem. Therefore other combinations might be more efficient. For those interested in the workings of DE, I suggest reading my previous article, in which I describe it in detail.

Next, let us define termination criteria based on changes in f (f_tol), x (x_tol), constraint violation (cv_tol), and maximum number of generations (n_max_gen). The parameter nth_gen controls the frequency for checking the criteria, and the parameter n_last controls how many previous generations are considered when evaluating changes.

termination_single = DefaultSingleObjectiveTermination(
xtol=1e-8,
cvtol=0.0,
ftol=1e-8,
period=20,
n_max_gen=200
)

And now, let us solve the problem using the minimize interface.

res_de_rastrigin = minimize(
Rastrigin(),
de,
termination=termination_single,
seed=12,
save_history=True,
verbose=False,
)

The algorithm was effective and led us to the global optimum of the function, which is a vector of zeros in x with the objective function also zero.

As I have specified the parameter save_history as True, I can access the population attributes over the generations and we can create amazing visuals such as the following.

Evolution of population throughout generations in the Rastrigin problem. (Animation by the author).

When creating it, I used the pyrecorder library, also by Julian Blank.

Back to contents.

A brief introduction to multi-objective optimization

As the name suggests, multi-objective optimization involves optimizing a number of objectives simultaneously. The problem becomes challenging when the objectives are of conflict to each other, that is, the optimal solution of an objective function is different from that of the other. In solving such problems, with or without the presence of constraints, these problems give rise to a set of trade-off optimal solutions, popularly known as Pareto-optimal solutions. Due to the multiplicity in solutions, these problems were proposed to be solved suitably using evolutionary algorithms which use a population approach in its search procedure (Deb, 2011).

The Pareto-optimal solutions can be understood using the concept of Domination. They are solutions not dominated by any other in a given population.

According to Deb (2001), one solution x1 dominates another x2 if:

  • x1 is no worse than x2 in all objectives.
  • x1 is strictly better than x2 in at least one of the objectives.

Several ideas have been proposed for handling constraints. A usual approach is the one adopted by Deb et al. (2002) in NSGA-II, in which feasible solutions are always better than infeasible, and infeasible are ranked by overall constraint violation.

The Pareto-optimal solutions for a two-objective minimization problem are represented below.

Pareto-optimal solutions. (Image by the author).

Survival operators must emphasize good solutions over bad ones. In modern multi-objective algorithms, Nondominated Sorting (or an analogous operation) is almost always present, which attributes each solution to a rank according to domination criteria. Ranks are sorted in ascending order from the best to the worst solutions and usually are the first survival criterion.

Nondominated sorting and ranks. (Image by the author).

Individuals with the same rank, when exceeding the population size, are sorted by several strategies, of which two usual are crowding distances present in NSGA-II (Deb et al., 2002) and reference directions present in NSGA-III (Deb & Jain, 2014).

In pymoode algorithms, by default, GDE3 and NSDE use the same survival strategy as NSGA-II, whereas NSDE-R uses the same as NSGA-III.

In the next section, we will solve a multi-objective problem using GDE3, NSDE, and NSGA-II (implemented in pymoo).

Back to contents.

Multi-objective algorithms: GDE3 and NSDE

GDE3 and NSDE are two multi-objective variants of DE using Nondominated Sorting and crowding metrics in the survival stage. They share the same reproduction operators, although different in their survival strategies.

Whereas NSDE uses a full (µ+λ) survival strategy, which combines the parent population with offspring, GDE3 has a one-to-one comparison between each member in the parent population and its corresponding trial vector before truncating the population into the original size. In this one-to-one comparison, one of the individuals of each pair might be removed in advance from the combined population before (µ+λ) survival if dominated by the other.

For most problems, GDE3 and NSDE will lead to very similar results. NSDE might be slightly faster in some problems, whereas GDE3 might be better for avoiding premature convergence when using low CR values.

Let us import the operators used in this section.

# Algorithms
from pymoode.algorithms import GDE3
from pymoode.algorithms import NSDE
from pymoo.algorithms.moo.nsga2 import NSGA2

# Termination
from pymoo.termination.default import DefaultMultiObjectiveTermination

In this example, I will introduce two conflicting convex objectives with additional difficulty introduced by constraints. The problem will be defined over two decision variables x1 and x2, both bounded by -5.0 and 5.0.

Objective functions for multi-objective example. (Image by the author).
Constraint functions for multi-objective example. (Image by the author).

In Python code, using the pymoo Problem structure.

class TwoObjExample(Problem):

def __init__(self):
xl = np.full(2, -5.0)
xu = np.full(2, 5.0)
super().__init__(n_var=2, n_obj=2, n_ieq_constr=2, xl=xl, xu=xu)

def _evaluate(self, x, out, *args, **kwargs):

F1 = (x[:, 0] - 0.5) ** 2 + 0.7 * x[:, 0] * \
x[:, 1] + 1.2 * (x[:, 1] + 0.7) ** 2

F2 = 1.1 * (x[:, 0] + 1.5) ** 2 + 0.8 * x[:, 0] * \
x[:, 1] + 1.3 * (x[:, 1] - 1.7) ** 2

out["F"] = np.column_stack([F1, F2])

G1 = x[:, 0] ** 2 + (x[:, 1] - 1) ** 2 - 9
G2 = - (x[:, 0] + 0.5) ** 2 - (x[:, 1] - 1) ** 2 + 2
out["G"] = np.column_stack([G1, G2])

Then, let us define the stopping criteria. As this is a multi-objective problem, the differences in f and x are not so intuitive. In pymoo, the IGD metric is used to evaluate differences in f and x between two distinct generations. This definition was performed just to illustrate the workings of the DefaultMultiObjectiveTerminationoperator.

termination_multi = DefaultMultiObjectiveTermination(
xtol=1e-8,
cvtol=1e-8,
ftol=1e-8,
period=50,
n_max_gen=200
)

When solving this problem, in addition to GDE3 and NSDE, we will use the implementation of NSGA-II present in pymoo. Once again, I haven’t tuned the control parameters of the DE algorithms. Therefore they might have been improved. In NSGA-II, the default control parameters for Simulated Binary Crossover and Polynomial Mutation have been adopted, implicit as optional keyword arguments.

gde3 = GDE3(pop_size=50, variant="DE/rand/1/bin",\
F=(0.0, 1.0), CR=0.7)

nsde = NSDE(pop_size=50, variant="DE/rand/1/bin",\
F=(0.0, 1.0), CR=0.7)

nsga2 = NSGA2(pop_size=30)

Once we have the algorithms and the problem defined, let us then solve it using the minimize interface.

res_gde3_problem_2 = minimize(
TwoObjExample(),
gde3,
termination_multi,
seed=12,
save_history=True,
verbose=False,
)

The process was the same for all algorithms, just changing the second argument passed to minimize. Therefore, I will omit the other implementations.

Several metrics can be used to compare performances in multi-objective problems. Some of the most common are Generational Distances, Inverted Generational Distances, and Hypervolume. They are all implemented in pymoo with examples. As in this problem the true front is unknown a priori, I have used only hypervolume to compare algorithm performances.

The complete implementation is available in the example notebook, and the main results are summarized below.

hv GDE3 0.753470843848252
hv NSDE 0.752272658484699
hv NSGA-II 0.7527205037080124

The three algorithms have presented very similar performances, although GDE3 was slightly better. Probably, on several independent runs, this would not present statistical significance.

To illustrate the evolutionary process, I have created short animations using pyrecorder for GDE3 solutions. The first 70 generations are illustrated in both decision variable and objective spaces.

The evolutionary process in decision space of multi-objective example using GDE3. (Animation by the author).
The evolutionary process in objective space of multi-objective example using GDE3. (Animation by the author).

Back to contents.

Improving solutions in Bi-objective problems

Although the results of the previous section were great, the Pareto fronts could have been smoother and the distribution of elements more uniform. This is due to the superior performance of other variants of the Rank and Crowding survival compared to the original strategy proposed in NSGA-II. To provide the user with more options in the survival stage, the RankAndCrowding class has been implemented in pymoode.

A relevant strategy adopted to improve diversity is recursively eliminating individuals and recalculating crowding metrics rather than at once eliminating all individuals from the last front exceeding the population size based on crowding metrics for the entire front. This was performed by Kukkonen & Deb (2006a) with a deep analysis of the modified strategy.

To use the Pruning strategy of Kukkonen & Deb (2006a), we must choose a different crowding function in the RankAndCrowding operator. In pymoode, it is denoted ‘pcd’ which stands for ‘Pruning Crowding Distances’. Due to Cython implementation, it is as fast as the original operator.

For now, it can be performed as below.

# Import survival operator
from pymoode.survival import RankAndCrowding

# Instantiate it with respective elimination rule and crowding metric
pruning_survival = RankAndCrowding(crowding_func="pcd")

# Pass it in the survival argument of the algorithm
gde3_ps = GDE3(pop_size=50, variant="DE/rand/1/bin", F=(0.0, 1.0),
CR=0.7, survival=pruning_survival)

Which produces a more even distribution of solutions than the original crowding distances algorithm.

Pruning crowding distances versus original crowding distances in GDE3. (Image by the author).

And to evaluate how even the distribution of elements in the Pareto front is one can use the Spacing indicator, which is implemented in pymoode. The lesser the value of this indicator, the more even the distribution of elements in the Pareto front.

from pymoode.performance import SpacingIndicator

ideal = np.vstack(
(res_gde3_problem_2.F, res_gde3_ps_problem_2.F)
).min(axis=0) - 1e-3

nadir= np.vstack(
(res_gde3_problem_2.F, res_gde3_ps_problem_2.F)
).max(axis=0) + 1e-3

sp_p2 = SpacingIndicator(
zero_to_one=True, ideal=ideal, nadir=nadir
)

print("SP GDE3", sp_p2.do(res_gde3_problem_2.F))
print("SP GDE3-PS", sp_p2.do(res_gde3_ps_problem_2.F))

Which returns:

SP GDE3 0.012024880243150753
SP GDE3-PS 0.004881671664926163

Providing now a quantitative metric to measure the improvement in distribution as a consequence of adopting the modified pruning solutions strategy.

Back to contents.

Many-objective algorithms: NSDE-R and GDE3-MNN

As GDE3 and NSDE have been originally designed using NSGA-II Rank and Crowding survival, they perform poorly in many-objective problems. However, simple modifications from a user perspective can solve this issue.

NSDE-R (Reddy & Dulikravich, 2019) combines the survival operator of NSGA-III with the reproduction operators of DE, which leads to great performance in many-objective problems. GDE3-MNN is a variant of GDE3 proposed by Kukkonen and Deb (2006b) that replaces original crowding distances of Rank and Crowding survival with an M-Nearest Neighbors based crowding metric with recursive elimination and re-calculation. It has improved a lot the performance of GDE3 in many-objective problems.

Especially in many-objective problems, due to the sparsity of objective space, when using the same survival strategy, NSDE and GDE3 will produce even more similar results compared to two-objective problems. Therefore, throughout this section, only one variant of each will be implemented.

Besides the DE algorithms, we will also implement NSGA-III, one of the most successful algorithms in many-objective optimization.

Let us import the new operators.

# Algorithms
from pymoode.algorithms import NSDER, GDE3
from pymoo.algorithms.moo.nsga3 import NSGA3

# Interface to obtain reference direction
from pymoo.util.ref_dirs import get_reference_directions

In this example, I will use the get_problem interface to import a many-objective problem implemented in pymoo. It will be the DTLZ2, in which each component in x is defined in the range [0, 1] and there are no functional constraints. The objectives are described by the equations below.

Objective functions of DTLZ2 problem. (Image by the author).

Let us instantiate the algorithms used in this section. Notice reference directions are a mandatory argument for NSDE-R and NSGA-III. Fortunately, pymoo has an interface to easily obtain usual directions. Moreover, notice that in GDE3 the RankAndCrowding operator is being used with crowding_func = ‘mnn’, which uses the M-NN strategy aforementioned. Alternatively, we could have just imported the GDE3MNN class.

# Define the reference directions
ref_dirs = get_reference_directions(
"das-dennis", 3, n_partitions=15)

# Suggestion for NSGA-III
popsize = ref_dirs.shape[0] + ref_dirs.shape[0] % 4

nsder = NSDER(ref_dirs, pop_size=popsize,
variant="DE/rand/1/bin", F=(0.0, 1.0), CR=0.5)

gde3mnn = GDE3(pop_size=popsize,
variant="DE/rand/1/bin", F=(0.0, 1.0), CR=0.2,
survival=RankAndCrowding(crowding_func="mnn"))

nsga3 = NSGA3(ref_dirs, pop_size=popsize)

In this section, I will adopt a simplified termination criterion (‘n_gen’, 250), based only on the number of generations, which is usual for multi-objective problems.

res_nsder_problem_3 = minimize(
get_problem("dtlz2"),
nsder,
('n_gen', 250),
seed=SEED,
save_history=True,
verbose=True,
)

NSGA-III has produced solutions closer to reference points than the DE algorithms in this problem, although the performances of DE algorithms were great as well.

The Pareto fronts obtained by completion of the algorithms are presented in the animation below.

Pareto fronts obtained in DTLZ2. (Animation by the author).

Great! Now we’ve been through the operators available in pymoode solving several different problems.

Back to contents.

Applications and perspective

Multi-objective evolutionary algorithms have been applied in many distinct areas including portfolio optimization (Zhao et al., 2020), power plant operation (Wang et al., 2014), water distribution systems (Monsef et al., 2019), and network control (Gonçalves et al., 2022). Furthermore, there are many applications to engineering process design and control and, as a chemical engineer, that is where I started.

The first time I ran into multi-objective evolutionary algorithms was doing literature review for my first article on simulation and optimization of styrene reactors (Leite et al., 2021). The first application to this problem was performed by Yee et al. (2003) using the original NSGA algorithm by Srinivas & Deb (1994). It produced great results and has later motivated the study of Tarafader et al. (2005), which besides increasing complexity in problem formulation for incorporating more processes in the objectives has adopted the more efficient algorithm: NSGA-II (Deb et al., 2002). A multi-objective differential evolution approach was also proposed to the styrene reactor problem by Babu et al. (2005) and by Gujarathi & Babu (2010).

I find it amazing to see how the algorithms have improved over the last decades and been made available to users. The package pymoode started with an attempt to optimize a complex reactor simulation model by evolutionary approaches, but now I believe it has become much more, as it provides any Python user with some of the most efficient differential evolution algorithms and tools proposed so far.

As I now update this Medium article, the main research paper on the multi-objective optimization of styrene reactors in which pymoode was developed is published under the title Multi-objective optimization of adiabatic styrene reactors using Generalized Differential Evolution 3 (GDE3) (Leite et al., 2023). If you use pymoode for academic purposes, please cite it accordingly.

Back to contents.

Conclusions

In this article, the Python package pymoode was presented with tutorials for solving single-, multi-, and many-objective problems. The package is an extension of pymoo focusing on Differential Evolution algorithms, with a variety of operators that ensure great performance and allow high customization. All the code used to produce this article is available on Github. The new features of pymoode might be later incorporated by pymoo.

Acknowledgements

To Julian Blank, who created the amazing structure of pymoo, making such a project possible.

To Esly Ferreira Da Costa Junior, who made it possible all along.

References

Babu, B. V., Chakole, P. G. & Mubeen, J. H. S., 2005. Multiobjective differential evolution (MODE) for optimization of adiabatic styrene reactor. Chem. Eng. Sci., Volume 60, pp. 4822–4837.

Blank, J. & Deb, K., 2020. pymoo: Multi-Objective Optimization in Python. IEEE Access, Volume 8, pp. 89497–89509.

Deb, K., 2001. Multi-Objective Optimization Using Evolutionary Algorithms. 1st ed. Chichester: John Wiley & Sons.

Deb, K., 2004. Introduction to Genetic Algorithms for Engineering Optimization. In: G. C. Onwubolu & B. V. Babu, eds. New Optimization Techniques in Engineering Studies in Fuzziness and Soft Computing, vol 141. Heidelberg: Springer Verlag Berlin Heidelberg, pp. 13–51.

Deb, K., 2011. Multi-objective optimisation using evolutionary algorithms: an introduction. In: Multi-objective evolutionary optimisation for product design and manufacturing. London: Springer, pp. 3–34.

Deb, K. & Jain, H., 2014. An evolutionary many-objective optimization algorithm using reference-point-based nondominated sorting approach, part I: solving problems with box constraints. IEEE Transactions on Evolutionary Computation, 18(4), pp. 577–601.

Deb, K., Pratap, A., Agarwal, S. & Meyarivan, T. A. M. T., 2002. A Fast and Elitist Multiobjective Genetic Algorithm: NSGA-II. IEEE transactions on evolutionary computation, 6(2), pp. 182–197.

Gonçalves, E. N., Belo, M. A. R. & Batista, A. P., 2022. Self-adaptive multi-objective differential evolution algorithm with first front elitism for optimizing network usage in networked control systems. Appl. Soft Comput., Volume 114, p. 108112.

Gujarathi, A. M. & Babu, B. V., 2010. Multi-objective optimization of industrial styrene reactor: Adiabatic and pseudo-isothermal operation. Chem. Eng. Sci., Volume 65, pp. 2009–2026.

Kukkonen, S. & Deb, K., 2006a. Improved Pruning of Non-Dominated Solutions Based on Crowding Distance for Bi-Objective Optimization Problems. Vancouver, s.n., pp. 1179–1186.

Kukkonen, S. & Deb, K., 2006b. A fast and effective method for pruning of non-dominated solutions in many-objective problems. In: Parallel problem solving from nature-PPSN IX. Berlin: Springer, pp. 553–562.

Kukkonen, S. & Lampinen, J., 2005. GDE3: The third evolution step of generalized differential evolution. 2005 IEEE congress on evolutionary computation, Volume 1, pp. 443–450.

Leite, B., Costa, A. O. S. & Costa Junior, E. F., 2021. Simulation and optimization of axial-flow and radial-flow reactors for dehydrogenation of ethylbenzene into styrene based on a heterogeneous kinetic model. Chem. Eng. Sci., Volume 244, Article 116805.

Leite, B., Costa, A. O. S., Costa, E. F., 2023. Multi-objective optimization of adiabatic styrene reactors using Generalized Differential Evolution 3 (GDE3). Chem. Eng. Sci., Volume 265, Article 118196.

Monsef, H., Naghashzadegan, M., Jamali, A. & Farmani, R., 2019. Comparison of evolutionary multi objective optimization algorithms in optimum design of water distribution network. Ain Shams Engineering Journal, Volume 10, pp. 103–111.

Price, K. V., Storn, R. M. & Lampinen, J. A., 2005. Differential Evolution: A Practical Approach to Global Optimization. 1st ed. Springer: Berlin.

Reddy, S. R. & Dulikravich, G. S., 2019. Many-objective differential evolution optimization based on reference points: NSDE-R. Struct. Multidisc. Optim., Volume 60, pp. 1455–1473.

Srinivas, N. & Deb, K., 1994. Muiltiobjective Optimization Using Nondominated Sorting in Genetic Algorithms. Evolutionary Computation, 2(3), pp. 221–248.

Storn, R. & Price, K., 1997. Differential evolution–a simple and efficient heuristic for global optimization over continuous spaces. J. Glob. Optim., 11(4), pp. 341–359.

Tarafder, A., Rangaiah, G. & Ray, A. K., 2005. Multiobjective optimization of an industrial styrene monomer manufacturing process. Chem. Eng. Sci., 60(2), pp. 347–363.

Wang, L. et al., 2014. Multi-objective optimization of coal-fired power plants using differential evolution. Applied Energy, Volume 115, pp. 254–264.

Wang, Y.-N., Wu, L.-H. & Yuan, X.-F., 2010. Multi-objective self-adaptive differential evolution with elitist archive and crowding entropy-based diversity measure. Soft Comput., 14(3), pp. 193–209.

Yee, A. K., Ray, A. K. & Rangaiah, G., 2003. Multiobjective optimization of an industrial styrene reactor. Comput. Chem. Eng., 27(1), pp. 111–130.

Zhao, P., Gao, S. & Yang, N., 2020. Solving Multi-Objective Portfolio Optimization Problem Based on MOEA/D. 2020 12th International Conference on Advanced Computational Intelligence (ICACI), pp. 30–37.

--

--

Chemical Engineer, Researcher, Optimization Enthusiast, and Data Scientist passionate about describing phenomena using mathematical models.