# Defining custom problems
Many classic optimisation problems are [already implemented in PYMOO](https://www.pymoo.org/problems/index.html). However, you will want to implement your own problem at some point. Here's how to do that. 


```python
import numpy as np
import autograd.numpy as anp
from pymoo.model.problem import Problem
from pymoo.util.misc import stack
from pymoo.model.problem import FunctionalProblem
```

Let's consider the following problem.

\begin{align}
\min \quad f_1(x) & = (x_1^2 + x_2^2) \\
\min \quad f_2(x) & = (x_1 - 1)^2 + x_2^2 \\
\textrm{s.t.} \quad 
g_1(x) & = 2(x_1−0.1)(x_1−0.9)/0.18 ≤ 0 \\
g_2(x) & = −20(x_1−0.4)(x_1−0.6)/4.8 ≤ 0 \\
−2 & ≤ x_1 ≤ 2  \\
−2 & ≤ x_2 ≤ 2
\end{align}

It consists of:
- 2 objective functions $f_1(x)$ and $f_2(x)$
- 2 design variables $x_1$ and $x_2$, both in the range $[−2,2]$
- 2 inequality contraints $g_1(x)$ and $g_2(x)$

There are 3 methods to define a custom problem in PYMOO: vectorized problem, elementwise problem and functional problem.

## Vectorized problem
The _evaluate method takes a **two-dimensional** NumPy array X with n rows and m columns as an input. Each row represents an individual, and each column an optimization variable. After doing the necessary calculations, the objective values must be added to the dictionary out with the key F and the constraints with key G.


```python
class MyVectorizedProblem(Problem):

    def __init__(self):
        super().__init__(n_var=2,
                         n_obj=2,
                         n_constr=2,
                         xl=np.array([-2,-2]),
                         xu=np.array([2,2]))

    def _evaluate(self, X, out, *args, **kwargs):
        f1 = X[:, 0]**2 + X[:, 1]**2
        f2 = (X[:, 0]-1)**2 + X[:, 1]**2

        g1 = 2*(X[:, 0]-0.1) * (X[:, 0]-0.9) / 0.18
        g2 = - 20*(X[:, 0]-0.4) * (X[:, 0]-0.6) / 4.8

        out["F"] = np.column_stack([f1, f2])
        out["G"] = np.column_stack([g1, g2])


vectorized_problem = MyVectorizedProblem()
```

## Elementwise problem
The _evaluate method takes a **one-dimensional** NumPy array x number of entries equal to n_var. This behavior is enabled by setting elementwise_evaluation=True while calling the super() method.


```python
class MyElementwiseProblem(Problem):

    def __init__(self):
        super().__init__(n_var=2,
                         n_obj=2,
                         n_constr=2,
                         xl=np.array([-2,-2]),
                         xu=np.array([2,2]),
                         elementwise_evaluation=True)

    def _evaluate(self, x, out, *args, **kwargs):
        f1 = x[0]**2 + x[1]**2
        f2 = (x[0]-1)**2 + x[1]**2

        g1 = 2*(x[0]-0.1) * (x[0]-0.9) / 0.18
        g2 = - 20*(x[0]-0.4) * (x[0]-0.6) / 4.8

        out["F"] = [f1, f2]
        out["G"] = [g1, g2]


elementwise_problem = MyElementwiseProblem()
```

## Functional problem


```python
objs = [
    lambda x: x[0]**2 + x[1]**2,
    lambda x: (x[0]-1)**2 + x[1]**2
]

constr_ieq = [
    lambda x: 2*(x[0]-0.1) * (x[0]-0.9) / 0.18,
    lambda x: - 20*(x[0]-0.4) * (x[0]-0.6) / 4.8
]

functional_problem = FunctionalProblem(n_var=2,
                                       objs=objs,
                                       constr_ieq=constr_ieq,
                                       xl=np.array([-2,-2]),
                                       xu=np.array([2,2]))
```

## More vectorized problem examples


```python
class SphereWithConstraint(Problem):

    def __init__(self):
        super().__init__(n_var=10, 
                         n_obj=1, 
                         n_constr=1, 
                         xl=0, 
                         xu=1)

    def _evaluate(self, x, out, *args, **kwargs):
        out["F"] = np.sum((x - 0.5) ** 2, axis=1)
        out["G"] = 0.1 - out["F"]
        
sphere_problem = SphereWithConstraint()
```


```python
class MyVectorizedProblem2(Problem):

    def __init__(self, const_1=5, const_2=0.1):

        # define lower and upper bounds -  1d array with length equal to number of variable
        xl = -5 * anp.ones(10)
        xu = 5 * anp.ones(10)

        super().__init__(n_var=10, 
                         n_obj=1, 
                         n_constr=2, 
                         xl=xl, 
                         xu=xu, 
                         evaluation_of="auto")

        # store custom variables needed for evaluation
        self.const_1 = const_1
        self.const_2 = const_2

    def _evaluate(self, x, out, *args, **kwargs):
        f = anp.sum(anp.power(x, 2) - self.const_1 * anp.cos(2 * anp.pi * x), axis=1)
        g1 = (x[:, 0] + x[:, 1]) - self.const_2
        g2 = self.const_2 - (x[:, 2] + x[:, 3])

        out["F"] = f
        out["G"] = anp.column_stack([g1, g2])
        
        
vectorized_problem_2 = MyVectorizedProblem2()
```

## Another elementwise problem example


```python
class MyElementwiseProblem2(Problem):

    def __init__(self, **kwargs):
        super().__init__(n_var=2, 
                         n_obj=1, 
                         elementwise_evaluation=True, 
                         **kwargs)

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


elementwise_problem_2 = MyElementwiseProblem2()
```

## Another functional problem example


```python
objs = [
    lambda x: np.sum((x - 2) ** 2),
    lambda x: np.sum((x + 2) ** 2)
]

constr_ieq = [
    lambda x: np.sum((x - 1) ** 2)
]


functional_problem_2 = FunctionalProblem(10,
                            objs,
                            constr_ieq=constr_ieq,
                            xl=np.array([-10, -5, -10]),
                            xu=np.array([10, 5, 10])
                            )
```

# Problem evaluation
We can evaluate a problem with the evaluate() method. This method takes as input a two dimensional matrix where each row is a point to evaluate and each column a variable and returns a dictionary with the result of the evaluation.


```python
# input vector: a random population of 3 individuals, each defined by 2 or 10 design variables.
X_2 = np.random.rand(3, 2)
X_10 = np.random.rand(3, 10)
```


```python
vectorized_problem.evaluate(X_2, return_as_dictionary=True)
```




    {'F': array([[0.08865958, 0.92178874],
            [0.25352178, 0.94993085],
            [0.06625316, 1.06159225]]), 'CV': array([[0.15028942],
            [0.        ],
            [0.97416637]]), 'G': array([[ 0.15028942, -0.68135853],
            [-0.4305956 , -0.46352665],
            [ 0.97416637, -0.99031239]])}




```python
elementwise_problem.evaluate(X_2, return_as_dictionary=True)
```




    {'F': array([[0.08865958, 0.92178874],
            [0.25352178, 0.94993085],
            [0.06625316, 1.06159225]]), 'CV': array([[0.15028942],
            [0.        ],
            [0.97416637]]), 'G': array([[ 0.15028942, -0.68135853],
            [-0.4305956 , -0.46352665],
            [ 0.97416637, -0.99031239]])}




```python
functional_problem.evaluate(X_2, return_as_dictionary=True)
```




    {'F': array([[0.08865958, 0.92178874],
            [0.25352178, 0.94993085],
            [0.06625316, 1.06159225]]), 'CV': array([[0.15028942],
            [0.        ],
            [0.97416637]]), 'G': array([[0.15028942, 0.        ],
            [0.        , 0.        ],
            [0.97416637, 0.        ]])}




```python
sphere_problem.evaluate(X_10, return_as_dictionary=True)
```




    {'F': array([[0.75168175],
            [0.83946771],
            [1.20842157]]), 'CV': array([[0.],
            [0.],
            [0.]]), 'G': array([[-0.65168175],
            [-0.73946771],
            [-1.10842157]])}




```python
# We can return more values in the output dictionary
vectorized_problem_2.evaluate(X_10, 
                              return_as_dictionary=True,
                              return_values_of=["F", "G", "CV", "feasible", "dF", "dG"])
```




    {'F': array([[ 13.02018527],
            [  2.152319  ],
            [-17.00788227]]), 'G': array([[ 0.90149626, -1.01947164],
            [ 0.82882442, -0.52283495],
            [ 1.57541812, -0.37100353]]), 'CV': array([[0.90149626],
            [0.82882442],
            [1.57541812]]), 'feasible': array([[False],
            [False],
            [False]]), 'dF': array([[[  3.30302764,  -1.59446704,  -3.98945639, -16.27825395,
               30.18928505,   5.39321748, -19.75971148, -11.25048504,
                1.60219873, -27.99258105]],
     
            [[ -2.64141317,  17.62966488,  31.89409186,  22.63569441,
              -21.58133575,   5.47558745, -23.60753848, -18.56252313,
               22.96358009, -28.8773237 ]],
     
            [[ -1.88687626, -28.16606694,  31.67896801,  30.26315304,
               22.28974347,  30.88947072,  -7.9440922 ,  15.36147014,
               31.89275415,  15.38885776]]]), 'dG': array([[[ 1.,  1.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.],
             [ 0.,  0., -1., -1.,  0.,  0.,  0.,  0.,  0.,  0.]],
     
            [[ 1.,  1.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.],
             [ 0.,  0., -1., -1.,  0.,  0.,  0.,  0.,  0.,  0.]],
     
            [[ 1.,  1.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.],
             [ 0.,  0., -1., -1.,  0.,  0.,  0.,  0.,  0.,  0.]]]), '__autograd__': array([[0.48820239, 0.51329387, 0.52564761, 0.59382403, 0.19860487,
             0.02717889, 0.87990381, 0.56446297, 0.0080388 , 0.69234881],
            [0.51867974, 0.41014468, 0.24546344, 0.37737151, 0.86690449,
             0.02759812, 0.64562427, 0.88793008, 0.12856541, 0.7894407 ],
            [0.98045779, 0.69496033, 0.27124377, 0.19975976, 0.12377091,
             0.29245994, 0.94928248, 0.08038163, 0.25793561, 0.0805384 ]])}




```python
# in this problem, there is no constraints defined. The single objective function returns the sum of the 2 design variables.
# elementwise_problem_2.evaluate(X_2, return_as_dictionary=True)
elementwise_problem_2.evaluate([1, 0.5], return_as_dictionary=True)
```




    {'F': array([1.5])}




```python
functional_problem_2.evaluate(X_10, return_as_dictionary=True)
```




    {'F': array([[24.77716362, 60.70921197],
            [23.64630035, 62.82807997],
            [26.91604974, 58.36237461]]), 'CV': array([[3.76017571],
            [3.44174526],
            [4.77763096]]), 'G': array([[3.76017571],
            [3.44174526],
            [4.77763096]])}




```python

```


```python

```

11802545d5e732b50040efcc3f42c5898caea277
ipynb
Jupyter Notebook
3_define_custom_problems.ipynb
PierreExeter/pymoo-tutorials
e65cfbe8aae5f684dd024ab502779051d103435a
MIT
2020-12-01T09:51:23.000Z
2020-12-01T09:51:23.000Z
3_define_custom_problems.ipynb
PierreExeter/pymoo-tutorials
e65cfbe8aae5f684dd024ab502779051d103435a
MIT
2020-11-02T15:36:29.000Z
2020-11-03T14:23:12.000Z
3_define_custom_problems.ipynb
PierreExeter/pymoo-tutorials
e65cfbe8aae5f684dd024ab502779051d103435a
MIT
Qwen/Qwen-72B
1. YES
2. YES