Package teamwork :: Package messages :: Module PsychMessage
[hide private]
[frames] | no frames]

Source Code for Module teamwork.messages.PsychMessage

  1  """Class definition of messages 
  2  @author: David V. Pynadath <pynadath@isi.edu> 
  3  """ 
  4   
  5  import string 
  6  from types import * 
  7  from xml.dom.minidom import * 
  8   
  9  from teamwork.math.Interval import Interval 
 10  from teamwork.math.KeyedMatrix import KeyedMatrix 
 11  from teamwork.math.probability import Distribution 
 12  from teamwork.action.PsychActions import * 
 13   
14 -class Message(Action):
15 """Subclass of L{Action} corresponding to messages, which are realized as attempts to modify the beliefs of others 16 @cvar acceptString: the flag indicating that a hearer should be forced to believe this message 17 @cvar rejectString: the flag indicating that a hearer should be forced to disbelieve this message 18 @type acceptString,rejectString: C{str} 19 @cvar fields: the available keys for messages of this class (unless otherwise noted, the value under each key is a string) 20 - I{sender}: the agent sending the message (i.e., the I{actor}) 21 - I{receiver}: the I{sender}'s intended hearer (i.e., the I{object}) 22 - I{type}: the action type (default is C{_message}) 23 - I{performative}: as defined by ACL conventions (default is C{tell}) 24 - I{command}: the action that the I{sender} is commanding the I{receiver} to perform (probably doesn't work) 25 - I{force}: dictionary (over receivers) of flags indicating whether acceptance/rejection of the message should be forced. For each name key, if the value is C{None}, then the agent decides for itself; if L{acceptString}, then theagent must believe the message; if L{rejectString}, then the agent must reject the message. You should not set this field's value directly; rather, use the L{forceAccept}, L{forceReject}, L{mustAccept}, and L{mustReject} methods as appropriate 26 - I{factors}: the content of the message, in the form of a list of dictionaries, specifying the intended change to individual beliefs 27 """ 28 fields = { 29 # Redundant fields between actions and messages 30 'actor':{},'sender':{}, 31 'object':{},'receiver':{}, 32 # Same as in actions 33 'type':{},'command':{}, 34 # Unique to messages 35 'factors':{},'performative':{},'force':{}, 36 '_observed':{},'_unobserved':{}, 37 # The implied belief change of this message content 38 'matrix':{}, 39 # Flags indicating whether acceptance/rejection is forced 40 } 41 acceptString = 'accept' 42 rejectString = 'reject' 43
44 - def __init__(self,arg={}):
45 if isinstance(arg,str): 46 Action.__init__(self) 47 self.extractFromString(arg) 48 else: 49 Action.__init__(self,arg) 50 if not self['type']: 51 self['type'] = '_message' 52 # Flag (used by internal Entity methods) to force the 53 # acceptance of this message...useful for what-if reasoning 54 if not self.has_key('force'): 55 self['force'] = {} 56 if not self.has_key('performative'): 57 self['performative'] = 'tell'
58
59 - def __setitem__(self,index,value):
60 Action.__setitem__(self,index,value) 61 if index == 'actor': 62 Action.__setitem__(self,'sender',value) 63 elif index == 'sender': 64 Action.__setitem__(self,'actor',value) 65 elif index == 'object': 66 Action.__setitem__(self,'receiver',value) 67 elif index == 'receiver': 68 Action.__setitem__(self,'object',value)
69
70 - def extractFromString(self,str):
71 """The string representation of a non-command message is: 72 <key11>:<key12>:...:<key1n>=<value1>;<key21>:<key22>:...:<key2m>=<value2>;... 73 e.g., entities:Psyops:state:power=0.7;entities:Paramilitary:entities:Psyops:state:power=0.7 74 e.g., message Psyops Paramilitary entities:Psyops:policy:observation depth 3 actor Paramilitary type violence object UrbanPoor violence-to-Paramilitary 75 For commands, the syntax is 76 'command;<key1>=<val1>;<key2>=<val2>;...', where the usual keys 77 are 'object' (e.g., 'opponent') and 'type' (e.g., 'violence'). 78 This same format is expected by the constructor and returned by 79 the string converter.""" 80 print 'WARNING: You should migrate to dictionary spec of messages' 81 self['factors'] = [] 82 factors = string.split(str,';') 83 if factors[0] == 'command': 84 self['command'] = {} 85 for factor in factors[1:]: 86 key,value = string.split(factor,'=') 87 self['command'][key] = value 88 else: 89 self['command'] = None 90 for factor in factors: 91 factor = string.strip(factor) 92 if factor == self.acceptString: 93 self.forceAccept() 94 elif factor == self.rejectString: 95 self.forceReject() 96 else: 97 relation = '=' 98 pair = string.split(factor,relation) 99 if len(pair) == 1: 100 relation = '>>' 101 pair = string.split(factor,relation) 102 lhs = string.split(string.strip(pair[0]),':') 103 rhs = string.split(string.strip(pair[1]),':') 104 factor = {'lhs':lhs,'rhs':rhs,'relation':relation, 105 'topic':'state'} 106 if len(rhs) == 1: 107 try: 108 value = float(pair[1]) 109 except ValueError: 110 value = pair[1] 111 factor['value'] = value 112 elif len(rhs) == 2: 113 try: 114 lo = float(rhs[0]) 115 hi = float(rhs[1]) 116 factor['value'] = Interval(lo,hi) 117 except ValueError: 118 pass 119 self['factors'].append(factor)
120
121 - def force(self,agent='',value=None):
122 """ 123 @param value: if a positive number or equals the 'acceptString' 124 attribute, then sets the message to force acceptance; if value 125 is a negative number of equals the 'rejectString' attribute, 126 then sets the message to force rejection; otherwise, the 127 acceptance of this message is left up to the receiver 128 @param agent: the agent whose acceptance is being forced. If the empty string, then all agents are forced (the default) 129 @type agent: str 130 """ 131 if value: 132 if type(value) is StringType: 133 if value == self.acceptString: 134 self['force'][agent] = self.acceptString 135 elif value == self.rejectString: 136 self['force'][agent] = self.rejectString 137 else: 138 raise TypeError,'Unknown forced value: '+`value` 139 elif value > 0: 140 self['force'][agent] = self.acceptString 141 else: 142 self['force'][agent] = self.rejectString 143 else: 144 self['force'][agent] = None
145
146 - def forceAccept(self,agent=''):
147 """Sets the message to force acceptance by the receiver 148 @param agent: the agent whose acceptance is being forced. If the empty string, then all agents are forced (the default) 149 @type agent: str 150 """ 151 self.force(agent,self.acceptString)
152
153 - def forceReject(self,agent=''):
154 """Sets the message to force rejection by the receiver 155 @param agent: the agent whose acceptance is being forced. If the empty string, then all agents are forced (the default) 156 @type agent: str 157 """ 158 self.force(agent,self.rejectString)
159
160 - def mustAccept(self,agent=''):
161 """ 162 @param agent: the agent whose forcing is being tested. If the empty string, then the test is over all agents (the default) 163 @type agent: str 164 @return: true iff this message has been set to force 165 acceptance by the specified receiver 166 @rtype: boolean 167 """ 168 try: 169 return self['force'][agent] == self.acceptString 170 except KeyError: 171 try: 172 return self['force'][''] == self.acceptString 173 except KeyError: 174 return False
175
176 - def mustReject(self,agent=''):
177 """ 178 @param agent: the agent whose forcing is being tested. If the empty string, then the test is over all agents (the default) 179 @type agent: str 180 @return: true iff this message has been set to force 181 rejection by the receiver 182 @rtype: boolean""" 183 try: 184 return self['force'][agent] == self.rejectString 185 except KeyError: 186 try: 187 return self['force'][''] == self.rejectString 188 except KeyError: 189 return False
190
191 - def __cmp__(self,other):
192 if len(self['factors']) == len(other['factors']): 193 for factor in self['factors']: 194 if not factor in other['factors']: 195 return -1 196 else: 197 return 0 198 else: 199 return -1
200
201 - def __copy__(self):
202 return Message(self)
203
204 - def __xml__(self):
205 doc = Action.__xml__(self) 206 # Record who is forced to do what 207 node = doc.createElement('force') 208 doc.documentElement.appendChild(node) 209 for name in self['force'].keys(): 210 if self.mustAccept(name): 211 child = doc.createElement(self.acceptString) 212 node.appendChild(child) 213 child.setAttribute('agent',name) 214 elif self.mustReject(name): 215 child = doc.createElement(self.rejectString) 216 node.appendChild(child) 217 child.setAttribute('agent',name) 218 # Record factors 219 for factor in self['factors']: 220 node = doc.createElement('factor') 221 doc.documentElement.appendChild(node) 222 node.setAttribute('topic',factor['topic']) 223 if factor.has_key('matrix'): 224 node.appendChild(factor['matrix'].__xml__().documentElement) 225 return doc
226
227 - def parse(self,doc):
228 Action.parse(self,doc) 229 if doc.nodeType == doc.DOCUMENT_NODE: 230 element = doc.documentElement 231 else: 232 element = doc 233 self['factors'] = [] 234 child = element.firstChild 235 while child: 236 if child.nodeType == child.ELEMENT_NODE: 237 if child.tagName == 'factor': 238 factor = {'topic':str(child.getAttribute('topic'))} 239 if child.firstChild: 240 distribution = Distribution() 241 distribution.parse(child.firstChild,KeyedMatrix) 242 factor['matrix'] = distribution 243 self['factors'].append(factor) 244 child = child.nextSibling 245 return self
246
247 - def __str__(self):
248 return self.pretty()
249
250 - def pretty(self,query=None):
251 """ 252 @return: a more user-friendly string rep of this message""" 253 rep = '' 254 for factor in self['factors']: 255 if factor['topic'] == 'state': 256 substr = '' 257 if factor['lhs'][0] == 'entities': 258 entity = factor['lhs'][1] 259 if factor['lhs'][2] == 'state': 260 feature = factor['lhs'][3] 261 if query: 262 substr = 'What is the' 263 else: 264 substr = 'The' 265 substr += ' %s of %s' % (feature,entity) 266 if isinstance(factor['value'],Distribution): 267 if len(factor['value']) == 1: 268 value = str(factor['value'].domain()[0]) 269 else: 270 value = str(factor['value']) 271 else: 272 try: 273 value = '%4.2f' % (float(factor['rhs'][0])) 274 except KeyError: 275 value = str(factor['value']) 276 if len(substr) == 0: 277 # Default rep of messages we can't yet pretty print 278 substr = string.join(factor['lhs'],':') 279 value = string.join(factor['rhs'],':') 280 if query: 281 substr += '?' 282 rep += '; %s' % (substr) 283 if not query: 284 rep += ' %s %s' % (factor['relation'],value) 285 elif factor['topic'] == 'observation': 286 factors = map(lambda a:'%s to %s' % (a['type'],a['object']), 287 factor['action']) 288 actType = string.join(factors,', ') 289 if query: 290 rep += '; Who did %s?' % (actType) 291 else: 292 rep += '; %s chose to %s' % (factor['actor'],actType) 293 elif factor['topic'] == 'model': 294 substr = ' is %s' % (factor['value']) 295 entity = string.join(factor['entity'],' believes ') 296 rep += '; %s %s' % (entity,substr) 297 else: 298 raise NotImplementedError,'No pretty printing for messages of type %s' % (factor['topic']) 299 if self.mustAccept(): 300 rep += ' (force to accept)' 301 elif self.mustReject(): 302 rep += ' (force to reject)' 303 return rep[2:]
304 305 if __name__ == '__main__': 306 from teamwork.examples.PsychSim.PsychUtils import load 307 308 ## entities = load('/home/pynadath/teamwork/examples/PsychSim/Scenarios/cortina.scn') 309 310 311 ## msg1 = Message('entities:CortinaGov:policy = observation depth 1 actor opponent type violence -> command-to-RCNG-violence-to-opponent') 312 ## msg2 = Message('entities:CortinaGov:policy = belief entities opponent state militarypower 0.8 0.9 -> command-to-milSubordinate-violence-against-opponent') 313 314 msg1 = Message('entities:Coalition:entities:CortinaGov:state:support=0.7:1.0') 315 ## entity = entities['CLFLeader'] 316 ## print entity.incorporateMessage(msg1) 317 318 from xml.marshal.generic import * 319 320 xmlStr = dumps(msg1) 321 322 msg2 = loads(xmlStr) 323 324 if msg1 == msg2: 325 print msg2 326