1 """
2 Defines dynamics binary relationships between agents
3 """
4 import copy
5
6 from GoalBased import *
7 from teamwork.math.KeyedVector import KeyedVector
8 from teamwork.math.Keys import LinkKey
9
11 """Mix-in class that enables and updates support relationships
12 @cvar supportLimit: the maximum distance allowed between the goals as stated in another entity's messages and one's own beliefs, when determining whether that entity's messages are in support of one's current beliefs
13 @type supportLimit: float
14 @ivar links: the current dynamic relationship values
15 @type links: L{KeyedVector}
16 @ivar linkTypes: list of current dynamic relationship types
17 @type linkTypes: str[]
18 @ivar linkDynamics: the decision tree defining the effects on the dynamic relationship values, indexed by link type, then by action type
19 @type: strS{->}strS{->}PWLDynamics
20 """
21
22 _supportFeature = 'likes'
23 _trustFeature = 'trusts'
24 supportWeights = {'support':0.1,
25 'legitimacy':0.1,
26 'past':0.5,
27 'future':0.3
28 }
29 supportLimit = 0.5
30
41
42
43
45 """
46 @return: the vector index for this entity's relation to the given
47 entity
48 @rtype: L{LinkKey}
49 """
50 return LinkKey({'subject':self.name,
51 'verb':relation,
52 'object':entity})
53
54 - def getLink(self,relationship,entity):
55 """
56 @param relationship: the dynamic relationship (e.g., 'likes',
57 'trusts') to evaluate
58 @param entity: the entity who is the object of the
59 relationship (e.g., the entity being liked or trusted)
60 @type relationship,entity: str
61 @return: the current value of the link
62 @rtype: float
63 """
64 key = self.getLinkKey(relationship,entity)
65 try:
66 return self.links[key]
67 except KeyError:
68 return 0.
69
70 - def setLink(self,relationship,entity,value):
71 """
72 @param relationship: the dynamic relationship (e.g., 'likes',
73 'trusts') to evaluate
74 @param entity: the entity who is the object of the
75 relationship (e.g., the entity being liked or trusted)
76 @type relationship,entity: str
77 @param value: the new value for my trust level
78 @type value: float
79 """
80 key = self.getLinkKey(relationship,entity)
81 self.links[key] = value
82
84 """
85 Removes the given relationship from this entity to the given one
86 @param relationship: the dynamic relationship (e.g., 'likes',
87 'trusts') to evaluate
88 @param entity: the entity who is the object of the
89 relationship (e.g., the entity being liked or trusted)
90 @type relationship,entity: str
91 """
92 key = self.getLinkKey(relationship,entity)
93 del self.links[key]
94
96 """
97 @param relationship: the dynamic relationship (e.g., 'likes',
98 'trusts') to evaluate
99 @type relationship: str
100 @return: the others to which this entity has explicit
101 relationships of the specified type
102 @rtype: str[]
103 """
104 result = []
105 for key in self.links.keys():
106 if key['subject'] == self.name and key['verb'] == relationship:
107 result.append(key['object'])
108 return result
109
111 """
112 @return: all of the available dynamics relationship types that
113 his entity has
114 @rtype: str[]
115 """
116 return self.linkTypes[:]
117
119 """
120 @return: the trust that I have in the given entity
121 @rtype: float
122 """
123 return self.getLink(self._trustFeature,entity)
124
126 """Sets the trust that I have in the given entity
127 @param entity: the entity I (dis)trust
128 @type entity: str
129 @param value: the new value for my trust level
130 """
131 self.setLink(self._trustFeature,entity,value)
132
134 """
135 @return: the support/liking that I have in the given entity
136 @rtype: L{teamwork.math.probability.Distribution}
137 """
138 return self.getLink(self._supportFeature,entity)
139
141 """Sets the support/liking that I have in the given entity
142 @param entity: the entity I (dis)like
143 @type entity: str
144 @param value: the new value for my support/liking level
145 """
146 self.setLink(self._supportFeature,entity,value)
147
149 """Sets the entities linked to
150 """
151
152 for myClass in self.classes:
153 myType = self.hierarchy[myClass]
154 for linkType in myType.getLinkTypes():
155 if linkType == self._supportFeature or \
156 linkType == self._trustFeature:
157 pass
158 elif linkType in self.linkTypes:
159
160 for action in myType.linkDynamics[linkType].keys():
161 if not self.linkDynamics[linkType].has_key(action):
162 dyn = myType.linkDynamics[linkType][action]
163 self.linkDynamics[linkType][action] = dyn
164 else:
165 self.linkTypes.append(linkType)
166 self.linkDynamics[linkType] = {}
167 for action,dyn in myType.linkDynamics[linkType].items():
168 self.linkDynamics[linkType][action] = dyn
169
170 for other in entityList:
171 if other.name != self.name:
172
173 for myClass in self.classes:
174 myType = self.hierarchy[myClass]
175 for linkType in myType.getLinkTypes():
176 if not other.name in self.getLinkees(linkType):
177 for yrClass in other.classes:
178 if yrClass in myType.getLinkees(linkType):
179 value = myType.getLink(linkType,yrClass)
180 self.setLink(linkType,other.name,value)
181 break
182
184 """Packages up all of this agent's beliefs into a handy dictionary
185 @return: the dictionary has the following indices:
186 - state: what this agent believes about the state of the world
187 - I{name}: what this agent think agent, I{name}, believes (i.e., a recursive call to L{getAllBeliefs})
188 @rtype: dict
189 """
190 result = GoalBasedAgent.getAllBeliefs(self)
191 result['relationships'] = self.links
192 return result
193
195 """
196 Computes the hypothetical changes to the given beliefs in response to the given actions
197 @param beliefs: the beliefs to be updated (traditionally, the result from L{getAllBeliefs})
198 @type beliefs: dict
199 @param actions: the actions observed by this agent
200 @type actions: C{dict:strS{->}L{Action}}
201 @param epoch: the current epoch in which these observations occurred (currently ignored, but potentially important)
202 @type epoch: C{int}
203 @type debug: L{Debugger}
204 @return: the belief changes that would result from the specified observed actions, in dictionary form:
205 - beliefs: results as returned by L{hypotheticalAct<SequentialAgents.hypotheticalAct>}
206 - observations: the given actions
207 @rtype: C{dict}
208 """
209 delta = GoalBasedAgent.preComStateEstimator(self,beliefs,actions,
210 epoch,debug)
211 delta['relationships'] = {}
212 diff = None
213 change = None
214 goals = None
215 for link in self.links.keys():
216 if actions.has_key(link['object']):
217
218 if link['verb'] == self._supportFeature:
219
220 if change is None:
221
222 change = copy.deepcopy(delta['state'].expectation())
223 for key in change.rowKeys():
224 change.set(key,key,change[key][key]-1.)
225 if goals is None:
226
227 goals = self.getGoalVector()['state']
228 goals.fill(change.rowKeys())
229 goals = goals.expectation()
230 if diff is None:
231
232
233
234 diff = goals*change
235 diff[link] = 1.
236 delta['relationships'][link] = diff
237 elif link['verb'] == self._trustFeature:
238 pass
239 else:
240
241 table = self.linkDynamics[link['verb']]
242 for action in actions[link['object']]:
243 try:
244 dynamics = table[action['type']]
245 except KeyError:
246 dynamics = None
247 if dynamics:
248 if beliefs is None:
249 beliefs = self.getAllBeliefs()
250 vector = dynamics.instantiateAndApply(beliefs['state'],self,action)[link]
251 try:
252 delta['relationships'][link] += vector
253 except KeyError:
254 delta['relationships'][link] = vector
255 return delta
256
258 """Updates the given delta based on any changes due to the
259 acceptance/reject of a message
260 @param sender: the agent sending the message
261 @type sender: str
262 @param delta: the current effect dictionary
263 @type delta: dict
264 @param accept: flat indicating whether the message has been accepted
265 (C{True} means accepted)
266 @type accept: bool
267 @return: the updated effect dictionary (original dictionary is changed
268 as a side effect)
269 @rtype: dict
270 """
271 if sender in self.getLinkees(self._trustFeature):
272 key = self.getLinkKey(self._trustFeature,sender)
273 vector = KeyedVector()
274 if accept:
275 vector[keyConstant] = 0.1
276 else:
277 vector[keyConstant] = -0.1
278 vector.fill(self.entities.state.domain()[0].keys())
279 try:
280 delta['relationships'][key] = vector
281 except KeyError:
282 delta['relationships'] = {key: vector}
283 if not delta.has_key(self.name):
284 delta[self.name] = {}
285 return delta
286
288 """Takes changes to relationships and modifies them accordingly
289 """
290 goals = None
291 for key,diff in delta.items():
292 if key['subject'] == self.name:
293 try:
294 self.links[key] *= diff[key]
295 except KeyError:
296 self.links[key] = 0.
297 self.links[key] += diff*self.state.expectation()
298
300 if doc is None:
301 doc = GoalBasedAgent.__xml__(self)
302 node = doc.createElement('links')
303 element = self.links.__xml__().documentElement
304 node.appendChild(element)
305 doc.documentElement.appendChild(node)
306
307 for linkType in self.getLinkTypes():
308 node = doc.createElement('linktype')
309 node.setAttribute('name',linkType)
310 doc.documentElement.appendChild(node)
311
312 for linkType,table in self.linkDynamics.items():
313 for action,dynamics in table.items():
314 if isinstance(action,str):
315 node = doc.createElement('linkdynamics')
316 node.setAttribute('feature',linkType)
317 node.setAttribute('action',action)
318 node.appendChild(dynamics.__xml__().documentElement)
319 doc.documentElement.appendChild(node)
320 return doc
321
322 - def parse(self,element):
323 if self.name is None:
324 GoalBasedAgent.parse(self,element)
325 child = element.firstChild
326 while child:
327 if child.nodeType == child.ELEMENT_NODE:
328 if child.tagName == 'links':
329 subChild = child.firstChild
330 while subChild:
331 if subChild.nodeType == child.ELEMENT_NODE:
332 self.links = self.links.parse(subChild)
333 subChild = subChild.nextSibling
334 elif child.tagName == 'linktype':
335 linkType = str(child.getAttribute('name'))
336 if not self.linkDynamics.has_key(linkType):
337 self.linkTypes.append(linkType)
338 self.linkDynamics[linkType] = {}
339 elif child.tagName == 'linkdynamics':
340 linkType = str(child.getAttribute('feature'))
341 action = str(child.getAttribute('action'))
342 dyn = PWLDynamics()
343 subChild = child.firstChild
344 while subChild:
345 if subChild.nodeType == child.ELEMENT_NODE:
346 dyn.parse(subChild)
347 break
348 subChild = subChild.nextSibling
349 try:
350 self.linkDynamics[linkType][action] = dyn
351 except KeyError:
352 self.linkDynamics[linkType] = {action: dyn}
353 child = child.nextSibling
354