Module ortools_utils.explainability.constraints_relaxation
Expand source code
import copy
import logging
from typing import *
from ortools_utils.model_indexation.constraints import NamedBlock, UsualConstraint, ConstantConstraint
logger = logging.getLogger(__name__)
class ConstraintsRelaxation:
"""
Class used to release or reset a constraint or a block of constraints, depending on the chosen method
"""
def __init__(self, model):
self.model = model
self.dict_indexing = self.model.dict_indexing()
self.is_relaxed_usual_constraint = {uc: None for uc in self.dict_indexing.list_usual_constraints()} # UsualConstraint --> None si pas relachée, dico des contraintes du proto sinon
self.is_relaxed_constant = {cc: False for cc in self.dict_indexing.list_constant_constraints()} # ConstantConstraint --> Boolean, True ssi la contrainte est relachée
def relax_all_real_constraints_but(self, all_blocks):
"""Releases all true constraints except for those which are in all_blocks"""
uc_a_garder = {}
for nb in all_blocks:
if nb.is_true_constraint():
for ec in nb.contenu():
uc_a_garder[ec] = True
for uc in self.is_relaxed_usual_constraint:
if not self.is_relaxed_usual_constraint[uc] and uc not in uc_a_garder:
self._relax_single_usual_constraint(uc)
def relax_all_but(self, all_blocks, method):
"""
Releases all constraints that are not in all_blocks
If method == 'sufficient assumption', we do not relax constant constraints
"""
uc_a_garder = {}
cc_a_garder = {}
for nb in all_blocks:
if nb.is_true_constraint():
for ec in nb.contenu():
uc_a_garder[ec] = True
elif method != 'sufficient_assumption':
for ec in nb.contenu():
cc_a_garder[ec] = True
for uc in self.is_relaxed_usual_constraint:
if not self.is_relaxed_usual_constraint[uc] and uc not in uc_a_garder:
self._relax_single_usual_constraint(uc)
if method != 'sufficient_assumption':
for cc in self.is_relaxed_constant:
if not self.is_relaxed_constant[cc] and cc not in cc_a_garder:
self._relax_single_constant_constraint(cc)
def _relax_single_usual_constraint(self, uc: UsualConstraint):
"""Relaxes one single constraint (checks that it should be relaxed and then calls core function)"""
li = uc.list_idx()
self.is_relaxed_usual_constraint[uc] = {}
for i in li:
self.is_relaxed_usual_constraint[uc][i] = copy.copy(self.model.Proto().constraints[i])
self.model.Proto().constraints[i].Clear()
def _relax_single_constant_constraint(self, cc: ConstantConstraint):
"""Relaxes one single constraint (checks that it should be relaxed and then calls core function)"""
li = cc.list_idx()
for i in li:
self.is_relaxed_constant[cc] = True
default_range = cc.def_range(i)
self.model.Proto().variables[i].domain[:] = [default_range[0], default_range[1]]
def relax_list_blocks(self, my_blocks: List[NamedBlock]):
"""Function that releases a list of elementary blocks"""
list_uc = []
list_cc = []
for mb in my_blocks:
if mb.is_true_constraint():
list_uc = list_uc + mb.contenu()
else:
list_cc = list_cc + mb.contenu()
for uc in list_uc:
if not self.is_relaxed_usual_constraint[uc]:
self._relax_single_usual_constraint(uc)
for cc in list_cc:
if not self.is_relaxed_constant[cc]:
self._relax_single_constant_constraint(cc)
def inv_relax_all(self):
"""Reinforces all constraints"""
for uc in self.is_relaxed_usual_constraint:
if self.is_relaxed_usual_constraint[uc]:
self._inv_relax_single_usual_constraint(uc)
for cc in self.is_relaxed_constant:
if self.is_relaxed_constant[cc]:
self._inv_relax_single_constant_constraint(cc)
self.is_relaxed_usual_constraint = {uc: None for uc in self.dict_indexing.list_usual_constraints()} # UsualConstraint --> None si pas relachée, contrainte du proto sinon
self.is_relaxed_constant = {cc: False for cc in self.dict_indexing.list_constant_constraints()} # ConstantConstraint --> Boolean, True ssi la contrainte est relachée
def _inv_relax_single_usual_constraint(self, uc: UsualConstraint):
li = uc.list_idx()
for i in li:
del self.model.Proto().constraints[i]
self.model.Proto().constraints.insert(i, self.is_relaxed_usual_constraint[uc][i])
def _inv_relax_single_constant_constraint(self, cc: ConstantConstraint):
li = cc.list_idx()
for i in li:
default_value = cc.def_value(i)
self.model.Proto().variables[i].domain[:] = [default_value, default_value]
def inv_relax_list_blocks(self, my_blocks: List[NamedBlock], list_usual_idx_not_to_release=None, list_constant_idx_not_to_release=None):
"""Function that releases a list of elementary blocks"""
if not list_usual_idx_not_to_release:
list_usual_idx_not_to_release = {}
if not list_constant_idx_not_to_release:
list_constant_idx_not_to_release = {}
list_uc = []
list_cc = []
for mb in my_blocks:
if mb.is_true_constraint():
list_uc = list_uc + mb.contenu()
else:
list_cc = list_cc + mb.contenu()
for uc in list_uc:
if self.is_relaxed_usual_constraint[uc] and uc not in list_usual_idx_not_to_release:
self._inv_relax_single_usual_constraint(uc)
self.is_relaxed_usual_constraint[uc] = None
for cc in list_cc:
if self.is_relaxed_constant[cc] and cc not in list_constant_idx_not_to_release:
self._inv_relax_single_constant_constraint(cc)
self.is_relaxed_constant[cc] = False
Classes
class ConstraintsRelaxation (model)
-
Class used to release or reset a constraint or a block of constraints, depending on the chosen method
Expand source code
class ConstraintsRelaxation: """ Class used to release or reset a constraint or a block of constraints, depending on the chosen method """ def __init__(self, model): self.model = model self.dict_indexing = self.model.dict_indexing() self.is_relaxed_usual_constraint = {uc: None for uc in self.dict_indexing.list_usual_constraints()} # UsualConstraint --> None si pas relachée, dico des contraintes du proto sinon self.is_relaxed_constant = {cc: False for cc in self.dict_indexing.list_constant_constraints()} # ConstantConstraint --> Boolean, True ssi la contrainte est relachée def relax_all_real_constraints_but(self, all_blocks): """Releases all true constraints except for those which are in all_blocks""" uc_a_garder = {} for nb in all_blocks: if nb.is_true_constraint(): for ec in nb.contenu(): uc_a_garder[ec] = True for uc in self.is_relaxed_usual_constraint: if not self.is_relaxed_usual_constraint[uc] and uc not in uc_a_garder: self._relax_single_usual_constraint(uc) def relax_all_but(self, all_blocks, method): """ Releases all constraints that are not in all_blocks If method == 'sufficient assumption', we do not relax constant constraints """ uc_a_garder = {} cc_a_garder = {} for nb in all_blocks: if nb.is_true_constraint(): for ec in nb.contenu(): uc_a_garder[ec] = True elif method != 'sufficient_assumption': for ec in nb.contenu(): cc_a_garder[ec] = True for uc in self.is_relaxed_usual_constraint: if not self.is_relaxed_usual_constraint[uc] and uc not in uc_a_garder: self._relax_single_usual_constraint(uc) if method != 'sufficient_assumption': for cc in self.is_relaxed_constant: if not self.is_relaxed_constant[cc] and cc not in cc_a_garder: self._relax_single_constant_constraint(cc) def _relax_single_usual_constraint(self, uc: UsualConstraint): """Relaxes one single constraint (checks that it should be relaxed and then calls core function)""" li = uc.list_idx() self.is_relaxed_usual_constraint[uc] = {} for i in li: self.is_relaxed_usual_constraint[uc][i] = copy.copy(self.model.Proto().constraints[i]) self.model.Proto().constraints[i].Clear() def _relax_single_constant_constraint(self, cc: ConstantConstraint): """Relaxes one single constraint (checks that it should be relaxed and then calls core function)""" li = cc.list_idx() for i in li: self.is_relaxed_constant[cc] = True default_range = cc.def_range(i) self.model.Proto().variables[i].domain[:] = [default_range[0], default_range[1]] def relax_list_blocks(self, my_blocks: List[NamedBlock]): """Function that releases a list of elementary blocks""" list_uc = [] list_cc = [] for mb in my_blocks: if mb.is_true_constraint(): list_uc = list_uc + mb.contenu() else: list_cc = list_cc + mb.contenu() for uc in list_uc: if not self.is_relaxed_usual_constraint[uc]: self._relax_single_usual_constraint(uc) for cc in list_cc: if not self.is_relaxed_constant[cc]: self._relax_single_constant_constraint(cc) def inv_relax_all(self): """Reinforces all constraints""" for uc in self.is_relaxed_usual_constraint: if self.is_relaxed_usual_constraint[uc]: self._inv_relax_single_usual_constraint(uc) for cc in self.is_relaxed_constant: if self.is_relaxed_constant[cc]: self._inv_relax_single_constant_constraint(cc) self.is_relaxed_usual_constraint = {uc: None for uc in self.dict_indexing.list_usual_constraints()} # UsualConstraint --> None si pas relachée, contrainte du proto sinon self.is_relaxed_constant = {cc: False for cc in self.dict_indexing.list_constant_constraints()} # ConstantConstraint --> Boolean, True ssi la contrainte est relachée def _inv_relax_single_usual_constraint(self, uc: UsualConstraint): li = uc.list_idx() for i in li: del self.model.Proto().constraints[i] self.model.Proto().constraints.insert(i, self.is_relaxed_usual_constraint[uc][i]) def _inv_relax_single_constant_constraint(self, cc: ConstantConstraint): li = cc.list_idx() for i in li: default_value = cc.def_value(i) self.model.Proto().variables[i].domain[:] = [default_value, default_value] def inv_relax_list_blocks(self, my_blocks: List[NamedBlock], list_usual_idx_not_to_release=None, list_constant_idx_not_to_release=None): """Function that releases a list of elementary blocks""" if not list_usual_idx_not_to_release: list_usual_idx_not_to_release = {} if not list_constant_idx_not_to_release: list_constant_idx_not_to_release = {} list_uc = [] list_cc = [] for mb in my_blocks: if mb.is_true_constraint(): list_uc = list_uc + mb.contenu() else: list_cc = list_cc + mb.contenu() for uc in list_uc: if self.is_relaxed_usual_constraint[uc] and uc not in list_usual_idx_not_to_release: self._inv_relax_single_usual_constraint(uc) self.is_relaxed_usual_constraint[uc] = None for cc in list_cc: if self.is_relaxed_constant[cc] and cc not in list_constant_idx_not_to_release: self._inv_relax_single_constant_constraint(cc) self.is_relaxed_constant[cc] = False
Methods
def inv_relax_all(self)
-
Reinforces all constraints
Expand source code
def inv_relax_all(self): """Reinforces all constraints""" for uc in self.is_relaxed_usual_constraint: if self.is_relaxed_usual_constraint[uc]: self._inv_relax_single_usual_constraint(uc) for cc in self.is_relaxed_constant: if self.is_relaxed_constant[cc]: self._inv_relax_single_constant_constraint(cc) self.is_relaxed_usual_constraint = {uc: None for uc in self.dict_indexing.list_usual_constraints()} # UsualConstraint --> None si pas relachée, contrainte du proto sinon self.is_relaxed_constant = {cc: False for cc in self.dict_indexing.list_constant_constraints()} # ConstantConstraint --> Boolean, True ssi la contrainte est relachée
def inv_relax_list_blocks(self, my_blocks: List[NamedBlock], list_usual_idx_not_to_release=None, list_constant_idx_not_to_release=None)
-
Function that releases a list of elementary blocks
Expand source code
def inv_relax_list_blocks(self, my_blocks: List[NamedBlock], list_usual_idx_not_to_release=None, list_constant_idx_not_to_release=None): """Function that releases a list of elementary blocks""" if not list_usual_idx_not_to_release: list_usual_idx_not_to_release = {} if not list_constant_idx_not_to_release: list_constant_idx_not_to_release = {} list_uc = [] list_cc = [] for mb in my_blocks: if mb.is_true_constraint(): list_uc = list_uc + mb.contenu() else: list_cc = list_cc + mb.contenu() for uc in list_uc: if self.is_relaxed_usual_constraint[uc] and uc not in list_usual_idx_not_to_release: self._inv_relax_single_usual_constraint(uc) self.is_relaxed_usual_constraint[uc] = None for cc in list_cc: if self.is_relaxed_constant[cc] and cc not in list_constant_idx_not_to_release: self._inv_relax_single_constant_constraint(cc) self.is_relaxed_constant[cc] = False
def relax_all_but(self, all_blocks, method)
-
Releases all constraints that are not in all_blocks If method == 'sufficient assumption', we do not relax constant constraints
Expand source code
def relax_all_but(self, all_blocks, method): """ Releases all constraints that are not in all_blocks If method == 'sufficient assumption', we do not relax constant constraints """ uc_a_garder = {} cc_a_garder = {} for nb in all_blocks: if nb.is_true_constraint(): for ec in nb.contenu(): uc_a_garder[ec] = True elif method != 'sufficient_assumption': for ec in nb.contenu(): cc_a_garder[ec] = True for uc in self.is_relaxed_usual_constraint: if not self.is_relaxed_usual_constraint[uc] and uc not in uc_a_garder: self._relax_single_usual_constraint(uc) if method != 'sufficient_assumption': for cc in self.is_relaxed_constant: if not self.is_relaxed_constant[cc] and cc not in cc_a_garder: self._relax_single_constant_constraint(cc)
def relax_all_real_constraints_but(self, all_blocks)
-
Releases all true constraints except for those which are in all_blocks
Expand source code
def relax_all_real_constraints_but(self, all_blocks): """Releases all true constraints except for those which are in all_blocks""" uc_a_garder = {} for nb in all_blocks: if nb.is_true_constraint(): for ec in nb.contenu(): uc_a_garder[ec] = True for uc in self.is_relaxed_usual_constraint: if not self.is_relaxed_usual_constraint[uc] and uc not in uc_a_garder: self._relax_single_usual_constraint(uc)
def relax_list_blocks(self, my_blocks: List[NamedBlock])
-
Function that releases a list of elementary blocks
Expand source code
def relax_list_blocks(self, my_blocks: List[NamedBlock]): """Function that releases a list of elementary blocks""" list_uc = [] list_cc = [] for mb in my_blocks: if mb.is_true_constraint(): list_uc = list_uc + mb.contenu() else: list_cc = list_cc + mb.contenu() for uc in list_uc: if not self.is_relaxed_usual_constraint[uc]: self._relax_single_usual_constraint(uc) for cc in list_cc: if not self.is_relaxed_constant[cc]: self._relax_single_constant_constraint(cc)