# -*- coding: utf-8 -*-
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
#
# Copyright (c) 2017 Vesa Ojalehto
"""
Synchronous NIMBUS method
Miettinen, K. & Mäkelä, M. M.
Synchronous approach in interactive multiobjective optimization
European Journal of Operational Research, 2006, 170, 909-922
"""
import logging
from typing import List
import numpy as np
from desdeo.core.ResultFactory import IterationPointFactory
from desdeo.optimization.OptimizationProblem import (
NIMBUSAchievementProblem,
NIMBUSGuessProblem,
NIMBUSProblem,
NIMBUSStomProblem,
)
from desdeo.result.Result import ResultSet
from .base import InteractiveMethod
[docs]class NIMBUS(InteractiveMethod):
"""'
Abstract class for optimization methods
Attributes
----------
_preference : ClNIMBUSClassificationdefault:None)
Preference, i.e., classification information information for current iteration
"""
__SCALARS = ["NIM", "ACH", "GUESS", "STOM"]
[docs] def __init__(self, problem, method_class):
super().__init__(problem, method_class)
self._factories = [
IterationPointFactory(self.method_class(prob_cls(self.problem)))
for prob_cls in [
NIMBUSProblem,
NIMBUSAchievementProblem,
NIMBUSGuessProblem,
NIMBUSStomProblem,
]
]
self._classification = None
self._problem = problem
self.selected_solution = None
[docs] def next_iteration(self, *args, **kwargs):
"""
Generate the next iteration's solutions using the DM's preferences and
the NIMBUS scalarization functions.
Parameters
----------
preference: NIMBUSClassification
Preference classifications obtained from the DM
scalars: list of strings
List containing one or more of the scalarizing functions: NIM, ACH, GUESS, STOM
num_scalars: number
The number of scalarizing functions to use (mutually exclusive with `scalars`)
"""
return super().next_iteration(*args, **kwargs)
[docs] def _next_iteration(self, *args, **kwargs) -> ResultSet:
try:
self._classification = kwargs["preference"]
except KeyError:
logging.error("Failed to obtain preferences for NIMBUS method")
if "scalars" in kwargs:
self._scalars = kwargs["scalars"]
elif "num_scalars" in kwargs:
num_scalars = int(kwargs["num_scalars"])
self._scalars = self.__SCALARS[:num_scalars]
else:
self._scalars = self.__SCALARS
po = []
for scalar in self._scalars:
po.append(
self._factories[self.__SCALARS.index(scalar)].result(
self._classification, self.selected_solution
)
)
return ResultSet(po, self._scalars)
[docs] def _get_ach(self):
return self._factories[self.__SCALARS.index("ACH")]
[docs] def _init_iteration(self, *args, **kwargs) -> ResultSet:
from desdeo.preference import NIMBUSClassification
ref = (np.array(self.problem.nadir) - np.array(self.problem.ideal)) / 2
cls = []
# Todo calculate ideal and nadir values
for v in ref:
cls.append(("<=", v))
self.selected_solution = self._get_ach().result(
NIMBUSClassification(self, cls), None
)
return ResultSet([self.selected_solution])
[docs] def between(self, objs1: List[float], objs2: List[float], n=1):
"""
Generate `n` solutions which attempt to trade-off `objs1` and `objs2`.
Parameters
----------
objs1
First boundary point for desired objective function values
objs2
Second boundary point for desired objective function values
n
Number of solutions to generate
"""
from desdeo.preference.base import ReferencePoint
objs1_arr = np.array(objs1)
objs2_arr = np.array(objs2)
segments = n + 1
diff = objs2_arr - objs1_arr
solutions = []
for x in range(1, segments):
btwn_obj = objs1_arr + float(x) / segments * diff
solutions.append(
self._get_ach().result(ReferencePoint(self, btwn_obj), None)
)
return ResultSet(solutions)