Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tutorials/examples for multi-objective custom UDPs #149

Closed
ckaldemeyer opened this issue Jan 2, 2018 · 17 comments
Closed

Tutorials/examples for multi-objective custom UDPs #149

ckaldemeyer opened this issue Jan 2, 2018 · 17 comments

Comments

@ckaldemeyer
Copy link
Contributor

ckaldemeyer commented Jan 2, 2018

Hi everybody,

I have read the complete python tutorials and have successfully tested my pagmo2/pygmo installation with some simple mono-objective examples.

Since I intend to use it for multi-objective optimization, I have been looking for some examples on how to do this. In the mono-objective UDP example tutorial is says that the number of objectives can be defined similarly to the number of constraints (eqs/iqs) but " Since we do not define, in this case, any other method pygmo will assume a single objective, no constraints, no gradients etc…" and in the MOO tutorial a pre-defined standard problem is chosen.

Are there any tutorials or code examples that show how to set up multi-objective customized UDPs such as in the simple UDP tutorial?

Your packages seems really promising but I got stuck here..

Thanks in advance!
Cord

@ckaldemeyer
Copy link
Contributor Author

If you pass me some snippets I could also integrate in in the docs via PR..

@ckaldemeyer
Copy link
Contributor Author

Or is it as simple as returning a multi-dimensional array in the fitness function?

def fitness(self, x):
     return [x*x, 1/x]

But where can I pass the senses (min/max) or weights, etc.. Some information would be nice ;-)

@darioizzo
Copy link
Member

I am writing quickly from mobile .... sorry for the brevity.
I suggest you to look into the docs of the class problem in the python version. There you can see all the optional methods that empower you to define all sorts of problems...

In your case it's the method 'get_nobj(self)' that needs to be implemented and needs to return the number of objectives ....

In case you do write a new tutorial on how to write a moo problem and solve it .... we do welcome PR

@ckaldemeyer
Copy link
Contributor Author

I am writing quickly from mobile .... sorry for the brevity.
I suggest you to look into the docs of the class problem in the python version. There you can see all the optional methods that empower you to define all sorts of problems...

In your case it's the method 'get_nobj(self)' that needs to be implemented and needs to return the number of objectives ....

In case you do write a new tutorial on how to write a moo problem and solve it .... we do welcome PR

Thanks for your quick answer! I'll give it a try and post my progress here.

@ckaldemeyer
Copy link
Contributor Author

It works now!

I have implemented the Fonseca and Fleming function for n=2 test-wise:

import pygmo as pg
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt


class my_udp:
    def fitness(self, x):
        f1 = 1-np.exp(-((x[0]-1/np.sqrt(1))**2+(x[1]-1/np.sqrt(2))**2))
        f2 = 1-np.exp(-((x[0]+1/np.sqrt(1))**2+(x[1]+1/np.sqrt(2))**2))
        return [f1, f2]

    def get_nobj(self):
        return 2

    def get_bounds(self):
        return ([-4]*2, [4]*2)


pro = pg.problem(my_udp())

pop = pg.population(pro, size=100)

algo = pg.algorithm(pg.nsga2(gen=1000))

algo.set_verbosity(100)

pop = algo.evolve(pop)

fits, vectors = pop.get_f(), pop.get_x()

print(fits, vectors)

df = pd.DataFrame(fits)
ax = df.plot(kind='scatter', x=0, y=1, grid=True)
ax.set_xlabel('f1(x)')
ax.set_ylabel('f2(x)')
plt.show()

Result:

fonseca_fleming

Nevertheless, I think some simple examples within the documentation would help new users getting started.

In case you do write a new tutorial on how to write a moo problem and solve it .... we do welcome PR

If you want, I can adapt my example to be more generic (variable n) and add it somewhere where it can be found whether in the docs or in the examples.

Thanks already for your help!

@darioizzo
Copy link
Member

Sure, a tutorial on coding and solving the Fonseca and Fleming UDP would be helpful. Feel free to issue a PR. Do not import pandas though as we do not force it as a third party dep.

@ckaldemeyer
Copy link
Contributor Author

ckaldemeyer commented Jan 3, 2018

Sure, a tutorial on coding and solving the Fonseca and Fleming UDP would be helpful. Feel free to issue a PR. Do not import pandas though as we do not force it as a third party dep.

I have now tried to model the "Constr.-Ex problem" since it also contains some (linear) constraints and would be a bit more illustrative than the example from above:

import pygmo as pg


class constr_ex_problem:
    """
    The Constr-Ex problem.

    See:
    https://en.wikipedia.org/wiki/
    Test_functions_for_optimization
    #Test_functions_for_multi-objective_optimization
    """

    def fitness(self, x):
        f1 = x[0]
        f2 = (1+x[1])/x[0]

        ci1 = -x[1]-9*x[0]+6
        ci2 = x[1]-9*x[0]+1

        return [f1, f2, ci1, ci2]

    def get_nobj(self):
        return 2

    def get_bounds(self):
        return ([0.1, 0], [1, 5])

    def get_nic(self):
        return 2


prob = pg.problem(constr_ex_problem())

pop = pg.population(prob, size=500)

# https://github.com/esa/pagmo2/blob/master/include/pagmo/algorithms/
algo = pg.algorithm(pg.nsga2(gen=250))

algo.set_verbosity(100)

pop = algo.evolve(pop)

fits, vectors = pop.get_f(), pop.get_x()

print(fits)

But i get an error due to non-linear constraints:

ValueError: 
function: evolve
where: /home/xyz/.anaconda3/include/pagmo/algorithms/nsga2.hpp, 144
what: Non linear constraints detected in <class '__main__.constr_ex_problem'> instance. NSGA-II cannot deal with them.

From my point of view the constraints are linear and only one of the objectives is non-linear.

Am I missing something here?

Thanks in advance!

@darioizzo
Copy link
Member

The error message is, in this case, misleading it should be:
Constraints defined in<class '__main__.constr_ex_problem'> instance. NSGA-II cannot deal with them.

Algorithms that have constarints (also linear) cannot be solved by nsga-II. Box constraints (i.e. the bounds) though can.

@ckaldemeyer
Copy link
Contributor Author

The error message is, in this case, misleading it should be:
Constraints defined in<class 'main.constr_ex_problem'> instance. NSGA-II cannot deal with them.

Algorithms that have constarints (also linear) cannot be solved by nsga-II. Box constraints (i.e. the bounds) though can.

Thanks for your answer. Is there any algorithm implemented that can solve constrained multi-objective problems?

@bluescarni
Copy link
Member

Looking at the algorithm list, only improved harmony search seems to be able to solve constr. MO problems:

https://esa.github.io/pagmo2/docs/algorithm_list.html

Note that IHS is not avaialble yet in any released PaGMO/PyGMO version (it will be in the next version, 2.7).

Another possibility is to unconstrain your problem using the unconstrain meta problem:

https://esa.github.io/pagmo2/docs/python/problems/py_problems.html#pygmo.unconstrain

This will transform your constrained problem into an unconstrained one, which can then be solved with NSGA2. Of course, whether this approach is sound depends highly on your problem.

@ckaldemeyer
Copy link
Contributor Author

Looking at the algorithm list, only improved harmony search seems to be able to solve constr. MO problems:

https://esa.github.io/pagmo2/docs/algorithm_list.html

Note that IHS is not avaialble yet in any released PaGMO/PyGMO version (it will be in the next version, 2.7).

Another possibility is to unconstrain your problem using the unconstrain meta problem:

https://esa.github.io/pagmo2/docs/python/problems/py_problems.html#pygmo.unconstrain

This will transform your constrained problem into an unconstrained one, which can then be solved with NSGA2. Of course, whether this approach is sound depends highly on your problem.
@ckaldemeyer

Thanks for your answer and explanation! I'll check it and then provide a suitable example via PR

@ckaldemeyer
Copy link
Contributor Author

ckaldemeyer commented Jul 9, 2018

Where exactly in the documentation can I place the example?

My first idea would be here within the section Coding your own problem (UDP) as Coding a User Defined Problem with multiple objectives.

I have created a simple example using an archipelago with three islands:

"""Example that illustrates multi-objective optimization using pygmo."""

import pygmo as pg
import numpy as np
from multiprocessing import Process, freeze_support


class FonsecaFleming():
    """
    User defined problem: Fonseca and Fleming function.

    See: https://en.wikipedia.org/wiki/Test_functions_for_optimization
    """

    def __init__(self, n):
        """Pass dimensions to constructor."""
        self.n = n

    def fitness(self, x):
        """Define objectives."""
        f1 = 1-np.exp(-sum([(x[n]-1/np.sqrt(n))**2 for n in range(1, self.n)]))
        f2 = 1-np.exp(-sum([(x[n]+1/np.sqrt(n))**2 for n in range(1, self.n)]))
        return [f1, f2]

    def get_nobj(self):
        """Return number of objectives."""
        return 2

    def get_bounds(self):
        """Return bounds of decision variables."""
        return ([-4]*self.n, [4]*self.n)

    def get_name(self):
        """Return function name."""
        return "Fonseca and Fleming function"


def optimize(popsize=100, generations=10, islands=3, evolutions=10,
             prob=pg.problem(FonsecaFleming(5))):
    """Start optimization process."""
    # set up algorithm and optimize
    algo = pg.algorithm(pg.nsga2(gen=generations))
    archi = pg.archipelago(islands, algo=algo, prob=prob, pop_size=popsize)

    # evolution
    fits_log, vectors_log = [], []
    for e in range(0, evolutions):
        # evolve islands
        archi.evolve(e)
        archi.wait()
        # extract data
        vectors = [isl.get_population().get_x() for isl in archi]
        vectors_log.append(vectors)
        fits = [isl.get_population().get_f() for isl in archi]
        fits_log.append(fits)

    return [fits_log, vectors_log]


if __name__ == '__main__':
    freeze_support()
    Process(target=optimize).start()

If the code is fine for you, I could provide a PR. Or I just start with the PR and we keep on discussing there. As you like it..

@darioizzo
Copy link
Member

Before opening the PR:

  1. I think the place Coding your own problem (UDP) is OK
  2. Your example, though, puts too much emphasis on the solution method. Since the tutorial is on coding the UDP, not solving it, I would just show the solution without the use of multiprocessing etc. just call pop=algo.evolve(pop)

@ckaldemeyer
Copy link
Contributor Author

Before opening the PR:

I think the place Coding your own problem (UDP) is OK
Your example, though, puts too much emphasis on the solution method. Since the tutorial is on coding the UDP, not solving it, I would just show the solution without the use of multiprocessing etc. just call pop=algo.evolve(pop)

Allright. I'll then provide a PR!

@darioizzo
Copy link
Member

Cool, thanks!

@ckaldemeyer
Copy link
Contributor Author

For PR, see: #191

@sowuy
Copy link

sowuy commented Jan 4, 2023

Hi @ckaldemeyer , thanks for your great example. Could you elaborate on the reasons why you evolve your archipelago multiple times (10x) in your multiprocessing example ? Knowing that the nsga algorithm is already working on 10 generations ? Is it a requirement that the number of evolutions should match the number of generations ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants