1 """Linear reward subfunctions
2 @author: David V. Pynadath <pynadath@ict.usc.edu>
3 """
4
5 import copy
6 import string
7 from types import *
8 from xml.dom.minidom import *
9
10 from teamwork.math.probability import *
11 from teamwork.math.KeyedMatrix import *
12 from teamwork.action.PsychActions import *
13 from teamwork.utils.PsychUtils import *
14 from teamwork.agent.Agent import Agent
15
17 """A reward subfunction that is linear in a single feature/action
18
19 0) Creating a new goal (i.e., __init__()):
20 goal = L{MinMaxGoal}(entity,direction,type,key)
21
22 1) Accessing elements of this goal
23 goal.L{isMax}()
24 """
25 - def __init__(self,entity=None,direction=None,goalType=None,
26 key=None,value={}):
27 """Constructs a goal object with the specified field values
28
29 The named arguments are stored as corresponding attributes:
30 @param entity: list of names (for recursive belief access if a state goal, or for relevant actor/object if action goal)
31 @type entity: C{str[]}
32 @param direction: either 'min' or 'max'
33 @type direction,goalType,key: C{str}
34 @param goalType: either 'state' (value of a state feature), 'act' (number of occurences of action of given type), 'actActor' (number of occurrences of action of given type by given actor), 'actObject' (number of occurences of action of given type to given object)
35 @param key: for a 'state' goal, is the state feature to be min/maximized for an 'act', goal, is the act type to be min/maximized"""
36 if type(entity) is StringType:
37 raise DeprecationWarning,'Entity specification should be list of names'
38 self.entity = entity
39 if direction:
40 self.direction = string.lower(direction)
41 else:
42
43 self.direction = None
44 if goalType:
45 self.type = goalType
46 else:
47
48 self.type = 'state'
49 if key:
50 self.key = key
51 else:
52 self.key = ''
53 self.name = self.generateName()
54 self.value = value
55 self.weight = 1.
56
58 newGoal = self.__class__(self.entity,self.direction,self.type,
59 self.key,copy.deepcopy(self.value))
60 newGoal.weight = self.weight
61 return newGoal
62
64 """Returns a canonical string representation"""
65 if not self.direction:
66
67 return ''
68
69 name = self.direction+'imize '
70
71 if self.key[0] == '_':
72 feature = self.key[1:]
73 else:
74 feature = self.key
75
76 if len(self.entity) == 1:
77 entity = self.entity[0]
78 elif len(self.entity) == 2:
79 entity = '%s to %s' % (self.entity[1],self.entity[0])
80 elif len(self.entity) > 2:
81 entity = self.entity[0]
82 for other in self.entity[1:]:
83 entity += '->%s' % (other)
84
85 if self.type == 'state':
86 name += '%s of %s' % (feature,entity)
87 else:
88 name += 'number of %s' % (feature)
89 if self.type == 'actActor':
90 name += ' by %s' % (entity)
91 elif self.type == 'actObject':
92 name += ' to %s' % (entity)
93 return name
94
96 """
97 @return: true if maximization goal, false if minimization
98 @rtype: C{boolean}
99 """
100 if self.direction == 'min':
101 return False
102 else:
103 return True
104
106 """
107 @return: the vector key corresponding to this goal
108 @rtype: L{Key}
109 @warning: There is no L{Key} subclass for beliefs
110 """
111 if self.type == 'state':
112 if len(self.entity) == 1:
113 return StateKey({'entity':self.entity[0],
114 'feature':self.key})
115 else:
116 raise NotImplementedError,'Goals on beliefs cannot yet be converted into keys'
117 else:
118
119 return makeActionKey(Action({'type':self.key}))
120
122 """Applies this goal in the specified context"""
123
124 entity = None
125 if self.entity:
126 entity = context
127 for name in self.entity:
128 try:
129 entity = entity[name]
130 except TypeError:
131
132 entity = entity.getEntity(name)
133 except KeyError:
134
135 value = Distribution({0.0:1.})
136 return value
137 if self.type == 'state':
138 try:
139 value = entity.getState(self.key)
140 except KeyError:
141 print entity.getGoals()
142 raise KeyError,'%s has no %s' % (entity.ancestry(),self.key)
143 elif self.type[:3] == 'act':
144 if self.key == 'obey':
145
146
147 value = 0.
148 try:
149 superiors = entity.relationships['_commander']
150 except KeyError:
151 superiors = []
152
153 myAct,myDepth = context.findObservation({'actor':entity.name})
154 if myAct:
155 for superior in superiors:
156
157 command = {'type':'command',
158 'object':entity.name,
159 'actor':superior}
160 act,depth = context.findObservation(command)
161 if act and depth > myDepth:
162
163 if myAct == act['command']:
164 try:
165 value += entity.getSupport(act['actor']).mean()
166 except KeyError:
167 value += 0.1
168 else:
169
170 if self.type == 'actObject':
171 targetList = []
172 for entity in self.entity:
173 targetList.append({'type':self.key,
174 'object':entity})
175 elif self.type == 'actActor':
176 targetList = []
177 for entity in self.entity:
178 targetList.append({'type':self.key,
179 'actor':entity})
180 else:
181 targetList = [{'type':self.key}]
182
183 value = 0.
184 for target in targetList:
185 act,depth = context.findObservation(target)
186 if act:
187 value += pow(ActionKey.decayRate,float(depth))
188
189 value = Distribution({value:1.})
190 else:
191 raise NameError,'unknown goal type '+self.type
192 if self.direction == 'max':
193 pass
194 elif self.direction == 'min':
195 value = -value
196 else:
197 raise NameError,'unknown goal direction '+ self.direction
198 return value
199
201 """Finds the element that has the highest value
202 @return: a tuple of the key and value
203 @rtype: (C{str},L{Distribution})"""
204 maxKey = None
205 maxValue = Interval.FLOOR
206 for key in self.keys():
207 if self[key] > maxValue:
208 maxKey = key
209 maxValue = self[key]
210 return maxKey,maxValue
211
213 """Accessor: supports access in the form `self[index]'"""
214 if self.value:
215 return self.value[index]
216 else:
217 return 0.0
218
220 """Accessor: supports access in the form `self[index]=x'"""
221 self.value[index] = value
222
224 """@return: all of the element names in this goal"""
225 if self.value:
226 return self.value.keys()
227 else:
228 return []
229
231 """Returns a new goal instance in the given context by computing the
232 reward and storing it in its value attribute"""
233 return self.__class__(self.entity,self.direction,self.type,self.key,
234 {self.name:self.reward(context)})
235
250
253
255 return self + (-goal)
256
258 if type(factor) is FloatType:
259
260 value = copy.deepcopy(self.value)
261 try:
262 for key in value.keys():
263 value[key] = value[key] * factor
264 except AttributeError,e:
265 print value
266 raise AttributeError,e
267 elif factor.__class__ == self.__class__:
268
269 value = copy.deepcopy(self.value)
270 for key in value.keys():
271 try:
272 value[key] = value[key] * goal.value[key]
273 except KeyError:
274 pass
275 elif type(factor) is DictType:
276
277 value = copy.deepcopy(self.value)
278 for key in value.keys():
279 try:
280 value[key] = value[key] * factor[key]
281 except KeyError:
282
283 del value[key]
284 elif self.value:
285
286 value = copy.deepcopy(self.value)
287 for key in value.keys():
288 value[key] = factor * value[key]
289 else:
290 value = 0.0
291 return self.__class__(None,None,None,None,value)
292
294 if type(factor) is InstanceType and \
295 factor.__class__ == self.__class__:
296 value = copy.deepcopy(factor.value)
297 for key in value.keys():
298 value[key] = 1.0 / value[key]
299 return self * value
300 else:
301 return self * (1.0/factor)
302
310
335
337 return hash(self.name)
338
340 doc = Document()
341 root = doc.createElement('goal')
342 doc.appendChild(root)
343 root.setAttribute('direction',self.direction)
344 root.setAttribute('type',self.type)
345 root.setAttribute('key',self.key)
346 root.setAttribute('weight',self.weight)
347 node = doc.createElement('entity')
348 root.appendChild(node)
349 for name in self.entity:
350 subNode = doc.createElement('name')
351 node.appendChild(subNode)
352 subNode.appendChild(doc.createTextNode(name))
353 return doc
354
355 - def parse(self,element):
356 entity = []
357 child = element.firstChild
358 while child:
359 if child.nodeType == Node.ELEMENT_NODE:
360 assert(child.tagName,'entity')
361 subNodes = child.getElementsByTagName('name')
362 for subNode in subNodes:
363 entity.append(string.strip(str(subNode.firstChild.data)))
364 child = child.nextSibling
365 self.entity = entity
366 self.direction = str(element.getAttribute('direction'))
367 self.type = str(element.getAttribute('type'))
368 self.key = str(element.getAttribute('key'))
369 self.weight = float(element.getAttribute('weight'))
370 self.name = self.generateName()
371
373 total = 0.0
374 for act in actions:
375 if type(act) is StringType:
376 act = act2dict(act)
377 if type(act['actor']) is StringType:
378 try:
379 act['actor'] = entities[act['actor']]
380 except KeyError:
381
382 continue
383 try:
384 if type(act['object']) is StringType:
385 act['object'] = entities[act['object']]
386 except KeyError:
387 pass
388 if act['type'] == 'violence' and \
389 act['actor'].name == entity.name:
390 try:
391 grievance = entity.getBelief(act['object'],'blame') * \
392 entity.getSelfBelief('hardship')
393 except TypeError:
394
395 grievance = 0.0
396 else:
397 grievance = 0.0
398 total = total + grievance
399 return total
400