Module ortools_utils.model_indexation.constraints
Module defining different private classes that enable us to track constraints
Expand source code
"""
Module defining different private classes that enable us to track constraints
"""
from abc import ABC, abstractmethod
from functools import total_ordering
from typing import *
import logging
logger = logging.getLogger(__name__)
DEBUG_EXPLAINATIONS = False
class ModelisationError(Exception):
"""Raised when the user makes a mistake while creating a ModelWithIndexing"""
pass
@total_ordering
class ElementaryConstraint (ABC):
"""
Class defining the smallest possible constraint for the model.
An Elementary Constraint can encompass several actual constraints, but these constraints will always be enforced or relaxed as a set
(at any given time, they are either all relaxed or all enforced)
It contains :
**constraint**: the name of the general type of constraint
**dict_dimensions**: the dimensions of the constraint block (dimension key and value on this dimension)
"""
def __init__(self, constraint, idx, **dict_dimensions):
"""
"""
self._constraint = constraint
self._dict_dimensions = dict_dimensions
self._dimensions = sorted(list(dict_dimensions.keys()))
self._first_id = idx
def dimensions(self):
return self._dimensions
def name_macroblock(self):
return self._constraint
def dim_value(self, dimension):
return self._dict_dimensions[dimension]
def __str__(self):
if not self._dimensions:
return self._constraint
else:
return (
self._constraint + "("
+ ", ".join(["%s = %s" % (k, str(self._dict_dimensions[k])) for k in self._dimensions]) + ")"
)
def __lt__(self, other):
# First we sort by name
if self.name_macroblock() != other.name_macroblock():
return self.name_macroblock() < other.name_macroblock()
# If they have the same name then they have the same dimensions
for d in self.dimensions():
if self.dim_value(d) != other.dim_value(d):
return self.dim_value(d) < other.dim_value(d)
def __hash__(self):
return hash(str(self))
def __eq__(self, other):
return hash(self) == hash(other)
def __repr__(self):
return str(self)
def first_id(self) -> int:
return self._first_id
@abstractmethod
def list_idx(self) -> List[int]:
pass
def size(self) -> int:
return len(self.list_idx())
class UsualConstraint(ElementaryConstraint):
"""
Class defining a single constraint
The constraint is of the usual type (eg. model.Add(x + y < 2))
"""
def __init__(self, constraint, idx, **dict_dimensions):
super().__init__(constraint, idx, **dict_dimensions)
self._idx = [idx]
def add_id(self, idx):
self._idx.append(idx)
def list_idx(self) -> List[int]:
return self._idx
class ConstantConstraint(ElementaryConstraint):
"""
Class defining a single "constant" constraint
The constraint is in fact a constant (eg. x = model.NewConstant(1))
"""
def __init__(self, constraint, idx, default_value, default_range, **dict_dimensions):
super().__init__(constraint, idx, **dict_dimensions)
self._idx = {idx: {"value": default_value, "range": default_range}}
def def_value(self, idx):
return self._idx[idx]["value"]
def def_range(self, idx):
return self._idx[idx]["range"]
def add_id(self, idx, default_value, default_range):
self._idx[idx] = {"value": default_value, "range": default_range}
def print_specifique(self):
return str(self)
def list_idx(self) -> List[int]:
return list(self._idx.keys())
class MacroBlock:
"""
Class defining a set of elementary constraints (eg. pos)
It contains a set of BlocElementaryConstraint
A bloc can be projected on various dimensions
"""
def __init__(self, name, firstConstraint: ElementaryConstraint, is_constraint):
self._name = name
self._dimensions = firstConstraint.dimensions()
if not self._dimensions:
self._dimensions = []
if len(self._dimensions) == 0:
clef = ""
else:
clef = tuple([firstConstraint.dim_value(d) for d in self.dimensions()])
self.list_ec = {clef: firstConstraint}
self._is_true_constraint = is_constraint # True if true constraint, False if constant
self._explanations = dict()
self._dim_a_ignorer = []
def contenu(self) -> [ElementaryConstraint]:
return list(self.list_ec.values())
def to_named_block(self):
dim_peut_split = [d for d in self.dimensions() if d not in self._dim_a_ignorer]
return NamedBlock(self, [], dim_peut_split, list(self.list_ec.values()), self._is_true_constraint)
def name(self) -> str:
return self._name
def dimensions(self) -> [str]:
return self._dimensions
def do_not_split_on(self, *dim):
"""Indicates that we must not split on this dimension because it does not mean anything for the user"""
for d in dim:
if d in self.dimensions() and d not in self._dim_a_ignorer:
self._dim_a_ignorer.append(d)
def size(self) -> int:
return sum(ec.size() for ec in self.list_ec.values())
def is_true_constraint(self):
return self._is_true_constraint
def __repr__(self):
if self.is_true_constraint():
return "{} -- dim: {} -- size: {}".format(self.name(), self.dimensions(), self.size())
else:
return "{} (CONSTANTES) -- dim: {} -- size: {}".format(self.name(), self.dimensions(), self.size())
def ajoute_ec(self, nouvelleContrainte: ElementaryConstraint) -> ElementaryConstraint:
"""
Adds a new constraint to the block after checking that the model is consistent
"""
# Raises an error if is_true_constraint is not consistent
if self.is_true_constraint() and isinstance(nouvelleContrainte, ConstantConstraint):
raise ModelisationError("Le nom {} est utilisé à la fois pour des contraintes et des constantes".format(self.name()))
if not self.is_true_constraint() and isinstance(nouvelleContrainte, UsualConstraint):
raise ModelisationError("Le nom {} est utilisé à la fois pour des contraintes et des constantes".format(self.name()))
# Raises an error if dimensions are not consistent throughout the model for the same block name
if nouvelleContrainte.dimensions() != self.dimensions():
raise ModelisationError("Erreur de cohérence sur les dimensions de la contrainte {}. Le modèle a rencontré {} puis {}".format(self.name(),
self.dimensions(), nouvelleContrainte.dimensions()))
# Overloads the existing constraint if it exists with the same dimensions
clef = tuple([nouvelleContrainte.dim_value(d) for d in self.dimensions()])
if clef in self.list_ec:
vieilleContrainte = self.list_ec[clef]
if isinstance(vieilleContrainte, UsualConstraint):
vieilleContrainte.add_id(nouvelleContrainte.first_id())
return vieilleContrainte
elif isinstance(vieilleContrainte, ConstantConstraint):
assert isinstance(nouvelleContrainte, ConstantConstraint)
new_id = nouvelleContrainte.first_id()
new_val = nouvelleContrainte.def_value(new_id)
new_range = nouvelleContrainte.def_range(new_id)
vieilleContrainte.add_id(new_id, new_val, new_range)
return vieilleContrainte
# raise ModelisationError("La contrainte {} pour les dimensions {} est définie deux fois.".format(self.name(), clef))
else:
self.list_ec[clef] = nouvelleContrainte
return nouvelleContrainte
def project_on_dimensions(self, **dimensions) -> [ElementaryConstraint]:
"""Returns the list of BlocKElementaryConstraints that match the given dimensions"""
if len(dimensions.keys()) == 0:
return [ec for ec in self.list_ec.values()]
for k in dimensions.keys():
if k not in self._dimensions:
logger.info("La dimension {} n'existe pas. Les dimensions existantes sont {}.".format(k, self._dimensions))
return []
ma_liste = []
for ec in self.list_ec.values():
on_garde = True
for (k, v) in dimensions.items():
if ec.dim_value(k) != v:
on_garde = False
pass
if on_garde:
ma_liste.append(ec)
return ma_liste
def add_explanations(self, *sentences):
"""
Adds explanations to the macroblock in natural language
"""
for s in sentences:
dimensions_utilisees = []
for d in self.dimensions():
clef = '{' + d + '}'
if clef in s:
dimensions_utilisees.append(d)
mes_dim = '-'
for d in sorted(dimensions_utilisees):
mes_dim = mes_dim + d + '-'
self._explanations[mes_dim] = s
def get_explanation_for_key(self, key):
if key in self._explanations:
return self._explanations[key]
else:
return None
@total_ordering
class NamedBlock:
"""
Class which contains a list of elementary constraints
The main interest of this class is to be able to name easily this list
NamedBlock can have different granularities (from entire macroblock to single elementary constraint)
"""
def __init__(self, mon_macro_block, dim_already_split: [str], dim_not_yet_split: [str], contenu: [ElementaryConstraint], is_true_constraint):
self._macro_bloc = mon_macro_block
self._type_bloc = mon_macro_block.name()
self._dim_already_split = dim_already_split
self._dim_not_yet_split = dim_not_yet_split
self._contenu = contenu
self._is_true_constraint = is_true_constraint # True if true constraint, False if constant
def __lt__(self, other):
# First we sort by name
if self.type_bloc() != other.type_bloc():
return self.type_bloc() < other.type_bloc()
# Then we sort on how precise they have
if len(self.dimensions()) != len(other.dimensions()):
return self.dimensions() < other.dimensions()
# Then we split on the sentence
return self.elegant_name() < other.elegant_name()
def __hash__(self):
return hash(self.name())
def name(self):
if len(self.contenu()) == 0:
return 'Bloc vide'
if len(self._dim_already_split) == 0:
return self.type_bloc()
ec = self.contenu()[0]
key = None
for d in self._dim_already_split:
if key:
key = "{}, {} = {}".format(key, d, ec.dim_value(d))
else:
key = "{} = {}".format(d, ec.dim_value(d))
return "{} ({})".format(self.type_bloc(), key)
def contenu(self) -> [ElementaryConstraint]:
return self._contenu
def type_bloc(self):
return self._type_bloc
def dimensions(self):
return self._dim_already_split
def size(self):
return sum(ec.size() for ec in self._contenu)
def is_true_constraint(self):
return self._is_true_constraint
def can_be_split(self) -> bool:
return len(self._dim_not_yet_split) > 0
def to_dict(self) -> Dict:
"""Used for returning a dictionary instead of a NamedBlock object"""
my_dimensions = {}
example = self.contenu()[0]
for d in self._dim_already_split:
my_dimensions[d] = example.dim_value(d)
return {"sentence": self.elegant_name(),
"type": self._type_bloc,
"dimensions": my_dimensions}
def elegant_name(self):
if DEBUG_EXPLAINATIONS:
return '{} ({})'.format(self.name(), self.size())
if len(self.contenu()) == 0:
return 'Bloc vide'
example = self.contenu()[0]
mes_dim_actuelles = {}
for d in self._dim_already_split:
mes_dim_actuelles[d] = example.dim_value(d)
ma_clef = '-'
for d in sorted(list(mes_dim_actuelles.keys())):
ma_clef = ma_clef + d + '-'
my_sentence = self._macro_bloc.get_explanation_for_key(ma_clef)
if not my_sentence:
return self.name()
for d in mes_dim_actuelles:
dim_key = '{' + d + '}'
my_sentence = my_sentence.replace(dim_key, str(mes_dim_actuelles[d]))
return my_sentence
def __repr__(self):
# return self.name()
# return '{} ({})'.format(self._name, self.size())
return self.elegant_name()
def remove_dim_to_split(self):
self._dim_not_yet_split = [c for c in self._dim_not_yet_split[1:]]
def split_on_next_dimension(self):
"""
Returns this block split in several blocks with smaller granularity
"""
if len(self._dim_not_yet_split) == 0:
return None
nouv_dim = self._dim_not_yet_split[0]
logger.info('On scinde {} sur la dimension {}'.format(self.name(), nouv_dim))
dimensions = [d for d in self._dim_already_split] + [nouv_dim]
remaining_dim = [d for d in self._dim_not_yet_split if d != nouv_dim]
dico_val = {}
for ec in self.contenu():
assert isinstance(ec, ElementaryConstraint)
key = None
for d in dimensions:
if key:
key = "{}, {} = {}".format(key, d, ec.dim_value(d))
else:
key = "{} = {}".format(d, ec.dim_value(d))
if key in dico_val:
dico_val[key].append(ec)
else:
dico_val[key] = [ec]
liste_blocks_res = []
for key in dico_val:
b = NamedBlock(self._macro_bloc, dimensions, remaining_dim, dico_val[key], self._is_true_constraint)
liste_blocks_res.append(b)
return liste_blocks_res
class DictIndexConstraints:
"""
Class for managing the granularity of constraint blocks
It contains dictionaries that enable us to convert idx into elementary constraint and vice versa
It keeps in memory the granularity of the model
"""
def __init__(self):
self._dict_usual_constraints: Dict[int, UsualConstraint] = dict()
self._dict_constant_constraints: Dict[int, ConstantConstraint] = dict()
self._dict_variables = dict()
self._list_blocks = dict()
self._unmovable_usual_constraints = []
self._unmovable_constant_constraints = []
def print_status(self):
print('{} contraintes constantes'.format(len(self._dict_constant_constraints.keys())))
for cc in self._dict_constant_constraints.values():
print(cc.print_specifique())
print('{} contraintes usuelles'.format(len(self._dict_usual_constraints.keys())))
for uc in self._dict_usual_constraints.values():
print(uc)
def add_variable(self, variable, variable_idx: int):
"""Remembers a given variable, so that if later we define it as a constant, we know where it is in our Proto
"""
self._dict_variables[variable] = variable_idx
def get_variable_idx(self, variable):
"""Returns the idx of a variable in the Proto. Returns -1 if it does not exist."""
if variable in self._dict_variables:
return self._dict_variables[variable]
return -1
def get_list_variables(self):
return self._dict_variables.keys()
def add_usual_constraint(self, block: UsualConstraint, constraint_idx: int):
"""
Function that is called when the model is being created
Associates a block to an OR-TOOLS id
Updates the list of blocks within the model
:param block: constraint that must be added
:param constraint_idx: id of the constraints
"""
# Looks whether this constraint has already been defined
name_set = block.name_macroblock()
if name_set not in self._list_blocks:
self._list_blocks[name_set] = MacroBlock(name_set, block, True)
else:
block = self._list_blocks[name_set].ajoute_ec(block)
# Keeps in memory the link between the elementary constraint and the idx
self._dict_usual_constraints[constraint_idx] = block
def add_constant_constraint(self, block: ConstantConstraint, constraint_idx: int):
"""
Function that is called when the model is being created
Associates a block to an OR-TOOLS id
Updates the list of blocks within the model
Function that associates to a block the OR-TOOLS constraint "constraint_idx"
:param block: a single constraint
:param constraint_idx: index of the constraint in the OR-Tools proto
"""
# Updates the list of blocs
name_set = block.name_macroblock()
if name_set not in self._list_blocks:
self._list_blocks[name_set] = MacroBlock(name_set, block, False)
else:
block = self._list_blocks[name_set].ajoute_ec(block)
self._dict_constant_constraints[constraint_idx] = block
def list_usual_constraints(self) -> [UsualConstraint]:
return list(self._dict_usual_constraints.values())
def list_constant_constraints(self) -> [ConstantConstraint]:
res = []
for mb in self.list_all_macro_blocks():
if not mb.is_true_constraint():
res = res + mb.contenu()
return res
def list_all_macro_blocks(self) -> [MacroBlock]:
"""Returns the list of macro blocks (eg: "pos", "line", "col", "square" for sudoku)"""
return list(set(self._list_blocks.values()))
def list_macro_block_names(self) -> [str]:
"""Returns the list of block names"""
# return list(set(b.name_associated_constraint() for b in self.list_elementary_blocks()))
return list(self._list_blocks.keys())
def get_mb(self, name) -> MacroBlock:
return self._list_blocks[name]
def has_mb(self, name) -> bool:
return name in self._list_blocks
def has_constant_block(self, idx: int):
return idx in self._dict_constant_constraints
def get_constant_block(self, idx: int) -> ConstantConstraint:
"""Converts from constraint to id"""
return self._dict_constant_constraints[idx]
def get_usual_block(self, idx: int) -> UsualConstraint:
"""Converts from constraint to id"""
return self._dict_usual_constraints[idx]
def define_as_unamovible(self, name: str, **dict_dimensions):
"""Remembers that some constraints cannot be touched"""
if name not in self.list_macro_block_names():
logger.info("Le bloc de contraintes {} n'existe pas dans notre modèle".format(name))
return
else:
ma_liste = self._list_blocks[name].project_on_dimensions(dict_dimensions)
is_true_constraint = self._list_blocks[name].is_true_constraint()
if is_true_constraint:
for elt in ma_liste:
idx = elt.list_idx()
if idx not in self._unmovable_usual_constraints:
self._unmovable_usual_constraints.append(idx)
else:
for elt in ma_liste:
idx = elt.list_idx()
if idx not in self._unmovable_constant_constraints:
self._unmovable_constant_constraints.append(idx)
def reset_unamovible(self):
"""Resets the entire list of unamovible constraints"""
self._unmovable_usual_constraints.clear()
self._unmovable_constant_constraints.clear()
def define_as_amovible(self, name: str, **dict_dimensions):
"""Removes the indication that one constraint cannot be touched"""
if name not in self.list_macro_block_names():
logger.info("Le bloc de contraintes {} n'existe pas dans notre modèle".format(name))
return
else:
ma_liste = self._list_blocks[name].project_on_dimensions(dict_dimensions)
is_true_constraint = self._list_blocks[name].is_true_constraint()
if is_true_constraint:
for elt in ma_liste:
idx = elt.list_idx()
if idx in self._unmovable_usual_constraints:
self._unmovable_usual_constraints.remove(idx)
else:
for elt in ma_liste:
idx = elt.list_idx()
if idx in self._unmovable_constant_constraints:
self._unmovable_constant_constraints.remove(idx)
def get_list_usual_inamovible(self):
return self._unmovable_usual_constraints
def get_list_constant_inamovible(self):
return self._unmovable_constant_constraints
def return_all_blocks_as_named(self) -> [NamedBlock]:
return [b.to_named_block() for b in self.list_all_macro_blocks()]
def print(self):
print("Mes blocs de contraintes : ")
for b in self._list_blocks.values():
print('-- ' + str(b))
Classes
class ConstantConstraint (constraint, idx, default_value, default_range, **dict_dimensions)
-
Class defining a single "constant" constraint The constraint is in fact a constant (eg. x = model.NewConstant(1))
Expand source code
class ConstantConstraint(ElementaryConstraint): """ Class defining a single "constant" constraint The constraint is in fact a constant (eg. x = model.NewConstant(1)) """ def __init__(self, constraint, idx, default_value, default_range, **dict_dimensions): super().__init__(constraint, idx, **dict_dimensions) self._idx = {idx: {"value": default_value, "range": default_range}} def def_value(self, idx): return self._idx[idx]["value"] def def_range(self, idx): return self._idx[idx]["range"] def add_id(self, idx, default_value, default_range): self._idx[idx] = {"value": default_value, "range": default_range} def print_specifique(self): return str(self) def list_idx(self) -> List[int]: return list(self._idx.keys())
Ancestors
- ElementaryConstraint
- abc.ABC
Methods
def add_id(self, idx, default_value, default_range)
-
Expand source code
def add_id(self, idx, default_value, default_range): self._idx[idx] = {"value": default_value, "range": default_range}
def def_range(self, idx)
-
Expand source code
def def_range(self, idx): return self._idx[idx]["range"]
def def_value(self, idx)
-
Expand source code
def def_value(self, idx): return self._idx[idx]["value"]
def list_idx(self) ‑> List[int]
-
Expand source code
def list_idx(self) -> List[int]: return list(self._idx.keys())
def print_specifique(self)
-
Expand source code
def print_specifique(self): return str(self)
class DictIndexConstraints
-
Class for managing the granularity of constraint blocks It contains dictionaries that enable us to convert idx into elementary constraint and vice versa It keeps in memory the granularity of the model
Expand source code
class DictIndexConstraints: """ Class for managing the granularity of constraint blocks It contains dictionaries that enable us to convert idx into elementary constraint and vice versa It keeps in memory the granularity of the model """ def __init__(self): self._dict_usual_constraints: Dict[int, UsualConstraint] = dict() self._dict_constant_constraints: Dict[int, ConstantConstraint] = dict() self._dict_variables = dict() self._list_blocks = dict() self._unmovable_usual_constraints = [] self._unmovable_constant_constraints = [] def print_status(self): print('{} contraintes constantes'.format(len(self._dict_constant_constraints.keys()))) for cc in self._dict_constant_constraints.values(): print(cc.print_specifique()) print('{} contraintes usuelles'.format(len(self._dict_usual_constraints.keys()))) for uc in self._dict_usual_constraints.values(): print(uc) def add_variable(self, variable, variable_idx: int): """Remembers a given variable, so that if later we define it as a constant, we know where it is in our Proto """ self._dict_variables[variable] = variable_idx def get_variable_idx(self, variable): """Returns the idx of a variable in the Proto. Returns -1 if it does not exist.""" if variable in self._dict_variables: return self._dict_variables[variable] return -1 def get_list_variables(self): return self._dict_variables.keys() def add_usual_constraint(self, block: UsualConstraint, constraint_idx: int): """ Function that is called when the model is being created Associates a block to an OR-TOOLS id Updates the list of blocks within the model :param block: constraint that must be added :param constraint_idx: id of the constraints """ # Looks whether this constraint has already been defined name_set = block.name_macroblock() if name_set not in self._list_blocks: self._list_blocks[name_set] = MacroBlock(name_set, block, True) else: block = self._list_blocks[name_set].ajoute_ec(block) # Keeps in memory the link between the elementary constraint and the idx self._dict_usual_constraints[constraint_idx] = block def add_constant_constraint(self, block: ConstantConstraint, constraint_idx: int): """ Function that is called when the model is being created Associates a block to an OR-TOOLS id Updates the list of blocks within the model Function that associates to a block the OR-TOOLS constraint "constraint_idx" :param block: a single constraint :param constraint_idx: index of the constraint in the OR-Tools proto """ # Updates the list of blocs name_set = block.name_macroblock() if name_set not in self._list_blocks: self._list_blocks[name_set] = MacroBlock(name_set, block, False) else: block = self._list_blocks[name_set].ajoute_ec(block) self._dict_constant_constraints[constraint_idx] = block def list_usual_constraints(self) -> [UsualConstraint]: return list(self._dict_usual_constraints.values()) def list_constant_constraints(self) -> [ConstantConstraint]: res = [] for mb in self.list_all_macro_blocks(): if not mb.is_true_constraint(): res = res + mb.contenu() return res def list_all_macro_blocks(self) -> [MacroBlock]: """Returns the list of macro blocks (eg: "pos", "line", "col", "square" for sudoku)""" return list(set(self._list_blocks.values())) def list_macro_block_names(self) -> [str]: """Returns the list of block names""" # return list(set(b.name_associated_constraint() for b in self.list_elementary_blocks())) return list(self._list_blocks.keys()) def get_mb(self, name) -> MacroBlock: return self._list_blocks[name] def has_mb(self, name) -> bool: return name in self._list_blocks def has_constant_block(self, idx: int): return idx in self._dict_constant_constraints def get_constant_block(self, idx: int) -> ConstantConstraint: """Converts from constraint to id""" return self._dict_constant_constraints[idx] def get_usual_block(self, idx: int) -> UsualConstraint: """Converts from constraint to id""" return self._dict_usual_constraints[idx] def define_as_unamovible(self, name: str, **dict_dimensions): """Remembers that some constraints cannot be touched""" if name not in self.list_macro_block_names(): logger.info("Le bloc de contraintes {} n'existe pas dans notre modèle".format(name)) return else: ma_liste = self._list_blocks[name].project_on_dimensions(dict_dimensions) is_true_constraint = self._list_blocks[name].is_true_constraint() if is_true_constraint: for elt in ma_liste: idx = elt.list_idx() if idx not in self._unmovable_usual_constraints: self._unmovable_usual_constraints.append(idx) else: for elt in ma_liste: idx = elt.list_idx() if idx not in self._unmovable_constant_constraints: self._unmovable_constant_constraints.append(idx) def reset_unamovible(self): """Resets the entire list of unamovible constraints""" self._unmovable_usual_constraints.clear() self._unmovable_constant_constraints.clear() def define_as_amovible(self, name: str, **dict_dimensions): """Removes the indication that one constraint cannot be touched""" if name not in self.list_macro_block_names(): logger.info("Le bloc de contraintes {} n'existe pas dans notre modèle".format(name)) return else: ma_liste = self._list_blocks[name].project_on_dimensions(dict_dimensions) is_true_constraint = self._list_blocks[name].is_true_constraint() if is_true_constraint: for elt in ma_liste: idx = elt.list_idx() if idx in self._unmovable_usual_constraints: self._unmovable_usual_constraints.remove(idx) else: for elt in ma_liste: idx = elt.list_idx() if idx in self._unmovable_constant_constraints: self._unmovable_constant_constraints.remove(idx) def get_list_usual_inamovible(self): return self._unmovable_usual_constraints def get_list_constant_inamovible(self): return self._unmovable_constant_constraints def return_all_blocks_as_named(self) -> [NamedBlock]: return [b.to_named_block() for b in self.list_all_macro_blocks()] def print(self): print("Mes blocs de contraintes : ") for b in self._list_blocks.values(): print('-- ' + str(b))
Methods
def add_constant_constraint(self, block: ConstantConstraint, constraint_idx: int)
-
Function that is called when the model is being created Associates a block to an OR-TOOLS id Updates the list of blocks within the model Function that associates to a block the OR-TOOLS constraint "constraint_idx" :param block: a single constraint :param constraint_idx: index of the constraint in the OR-Tools proto
Expand source code
def add_constant_constraint(self, block: ConstantConstraint, constraint_idx: int): """ Function that is called when the model is being created Associates a block to an OR-TOOLS id Updates the list of blocks within the model Function that associates to a block the OR-TOOLS constraint "constraint_idx" :param block: a single constraint :param constraint_idx: index of the constraint in the OR-Tools proto """ # Updates the list of blocs name_set = block.name_macroblock() if name_set not in self._list_blocks: self._list_blocks[name_set] = MacroBlock(name_set, block, False) else: block = self._list_blocks[name_set].ajoute_ec(block) self._dict_constant_constraints[constraint_idx] = block
def add_usual_constraint(self, block: UsualConstraint, constraint_idx: int)
-
Function that is called when the model is being created Associates a block to an OR-TOOLS id Updates the list of blocks within the model :param block: constraint that must be added :param constraint_idx: id of the constraints
Expand source code
def add_usual_constraint(self, block: UsualConstraint, constraint_idx: int): """ Function that is called when the model is being created Associates a block to an OR-TOOLS id Updates the list of blocks within the model :param block: constraint that must be added :param constraint_idx: id of the constraints """ # Looks whether this constraint has already been defined name_set = block.name_macroblock() if name_set not in self._list_blocks: self._list_blocks[name_set] = MacroBlock(name_set, block, True) else: block = self._list_blocks[name_set].ajoute_ec(block) # Keeps in memory the link between the elementary constraint and the idx self._dict_usual_constraints[constraint_idx] = block
def add_variable(self, variable, variable_idx: int)
-
Remembers a given variable, so that if later we define it as a constant, we know where it is in our Proto
Expand source code
def add_variable(self, variable, variable_idx: int): """Remembers a given variable, so that if later we define it as a constant, we know where it is in our Proto """ self._dict_variables[variable] = variable_idx
def define_as_amovible(self, name: str, **dict_dimensions)
-
Removes the indication that one constraint cannot be touched
Expand source code
def define_as_amovible(self, name: str, **dict_dimensions): """Removes the indication that one constraint cannot be touched""" if name not in self.list_macro_block_names(): logger.info("Le bloc de contraintes {} n'existe pas dans notre modèle".format(name)) return else: ma_liste = self._list_blocks[name].project_on_dimensions(dict_dimensions) is_true_constraint = self._list_blocks[name].is_true_constraint() if is_true_constraint: for elt in ma_liste: idx = elt.list_idx() if idx in self._unmovable_usual_constraints: self._unmovable_usual_constraints.remove(idx) else: for elt in ma_liste: idx = elt.list_idx() if idx in self._unmovable_constant_constraints: self._unmovable_constant_constraints.remove(idx)
def define_as_unamovible(self, name: str, **dict_dimensions)
-
Remembers that some constraints cannot be touched
Expand source code
def define_as_unamovible(self, name: str, **dict_dimensions): """Remembers that some constraints cannot be touched""" if name not in self.list_macro_block_names(): logger.info("Le bloc de contraintes {} n'existe pas dans notre modèle".format(name)) return else: ma_liste = self._list_blocks[name].project_on_dimensions(dict_dimensions) is_true_constraint = self._list_blocks[name].is_true_constraint() if is_true_constraint: for elt in ma_liste: idx = elt.list_idx() if idx not in self._unmovable_usual_constraints: self._unmovable_usual_constraints.append(idx) else: for elt in ma_liste: idx = elt.list_idx() if idx not in self._unmovable_constant_constraints: self._unmovable_constant_constraints.append(idx)
def get_constant_block(self, idx: int) ‑> ConstantConstraint
-
Converts from constraint to id
Expand source code
def get_constant_block(self, idx: int) -> ConstantConstraint: """Converts from constraint to id""" return self._dict_constant_constraints[idx]
def get_list_constant_inamovible(self)
-
Expand source code
def get_list_constant_inamovible(self): return self._unmovable_constant_constraints
def get_list_usual_inamovible(self)
-
Expand source code
def get_list_usual_inamovible(self): return self._unmovable_usual_constraints
def get_list_variables(self)
-
Expand source code
def get_list_variables(self): return self._dict_variables.keys()
def get_mb(self, name) ‑> MacroBlock
-
Expand source code
def get_mb(self, name) -> MacroBlock: return self._list_blocks[name]
def get_usual_block(self, idx: int) ‑> UsualConstraint
-
Converts from constraint to id
Expand source code
def get_usual_block(self, idx: int) -> UsualConstraint: """Converts from constraint to id""" return self._dict_usual_constraints[idx]
def get_variable_idx(self, variable)
-
Returns the idx of a variable in the Proto. Returns -1 if it does not exist.
Expand source code
def get_variable_idx(self, variable): """Returns the idx of a variable in the Proto. Returns -1 if it does not exist.""" if variable in self._dict_variables: return self._dict_variables[variable] return -1
def has_constant_block(self, idx: int)
-
Expand source code
def has_constant_block(self, idx: int): return idx in self._dict_constant_constraints
def has_mb(self, name) ‑> bool
-
Expand source code
def has_mb(self, name) -> bool: return name in self._list_blocks
def list_all_macro_blocks(self) ‑> [
MacroBlock'>] -
Returns the list of macro blocks (eg: "pos", "line", "col", "square" for sudoku)
Expand source code
def list_all_macro_blocks(self) -> [MacroBlock]: """Returns the list of macro blocks (eg: "pos", "line", "col", "square" for sudoku)""" return list(set(self._list_blocks.values()))
def list_constant_constraints(self) ‑> [
ConstantConstraint'>] -
Expand source code
def list_constant_constraints(self) -> [ConstantConstraint]: res = [] for mb in self.list_all_macro_blocks(): if not mb.is_true_constraint(): res = res + mb.contenu() return res
def list_macro_block_names(self) ‑> [
] -
Returns the list of block names
Expand source code
def list_macro_block_names(self) -> [str]: """Returns the list of block names""" # return list(set(b.name_associated_constraint() for b in self.list_elementary_blocks())) return list(self._list_blocks.keys())
def list_usual_constraints(self) ‑> [
UsualConstraint'>] -
Expand source code
def list_usual_constraints(self) -> [UsualConstraint]: return list(self._dict_usual_constraints.values())
def print(self)
-
Expand source code
def print(self): print("Mes blocs de contraintes : ") for b in self._list_blocks.values(): print('-- ' + str(b))
def print_status(self)
-
Expand source code
def print_status(self): print('{} contraintes constantes'.format(len(self._dict_constant_constraints.keys()))) for cc in self._dict_constant_constraints.values(): print(cc.print_specifique()) print('{} contraintes usuelles'.format(len(self._dict_usual_constraints.keys()))) for uc in self._dict_usual_constraints.values(): print(uc)
def reset_unamovible(self)
-
Resets the entire list of unamovible constraints
Expand source code
def reset_unamovible(self): """Resets the entire list of unamovible constraints""" self._unmovable_usual_constraints.clear() self._unmovable_constant_constraints.clear()
def return_all_blocks_as_named(self) ‑> [
NamedBlock'>] -
Expand source code
def return_all_blocks_as_named(self) -> [NamedBlock]: return [b.to_named_block() for b in self.list_all_macro_blocks()]
class ElementaryConstraint (constraint, idx, **dict_dimensions)
-
Class defining the smallest possible constraint for the model.
An Elementary Constraint can encompass several actual constraints, but these constraints will always be enforced or relaxed as a set (at any given time, they are either all relaxed or all enforced)
It contains :
constraint: the name of the general type of constraint
dict_dimensions: the dimensions of the constraint block (dimension key and value on this dimension)
Expand source code
class ElementaryConstraint (ABC): """ Class defining the smallest possible constraint for the model. An Elementary Constraint can encompass several actual constraints, but these constraints will always be enforced or relaxed as a set (at any given time, they are either all relaxed or all enforced) It contains : **constraint**: the name of the general type of constraint **dict_dimensions**: the dimensions of the constraint block (dimension key and value on this dimension) """ def __init__(self, constraint, idx, **dict_dimensions): """ """ self._constraint = constraint self._dict_dimensions = dict_dimensions self._dimensions = sorted(list(dict_dimensions.keys())) self._first_id = idx def dimensions(self): return self._dimensions def name_macroblock(self): return self._constraint def dim_value(self, dimension): return self._dict_dimensions[dimension] def __str__(self): if not self._dimensions: return self._constraint else: return ( self._constraint + "(" + ", ".join(["%s = %s" % (k, str(self._dict_dimensions[k])) for k in self._dimensions]) + ")" ) def __lt__(self, other): # First we sort by name if self.name_macroblock() != other.name_macroblock(): return self.name_macroblock() < other.name_macroblock() # If they have the same name then they have the same dimensions for d in self.dimensions(): if self.dim_value(d) != other.dim_value(d): return self.dim_value(d) < other.dim_value(d) def __hash__(self): return hash(str(self)) def __eq__(self, other): return hash(self) == hash(other) def __repr__(self): return str(self) def first_id(self) -> int: return self._first_id @abstractmethod def list_idx(self) -> List[int]: pass def size(self) -> int: return len(self.list_idx())
Ancestors
- abc.ABC
Subclasses
Methods
def dim_value(self, dimension)
-
Expand source code
def dim_value(self, dimension): return self._dict_dimensions[dimension]
def dimensions(self)
-
Expand source code
def dimensions(self): return self._dimensions
def first_id(self) ‑> int
-
Expand source code
def first_id(self) -> int: return self._first_id
def list_idx(self) ‑> List[int]
-
Expand source code
@abstractmethod def list_idx(self) -> List[int]: pass
def name_macroblock(self)
-
Expand source code
def name_macroblock(self): return self._constraint
def size(self) ‑> int
-
Expand source code
def size(self) -> int: return len(self.list_idx())
class MacroBlock (name, firstConstraint: ElementaryConstraint, is_constraint)
-
Class defining a set of elementary constraints (eg. pos) It contains a set of BlocElementaryConstraint A bloc can be projected on various dimensions
Expand source code
class MacroBlock: """ Class defining a set of elementary constraints (eg. pos) It contains a set of BlocElementaryConstraint A bloc can be projected on various dimensions """ def __init__(self, name, firstConstraint: ElementaryConstraint, is_constraint): self._name = name self._dimensions = firstConstraint.dimensions() if not self._dimensions: self._dimensions = [] if len(self._dimensions) == 0: clef = "" else: clef = tuple([firstConstraint.dim_value(d) for d in self.dimensions()]) self.list_ec = {clef: firstConstraint} self._is_true_constraint = is_constraint # True if true constraint, False if constant self._explanations = dict() self._dim_a_ignorer = [] def contenu(self) -> [ElementaryConstraint]: return list(self.list_ec.values()) def to_named_block(self): dim_peut_split = [d for d in self.dimensions() if d not in self._dim_a_ignorer] return NamedBlock(self, [], dim_peut_split, list(self.list_ec.values()), self._is_true_constraint) def name(self) -> str: return self._name def dimensions(self) -> [str]: return self._dimensions def do_not_split_on(self, *dim): """Indicates that we must not split on this dimension because it does not mean anything for the user""" for d in dim: if d in self.dimensions() and d not in self._dim_a_ignorer: self._dim_a_ignorer.append(d) def size(self) -> int: return sum(ec.size() for ec in self.list_ec.values()) def is_true_constraint(self): return self._is_true_constraint def __repr__(self): if self.is_true_constraint(): return "{} -- dim: {} -- size: {}".format(self.name(), self.dimensions(), self.size()) else: return "{} (CONSTANTES) -- dim: {} -- size: {}".format(self.name(), self.dimensions(), self.size()) def ajoute_ec(self, nouvelleContrainte: ElementaryConstraint) -> ElementaryConstraint: """ Adds a new constraint to the block after checking that the model is consistent """ # Raises an error if is_true_constraint is not consistent if self.is_true_constraint() and isinstance(nouvelleContrainte, ConstantConstraint): raise ModelisationError("Le nom {} est utilisé à la fois pour des contraintes et des constantes".format(self.name())) if not self.is_true_constraint() and isinstance(nouvelleContrainte, UsualConstraint): raise ModelisationError("Le nom {} est utilisé à la fois pour des contraintes et des constantes".format(self.name())) # Raises an error if dimensions are not consistent throughout the model for the same block name if nouvelleContrainte.dimensions() != self.dimensions(): raise ModelisationError("Erreur de cohérence sur les dimensions de la contrainte {}. Le modèle a rencontré {} puis {}".format(self.name(), self.dimensions(), nouvelleContrainte.dimensions())) # Overloads the existing constraint if it exists with the same dimensions clef = tuple([nouvelleContrainte.dim_value(d) for d in self.dimensions()]) if clef in self.list_ec: vieilleContrainte = self.list_ec[clef] if isinstance(vieilleContrainte, UsualConstraint): vieilleContrainte.add_id(nouvelleContrainte.first_id()) return vieilleContrainte elif isinstance(vieilleContrainte, ConstantConstraint): assert isinstance(nouvelleContrainte, ConstantConstraint) new_id = nouvelleContrainte.first_id() new_val = nouvelleContrainte.def_value(new_id) new_range = nouvelleContrainte.def_range(new_id) vieilleContrainte.add_id(new_id, new_val, new_range) return vieilleContrainte # raise ModelisationError("La contrainte {} pour les dimensions {} est définie deux fois.".format(self.name(), clef)) else: self.list_ec[clef] = nouvelleContrainte return nouvelleContrainte def project_on_dimensions(self, **dimensions) -> [ElementaryConstraint]: """Returns the list of BlocKElementaryConstraints that match the given dimensions""" if len(dimensions.keys()) == 0: return [ec for ec in self.list_ec.values()] for k in dimensions.keys(): if k not in self._dimensions: logger.info("La dimension {} n'existe pas. Les dimensions existantes sont {}.".format(k, self._dimensions)) return [] ma_liste = [] for ec in self.list_ec.values(): on_garde = True for (k, v) in dimensions.items(): if ec.dim_value(k) != v: on_garde = False pass if on_garde: ma_liste.append(ec) return ma_liste def add_explanations(self, *sentences): """ Adds explanations to the macroblock in natural language """ for s in sentences: dimensions_utilisees = [] for d in self.dimensions(): clef = '{' + d + '}' if clef in s: dimensions_utilisees.append(d) mes_dim = '-' for d in sorted(dimensions_utilisees): mes_dim = mes_dim + d + '-' self._explanations[mes_dim] = s def get_explanation_for_key(self, key): if key in self._explanations: return self._explanations[key] else: return None
Methods
def add_explanations(self, *sentences)
-
Adds explanations to the macroblock in natural language
Expand source code
def add_explanations(self, *sentences): """ Adds explanations to the macroblock in natural language """ for s in sentences: dimensions_utilisees = [] for d in self.dimensions(): clef = '{' + d + '}' if clef in s: dimensions_utilisees.append(d) mes_dim = '-' for d in sorted(dimensions_utilisees): mes_dim = mes_dim + d + '-' self._explanations[mes_dim] = s
def ajoute_ec(self, nouvelleContrainte: ElementaryConstraint) ‑> ElementaryConstraint
-
Adds a new constraint to the block after checking that the model is consistent
Expand source code
def ajoute_ec(self, nouvelleContrainte: ElementaryConstraint) -> ElementaryConstraint: """ Adds a new constraint to the block after checking that the model is consistent """ # Raises an error if is_true_constraint is not consistent if self.is_true_constraint() and isinstance(nouvelleContrainte, ConstantConstraint): raise ModelisationError("Le nom {} est utilisé à la fois pour des contraintes et des constantes".format(self.name())) if not self.is_true_constraint() and isinstance(nouvelleContrainte, UsualConstraint): raise ModelisationError("Le nom {} est utilisé à la fois pour des contraintes et des constantes".format(self.name())) # Raises an error if dimensions are not consistent throughout the model for the same block name if nouvelleContrainte.dimensions() != self.dimensions(): raise ModelisationError("Erreur de cohérence sur les dimensions de la contrainte {}. Le modèle a rencontré {} puis {}".format(self.name(), self.dimensions(), nouvelleContrainte.dimensions())) # Overloads the existing constraint if it exists with the same dimensions clef = tuple([nouvelleContrainte.dim_value(d) for d in self.dimensions()]) if clef in self.list_ec: vieilleContrainte = self.list_ec[clef] if isinstance(vieilleContrainte, UsualConstraint): vieilleContrainte.add_id(nouvelleContrainte.first_id()) return vieilleContrainte elif isinstance(vieilleContrainte, ConstantConstraint): assert isinstance(nouvelleContrainte, ConstantConstraint) new_id = nouvelleContrainte.first_id() new_val = nouvelleContrainte.def_value(new_id) new_range = nouvelleContrainte.def_range(new_id) vieilleContrainte.add_id(new_id, new_val, new_range) return vieilleContrainte # raise ModelisationError("La contrainte {} pour les dimensions {} est définie deux fois.".format(self.name(), clef)) else: self.list_ec[clef] = nouvelleContrainte return nouvelleContrainte
def contenu(self) ‑> [
ElementaryConstraint'>] -
Expand source code
def contenu(self) -> [ElementaryConstraint]: return list(self.list_ec.values())
def dimensions(self) ‑> [
] -
Expand source code
def dimensions(self) -> [str]: return self._dimensions
def do_not_split_on(self, *dim)
-
Indicates that we must not split on this dimension because it does not mean anything for the user
Expand source code
def do_not_split_on(self, *dim): """Indicates that we must not split on this dimension because it does not mean anything for the user""" for d in dim: if d in self.dimensions() and d not in self._dim_a_ignorer: self._dim_a_ignorer.append(d)
def get_explanation_for_key(self, key)
-
Expand source code
def get_explanation_for_key(self, key): if key in self._explanations: return self._explanations[key] else: return None
def is_true_constraint(self)
-
Expand source code
def is_true_constraint(self): return self._is_true_constraint
def name(self) ‑> str
-
Expand source code
def name(self) -> str: return self._name
def project_on_dimensions(self, **dimensions) ‑> [
ElementaryConstraint'>] -
Returns the list of BlocKElementaryConstraints that match the given dimensions
Expand source code
def project_on_dimensions(self, **dimensions) -> [ElementaryConstraint]: """Returns the list of BlocKElementaryConstraints that match the given dimensions""" if len(dimensions.keys()) == 0: return [ec for ec in self.list_ec.values()] for k in dimensions.keys(): if k not in self._dimensions: logger.info("La dimension {} n'existe pas. Les dimensions existantes sont {}.".format(k, self._dimensions)) return [] ma_liste = [] for ec in self.list_ec.values(): on_garde = True for (k, v) in dimensions.items(): if ec.dim_value(k) != v: on_garde = False pass if on_garde: ma_liste.append(ec) return ma_liste
def size(self) ‑> int
-
Expand source code
def size(self) -> int: return sum(ec.size() for ec in self.list_ec.values())
def to_named_block(self)
-
Expand source code
def to_named_block(self): dim_peut_split = [d for d in self.dimensions() if d not in self._dim_a_ignorer] return NamedBlock(self, [], dim_peut_split, list(self.list_ec.values()), self._is_true_constraint)
class ModelisationError (*args, **kwargs)
-
Raised when the user makes a mistake while creating a ModelWithIndexing
Expand source code
class ModelisationError(Exception): """Raised when the user makes a mistake while creating a ModelWithIndexing""" pass
Ancestors
- builtins.Exception
- builtins.BaseException
class NamedBlock (mon_macro_block, dim_already_split: [
], dim_not_yet_split: [ ], contenu: [ ElementaryConstraint'>], is_true_constraint) -
Class which contains a list of elementary constraints The main interest of this class is to be able to name easily this list NamedBlock can have different granularities (from entire macroblock to single elementary constraint)
Expand source code
class NamedBlock: """ Class which contains a list of elementary constraints The main interest of this class is to be able to name easily this list NamedBlock can have different granularities (from entire macroblock to single elementary constraint) """ def __init__(self, mon_macro_block, dim_already_split: [str], dim_not_yet_split: [str], contenu: [ElementaryConstraint], is_true_constraint): self._macro_bloc = mon_macro_block self._type_bloc = mon_macro_block.name() self._dim_already_split = dim_already_split self._dim_not_yet_split = dim_not_yet_split self._contenu = contenu self._is_true_constraint = is_true_constraint # True if true constraint, False if constant def __lt__(self, other): # First we sort by name if self.type_bloc() != other.type_bloc(): return self.type_bloc() < other.type_bloc() # Then we sort on how precise they have if len(self.dimensions()) != len(other.dimensions()): return self.dimensions() < other.dimensions() # Then we split on the sentence return self.elegant_name() < other.elegant_name() def __hash__(self): return hash(self.name()) def name(self): if len(self.contenu()) == 0: return 'Bloc vide' if len(self._dim_already_split) == 0: return self.type_bloc() ec = self.contenu()[0] key = None for d in self._dim_already_split: if key: key = "{}, {} = {}".format(key, d, ec.dim_value(d)) else: key = "{} = {}".format(d, ec.dim_value(d)) return "{} ({})".format(self.type_bloc(), key) def contenu(self) -> [ElementaryConstraint]: return self._contenu def type_bloc(self): return self._type_bloc def dimensions(self): return self._dim_already_split def size(self): return sum(ec.size() for ec in self._contenu) def is_true_constraint(self): return self._is_true_constraint def can_be_split(self) -> bool: return len(self._dim_not_yet_split) > 0 def to_dict(self) -> Dict: """Used for returning a dictionary instead of a NamedBlock object""" my_dimensions = {} example = self.contenu()[0] for d in self._dim_already_split: my_dimensions[d] = example.dim_value(d) return {"sentence": self.elegant_name(), "type": self._type_bloc, "dimensions": my_dimensions} def elegant_name(self): if DEBUG_EXPLAINATIONS: return '{} ({})'.format(self.name(), self.size()) if len(self.contenu()) == 0: return 'Bloc vide' example = self.contenu()[0] mes_dim_actuelles = {} for d in self._dim_already_split: mes_dim_actuelles[d] = example.dim_value(d) ma_clef = '-' for d in sorted(list(mes_dim_actuelles.keys())): ma_clef = ma_clef + d + '-' my_sentence = self._macro_bloc.get_explanation_for_key(ma_clef) if not my_sentence: return self.name() for d in mes_dim_actuelles: dim_key = '{' + d + '}' my_sentence = my_sentence.replace(dim_key, str(mes_dim_actuelles[d])) return my_sentence def __repr__(self): # return self.name() # return '{} ({})'.format(self._name, self.size()) return self.elegant_name() def remove_dim_to_split(self): self._dim_not_yet_split = [c for c in self._dim_not_yet_split[1:]] def split_on_next_dimension(self): """ Returns this block split in several blocks with smaller granularity """ if len(self._dim_not_yet_split) == 0: return None nouv_dim = self._dim_not_yet_split[0] logger.info('On scinde {} sur la dimension {}'.format(self.name(), nouv_dim)) dimensions = [d for d in self._dim_already_split] + [nouv_dim] remaining_dim = [d for d in self._dim_not_yet_split if d != nouv_dim] dico_val = {} for ec in self.contenu(): assert isinstance(ec, ElementaryConstraint) key = None for d in dimensions: if key: key = "{}, {} = {}".format(key, d, ec.dim_value(d)) else: key = "{} = {}".format(d, ec.dim_value(d)) if key in dico_val: dico_val[key].append(ec) else: dico_val[key] = [ec] liste_blocks_res = [] for key in dico_val: b = NamedBlock(self._macro_bloc, dimensions, remaining_dim, dico_val[key], self._is_true_constraint) liste_blocks_res.append(b) return liste_blocks_res
Methods
def can_be_split(self) ‑> bool
-
Expand source code
def can_be_split(self) -> bool: return len(self._dim_not_yet_split) > 0
def contenu(self) ‑> [
ElementaryConstraint'>] -
Expand source code
def contenu(self) -> [ElementaryConstraint]: return self._contenu
def dimensions(self)
-
Expand source code
def dimensions(self): return self._dim_already_split
def elegant_name(self)
-
Expand source code
def elegant_name(self): if DEBUG_EXPLAINATIONS: return '{} ({})'.format(self.name(), self.size()) if len(self.contenu()) == 0: return 'Bloc vide' example = self.contenu()[0] mes_dim_actuelles = {} for d in self._dim_already_split: mes_dim_actuelles[d] = example.dim_value(d) ma_clef = '-' for d in sorted(list(mes_dim_actuelles.keys())): ma_clef = ma_clef + d + '-' my_sentence = self._macro_bloc.get_explanation_for_key(ma_clef) if not my_sentence: return self.name() for d in mes_dim_actuelles: dim_key = '{' + d + '}' my_sentence = my_sentence.replace(dim_key, str(mes_dim_actuelles[d])) return my_sentence
def is_true_constraint(self)
-
Expand source code
def is_true_constraint(self): return self._is_true_constraint
def name(self)
-
Expand source code
def name(self): if len(self.contenu()) == 0: return 'Bloc vide' if len(self._dim_already_split) == 0: return self.type_bloc() ec = self.contenu()[0] key = None for d in self._dim_already_split: if key: key = "{}, {} = {}".format(key, d, ec.dim_value(d)) else: key = "{} = {}".format(d, ec.dim_value(d)) return "{} ({})".format(self.type_bloc(), key)
def remove_dim_to_split(self)
-
Expand source code
def remove_dim_to_split(self): self._dim_not_yet_split = [c for c in self._dim_not_yet_split[1:]]
def size(self)
-
Expand source code
def size(self): return sum(ec.size() for ec in self._contenu)
def split_on_next_dimension(self)
-
Returns this block split in several blocks with smaller granularity
Expand source code
def split_on_next_dimension(self): """ Returns this block split in several blocks with smaller granularity """ if len(self._dim_not_yet_split) == 0: return None nouv_dim = self._dim_not_yet_split[0] logger.info('On scinde {} sur la dimension {}'.format(self.name(), nouv_dim)) dimensions = [d for d in self._dim_already_split] + [nouv_dim] remaining_dim = [d for d in self._dim_not_yet_split if d != nouv_dim] dico_val = {} for ec in self.contenu(): assert isinstance(ec, ElementaryConstraint) key = None for d in dimensions: if key: key = "{}, {} = {}".format(key, d, ec.dim_value(d)) else: key = "{} = {}".format(d, ec.dim_value(d)) if key in dico_val: dico_val[key].append(ec) else: dico_val[key] = [ec] liste_blocks_res = [] for key in dico_val: b = NamedBlock(self._macro_bloc, dimensions, remaining_dim, dico_val[key], self._is_true_constraint) liste_blocks_res.append(b) return liste_blocks_res
def to_dict(self) ‑> Dict[~KT, ~VT]
-
Used for returning a dictionary instead of a NamedBlock object
Expand source code
def to_dict(self) -> Dict: """Used for returning a dictionary instead of a NamedBlock object""" my_dimensions = {} example = self.contenu()[0] for d in self._dim_already_split: my_dimensions[d] = example.dim_value(d) return {"sentence": self.elegant_name(), "type": self._type_bloc, "dimensions": my_dimensions}
def type_bloc(self)
-
Expand source code
def type_bloc(self): return self._type_bloc
class UsualConstraint (constraint, idx, **dict_dimensions)
-
Class defining a single constraint The constraint is of the usual type (eg. model.Add(x + y < 2))
Expand source code
class UsualConstraint(ElementaryConstraint): """ Class defining a single constraint The constraint is of the usual type (eg. model.Add(x + y < 2)) """ def __init__(self, constraint, idx, **dict_dimensions): super().__init__(constraint, idx, **dict_dimensions) self._idx = [idx] def add_id(self, idx): self._idx.append(idx) def list_idx(self) -> List[int]: return self._idx
Ancestors
- ElementaryConstraint
- abc.ABC
Methods
def add_id(self, idx)
-
Expand source code
def add_id(self, idx): self._idx.append(idx)
def list_idx(self) ‑> List[int]
-
Expand source code
def list_idx(self) -> List[int]: return self._idx