Skip to content

Commit

Permalink
Mco (#186)
Browse files Browse the repository at this point in the history
* Added support for parallel computing
mco_process.py -> mco_method_many_lambdas.py

* fix tests

* Исправлена асинхронная схема

* fix comments

* fix comment

* fix comment
  • Loading branch information
kozinove authored Mar 13, 2024
1 parent 589e30f commit 4ff351b
Show file tree
Hide file tree
Showing 12 changed files with 143 additions and 183 deletions.
16 changes: 5 additions & 11 deletions examples/MCO_Grishagin_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,40 +5,34 @@
import matplotlib.pyplot as plt

if __name__ == "__main__":
"""
"""

# создание объекта задачи
problem = Grishagin_mco(2, [3, 2])

# Формируем параметры решателя
params = SolverParameters(r=2.5, eps=0.01, iters_limit=16000, number_of_lambdas=50, start_lambdas=[[0, 1]], is_scaling=False)
params = SolverParameters(r=2.5, eps=0.01, iters_limit=16000,
number_of_lambdas=50, start_lambdas=[[0, 1]],
is_scaling=False, number_of_parallel_points=2,
async_scheme=True)

# Создаем решатель
solver = Solver(problem=problem, parameters=params)

# Добавляем вывод результатов в консоль
cfol = ConsoleOutputListener(mode='full')
solver.add_listener(cfol)

# Решение задачи
sol = solver.solve()

# вывод множества Парето (координаты - значения функций)
# output of the Pareto set (coordinates - function values)
var = [trial.point.float_variables for trial in sol.best_trials]
val = [[trial.function_values[i].value for i in range(2)]for trial in sol.best_trials ]
print("size pareto set: ", len(var))
for fvar, fval in zip(var, val):
print(fvar, fval)

# Точки для постороения графика множества Парето x[0]-x[1]
x1 = [trial.point.float_variables[0] for trial in sol.best_trials]
x2 = [trial.point.float_variables[1] for trial in sol.best_trials]

plt.plot(x1, x2, 'ro')
plt.show()

# Точки для постороения графика множества Парето y[0]-y[1]
fv1 = [trial.function_values[0].value for trial in sol.best_trials]
fv2 = [trial.function_values[1].value for trial in sol.best_trials]

Expand Down
1 change: 1 addition & 0 deletions iOpt/method/async_calculator.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ def _take_calculated_point(
self, block: bool
) -> tuple[SearchDataItem, SearchDataItem]:
newpoint = self.done_queue.get(block=block)
self.evaluate_method.copy_functionals(newpoint, newpoint)
oldpoint = self.waiting_oldpoints.pop(newpoint.get_x())
oldpoint.blocked = False
return newpoint, oldpoint
Expand Down
3 changes: 2 additions & 1 deletion iOpt/method/async_parallel_process.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ def __init__(
super(AsyncParallelProcess, self).__init__(
parameters, task, evolvent, search_data, method, listeners, calculator
)
self.calculator = AsyncCalculator(IndexMethodEvaluate(task), parameters)
from iOpt.method.solverFactory import SolverFactory
self.calculator = AsyncCalculator(SolverFactory.create_evaluate_method(task), parameters)

def do_global_iteration(self, number: int = 1) -> None:
done_trials = []
Expand Down
39 changes: 15 additions & 24 deletions iOpt/method/multi_objective_method.py → iOpt/method/mco_method.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from iOpt.evolvent.evolvent import Evolvent
from iOpt.method.calculator import Calculator
from iOpt.method.mixed_integer_method import MixedIntegerMethod
from iOpt.method.multi_objective_optim_task import MultiObjectiveOptimizationTask
from iOpt.method.mco_optim_task import MCOOptimizationTask
from iOpt.method.search_data import SearchDataItem, SearchData
from iOpt.solver_parametrs import SolverParameters
from iOpt.trial import FunctionValue, FunctionType, Trial
Expand All @@ -19,14 +19,14 @@ class TypeOfParetoRelation(Enum):
NONCOMPARABLE = 0
NONDOMINATED = -1

class MultiObjectiveMethod(MixedIntegerMethod):
class MCOMethod(MixedIntegerMethod):
"""
Класс Method содержит реализацию Алгоритма Глобального Поиска
The MCOMethod class contains an implementation of the Global Search Algorithm for multi-criteria problems
"""

def __init__(self,
parameters: SolverParameters,
task: MultiObjectiveOptimizationTask,
task: MCOOptimizationTask,
evolvent: Evolvent,
search_data: SearchData,
calculator: Calculator):
Expand All @@ -43,10 +43,6 @@ def set_min_delta(self, min_delta) -> None:
self.is_recalc_all_convolution = True

def recalc_all_convolution(self) -> None:
# Флаг используется при
# 1. Запуске новой задачи (продолжение вычислений с новой сверткой)
# 2. Обновлении минимума и максимума одного из критериев
# По флагу необходимо пересчитать все свертки, затем все R и перрезаполнить очередь
if self.is_recalc_all_convolution is not True:
return

Expand All @@ -71,14 +67,14 @@ def calculate_iteration_point(self) -> Tuple[SearchDataItem, SearchDataItem]: #
if self.is_recalc_all_convolution is True:
self.recalc_all_convolution()

return super(MultiObjectiveMethod, self).calculate_iteration_point()
return super(MCOMethod, self).calculate_iteration_point()


def update_optimum(self, point: SearchDataItem) -> None:
r"""
Обновляет оценку оптимума.
Updates the estimate of the optimum.
:param point: точка нового испытания.
:param point: new trial point.
"""
if self.best is None or self.best.get_index() < point.get_index() or (
self.best.get_index() == point.get_index() and point.get_z() < self.best.get_z()):
Expand Down Expand Up @@ -167,10 +163,10 @@ def update_min_max_value(self,

def check_stop_condition(self) -> bool:
r"""
Проверка условия остановки.
Алгоритм должен завершить работу, когда достигнута точность eps или превышен лимит итераций.
Checking the stopping condition.
The algorithm must terminate when the eps accuracy is reached or the iteration limit is exceeded.
:return: True, если выполнен критерий остановки; False - в противном случае.
:return: True if the stopping criterion is met; False - otherwise.
"""
if self.min_delta < self.parameters.eps or self.iterations_count >= self.max_iter_for_convolution:
self.stop = True
Expand All @@ -181,12 +177,11 @@ def check_stop_condition(self) -> bool:

def calculate_m(self, curr_point: SearchDataItem, left_point: SearchDataItem) -> None:
r"""
Вычисление оценки константы Гельдера между между curr_point и left_point.
Compute the estimate of the Holder constant between curr_point and left_point.
:param curr_point: правая точка интервала
:param left_point: левая точка интервала
:param curr_point: right point of the interval
:param left_point: left point of the interval
"""
# Обратить внимание на вычисление расстояния, должен использоваться метод CalculateDelta
if curr_point is None:
print("CalculateM: curr_point is None")
raise RuntimeError("CalculateM: curr_point is None")
Expand All @@ -196,10 +191,9 @@ def calculate_m(self, curr_point: SearchDataItem, left_point: SearchDataItem) ->
if index < 0:
return
m = 0.0
if left_point.get_index() == index: # А если не равны, то надо искать ближайший левый/правый с таким индексом
if left_point.get_index() == index:
m = abs(left_point.get_z() - curr_point.get_z()) / curr_point.delta
else:
# Ищем слева
other_point = left_point
while (other_point is not None) and (other_point.get_index() < curr_point.get_index()):
if other_point.get_discrete_value_index() == curr_point.get_discrete_value_index():
Expand All @@ -212,9 +206,8 @@ def calculate_m(self, curr_point: SearchDataItem, left_point: SearchDataItem) ->
m = abs(other_point.function_values[index].value - curr_point.get_z()) / \
self.calculate_delta(other_point, curr_point, self.dimension)

# Ищем справа
other_point = left_point.get_right()
if other_point is not None and other_point is curr_point: # возможно только при пересчёте M
if other_point is not None and other_point is curr_point:
other_point = other_point.get_right()
while (other_point is not None) and (other_point.get_index() < curr_point.get_index()):
if other_point.get_discrete_value_index() == curr_point.get_discrete_value_index():
Expand All @@ -225,8 +218,6 @@ def calculate_m(self, curr_point: SearchDataItem, left_point: SearchDataItem) ->

if other_point is not None and other_point.get_index() >= 0 \
and other_point.get_discrete_value_index() == curr_point.get_discrete_value_index():
#нужна проверка на индекс, если ограничение - то формула другая
# TODO: изменить формулу для задач с ограничениями
m = max(m, abs(curr_point.get_z() - other_point.get_z()) / \
self.calculate_delta(curr_point, other_point, self.dimension))

Expand Down
71 changes: 71 additions & 0 deletions iOpt/method/mco_method_evaluate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import copy
import sys

from iOpt.method.index_method_evaluate import IndexMethodEvaluate
from iOpt.method.optim_task import OptimizationTask
from iOpt.method.search_data import SearchDataItem
from iOpt.trial import FunctionValue, FunctionType
from iOpt.method.optim_task import TypeOfCalculation


class MCOMethodEvaluate(IndexMethodEvaluate):
"""
The MCOMethodEvaluate class contains an implementation of the method for evaluate
the criterion value of the Global Search Algorithm
"""

def __init__(self, task: OptimizationTask):
super().__init__(task)

def calculate_functionals(self, point: SearchDataItem) -> SearchDataItem:

try:
number_of_constraints = self.task.problem.number_of_constraints
for i in range(number_of_constraints):
point.function_values[i] = FunctionValue(FunctionType.CONSTRAINT, i)
point = self.task.calculate(point, i)
point.set_z(point.function_values[i].value)
point.set_index(i)
if point.get_z() > 0:
return point

for i in range(self.task.problem.number_of_objectives):
point.function_values[number_of_constraints+i] = FunctionValue(FunctionType.OBJECTIV, i)
point = self.task.calculate(point, number_of_constraints+i)

point = self.task.calculate(point, -1, TypeOfCalculation.CONVOLUTION)
point.set_index(number_of_constraints)

except Exception:
point.set_z(sys.float_info.max)
point.set_index(-10)

return point

def copy_functionals(self, dist_point: SearchDataItem, src_point: SearchDataItem):
r"""
Copy the search trial
:param dist_point: point to which the trial values are copied.
:param src_point: point with trial results.
"""

dist_point.function_values = copy.deepcopy(src_point.function_values)
dist_point.set_index(src_point.get_index())
self.update_min_max_value(src_point)
if dist_point.get_index() == self.task.problem.number_of_constraints:
dist_point = self.task.calculate(dist_point, -1, TypeOfCalculation.CONVOLUTION)

def update_min_max_value(self,
data_item: SearchDataItem):
# If the minimum and maximum values have not yet been changed after initialization
if (self.task.min_value[0] == self.task.max_value[0] and self.task.min_value[0] == 0):
self.task.min_value = [fv.value for fv in data_item.function_values]
self.task.max_value = [fv.value for fv in data_item.function_values]
else:
# comparing the value of the current min max with the values of the new point
for i in range(0, self.task.problem.number_of_objectives):
if self.task.min_value[i] > data_item.function_values[i].value:
self.task.min_value[i] = data_item.function_values[i].value
if self.task.max_value[i] < data_item.function_values[i].value:
self.task.max_value[i] = data_item.function_values[i].value
Loading

0 comments on commit 4ff351b

Please sign in to comment.