# Solving the river pollution problem using NIMBUS¶

Welcome to this notebook demonstrating the DESDEO interactive multi-objective optimisation framework.

In this notebook we will consider a toy problem dealing with water quality management, and the pollution produced by a fishery. We want to maximise the water quality as measured in two locations: the fishery and a downstream city (the measurements are based on dissolved oxygen concentration). Additionally, we want to maximise the ROI (Return On Investment) of the fishery. Simultaneously we want to minimise the additional tax which residents of the city must pay.

To run this example, you need to run each individual code block (the ones saying In [ ]: to their left) by clicking on each one and then clicking "Run". Please refer to the background section of the documentation for information about NIMBUS.

## Imports¶

This first code snippet simply imports the parts of DESDEO we need for this notebook. We import optimisation methods, NIMBUS and the RiverPollution problem definition from the desdeo module, and visualisation and preference selection tools and widgets from the desdeo_vis module.

In [ ]:
from desdeo.method.NIMBUS import NIMBUS
from desdeo.optimization import SciPyDE
from desdeo.problem.toy import RiverPollution

from desdeo_vis.widget import NimbusPrefWidget, ParplotWidget


## Initial iteration and preference selection¶

First we initialise the RiverPollution problem and the NIMBUS solution method. Then we get an initial result. We can plot solutions at any time using ParplotWidget.

In [ ]:
problem = RiverPollution()
method = NIMBUS(problem, SciPyDE)
results = method.init_iteration()

ParplotWidget(results.objective_vars, problem)


Next, we will give our first preference based on NIMBUS. We construct a NimbusPrefWidget, save it to a variable and display it. You can now specify your preferences using the displayed widget. If you're not sure how to use it, read the documentation on classification in NIMBUS.

In [ ]:
pref = NimbusPrefWidget(results.objective_vars, problem)
pref


## Solutions based on preference¶

We can now generate a new set of results based on this preference. Note that this will raise an InvalidNimbusPreferencesException if you run it while the above preferences are invalid.

In [ ]:
results2_all = method.next_iteration(preference=pref.nimbus_clf(method))

ParplotWidget(results2_all.objective_vars, problem)


We might choose to generate less extra solutions...

In [ ]:
results2_less = method.next_iteration(preference=pref.nimbus_clf(method), num_scalars=2)

ParplotWidget(results2_less.objective_vars, problem)


We can also choose a subset of scalarization functions from NIM, ACH, GUESS, STOM. These are the NIMBUS scalarization function and the NIMBUS version of the achievement, guess and satisficing trade-off functions respectively.

In [ ]:
results2_spec = method.next_iteration(preference=pref.nimbus_clf(method), scalars=['NIM', 'GUESS', 'STOM'])

ParplotWidget(results2_spec.objective_vars, problem)


## Generating intermediate solutions¶

If none of these solutions exactly satisfy us, we can generate and view solutions between two solutions we've generated so far. Here we generate 4 solutions between the solutions generated by the NIMBUS and GUESS scalarisation functions.

In [ ]:
results3 = method.between(results2_spec.objective_vars[0], results2_spec.objective_vars[1], 4)

ParplotWidget(results3.objective_vars, problem)