<jupyter_start><jupyter_text>NAUTILUS Navigator exampleThis example goes through the basic functionalities of the NAUTILUS Navigator method.We will consider a simple 2D Pareto front which we will define next alongside the method itself. Both objectives are to be minimized.Because of the nature of navigation based interactive optimization methods, the idea of NAUTILUS Navigator is best demonstrated using some graphical user interface. One such interface can be found [online](https://dash.misitano.xyz).<jupyter_code>import numpy as np 
from desdeo_mcdm.interactive.NautilusNavigator import NautilusNavigator

# half of a parabola to act as a Pareto front
f1 = np.linspace(1, 100, 50)
f2 = f1[::-1] ** 2

front = np.stack((f1, f2)).T
ideal = np.min(front, axis=0)
nadir = np.max(front, axis=0)

method = NautilusNavigator((front), ideal, nadir)<jupyter_output><empty_output><jupyter_text>To start, we can invoke the `start` method.<jupyter_code>req_first = method.start()

print(req_first)
print(req_first.content.keys())<jupyter_output><desdeo_mcdm.interactive.NautilusNavigator.NautilusNavigatorRequest object at 0x7ff8807cfd60>
dict_keys(['message', 'ideal', 'nadir', 'reachable_lb', 'reachable_ub', 'reachable_idx', 'step_number', 'steps_remaining', 'distance', 'allowed_speeds', 'current_speed', 'navigation_point'])<jupyter_text>The returned object is a NautilusNavigatorRequest. The keys should give an idea of what the contents of the request are. We will explain most of the in this example.At the moment, the `nadir`, `reachable_lb` and `reachable_ub` are most interesting to us. Navigation starts from the nadir and will proceed toward the Pareto optimal front enclosed between the limits defined in `reachable_lb` and `reachable_ub`.To interact with the method, we must fill out the `response` member of `req`. Let's see the contents of the message in `req` next.<jupyter_code>print(req_first.content["message"])<jupyter_output>Please supply aspirations levels for each objective between the upper and lower bounds as `reference_point`. Specify a speed between 1-5 as `speed`. If going to a previous step is desired, please set `go_to_previous` to True, otherwise it should be False. Lastly, if stopping is desired, `stop` should be True, otherweise it should be set to False.<jupyter_text>We should define the required values and set them as keys of a dictionary. Before that, it is useful to see the bounds to know the currently feasible objective values.<jupyter_code>print(req_first.content["reachable_lb"])
print(req_first.content["reachable_ub"])
reference_point = np.array([50, 6000])
go_to_previous = False
stop = False
speed = 1

response = dict(reference_point=reference_point, go_to_previous=False, stop=False, speed=1)<jupyter_output><empty_output><jupyter_text>`go_to_previous` should be set to `False` unless we desire going to a previous point. `stop` should be `True` if we wish to stop, otherwise it should be `False`. `speed` is the speed of the navigation. It is not used internally in the method. To continue, we call `iterate` with suppliying the `req` object with a defined `response` attribute. We should get a new request as a return value.<jupyter_code>req_first.response = response
req_snd = method.iterate(req_first)

print(req_snd.content["reachable_lb"])
print(req_snd.content["reachable_ub"])<jupyter_output>[3.02040816 9.12286547]
[  100. 10000.]<jupyter_text>We see that the bounds have narrowed down as they should.In reality, `iterate` should be called multiple times in succession with the same `response` contents. We can do this in a loop until the 30th step is computed, for example. NB: Steps are internally zero-index based.<jupyter_code>previous_requests = [req_first, req_snd]
req = req_snd
while method._step_number < 30:
    req.response = response
    req = method.iterate(req)

    previous_requests.append(req)

print(req.content["reachable_lb"])
print(req.content["reachable_ub"])
print(req.content["step_number"])<jupyter_output>[ 11.10204082 449.61307788]
[  81.81632653 8081.64306539]
30<jupyter_text>The region of reachable Pareto optimal solutions had narrowed down. Suppose now we wish to return to a previous step and change our preferences. Let's say, step 14.<jupyter_code># fetch the 14th step saved previously
req_14 = previous_requests[13]
print(req_14.content["reachable_lb"])
print(req_14.content["reachable_ub"])
print(req_14.content["step_number"])

req_14.response["go_to_previous"] = True
req_14.response["reference_point"] = np.array([50, 5000])
new_response = req_14.response<jupyter_output>[  5.04081633 123.25531029]
[  91.91836735 9208.16493128]
14<jupyter_text>When going to a previous point, the method assumes thath the state the method was in during that point is fully defined in the request object given to it when calling `iterate` with `go_to_previous` being `True`. This is why we saved the request previously in a list.<jupyter_code>req_14_new = method.iterate(req_14)
req = req_14_new

# remember to unser go_to_previous!
new_response["go_to_previous"] = False

# continue iterating for 16 steps
while method._step_number < 30:
    req.response = new_response
    req = method.iterate(req)

print("Old 30th step")
print(previous_requests[29].content["reachable_lb"])
print(previous_requests[29].content["reachable_ub"])
print(previous_requests[29].content["step_number"])

print("New 30th step")
print(req.content["reachable_lb"])
print(req.content["reachable_ub"])
print(req.content["step_number"])<jupyter_output>Old 30th step
[ 11.10204082 449.61307788]
[  81.81632653 8081.64306539]
30
New 30th step
[ 11.10204082 368.01332778]
[  81.81632653 8081.64306539]
30<jupyter_text>We can see a difference in the limits when we changed the preference point.To find the final solution, we can iterate till the end.<jupyter_code>while method._step_number < 100:
    req.response = new_response
    req = method.iterate(req)

print(req.content["reachable_idx"])<jupyter_output>19
{'fraction_non_alphanumeric': 0.09381805997654548, 'fraction_numerical': 0.049924610487518846, 'mean_word_length': 6.201447527141134, 'pattern_counts': {'":': 0, '<': 34, '<?xml version=': 0, '>': 31, 'https://': 1, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '9', 'dir': 'jupyter-structured-clean-dedup', 'id': '537549', 'license': 'MIT', 'n_tokens_mistral': 2076, 'n_tokens_neox': 1846, 'n_words': 625, 'path': 'docs/notebooks/nautilus_navigator.ipynb', 'repo_name': 'giomara-larraga/desdeo-mcdm', 'size': '5969'}