Source code for inference.query

'''
When making inference in a PRM, specifying a query is not as straight forward as in Bayesian Networks.
A :class:`.Query` instance consists of event and evidence variables, the inference goal being to
find a posterior distribution over the event variables given the evidence variables.

Two auxiliary data structures are used to specify event and evidence variables, 

* :class:`.Qvariable` for specifying attribute classes
* :class:`.ObjsVariable` for specifying attribute objects

'''

from network.vertices import computeID

from analytics.performance import time_analysis

[docs]class Qvariable(): ''' A :class:`Qvariable` instance induces a set of attribute objects (:class:`.GBNvertex` instances) that are used for specifying event and evidence variables when making inference. ''' def __init__(self, attr, objsVar, values=None): self.erClass = attr.erClass ''' :class:`.ObjsVariable` instance. ''' self.objs = objsVar ''' :class:`.ObjsVariable` instance. ''' self.attr = attr ''' :class:`.Attribute` instance ''' self.values = values # Possibility to specify the set of attribute objects of with a certain value, not implemented yet def __repr__(self): return 'Qvar = %s, %s, %s'%(self.attr.fullname,self.objs,self.values)
[docs]def createQvar(attrName,objsVar=None,objsConstraint=None,objsPkValues=None): ''' Creats a :class:`Qvariable` from the string name of an :class:`.Attribute` instance, e.g. `Professor.fame` :arg fullname: Full name of an :class:`.Attribute` :arg objsVar: :class:`.ObjsVariable` instance :arg objsConstraint: Type for the :class:`.ObjsVariable` instance, :attr:`.constraint` :arg objsPkValues: List of sets of primary keys., :attr:`.pkValues` :returns: :class:`Qvariable` instance ''' from prm import prm attr = prm.attributes[attrName] erClass = attr.erClass # If an ObjsVariable was passed as argument, use it. Otherwise construct one with # the constraint and pkValues if objsVar is None: objsVar = ObjsVariable(objsConstraint,objsPkValues) return Qvariable(attr, objsVar )
[docs]class ObjsVariable(): ''' Data structure that is used to define a set of attribute objects that are associated with a specific :class:`Qvariable` instance. :attr:`.pkValues` is a list of primary keys of the attribute class that the :class:`.Qvariable` instance is associated with. The :attr:`.ObjsVariable.constraint` allows to specify a subset of all attribute objects in an expressive manner * inclusive 'incl' : only these attribute objects * exclusive 'excl' : all but these attribute objects As an example, to create a query for an :class:`Entity`:: objsStudent = ObjsVariable('incl', [(1,),(4,),(11,)]) Or in case of a query for a :class:`Relationship`:: objsAdvisor = ObjsVariable('incl', [(1,3),(4,3),(11,3)]) ''' exclusive = 'excl' inclusive = 'incl' def __init__(self,constraint,pkValues,complete=True): self.constraint = constraint """ Constraint is either 'excl' or 'incl' """ self.pkValues = None """ List of sets of primary keys. Even in case of an :class:`Entity` with only one primary key, the list needs to consist of sets, e.g. * [ (pk,),(pk,),(pk,),(pk,),...] in case of an :class:`Entity` * [(pk1,pk2),(pk1,pk2),(pk1,pk2),(pk1,pk2),....] in case of a :class:`Relationship` """ if complete: #pkValues contains a full specification, e.g. a value for all primary keys in qvar.attr.erClass.pk self.pkValues = pkValues else: #pkValues is defined in from of a dictionary and the remaining pkValues have to be loaded from the data pass def __repr__(self): return '%s, %s'%(self.constraint,self.pkValues)
[docs]class Query(): ''' When performing inference on the PRM, we are given a set :math:`\mathbb{Y}` (`event` variables) and a set :math:`\mathbb{E}` (`evidence` variables), the inference goal being to find :math:`P(\mathbb{Y} \mid \mathbb{E})` ''' def __init__(self,event,evidence=None): self.event = event ''' List of :class:`.Qvariable` instances representing event variables ''' self.evidence = evidence ''' List of :class:`.Qvariable` instances representing evidence variables ''' self.objEvidenceLookup = None ''' Dictionary used to check whether attribute objects are part of the evidence. Format: { key = :class:`.Attribute` instance : value = ( :attr:`.ObjsVariable.constraint` , [ List of :attr:`.GBNvertex.ID` ] ) } When unrolling a GBN we are creating a d-separated BN for the query :math:`P(\mathbb{Y} \mid \mathbb{E})`. We need an efficient way to look up wheter a certain GBN node is in the evidence because this influences the structure of the induced graph, e.g. * If a loaded child is in :math:`\mathbb{E}` -> common cause -> load parents of node and don't load children' * If a loaded child is not in :math:`\mathbb{E}` -> load children of node The dictionary is computed by :meth:`.computeObjEvidenceLookup` ''' self.computeObjEvidenceLookup()
[docs] def computeObjEvidenceLookup(self): """ Computes the :attr:`.objEvidenceLookup` dictionary """ self.objEvidenceLookup = {} if self.evidence is not None: for qvar in self.evidence: self.objEvidenceLookup[qvar.attr] = (qvar.objs.constraint ,[computeID(qvar.attr,pkVal) for pkVal in qvar.objs.pkValues])
[docs] def gbnVertexInEvidence(self,gbnVertex): """Calls :meth:`.objInEvidence` with parameters `gbnVertex.attr` and `gbnVertex.ID` :arg gbnVertex: :class:`.GBNvertex` instance """ return self.objInEvidence(gbnVertex.attr,gbnVertex.ID)
[docs] def objInEvidence(self,attr,gbnID): ''' Returns `True` if attribute object passed as argument is part of the evidence. One would think that the a dictionary wouldn't be necessary and that a simple list containing all gbnvertexIDs would suffice. But this is not the case since a :class:`.Qvariable` (e.g. evidence for one attibute) can either be 'inclusive' or exclusive, so the dictionary is needed to check the :attr:`ObjsVariable.constraint`. :arg attr: :class:`.Attribute instance` :arg gbnID: Attribute object ID :returns: `True` if attribute object is in evidence ''' # **args = {attr:a,pkVal:pk,vertex:gbnV} #print gbnID if attr in self.objEvidenceLookup: (constraint,gbnVs) = self.objEvidenceLookup[attr] if constraint==ObjsVariable.inclusive: # ONLY THESE #print constraint, ' gbnID in gbnVs=',gbnID in gbnVs return gbnID in gbnVs else: # ALL BUT THESE #print constraint, ' gbnID not in gbnVs=',gbnID not in gbnVs return gbnID not in gbnVs else: #print attr,' not in evidence' return False
def __repr__(self): #r = [qvar.attr for qvar in self.event] #print r eventS = " ".join([qvar.attr.fullname for qvar in self.event]) if self.evidence != None: evidenceS = ",".join([qvar.attr.fullname for qvar in self.evidence]) return 'P(%s | %s)'%(eventS,evidenceS) return 'P(%s)'%(eventS)