"""
Class definition for representation of dependency structure among all variables in a PsychSim scenario
"""
from psychsim.pwl.keys import *
from psychsim.action import ActionSet
[docs]class DependencyGraph(dict):
"""
Representation of dependency structure among PsychSim variables
"""
def __init__(self,myworld=None):
self.world = myworld
self.clear()
dict.__init__(self)
[docs] def clear(self):
self.root = None
self.layers = None
self.evaluation = None
dict.clear(self)
[docs] def getLayers(self):
if self.layers is None:
self.computeLineage()
return self.layers
[docs] def getEvaluation(self):
if self.evaluation is None:
self.computeEvaluation()
return self.evaluation
[docs] def getRoot(self):
if self.root is None:
self.computeLineage()
return self.root
[docs] def deleteKeys(self,toDelete):
self.evaluation = [keySet-toDelete for keySet in self.evaluation if keySet-toDelete]
def __getitem__(self,key):
if len(self) == 0:
self.computeGraph()
return dict.__getitem__(self,key)
[docs] def computeGraph(self,agents=None,state=None,belief=False):
# Process the unary state features
if agents is None:
agents = sorted(self.world.agents.keys())
agents.append(WORLD)
if state is None:
state = self.world.state
for agent in agents:
if agent in self.world.locals:
variables = self.world.locals[agent]
for feature in variables.keys():
key = stateKey(agent,feature)
if key in state:
self[key] = {'agent': agent,'type': 'state pre',
'children': set(),'parents': set()}
self[makeFuture(key)] = {'agent': agent,'type': 'state post',
'children': set(),'parents': set()}
# Process the binary state features
for relation,table in self.world.relations.items():
for key,entry in table.items():
if key in state and entry['subject'] in agents and entry['object'] in agents:
self[key] = {'agent': entry['subject'],
'type': 'state pre',
'children': set(),
'parents': set()}
self[makeFuture(key)] = {'agent': entry['subject'],
'type': 'state post',
'children': set(),
'parents': set()}
for name in agents:
if name != WORLD:
# Create the agent reward node
agent = self.world.agents[name]
R = agent.getReward()
if R:
if {reward for reward in R.values()} != {None}:
self[name] = {'agent': name,
'type': 'utility',
'parents': set(),
'children': set()}
# Process the agent actions
for action in agent.actions:
action = ActionSet([a.root() for a in action])
if not action in self:
self[action] = {'agent': name,
'type': 'action',
'parents': set(),
'children': set()}
# Create links from dynamics
for key,dynamics in self.world.dynamics.items():
if not isinstance(key,str):
continue
if isTurnKey(key):
continue
if isStateKey(key) and not state2agent(key) in agents:
continue
if isBinaryKey(key) and not key2relation(key)['subject'] in agents and \
not key2relation(key)['object'] in agents:
continue
if not key in self:
continue
# assert self.has_key(key),'Graph has not accounted for key: %s' % (key)
if isinstance(dynamics,bool):
continue
for action,tree in dynamics.items():
if not action is True and action['subject'] in agents:
# Link between action to this feature
if action in self:
# assert self.has_key(action),'Graph has not accounted for action: %s' % (action)
dict.__getitem__(self,makeFuture(key))['parents'].add(action)
dict.__getitem__(self,action)['children'].add(makeFuture(key))
# Link between dynamics variables and this feature
for parent in tree.getKeysIn() - set([CONSTANT]):
if (state2agent(parent) == WORLD or state2agent(parent) in agents) and \
parent in self:
dict.__getitem__(self,makeFuture(key))['parents'].add(parent)
dict.__getitem__(self,parent)['children'].add(makeFuture(key))
for name in agents:
if name in self:
agent = self.world.agents[name]
# Create links from reward
model = agent.get_true_model() #self'%s0' % (agent.name)
R = agent.getReward(model)
for parent in R.getKeysIn() - set([CONSTANT]):
if isStateKey(parent) and not state2agent(parent) in agents:
continue
if parent in self:
# Link between variable and agent utility
dict.__getitem__(self,name)['parents'].add(makeFuture(parent))
dict.__getitem__(self,makeFuture(parent))['children'].add(name)
# Create links from legality
for action,tree in agent.legal.items():
action = ActionSet([a.root() for a in action])
for parent in tree.getKeysIn() - set([CONSTANT]):
if isStateKey(parent) and not state2agent(parent) in agents:
continue
if action in self and parent in self:
# Link between prerequisite variable and action
dict.__getitem__(self,action)['parents'].add(parent)
dict.__getitem__(self,parent)['children'].add(action)
[docs] def items(self):
if len(self) == 0:
self.computeGraph()
return dict.items(self)
[docs] def keys(self):
if len(self) == 0:
self.computeGraph()
return dict.keys(self)
[docs] def values(self):
if len(self) == 0:
self.computeGraph()
return dict.values(self)
[docs] def computeLineage(self):
"""
Add ancestors to everybody, also computes layers
"""
self.root = set()
self.layers = []
for key,node in self.items():
node['ancestors'] = set(node['parents'])
if len(node['parents']) == 0:
# Root node
self.root.add(key)
node['level'] = 0
self.layers = [self.root]
level = 0
while sum(map(len,self.layers)) < len(self):
layer = set()
for key in self.layers[level]:
for child in self[key]['children']:
# Update ancestors
self[child]['ancestors'] |= self[key]['ancestors']
if not child in layer:
# Check whether eligible for the new layer
for parent in self[child]['parents']:
if not 'level' in self[parent] or self[parent]['level'] > level:
# Ineligible to be in this layer
break
else:
# All parents are in earlier layers
layer.add(child)
self[child]['level'] = level + 1
# Add new layer
self.layers.append(layer)
level += 1
[docs] def computeEvaluation(self):
"""
Determine the order in which to compute new values for state features
"""
self.getLayers()
self.evaluation = []
# for key in self.world.variables:
# while len(self.evaluation) <= self[key]['level']:
# self.evaluation.append(set())
# self.evaluation[self[key]['level']].add(makePresent(key))
for agent,variables in self.world.locals.items():
for feature in variables.keys():
key = stateKey(agent,feature,True)
while len(self.evaluation) <= self[key]['level']:
self.evaluation.append(set())
self.evaluation[self[key]['level']].add(makePresent(key))
for relation,variables in self.world.relations.items():
for key,table in variables.items():
while len(self.evaluation) <= self[key]['level']:
self.evaluation.append(set())
self.evaluation[self[key]['level']].add(makePresent(key))