Package teamwork :: Package math :: Module Keys
[hide private]
[frames] | no frames]

Source Code for Module teamwork.math.Keys

  1  """A class of unique identifiers for symbolic identification of rows and columns in the PWL representations of state, dynamics, reward, etc. 
  2  @var keyConstant: a L{ConstantKey} instance to be reused (i.e., so as not to have to create millions of new instances 
  3  @var keyDelete: a flag string used internally""" 
  4  from xml.dom.minidom import * 
  5  import copy 
  6  import string 
  7  from teamwork.action.PsychActions import Action 
  8   
  9  keyDelete = '__delete__' 
 10   
11 -class Key(dict):
12 """Superclass for all different keys, used for indexing the various symbolic matrices and hyperplanes""" 13 # The label for this Key subclass 14 keyType = 'generic' 15 # A dictionary of the slot labels expected by this Key subclass 16 slots = {} 17 # The possible values for the above dictionary 18 CLASS = 0 19 ENTITY = 1 20 STATE = 2 21 ACTION = 3 22 VALUE = 4 23 ENTITIES = 5 24 TEST = 6 25
26 - def __str__(self):
27 try: 28 return self._string 29 except AttributeError: 30 self._string = self.simpleText() 31 return self._string
32
33 - def __hash__(self):
34 """Allows L{Key} objects to be used as dictionary keys 35 @return: A hash value for this L{Key}""" 36 try: 37 return self._hash 38 except AttributeError: 39 self._hash = hash(str(self)) 40 return self._hash
41
42 - def simpleText(self):
43 return dict.__str__(self)
44
45 - def instantiate(self,table):
46 """Utility method that takes a mapping from entity references (e.g., 'self', 'actor') to actual entity names (e.g., 'Bill', 'agent0'). It returns a new key string with the substitution applied. Subclasses should override this method for more instantiation of more specialized fields. 47 @param table: A dictionary of label-value pairs. Any slot in the key whose filler matches a label in the given table will be replaced by the associated value. 48 @type table: C{dict} 49 @return: Any new L{Key} instances with the appropriate label substitutions 50 @rtype: L{Key}[] 51 """ 52 objList = [copy.copy(self)] 53 for key,value in self.items(): 54 try: 55 entity = table[value] 56 except KeyError: 57 continue 58 if isinstance(entity,list): 59 entityList = entity 60 elif isinstance(entity,str): 61 entityList = [entity] 62 else: 63 # Assume that it's an Agent 64 entityList = [entity.name] 65 for obj in objList[:]: 66 objList.remove(obj) 67 for entity in entityList: 68 newObj = copy.copy(obj) 69 newObj[key] = entity 70 objList.append(newObj) 71 return objList
72
73 - def __copy__(self):
74 return self.__class__(self)
75
76 - def __xml__(self):
77 """XML Serialization 78 79 The format looks like:: 80 81 <key type=\"self.keyType\"> 82 <tag1>value1</tag1> 83 <tag2>value2</tag2> 84 ... 85 <tagn>valuen</tagn> 86 </key> 87 88 @return: An XML object representing this L{Key} 89 @rtype: Element""" 90 doc = Document() 91 root = doc.createElement('key') 92 doc.appendChild(root) 93 root.setAttribute('type',self.keyType) 94 for key,value in self.items(): 95 node = doc.createElement(key) 96 root.appendChild(node) 97 if isinstance(value,str): 98 node.appendChild(doc.createTextNode(value)) 99 elif isinstance(value,int): 100 node.appendChild(doc.createTextNode(str(value))) 101 elif value is None: 102 pass 103 elif isinstance(value,list): 104 for action in value: 105 if isinstance(action,Action): 106 node.appendChild(action.__xml__().documentElement) 107 else: 108 raise NotImplementedError,'Unable to serialize key, "%s", with lists of %s' % (key,action.__class__.__name__) 109 else: 110 raise NotImplementedError,'Unable to serialize key, "%s", with values of type %s' % (key,value.__class__.__name__) 111 return doc
112
113 - def parse(self,element):
114 """Updates the current key with the elements stored in the given XML element 115 @type element: Element 116 @return: the L{Key} object stored in the given XML element 117 """ 118 assert(element.tagName == 'key') 119 keyType = element.getAttribute('type') 120 for cls in self.__class__.__subclasses__(): 121 if cls.keyType == keyType: 122 key = cls() 123 node = element.firstChild 124 while node: 125 if node.nodeType == node.ELEMENT_NODE: 126 # Not sure whether this is robust enough unicode handling 127 label = string.strip(str(node.tagName)) 128 value = None 129 if label == 'action': 130 # Special case, how ugly 131 value = [] 132 subNode = node.firstChild 133 while subNode: 134 if subNode.nodeType == node.ELEMENT_NODE: 135 act = Action() 136 act.parse(subNode) 137 value.append(act) 138 subNode = subNode.nextSibling 139 elif label == 'depth': 140 # Ouch, another special case 141 value = int(node.childNodes[0].data) 142 else: 143 try: 144 value = string.strip(str(node.childNodes[0].data)) 145 except IndexError: 146 # Nothing there, hope that's OK 147 pass 148 key[label] = value 149 node = node.nextSibling 150 return key 151 else: 152 print element.getAttribute('type') 153 print map(lambda c:c.keyType,self.__class__.__subclasses__()) 154 raise NotImplementedError,'Unsupported key type: %s' % (keyType)
155
156 -class ConstantKey(Key):
157 """A L{Key} indicating the entry corresponding to a constant factor""" 158 keyType = 'constant' 159
160 - def simpleText(self):
161 return 'constant'
162 163 keyConstant = ConstantKey() 164
165 -class StateKey(Key):
166 """A L{Key} indicating the entry corresponding to a state feature value""" 167 keyType = 'state' 168 slots = {'entity':Key.ENTITY, 169 'feature':Key.STATE} 170
171 - def simpleText(self):
172 if self['entity'] == 'self': 173 return 'my %s' % (self['feature']) 174 else: 175 return '%s\'s %s' % (self['entity'],self['feature'])
176
177 -class ModelKey(StateKey):
178 """A L{Key} indicating the mental model corresponding to a given entity 179 """ 180 keyType = 'mental model' 181 slots = {'entity':Key.ENTITY, 182 'feature':Key.STATE, 183 }
184 - def __init__(self,args={}):
185 if not args.has_key('feature'): 186 args['feature'] = '_mentalmodel' 187 StateKey.__init__(self,args)
188
189 - def simpleText(self):
190 if self['entity'] == 'self': 191 return 'mental model of me' 192 else: 193 return 'mental model of %s' % (self['entity'])
194
195 -class BinaryKey(StateKey):
196 """A L{StateKey} whose values will be either 0. or 1. 197 @warning: the code does not enforce this restriction; it merely exploits it 198 """ 199 keyType = 'binary'
200
201 -class ObservationKey(Key):
202 """A L{Key} indicating the entry corresponding to an observation flag 203 204 >>> key = ObservationKey({'type':'heard left'}) 205 """ 206 keyType = 'observation' 207 decayRate = 0.5 208
209 - def simpleText(self):
210 return self['type']
211
212 -class ActionKey(ObservationKey,Key):
213 """A L{Key} indicating the entry corresponding to an observed action flag 214 215 >>> key = ActionKey({'type':'tax','entity':'manager','object':'market'}) 216 217 A minimum of one of the fields must be provided. Any omitted fields are assumed to be filled by wildcards. 218 """ 219 keyType = 'action' 220 slots = {'type':Key.ACTION, 221 'entity':Key.ENTITY, 222 'object':Key.ENTITY} 223
224 - def simpleText(self):
225 content = self['type'] 226 if self['object']: 227 content += ' %s' % (self['object']) 228 if self['entity']: 229 content += ' by %s' % (self['entity']) 230 return content
231
232 -class IdentityKey(Key):
233 """A L{Key} indicating the entry corresponding to a role flag. The I{relationship} slot can take on the following values: 234 - equals: tests that an agent equals the specified I{entity} field 235 - in: tests that an agent is in the specified I{entity} list of agents 236 """ 237 keyType = 'identity' 238 slots = {'entity':Key.ENTITY, 239 'relationship':Key.TEST} 240
241 - def __init__(self,args={}):
242 Key.__init__(self,args) 243 if self.has_key('entity') and 'entity' == 'self': 244 raise UserWarning,'Hey nimrod, why are you testing whether the agent is itself?'
245
246 - def instantiate(self,table):
247 """Utility method that takes a mapping from entity references (e.g., 'self', 'actor') to actual entity names (e.g., 'Bill', 'agent0'). It returns a new key string with the substitution applied. It also reduces any 'identity' keys to be either constant or null depending on whether the key matches the identity of the entity represented in the dictionary provided. Subclasses should override this method for more instantiation of more specialized fields. 248 @param table: A dictionary of label-value pairs. Any slot in the key whose filler matches a label in the given table will be replaced by the associated value. 249 @type table: C{dict} 250 @return: A new L{Key} instance with the appropriate label substitutions 251 @rtype: L{Key} 252 """ 253 if self['relationship'] == 'equals': 254 if table.has_key(self['entity']) and \ 255 table[self['entity']] == table['self'].name: 256 return keyConstant 257 else: 258 return keyDelete 259 elif self['relationship'] == 'in': 260 assert(isinstance(self['entity'],list), 261 'Non-list entity for "in" relationship: %s' \ 262 % (str(self['entity']))) 263 if table['self'].name in table[self['entity']]: 264 return keyConstant 265 else: 266 return keyDelete 267 else: 268 raise NotImplementedError,'Unknown relationship, "%s", in %s instance' \ 269 % (self['relationship'],self.__class__.__name__)
270
271 - def simpleText(self):
272 return 'I am %s' % (self['entity'])
273
274 -class ClassKey(Key):
275 """A L{Key} indicating the entry corresponding to a class membership flag""" 276 keyType = 'class' 277 slots = {'value':Key.CLASS, 278 'entity':Key.ENTITY} 279
280 - def simpleText(self):
281 if self['entity'] == 'self': 282 return 'I am %s' % (self['value']) 283 else: 284 return '%s is %s' % (self['entity'],self['value'])
285
286 - def instantiate(self,table):
287 """Utility method that takes a mapping from entity references (e.g., 'self', 'actor') to actual entity names (e.g., 'Bill', 'agent0'). It returns a new key string with the substitution applied. It also reduces any 'identity' keys to be either constant or null depending on whether the key matches the identity of the entity represented in the dictionary provided. Subclasses should override this method for more instantiation of more specialized fields. 288 @param table: A dictionary of label-value pairs. Any slot in the key whose filler matches a label in the given table will be replaced by the associated value. 289 @type table: C{dict} 290 @return: A new L{Key} instance with the appropriate label substitutions 291 @rtype: L{Key} 292 """ 293 if self['entity'] == 'self': 294 entity = table['self'] 295 else: 296 try: 297 entity = table['self'].getEntity(table[self['entity']]) 298 except KeyError: 299 # Assume false 300 return keyDelete 301 if entity.instanceof(self['value']): 302 return keyConstant 303 else: 304 return keyDelete
305
306 -class RelationshipKey(Key):
307 """A L{Key} indicating the entry corresponding to the slot for the corresponding inter-agent relationship 308 """ 309 keyType = 'relationship' 310 slots = {'feature':Key.STATE, 311 'relatee':Key.ENTITY, 312 } 313
314 - def simpleText(self):
315 if self['relatee'] == 'self': 316 content = 'I am my own %s' % (self['feature']) 317 else: 318 content = '%s is my %s' % (self['relatee'],self['feature']) 319 return content
320
321 - def instantiate(self,table):
322 """Utility method that takes a mapping from entity references (e.g., 'self', 'actor') to actual entity names (e.g., 'Bill', 'agent0'). It returns a new key string with the substitution applied. It also reduces any 'identity' keys to be either constant or null depending on whether the key matches the identity of the entity represented in the dictionary provided. Subclasses should override this method for more instantiation of more specialized fields. 323 @param table: A dictionary of label-value pairs. Any slot in the key whose filler matches a label in the given table will be replaced by the associated value. 324 @type table: C{dict} 325 @return: A new L{Key} instance with the appropriate label substitutions 326 @rtype: L{Key} 327 """ 328 try: 329 eligible = table['self'].relationships[self['feature']] 330 except KeyError: 331 return keyDelete 332 if self['relatee'] == 'self': 333 name = table[self['relatee']].name 334 else: 335 name = table[self['relatee']] 336 if name in eligible: 337 return keyConstant 338 else: 339 return keyDelete
340 341
342 -class LinkKey(Key):
343 """A L{Key} corresponding to a slot for a pairwise link between two entities 344 """ 345 keyType = 'link' 346 slots = { 347 'subject':Key.ENTITY, 348 'verb':Key.STATE, 349 'object':Key.ENTITY, 350 } 351
352 - def simpleText(self):
353 if self['subject'] == 'self': 354 content = 'I %s ' % (self['verb']) 355 if self['object'] == 'self': 356 content += 'myself' 357 else: 358 content += self['object'] 359 else: 360 content = '%s %s ' % (self['subject'],self['verb']) 361 if self['object'] == 'self': 362 content += 'me' 363 else: 364 content += self['object'] 365 return content
366
367 -class WorldKey(Key):
368 """A L{Key} indexing into a sample space of possible worlds 369 """ 370 keyType = 'world' 371 slots = {'world': Key.VALUE} 372
373 - def simpleText(self):
374 return 'Q=%d' % (self['world'])
375
376 -def makeStateKey(entity,feature):
377 """Helper function for creating StateKey objects 378 @param entity: The entity to be pointed to by this key 379 @type entity: string 380 @param feature: The state feature to be pointed to by this key 381 @type feature: string 382 @return: the corresponding StateKey object""" 383 return StateKey({'entity':entity,'feature':feature})
384
385 -def makeActionKey(action):
386 """Helper function for creating ActionKey objects 387 @param action: The action to be flagged by this Key 388 @type action: Action instance (though a generic dictionary will work, too) 389 @return: an ActionKey instance corresponding to the given action""" 390 return ActionKey({'type':action['type'], 391 'entity':action['actor'], 392 'object':action['object']})
393
394 -def makeIdentityKey(entity):
395 """Helper funtion for creating IdentityKey objects 396 @param entity: the relationship that should be matched on 397 @type entity: string (e.g., 'actor','object') 398 @return: an IdentityInstance testing for the given relationship""" 399 return IdentityKey({'entity':entity, 400 'relationship':'equals'})
401
402 -def makeClassKey(entity,className):
403 """Helper function for creating ClassKey objects 404 @param entity: the entity to be tested 405 @type entity: string (e.g., 'actor','object','self') 406 @param className: the class name to test membership for 407 @type className: string 408 @return: a ClassKey instance with the appropriate class test 409 @warning: may not actually work 410 """ 411 return ClassKey({'value':className, 412 'entity':entity})
413
414 -def temporaryTest(original):
415 """Debugging code""" 416 used = {} 417 for root in original: 418 trees = [root] 419 while len(trees) > 0: 420 tree = trees.pop() 421 trees += tree.children() 422 assert not used.has_key(id(tree)) 423 used[id(tree)] = True 424 if tree.isLeaf(): 425 matrix = tree.getValue() 426 assert not used.has_key(id(matrix)) 427 used[id(matrix)] = True 428 for row in matrix.values(): 429 assert not used.has_key(id(row)) 430 used[id(row)] = True 431 else: 432 for plane in tree.split: 433 assert not plane.weights._frozen 434 assert not used.has_key(id(plane.weights)) 435 assert not used.has_key(id(plane.weights._order)) 436 used[id(plane.weights)] = True 437 used[id(plane.weights._order)] = True 438 for root in original: 439 trees = [root] 440 while len(trees) > 0: 441 tree = trees.pop() 442 trees += tree.children() 443 if tree.isLeaf(): 444 for row in tree.getValue().values(): 445 for key in row.keys(): 446 if not row._order.has_key(key): 447 print 'leaf' 448 return root,key 449 else: 450 for plane in tree.split: 451 for key in plane.weights.keys(): 452 if not plane.weights._order.has_key(key): 453 print 'branch' 454 return root,key 455 else: 456 return None
457