Source code for pyomo.core.expr.symbol_map

#  ___________________________________________________________________________
#
#  Pyomo: Python Optimization Modeling Objects
#  Copyright (c) 2008-2022
#  National Technology and Engineering Solutions of Sandia, LLC
#  Under the terms of Contract DE-NA0003525 with National Technology and
#  Engineering Solutions of Sandia, LLC, the U.S. Government retains certain
#  rights in this software.
#  This software is distributed under the 3-clause BSD License.
#  ___________________________________________________________________________

from weakref import ref as weakref_ref


[docs]class SymbolMap(object): """ A class for tracking assigned labels for modeling components. Symbol maps are used, for example, when writing problem files for input to an optimizer. Warning: A symbol map should never be pickled. This class is typically constructed by solvers and writers, and it may be owned by models. Note: We should change the API to not use camelcase. Attributes: byObject (dict): maps (object id) to (string label) bySymbol (dict): maps (string label) to (object weakref) alias (dict): maps (string label) to (object weakref) default_labeler: used to compute a string label from an object """ def __init__(self, labeler=None): self.byObject = {} self.bySymbol = {} self.aliases = {} self.default_labeler = labeler class UnknownSymbol: pass def __getstate__(self): # # TODO: Why is this method defined given the previous # comment that this object should not be pickled? # # Note: byObject and bySymbol constitute a bimap. We only need # to pickle one of them, and bySymbol is easier. # return { 'bySymbol': tuple( (key, obj()) for key, obj in self.bySymbol.items() ), 'aliases': tuple( (key, obj()) for key, obj in self.aliases.items() ), } def __setstate__(self, state): self.byObject = {id(obj):key for key, obj in state['bySymbol']} self.bySymbol = {key:weakref_ref(obj) for key,obj in state['bySymbol']} self.aliases = {key:weakref_ref(obj) for key, obj in state['aliases']} def addSymbol(self, obj, symb): """ Add a symbol for a given object """ self.byObject[id(obj)] = symb self.bySymbol[symb] = weakref_ref(obj) def addSymbols(self, obj_symbol_tuples): """ Add (object, symbol) tuples from an iterable object. This method assumes that symbol names will not conflict. """ for obj, symbol in obj_symbol_tuples: self.byObject[id(obj)] = symbol self.bySymbol[symbol] = weakref_ref(obj) def createSymbol(self, obj, labeler=None, *args): """ Create a symbol for an object with a given labeler. No error checking is done to ensure that the generated symbol name is unique. """ #if args: # symb = labeler(obj, *args) #else: # symb = labeler(obj) if labeler: symb = labeler(obj) elif self.default_labeler: symb = self.default_labeler(obj) else: symb = str(obj) self.byObject[id(obj)] = symb self.bySymbol[symb] = weakref_ref(obj) return symb def createSymbols(self, objs, labeler, *args): """ Create a symbol for iterable objects with a given labeler. No error checking is done to ensure that the generated symbol names are unique. """ #if args: # self.addSymbols([(obj,labeler(obj, *args)) for obj in objs]) #else: # self.addSymbols([(obj,labeler(obj)) for obj in objs]) self.addSymbols([(obj,labeler(obj)) for obj in objs]) def getSymbol(self, obj, labeler=None, *args): """ Return the symbol for an object. If it has not already been cached in the symbol map, then create it. """ obj_id = id(obj) if obj_id in self.byObject: return self.byObject[obj_id] # # Create a new symbol, performing an error check if it is a duplicate # if labeler: symb = labeler(obj) elif self.default_labeler: symb = self.default_labeler(obj) else: symb = str(obj) if symb in self.bySymbol: if self.bySymbol[symb]() is not obj: raise RuntimeError( "Duplicate symbol '%s' already associated with " "component '%s' (conflicting component: '%s')" % (symb, self.bySymbol[symb]().name, obj.name) ) self.bySymbol[symb] = weakref_ref(obj) self.byObject[obj_id] = symb return symb
[docs] def alias(self, obj, name): """ Create an alias for an object. An aliases are symbols that do not have a one-to-one correspondence with objects. """ if name in self.aliases: # # If the alias exists and the objects are the same, # then return. Otherwise, raise an exception. # old_object = self.aliases[name]() if old_object is obj: return else: raise RuntimeError( "Duplicate alias '%s' already associated with " "component '%s' (conflicting component: '%s')" % (name, "UNKNOWN" if old_object is None else old_object.name, obj.name) ) else: # # Add the alias # self.aliases[name] = weakref_ref(obj)
def getObject(self, symbol): """ Return the object corresponding to a symbol """ if symbol in self.bySymbol: return self.bySymbol[symbol]() elif symbol in self.aliases: return self.aliases[symbol]() else: return SymbolMap.UnknownSymbol def removeSymbol(self, obj): symb = self.byObject.pop(id(obj)) self.bySymbol.pop(symb)