Module ortools_utils.model_indexation.objective

Those classes enable us to represent a problem with multiple objectives Partial objectives are either maximised or minimised sequentially or combined with coefficients Objectives are looked at by increasing order of rank

Expand source code
"""
Those classes enable us to represent a problem with multiple objectives
Partial objectives are either maximised or minimised sequentially or combined with coefficients
Objectives are looked at by increasing order of rank

"""

import logging

logger = logging.getLogger(__name__)


class PartObjective:
    """
    Part of the objectives of the model
    """

    def __init__(self, idx: str, priority: int):
        """
        :param idx: unique (at this stage) identifier of the partial objective
        :param priority: rank at which this objective will be looked at
        """
        self._priority = priority
        self._best_value = None
        self._idx = idx

    def get_id(self) -> str:
        return self._idx

    def priority(self) -> int:
        return self._priority

    def best_value(self) -> int:
        return self._best_value

    def set_opt_value(self, val) -> None:
        self._best_value = val


class BonusConstraint(PartObjective):
    """
    Constraint that is not mandatory but grants a bonus if respected
    """
    def __init__(self, model, constraint, idx: str, coef: int, priority: int):
        super().__init__(idx, priority)
        self.size = 1
        self.coef = coef
        boolvar = model.NewBoolVar('is_respected {} - {}'.format(idx, self.size))
        model.Add(constraint).OnlyEnforceIf(boolvar)
        self._list_constraints = [(constraint, boolvar)]

    def add(self, model, constraint):
        self.size = self.size + 1
        boolvar = model.NewBoolVar('is_respected {} - {}'.format(self._idx, self.size))
        model.Add(constraint).OnlyEnforceIf(boolvar)
        self._list_constraints.append((constraint, boolvar))

    def list_constraints(self):
        return self._list_constraints

    def size(self):
        return self.size


class MinObjective(PartObjective):
    """
    Part of an objective that we want to minimize
    """

    def __init__(self, expression, idx: str, priorite: int):
        """
        param expression: Linear expression that we want to MINIMIZE (eg. sum(Xij))
        param priorite: Rank of this objective
        """
        super().__init__(idx, priorite)
        self._expression = expression

    def expression(self):
        return self._expression


class MaxObjective(PartObjective):
    """
    Part of an objective that we want to maximize
    """

    def __init__(self, expression, idx: str, priority: int):
        """
        :param expression: Linear expression that we want to MAXIMIZE (eg. sum(Xij))

        :param idx: unique identifier of this partial objective
        """
        super().__init__(idx, priority)
        self._expression = expression

    def expression(self):
        return self._expression


class Objective:
    """
    The Objective class enables us to keep in memory the different parts of optimisation
    """
    def __init__(self):
        self.dic_elt = dict()
        self.dic_ids = dict()
        self._reach_absolute_max = dict()

    def get_id(self, idx):
        if idx in self.dic_ids:
            return self.dic_ids[idx]
        return None

    def add_part_objective(self, idx, objective: PartObjective, max_must_be_absolute: bool):

        priorite = objective.priority()
        if priorite not in self.dic_elt:
            self.dic_elt[priorite] = [objective]
        else:
            self.dic_elt[priorite].append(objective)

        if max_must_be_absolute:
            self._reach_absolute_max[priorite] = True

        self.dic_ids[idx] = objective

    def get_list_priority(self):
        """Returns the list of priorities in decreasing order"""
        return sorted(list(self.dic_elt.keys()))

    def must_reach_absolute_max(self, priority):
        """Returns whether or not we must go to the end of the optimization for this specific step"""
        return priority in self._reach_absolute_max

    def is_empty(self):
        return len(self.dic_elt) == 0

    def get_all_obj(self):
        return list(self.dic_ids.values())

    def best_value_at_rank(self, rank):
        """Computes the current optimisation value at a given rank"""
        if rank not in self.dic_elt:
            return None
        score = 0
        for obj in self.dic_elt[rank]:
            if isinstance(obj, BonusConstraint):
                score = score + (obj.coef * obj.best_value())
            elif isinstance(obj, MinObjective):
                score = score - obj.best_value()
            elif isinstance(obj, MaxObjective):
                score = score + obj.best_value()
        return score

Classes

class BonusConstraint (model, constraint, idx: str, coef: int, priority: int)

Constraint that is not mandatory but grants a bonus if respected

:param idx: unique (at this stage) identifier of the partial objective :param priority: rank at which this objective will be looked at

Expand source code
class BonusConstraint(PartObjective):
    """
    Constraint that is not mandatory but grants a bonus if respected
    """
    def __init__(self, model, constraint, idx: str, coef: int, priority: int):
        super().__init__(idx, priority)
        self.size = 1
        self.coef = coef
        boolvar = model.NewBoolVar('is_respected {} - {}'.format(idx, self.size))
        model.Add(constraint).OnlyEnforceIf(boolvar)
        self._list_constraints = [(constraint, boolvar)]

    def add(self, model, constraint):
        self.size = self.size + 1
        boolvar = model.NewBoolVar('is_respected {} - {}'.format(self._idx, self.size))
        model.Add(constraint).OnlyEnforceIf(boolvar)
        self._list_constraints.append((constraint, boolvar))

    def list_constraints(self):
        return self._list_constraints

    def size(self):
        return self.size

Ancestors

Methods

def add(self, model, constraint)
Expand source code
def add(self, model, constraint):
    self.size = self.size + 1
    boolvar = model.NewBoolVar('is_respected {} - {}'.format(self._idx, self.size))
    model.Add(constraint).OnlyEnforceIf(boolvar)
    self._list_constraints.append((constraint, boolvar))
def list_constraints(self)
Expand source code
def list_constraints(self):
    return self._list_constraints
def size(self)
Expand source code
def size(self):
    return self.size
class MaxObjective (expression, idx: str, priority: int)

Part of an objective that we want to maximize

:param expression: Linear expression that we want to MAXIMIZE (eg. sum(Xij))

:param idx: unique identifier of this partial objective

Expand source code
class MaxObjective(PartObjective):
    """
    Part of an objective that we want to maximize
    """

    def __init__(self, expression, idx: str, priority: int):
        """
        :param expression: Linear expression that we want to MAXIMIZE (eg. sum(Xij))

        :param idx: unique identifier of this partial objective
        """
        super().__init__(idx, priority)
        self._expression = expression

    def expression(self):
        return self._expression

Ancestors

Methods

def expression(self)
Expand source code
def expression(self):
    return self._expression
class MinObjective (expression, idx: str, priorite: int)

Part of an objective that we want to minimize

param expression: Linear expression that we want to MINIMIZE (eg. sum(Xij)) param priorite: Rank of this objective

Expand source code
class MinObjective(PartObjective):
    """
    Part of an objective that we want to minimize
    """

    def __init__(self, expression, idx: str, priorite: int):
        """
        param expression: Linear expression that we want to MINIMIZE (eg. sum(Xij))
        param priorite: Rank of this objective
        """
        super().__init__(idx, priorite)
        self._expression = expression

    def expression(self):
        return self._expression

Ancestors

Methods

def expression(self)
Expand source code
def expression(self):
    return self._expression
class Objective

The Objective class enables us to keep in memory the different parts of optimisation

Expand source code
class Objective:
    """
    The Objective class enables us to keep in memory the different parts of optimisation
    """
    def __init__(self):
        self.dic_elt = dict()
        self.dic_ids = dict()
        self._reach_absolute_max = dict()

    def get_id(self, idx):
        if idx in self.dic_ids:
            return self.dic_ids[idx]
        return None

    def add_part_objective(self, idx, objective: PartObjective, max_must_be_absolute: bool):

        priorite = objective.priority()
        if priorite not in self.dic_elt:
            self.dic_elt[priorite] = [objective]
        else:
            self.dic_elt[priorite].append(objective)

        if max_must_be_absolute:
            self._reach_absolute_max[priorite] = True

        self.dic_ids[idx] = objective

    def get_list_priority(self):
        """Returns the list of priorities in decreasing order"""
        return sorted(list(self.dic_elt.keys()))

    def must_reach_absolute_max(self, priority):
        """Returns whether or not we must go to the end of the optimization for this specific step"""
        return priority in self._reach_absolute_max

    def is_empty(self):
        return len(self.dic_elt) == 0

    def get_all_obj(self):
        return list(self.dic_ids.values())

    def best_value_at_rank(self, rank):
        """Computes the current optimisation value at a given rank"""
        if rank not in self.dic_elt:
            return None
        score = 0
        for obj in self.dic_elt[rank]:
            if isinstance(obj, BonusConstraint):
                score = score + (obj.coef * obj.best_value())
            elif isinstance(obj, MinObjective):
                score = score - obj.best_value()
            elif isinstance(obj, MaxObjective):
                score = score + obj.best_value()
        return score

Methods

def add_part_objective(self, idx, objective: PartObjective, max_must_be_absolute: bool)
Expand source code
def add_part_objective(self, idx, objective: PartObjective, max_must_be_absolute: bool):

    priorite = objective.priority()
    if priorite not in self.dic_elt:
        self.dic_elt[priorite] = [objective]
    else:
        self.dic_elt[priorite].append(objective)

    if max_must_be_absolute:
        self._reach_absolute_max[priorite] = True

    self.dic_ids[idx] = objective
def best_value_at_rank(self, rank)

Computes the current optimisation value at a given rank

Expand source code
def best_value_at_rank(self, rank):
    """Computes the current optimisation value at a given rank"""
    if rank not in self.dic_elt:
        return None
    score = 0
    for obj in self.dic_elt[rank]:
        if isinstance(obj, BonusConstraint):
            score = score + (obj.coef * obj.best_value())
        elif isinstance(obj, MinObjective):
            score = score - obj.best_value()
        elif isinstance(obj, MaxObjective):
            score = score + obj.best_value()
    return score
def get_all_obj(self)
Expand source code
def get_all_obj(self):
    return list(self.dic_ids.values())
def get_id(self, idx)
Expand source code
def get_id(self, idx):
    if idx in self.dic_ids:
        return self.dic_ids[idx]
    return None
def get_list_priority(self)

Returns the list of priorities in decreasing order

Expand source code
def get_list_priority(self):
    """Returns the list of priorities in decreasing order"""
    return sorted(list(self.dic_elt.keys()))
def is_empty(self)
Expand source code
def is_empty(self):
    return len(self.dic_elt) == 0
def must_reach_absolute_max(self, priority)

Returns whether or not we must go to the end of the optimization for this specific step

Expand source code
def must_reach_absolute_max(self, priority):
    """Returns whether or not we must go to the end of the optimization for this specific step"""
    return priority in self._reach_absolute_max
class PartObjective (idx: str, priority: int)

Part of the objectives of the model

:param idx: unique (at this stage) identifier of the partial objective :param priority: rank at which this objective will be looked at

Expand source code
class PartObjective:
    """
    Part of the objectives of the model
    """

    def __init__(self, idx: str, priority: int):
        """
        :param idx: unique (at this stage) identifier of the partial objective
        :param priority: rank at which this objective will be looked at
        """
        self._priority = priority
        self._best_value = None
        self._idx = idx

    def get_id(self) -> str:
        return self._idx

    def priority(self) -> int:
        return self._priority

    def best_value(self) -> int:
        return self._best_value

    def set_opt_value(self, val) -> None:
        self._best_value = val

Subclasses

Methods

def best_value(self) ‑> int
Expand source code
def best_value(self) -> int:
    return self._best_value
def get_id(self) ‑> str
Expand source code
def get_id(self) -> str:
    return self._idx
def priority(self) ‑> int
Expand source code
def priority(self) -> int:
    return self._priority
def set_opt_value(self, val) ‑> None
Expand source code
def set_opt_value(self, val) -> None:
    self._best_value = val