Source code for discrete_optimization.generic_tasks_tools.multimode

from __future__ import annotations

from abc import abstractmethod
from enum import Enum
from typing import Any

from discrete_optimization.generic_tasks_tools.base import (
    Task,
    TasksCpSolver,
    TasksProblem,
    TasksSolution,
)


[docs] class ModeConstraintType(Enum): SORTED_IMPLICATION = 0 UNORDERED = 1
import logging logger = logging.getLogger(__name__)
[docs] class MultimodeSolution(TasksSolution[Task]): """Class inherited by a solution exposing tasks modes.""" problem: MultimodeProblem[Task]
[docs] @abstractmethod def get_mode(self, task: Task) -> int: """Retrieve mode found for given task. Args: task: Returns: """ ...
[docs] def check_mode_constraint(self) -> bool: if len(self.problem.get_mode_constraints()) == 0: return True for constraint in self.problem.get_mode_constraints(): # First task that mode_constraint = constraint[0] list_task_mode = constraint[1] if mode_constraint == ModeConstraintType.SORTED_IMPLICATION: t0, m0 = list_task_mode[0] if self.get_mode(t0) == m0: for i in range(1, len(list_task_mode)): t, m = list_task_mode[i] if self.get_mode(t) != m: logger.debug( f"Mode constraint not satisfied, mode of {t}!={m}" ) return False if mode_constraint == ModeConstraintType.UNORDERED: b = (not any(self.get_mode(t) == m for t, m in list_task_mode)) or all( self.get_mode(t) == m for t, m in list_task_mode ) if not b: logger.debug(f"Mode constraint not satisfied, {list_task_mode}") return True
[docs] class MultimodeProblem(TasksProblem[Task]): """Class inherited by a solution exposing tasks modes."""
[docs] @abstractmethod def get_task_modes(self, task: Task) -> set[int]: """Retrieve mode found for given task. Args: task: Returns: """ ...
@property def is_multimode(self) -> bool: return self.max_number_of_mode > 1 @property def max_number_of_mode(self) -> int: return max(len(self.get_task_modes(task)) for task in self.tasks_list)
[docs] def get_mode_constraints( self, ) -> list[tuple[ModeConstraintType, list[tuple[Task, int]]]]: """ An element of the list is a tuple of (ModeConstraintType, list of (task,mode)) that implies the other choice of mode. For example (SORTED_IMPLICATION, [(T1, 1), (T2, 2), (T3, 1)]) means : if T1 is in mode 1, T2 is in mode 2, T3 is in mode 1.. This can be useful to model mode choice that has an influence on the future mode choice. For example, in an assembly line if we choose a given station path for a product, it should stay on it ! if mode_constraint_type == ModeConstraintType.SORTED_IMPLICATION: then the constraint is not active only when T1 is in mode 1, it should be true if any of the task,mode is active. So if mode(T2)==2 then the other mode are also forced! :return: """ return []
[docs] class WithoutModeConstraintMultimodeProblem(MultimodeProblem[Task]):
[docs] def get_mode_constraints( self, ) -> list[tuple[ModeConstraintType, list[tuple[Task, int]]]]: return []
[docs] class SinglemodeProblem(MultimodeProblem[Task]): @property def default_mode(self): """Default single mode. To be overriden when default value has more sense with another value (ex: in rcpsp, default mode is 1) """ return 0
[docs] def get_task_modes(self, task: Task) -> set[int]: return {self.default_mode}
@property def is_multimode(self) -> bool: return False @property def max_number_of_mode(self) -> int: return 1
[docs] class WithoutModeConstraintSingleModeProblem( SinglemodeProblem[Task], WithoutModeConstraintMultimodeProblem[Task] ): pass
[docs] class SinglemodeSolution(MultimodeSolution[Task]): problem: SinglemodeProblem[Task]
[docs] def get_mode(self, task: Task) -> int: return self.problem.default_mode
[docs] class MultimodeCpSolver(TasksCpSolver[Task]): """Class inherited by a solver managing constraints on tasks modes.""" problem: MultimodeProblem[Task]
[docs] @abstractmethod def add_constraint_on_task_mode(self, task: Task, mode: int) -> list[Any]: """Add constraint on task mode The mode of `task` is fixed to `mode`. Args: task: mode: Returns: resulting constraints """ ...