def perception(self, tx, perc): """ Patient perception function. Filters based on agent having sufficient energy for edge and end node. :param tx: neo4j write transaction :param perc: perception recieved from the node :return: None """ super(Patient, self).perception(tx, perc) if type(self.view) == list: edges = self.view else: edges = [self.view] # filter out options requiring too much energy valid_edges = [] self.mobility = intf.getnodevalue(tx, self.id, "mob", "Patient") self.energy = intf.getnodevalue(tx, self.id, "energy", "Patient") self.current_energy = self.energy if len(self.view) > 1: for edge in edges: if not edge.end_node["name"] in ["Care", "GP", "Hos"]: cost = 0 if edge.end_node["energy"]: cost = cost + edge.end_node["energy"] if self.energy > -cost: valid_edges = valid_edges + [edge] else: valid_edges = self.view self.view = valid_edges
def main(rl): """ Runs the monitor snapshot until clock reaches or exceeds run length. Then closes monitor. :param rl: run length :return: None """ monitor = specification.Monitor() clock = 0 while clock < rl: driver = GraphDatabase.driver(specification.database_uri, auth=specification.Monitor_auth, max_connection_lifetime=20000) with driver.session() as session: # modifying and redrawing plot over time and saving plot rather than an animation session.write_transaction(monitor.snapshot, clock) tx = session.begin_transaction() current_time = intf.gettime(tx) while clock == current_time: current_time = intf.gettime(tx) clock = current_time driver.close() print("Monitor Capture complete") driver = GraphDatabase.driver(specification.database_uri, auth=specification.Monitor_auth, max_connection_lifetime=2000) with driver.session() as session: session.write_transaction(monitor.close) driver.close() print("Monitor closed")
def generator(self, tx, params): """ Generates Fall Agents and inserts them into the network at the home node. :param self: Agent :param tx: neo4j database transaction with write permission :param params: [mobility, confidence, energy] these are the means for the normal distributions sampled to set parameters :return: None """ super(FallAgent, self).generator(tx, params) # generate a random set of parameters based on a distribution with mean set by params [mobility, confidence, energy] = params self.mobility = npr.normal( mobility, 0.05) # draw from normal distribution centred on given value self.energy = npr.normal(energy, 0.05) self.confidence = npr.normal(confidence, 0.05) self.wellbeing = "'At risk'" self.referral = "false" # Add agent with params to ind in graph with resources starting at 0 time = tx.run("MATCH (a:Clock) RETURN a.time").values()[0][0] self.log = "(CREATED," + str(time) + ")" intf.addagent(tx, {"name": "Home"}, "Agent", { "mob": self.mobility, "conf": self.confidence, "mob_res": 0, "conf_res": 0, "energy": self.energy, "wellbeing": self.wellbeing, "log": "'" + self.log + "'", "referral": self.referral }, "name")
def main(rl, ps): """ Checks population levels meet requirements and adds additional agents if needed until clock reaches or exceeds run length :param rl: run length :param ps: population size :return: None """ clock = 0 agent = specification.Agents(None) while clock < rl: dri = GraphDatabase.driver(specification.database_uri, auth=specification.Population_auth, max_connection_lifetime=2000) with dri.session() as ses: populationdeficite = specification.Population.check(ses, ps) if populationdeficite: for i in range(populationdeficite): ses.write_transaction(agent.generator, specification.Population.params) tx = ses.begin_transaction() time = intf.gettime(tx) while clock == time: time = intf.gettime(tx) clock = time dri.close() print("Population closed")
def logging(self, tx, entry): """ Utility function for adding information to the agents log of its activities :param tx: neo4j database write transaction :param entry: String to be added to the log :return: None """ self.log = intf.getnodevalue(tx, self.id, "log", "Agent") self.log = self.log + ", (" + entry + ")" intf.updateagent(tx, self.id, "log", str(self.log))
def look(self, tx): """ If not at home, find co-located agents :param tx: neo4j database write transaction :return: None """ super(Patient, self).look(tx) self.id = self.id[0] # If not at home, find co-located agents if intf.locateagent(tx, self.id)["name"] != "Home": self.colocated = intf.colocated(tx, self.id) self.social = intf.getnodevalue(tx, self.id, "social", "Agent")
def close(self, txc): """ Saves figure, figure data, logs of agents in system at end of run to the output directory given in Specification with name tag given in database. :param txc: neo4j database write transaction :return: None """ super(Monitor, self).close(txc) runname = intf.getrunname(txc) print(self.clock) logs = txc.run("MATCH (a:Agent) RETURN a.log").values() pickle_lout = open( specification.savedirectory + "logs_" + runname + ".p", "wb") pickle.dump(logs, pickle_lout) pickle_lout.close() pickle_out = open( specification.savedirectory + "records_" + runname + ".p", "wb") pickle.dump(self.records, pickle_out) pickle_out.close() pickle_gout = open( specification.savedirectory + "graphdata_" + runname + ".p", "wb") pickle.dump([ self.y11, self.y12, self.y13, self.y2, self.y3, self.y41, self.y42, self.y43, self.t ], pickle_gout) pickle_gout.close() # dump graph data as strings back into the database # save out graph plt.savefig("../FallData/figure_" + runname + "") if self.show: plt.show()
def choose(self, tx, perc): """ Patients conscious choice from possible edges. This is based on the patients mood exceeds the current threshold for that edge. If the agent has the mood for multiple choices the final choice is made as a weighted sampling of all possible choices with weights based on the Patients inclination applied to the types of edges. :param tx: neo4j database write transaction :param perc: perception from node perception function. :return: single edge as final choice """ super(Patient, self).choose(tx, perc) # filter out options where the agent does not reach the mood threshold options = [] self.mood = intf.getnodevalue(tx, self.id, "mood", "Patient") self.inclination = intf.getnodevalue(tx, self.id, "inclination", "Patient") self.log = intf.getnodevalue(tx, self.id, "log", "Patient") self.wellbeing = intf.getnodevalue(tx, self.id, "wellbeing", "Patient") self.referral = intf.getnodevalue(tx, self.id, "referral", "Patient") if len(self.view) < 2: if type(self.view) == list and self.view: choice = self.view[0] else: choice = self.view else: for edge in self.view: if edge["mood"] <= self.mood: options = options + [edge] # choose based on random sample bias by patients inclination if not options: return None types = [edge["type"] for edge in options] edge_types = ["social", "fall", "medical", "inactive"] types = [edge_types.index(lable) for lable in types] weights = [ self.positive(self.inclination[label]) for label in types ] if sum(weights): weights = [w / sum(weights) for w in weights] sample = npr.choice(range(len(options)), 1, p=weights) else: sample = npr.choice(range(len(options)), 1) choice = options[sample[0]] return choice
def talk(self, tx): """ Determine if agent forms a new social link with random co-located agent based on their social values and shortest social path :param tx: neo4j write transaction :return: None """ super(Patient, self).talk(tx) if self.colocated: newcontacts = [ nc for nc in self.colocated if nc not in self.contacts ] if newcontacts: if len(newcontacts) > 1: newfriend = npr.sample(newcontacts) else: newfriend = newcontacts # Based on relative social values and length of shortest path set probability for forming link with a # randomly sampled co-located unknown agent. social from 2-8 per agent, combined from 4-16. So combined -4 # over 24 gives value between 0 and 0.5 plus the if minimum path greater than 6 nothing, else from m=2-6 # then 1/(2m-2) gives 0.1-0.5 (m=1 or 0 means itself or already connected). prob1 = (newfriend.end_node["social"] + self.social - 4) / 24 sp = intf.shortestpath(tx, self.id, newfriend.end_node["id"], 'Agent', 'Social') if sp < 2: prob2 = 0 elif sp > 6: prob2 = 0 else: prob2 = 1 / (2 * sp - 2) if npr.random(1) <= (prob1 + prob2): # form friend link intf.createedge( self.id, newfriend.end_node["id"], 'Agent', 'Agent', 'SOCIAL:FRIEND', 'created: ' + intf.gettime() + ', usage: ' + intf.gettime() + ', carer: False') self.contacts = newfriend else: self.contacts = None else: self.contacts = None else: self.contacts = None
def update(self, tx): """ Check agent contacts compare list with co-located agents and update co-located contacts with new last usage :param tx: neo4j database write transaction :return: None """ super(Patient, self).update(tx) # Update existing com links with latest co-location self.contacts = intf.agentcontacts(tx, self.id, "Patient") if self.contacts and self.colocated: update = [ contact for contact in self.contacts if contact in self.colocated ] for contact in update: intf.updatecontactedge(tx, contact.end_node["id"], self.id, "last_usage", intf.gettime())
def timesincedischarge(txl): """ Utility function reports the time between any hospital discharge and attending an intervention. Only recorded if both events occur. :param txl: neo4j database write transaction :return: list of times in integer timesteps """ times = [] agents = intf.getnodeagents(txl, "Intervention", "name") for agent in agents: log = parselog(agent["log"]) log.reverse() lasthosdis = [entry for entry in log if entry[0] == "Hos discharge"] if lasthosdis: lasthosdis = lasthosdis[0] times = times + [intf.gettime(txl) - lasthosdis[1]] return times
def listen(self, tx): """ If the agent has a new social link check if the new friend has a link to a carer if they do for a random carer they form a link with a .5 chance. :param tx: neo4j write transaction :return: None """ super(Patient, self).listen(tx) if self.contacts: carers = intf.agentcontacts(tx, self.contacts.end_node["id"], "Agent", "Carer") carers = [carer for carer in carers if carer["carer"]] carer = npr.sample(carers) if npr.random(1) < 0.5: intf.createedge( self.id, carer.end_node["id"], 'Patient', 'Carer', 'SOCIAL:FRIEND', 'created: ' + intf.gettime() + ', usage: ' + intf.gettime() + ', carer: True')
def agentprediction(self, tx, agent): """ Predict how long the agent will stay at the node and its destination once it leaves. This must be implemented by the subclass to predict agent behaviour if node has a queue. :param tx: neo4j write transaction :param agent: agent id :return: view of local environment for agent """ view = intf.perception(tx, agent["id"]) return view
def generator(self, tx, params): """ Generates Fall Agents and inserts them into the network at the home node. :param self: Agent :param tx: neo4j database transaction with write permission :param params: [mobility, mood, energy, inclination] these are the means for the normal distributions sampled to set parameters :return: None """ super(Patient, self).generator(tx, params) # generate a random set of parameters based on a distribution with mean set by params [mobility, mood, energy, inclination, min_social, max_social] = params self.mobility = npr.normal( mobility, 0.05) # draw from normal distribution centred on given value self.energy = npr.normal(energy, 0.05) self.mood = npr.normal(mood, 0.05) self.inclination = [npr.normal(x) for x in inclination] self.inclination = [ inc / sum(self.inclination) for inc in self.inclination ] self.wellbeing = "'At risk'" self.referral = "false" self.social = npr.choice(range(min_social, max_social + 1), 1)[0] print(self.social) # Add agent with params to ind in graph with resources starting at 0 time = tx.run("MATCH (a:Clock) RETURN a.time").values()[0][0] self.log = "(CREATED," + str(time) + ")" intf.addagent(tx, {"name": "Home"}, "Agent:Patient", { "mob": self.mobility, "mood": self.mood, "energy": self.energy, "inclination": self.inclination, "wellbeing": self.wellbeing, "log": "'" + self.log + "'", "referral": self.referral, "social": self.social }, "name")
def main(rl): """ Implements a FlowReaction repeatedly until the clock in the database reaches the run length. :param rl: run length :return: None """ flowreaction = specification.Balancer.FlowReaction() clock = 0 while clock < rl: dri = GraphDatabase.driver(specification.database_uri, auth=specification.Balancer_auth, max_connection_lifetime=2000) with dri.session() as ses: ses.write_transaction(flowreaction.applyrules) tx = ses.begin_transaction() time = intf.gettime(tx) while clock == time: time = intf.gettime(tx) clock = time dri.close() print("Balancer closed")
def agentsready(self, tx): """ Identifies the set of current agents for processing, either the correct set in the queue or all the agents at the node. It checks for unqueued agents in nodes with queue and runs the nodes prediction function to add them to the queue. It then gathers the agents local environment perception and passes that to the agent when calling the move function. We then delete the part of the queue that has been processed to save space. Subclass must implement this function for any aspects unique to model. :param tx: neo4j write transaction :return: None """ agents = intf.getnodeagents(tx, self.name, "name") clock = intf.gettime(tx) if self.queue or self.queue == {}: queueagents = [ key for time in self.queue.keys() for key in self.queue[time].keys() ] newagents = [ag for ag in agents if ag["id"] not in queueagents] # run prediction on each unqueued agent for ag in newagents: self.agentprediction(tx, ag) for ag in agents: if self.queue: if clock in self.queue.keys(): if ag["id"] in self.queue[clock].keys(): agper = self.agentperception( tx, ag, self.queue[clock][ag["id"]][0], self.queue[clock][ag["id"]]) specification.Agent(ag["id"]).move(tx, agper) else: agper = self.agentperception(tx, ag) specification.Agent(ag["id"]).move(tx, agper) if self.queue and clock in self.queue.keys(): del self.queue[clock]
def agentperception(self, tx, agent, dest=None, waittime=None): """ The local environment of the node filtered by availability to a particular agent. Subclass must implement this function to add node filtering for particular model :param tx: neo4j read or write transaction :param agent: agent id :param dest: passed by nodes with queues if agent destination has already been determined by prediction. :param waittime: time the agent has been waiting at the node :return: view of local environment for agent as list of relationships """ if dest: view = dest else: view = intf.perception(tx, agent["id"])[1:] if type(view) == list: for edge in view: if "cap" in edge.end_node.keys(): if edge.end_node["cap"] <= edge.end_node["load"]: view.remove(edge) return view
def choose(self, tx, perc): """ Agents conscious choice from possible edges. This is based on the effort of the agent calculated from the combination of agent and edge values. If the agent has the effort for multiple choices the worth of the edge is used as a deciding factor. :param tx: neo4j database write transaction :param perc: perception from node perception function. :return: single edge as final choice """ super(FallAgent, self).choose(tx, perc) # filter out options where the agent does not reach the effort threshold options = [] self.confidence = intf.getnodevalue(tx, self.id, "conf", "Agent") self.mobility_resources = intf.getnodevalue(tx, self.id, "mob_res", "Agent") self.confidence_resources = intf.getnodevalue(tx, self.id, "conf_res", "Agent") self.log = intf.getnodevalue(tx, self.id, "log", "Agent") self.wellbeing = intf.getnodevalue(tx, self.id, "wellbeing", "Agent") self.referral = intf.getnodevalue(tx, self.id, "referral", "Agent") if len(self.view) < 2: if type(self.view) == list and self.view: choice = self.view[0] else: choice = self.view else: for edge in self.view: if edge["effort"] <= edge["mobility"] * (self.mobility + self.confidence * self.mobility_resources) + \ edge["confidence"] * (self.confidence + self.mobility * self.confidence_resources): options = options + [edge] # choose based on current highest worth edge ignores edges with no worth score, these are not choosable # edges, they are primarily edges indicating a fall if not options: return None choice = options[0] for edge in options: if "worth" in edge and "worth" in choice: if edge["worth"] > choice["worth"]: choice = edge elif "worth" in edge: choice = edge return choice
def learn(self, tx, choice): """ Agent is changed by node and can change node and edge it arrived by. This can include changes to decision making parameters. :param tx: neo4j database write transaction :param choice: Chosen edge for move :return: None """ super(Patient, self).learn(tx, choice) # modify mob, conf, res and energy based on new node if self.fall and self.fall != "Mild": if self.wellbeing != "Fallen": self.wellbeing = "Fallen" intf.updateagent(tx, self.id, "wellbeing", self.wellbeing) clock = tx.run("MATCH (a:Clock) " "RETURN a.time").values()[0][0] self.log = self.log + ", (Fallen, " + str(clock) + ")" if "modm" in choice.end_node: self.mobility = self.positive( npr.normal(choice.end_node["modm"], 0.05) + self.mobility) intf.updateagent(tx, self.id, "mob", self.mobility) # check for updates to wellbeing and log any changes if self.mobility == 0: if self.wellbeing != "Fallen": self.wellbeing = "Fallen" intf.updateagent(tx, self.id, "wellbeing", self.wellbeing) clock = tx.run("MATCH (a:Clock) " "RETURN a.time").values()[0][0] self.log = self.log + ", (Fallen, " + str(clock) + ")" elif self.mobility > 1: if self.wellbeing != "Healthy": self.wellbeing = "Healthy" intf.updateagent(tx, self.id, "wellbeing", self.wellbeing) clock = tx.run("MATCH (a:Clock) " "RETURN a.time").values()[0][0] self.log = self.log + ", (Healthy, " + str(clock) + ")" elif self.mobility <= 1: if self.wellbeing == "Healthy": self.wellbeing = "At risk" intf.updateagent(tx, self.id, "wellbeing", self.wellbeing) clock = tx.run("MATCH (a:Clock) " "RETURN a.time").values()[0][0] self.log = self.log + ", (At risk, " + str(clock) + ")" if "modmood" in choice.end_node: self.mood = self.positive( npr.normal(choice.end_node["modmood"], 0.05) + self.mood) intf.updateagent(tx, self.id, "mood", self.mood) if "energy" in choice.end_node: self.current_energy = npr.normal(choice.end_node["energy"], 0.05) + self.current_energy energy_change = self.current_energy - self.energy intf.updateagent(tx, self.id, "energy", self.current_energy) edge_types = ["social", "fall", "medical", "inactive"] for i in range(len(edge_types)): if choice["type"] == edge_types[i]: if energy_change < 0: self.inclination[i] = self.inclination[i] + 1 elif energy_change > 0: self.inclination[i] = self.positive( self.inclination[i] - 1) # change to inclination based on mobility, energy and mood if self.current_energy > 0.8: self.inclination[0] = self.inclination[0] + 1 elif self.current_energy < 0.2: self.inclination[3] = self.inclination[3] + 1 if self.mood > 0.8: self.inclination[0] = self.inclination[0] - 1 self.inclination[3] = self.inclination[3] + 1 elif self.mood < 0.2: self.inclination[0] = self.inclination[0] + 1 self.inclination[3] = self.inclination[3] - 1 if self.mobility < 0.4: self.inclination[2] = self.inclination[2] + 1 self.inclination[3] = self.inclination[3] + 1 elif self.mobility > 0.8: self.inclination[3] = self.inclination[3] - 1 intf.updateagent(tx, self.id, "inclination", self.inclination) # log going into care if choice.end_node["name"] == "Care": clock = tx.run("MATCH (a:Clock) " "RETURN a.time").values()[0][0] self.log = self.log + ", (Care, " + str(clock) + ")" if "cap" in choice.end_node.keys(): intf.updatenode(tx, choice.end_node["name"], "load", choice.end_node["load"] + 1, "name") if self.view[0]["name"] == "Hos" and choice.end_node["name"] != "Hos": clock = tx.run("MATCH (a:Clock) " "RETURN a.time").values()[0][0] self.log = self.log + ", (Discharged, " + str(clock) + ")" intf.updateagent(tx, self.id, "log", str(self.log))
def payment(self, tx): """ Modifies chosen edge and agent. These include mobility, confidence and energy modifications. :param tx: neo4j database write transaction :return: None """ super(Patient, self).payment(tx) # Deduct energy used on edge if "energy" in self.choice.keys(): if "energy" in self.choice.end_node.keys(): if self.choice["energy"] + self.choice.end_node[ "energy"] > self.current_energy: # Check for carers carers = intf.agentcontacts(tx, self.id, "Agent", "Carer") # Check for sufficient energy for carer in carers: print(carer) if carer.end_node["energy"] >= self.choice["energy"]: intf.updatenode(tx, carer.end_node["id"], "energy", carer.end_node["energy"] - self.choice["energy"], label='Carer') self.current_energy = self.current_energy + self.choice[ "energy"] intf.updateagent(tx, self.id, "energy", self.current_energy) intf.updatecontactedge(tx, self.id, carer.end_node["id"], "usage", intf.gettime(tx), "Agent", "Carer") break else: return False self.current_energy = npr.normal(self.choice["energy"], 0.05) + self.current_energy intf.updateagent(tx, self.id, "energy", self.current_energy) # mod variables based on edges if "modm" in self.choice: self.mobility = self.positive( npr.normal(self.choice["modm"], 0.05) + self.mobility) intf.updateagent(tx, self.id, "mob", self.mobility) if self.mobility == 0: if self.wellbeing != "Fallen": self.wellbeing = "Fallen" intf.updateagent(tx, self.id, "wellbeing", self.wellbeing) clock = tx.run("MATCH (a:Clock) " "RETURN a.time").values()[0][0] self.log = self.log + ", (Fallen, " + str(clock) + ")" elif self.mobility > 1: if self.wellbeing != "Healthy": self.wellbeing = "Healthy" intf.updateagent(tx, self.id, "wellbeing", self.wellbeing) clock = tx.run("MATCH (a:Clock) " "RETURN a.time").values()[0][0] self.log = self.log + ", (Healthy, " + str(clock) + ")" elif self.mobility <= 1: if self.wellbeing == "Healthy": self.wellbeing = "At risk" intf.updateagent(tx, self.id, "wellbeing", self.wellbeing) clock = tx.run("MATCH (a:Clock) " "RETURN a.time").values()[0][0] self.log = self.log + ", (At risk, " + str(clock) + ")" if "modmood" in self.choice: self.mood = self.positive( npr.normal(self.choice["modmood"], 0.05) + self.mood) intf.updateagent(tx, self.id, "mood", self.mood) return True
def react(self, tx): """ If the agent now has more social bonds than they can manage we drop new non carer friends then those not used recently and finally reduce the carers from most recent. :param tx: neo4j write transaction :return: None """ super(Patient, self).react(tx) self.contacts = intf.agentcontacts(tx, self.id, "Agent") carers = self.contacts + intf.agentcontacts(tx, self.id, "Agent", "Carer") if len(self.contacts) > self.social: while len(carers) > self.social: carer_drop = None latest_time = 0 for carer in carers: if carer["created"] > latest_time: carer_drop = carer latest_time = carer["created"] intf.deletecontact(tx, self.id, carer_drop.end_node["id"], "Agent", "Carer") if len(carers) == self.social: for contact in contacts: intf.deletecontact(tx, self.id, contact.end_node["id"], "Agent", "Agent") if len(carers) + len(contacts) > self.social: for contact in contacts: if len(carers) + len(contacts) > self.social: if intf.gettime() - contact['created'] < 5: intf.deletecontact(tx, self.id, contact.end_node["id"], "Agent", "Agent") oldest_usage = 0 contact_drop = None while len(carers) + len(contacts) > self.social: for contact in contacts: if intf.gettime() - contact["usage"] > oldest_usage: contact_drop = contact oldest_usage = intf.gettime() - contact["usage"] intf.deletecontact(tx, self.id, contact_drop.end_node["id"], "Agent", "Agent")
def adjustcapasity(txl, history, dynamic=True): """ Rule function that applies the capacity change algorithm in the case of two intervention node systems. This is uses a history variable that is cleared when the capacity is adjusted so that another five timesteps must pass before the capacity can be changed again. This only modifies OpenIntervention if dynamic is true else only intervention is modified. :param txl: neo4j database write transaction :param history: List of previous average times since discharge :param dynamic: indicates if adjustment to OpenIntervention is needed :return: history """ currenttimes = timesincedischarge(txl) if not currenttimes and history: currentav = history[-1] elif not currenttimes: currentav = 14 else: currentav = mean(currenttimes) history = history + [currentav] # If it has been less than 20 time steps since last change do nothing if len(history) < 20: return history else: if history[-5] - history[-1] < -1 and history[-1] > 5: if dynamic: if intf.getnodevalue( txl, "InterventionOpen", "cap", uid="name") > 0 and dynamic: intf.updatenode( txl, "Intervention", "cap", intf.getnodevalue( txl, "Intervention", "cap", uid="name") + 1, "name") intf.updatenode( txl, "InterventionOpen", "cap", intf.getnodevalue( txl, "InterventionOpen", "cap", uid="name") - 1, "name") else: intf.updatenode( txl, "Intervention", "cap", intf.getnodevalue(txl, "Intervention", "cap", uid="name") + 1, "name") return [] elif history[-5] - history[-1] > 0 and history[-1] < 5: if intf.getnodevalue(txl, "Intervention", "cap", uid="name") > 0: intf.updatenode( txl, "Intervention", "cap", intf.getnodevalue(txl, "Intervention", "cap", uid="name") - 1, "name") if dynamic: intf.updatenode( txl, "InterventionOpen", "cap", intf.getnodevalue( txl, "InterventionOpen", "cap", uid="name") + 1, "name") return [] else: return history
def learn(self, tx, choice): """ Agent is changed by node and can change node and edge it arrived by. This can include changes to decision making parameters. :param tx: neo4j database write transaction :param choice: Chosen edge for move :return: None """ super(FallAgent, self).learn(tx, choice) # modify mob, conf, res and energy based on new node if self.fall and self.fall != "Mild": if self.wellbeing != "Fallen": self.wellbeing = "Fallen" intf.updateagent(tx, self.id, "wellbeing", self.wellbeing) clock = tx.run("MATCH (a:Clock) " "RETURN a.time").values()[0][0] self.log = self.log + ", (Fallen, " + str(clock) + ")" if "modm" in choice.end_node: self.mobility = self.positive( npr.normal(choice.end_node["modm"], 0.05) + self.mobility) intf.updateagent(tx, self.id, "mob", self.mobility) # check for updates to wellbeing and log any changes if self.mobility == 0: if self.wellbeing != "Fallen": self.wellbeing = "Fallen" intf.updateagent(tx, self.id, "wellbeing", self.wellbeing) clock = tx.run("MATCH (a:Clock) " "RETURN a.time").values()[0][0] self.log = self.log + ", (Fallen, " + str(clock) + ")" elif self.mobility > 1: if self.wellbeing != "Healthy": self.wellbeing = "Healthy" intf.updateagent(tx, self.id, "wellbeing", self.wellbeing) clock = tx.run("MATCH (a:Clock) " "RETURN a.time").values()[0][0] self.log = self.log + ", (Healthy, " + str(clock) + ")" elif self.mobility <= 1: if self.wellbeing == "Healthy": self.wellbeing = "At risk" intf.updateagent(tx, self.id, "wellbeing", self.wellbeing) clock = tx.run("MATCH (a:Clock) " "RETURN a.time").values()[0][0] self.log = self.log + ", (At risk, " + str(clock) + ")" if "modc" in choice.end_node: self.confidence = self.positive( npr.normal(choice.end_node["modc"], 0.05) + self.confidence) intf.updateagent(tx, self.id, "conf", self.confidence) if "modrc" in choice.end_node: self.confidence_resources = self.positive( npr.normal(choice.end_node["modrc"], 0.05) + self.confidence_resources) intf.updateagent(tx, self.id, "conf_res", self.confidence_resources) if "modrm" in choice.end_node: self.mobility_resources = self.positive( npr.normal(choice.end_node["modrm"], 0.05) + self.mobility) intf.updateagent(tx, self.id, "mob_res", self.mobility_resources) if "energy" in choice.end_node: self.current_energy = npr.normal(choice.end_node["energy"], 0.05) + self.current_energy intf.updateagent(tx, self.id, "energy", self.current_energy) # log going into care if choice.end_node["name"] == "Care": clock = tx.run("MATCH (a:Clock) " "RETURN a.time").values()[0][0] self.log = self.log + ", (Care, " + str(clock) + ")" if "cap" in choice.end_node.keys(): intf.updatenode(tx, choice.end_node["name"], "load", choice.end_node["load"] + 1, "name") if self.view[0]["name"] == "Hos" and choice.end_node["name"] != "Hos": clock = tx.run("MATCH (a:Clock) " "RETURN a.time").values()[0][0] self.log = self.log + ", (Discharged, " + str(clock) + ")" intf.updateagent(tx, self.id, "log", str(self.log))
def payment(self, tx): """ Modifies chosen edge and agent. These include mobility, confidence and energy modifications. :param tx: neo4j database write transaction :param intf: Interface for databse calls :return: None """ super(FallAgent, self).payment(tx) # Deduct energy used on edge if "energy" in self.choice.keys(): self.current_energy = npr.normal(self.choice["energy"], 0.05) + self.current_energy intf.updateagent(tx, self.id, "energy", self.current_energy) # mod variables based on edges if "modm" in self.choice: self.mobility = self.positive( npr.normal(self.choice["modm"], 0.05) + self.mobility) intf.updateagent(tx, self.id, "mob", self.mobility) if self.mobility == 0: if self.wellbeing != "Fallen": self.wellbeing = "Fallen" intf.updateagent(tx, self.id, "wellbeing", self.wellbeing) clock = tx.run("MATCH (a:Clock) " "RETURN a.time").values()[0][0] self.log = self.log + ", (Fallen, " + str(clock) + ")" elif self.mobility > 1: if self.wellbeing != "Healthy": self.wellbeing = "Healthy" intf.updateagent(tx, self.id, "wellbeing", self.wellbeing) clock = tx.run("MATCH (a:Clock) " "RETURN a.time").values()[0][0] self.log = self.log + ", (Healthy, " + str(clock) + ")" elif self.mobility <= 1: if self.wellbeing == "Healthy": self.wellbeing = "At risk" intf.updateagent(tx, self.id, "wellbeing", self.wellbeing) clock = tx.run("MATCH (a:Clock) " "RETURN a.time").values()[0][0] self.log = self.log + ", (At risk, " + str(clock) + ")" if "modc" in self.choice: self.confidence = self.positive( npr.normal(self.choice["modc"], 0.05) + self.confidence) intf.updateagent(tx, self.id, "conf", self.confidence)
def snapshot(self, txl, ctime): """ Captures the current statistics of the system and updates graph grid, including average system interval, intervention interval, average number of falls at end of system interval and proportions of population in each population category. :param txl: neo4j database write transaction :param ctime: current timestep :return: None """ look = txl.run("MATCH (n:Node) " "WHERE n.name = {node} " "RETURN n", node="Intervention") self.nrecord = look.values() if super(Monitor, self).snapshot(txl, ctime): # Update plot 1 - Int Cap # Update to track average number of each type of fall for people in care. [mild, moderate, severe, agents_n] = txl.run( "MATCH (n:Node) " "WHERE n.name={node} " "RETURN n.mild, n.moderate, n.severe, n.agents", node="Care").values()[0] if agents_n: self.y11 = pylab.append(self.y11, mild / agents_n) self.p11.set_data(self.t, self.y11) self.y12 = pylab.append(self.y12, moderate / agents_n) self.p12.set_data(self.t, self.y12) self.y13 = pylab.append(self.y13, severe / agents_n) self.p13.set_data(self.t, self.y13) if max([ mild / agents_n, moderate / agents_n, severe / agents_n ]) > self.y1: self.y1 = max([ mild / agents_n, moderate / agents_n, severe / agents_n ]) self.p11.axes.set_ylim(0.0, self.y1 + 1.0) self.p12.axes.set_ylim(0.0, self.y1 + 1.0) self.p13.axes.set_ylim(0.0, self.y1 + 1.0) else: self.y11 = pylab.append(self.y11, 0) self.p11.set_data(self.t, self.y11) self.y12 = pylab.append(self.y12, 0) self.p12.set_data(self.t, self.y12) self.y13 = pylab.append(self.y13, 0) self.p13.set_data(self.t, self.y13) # Update plot 2 - Hos to Int gaps = timesincedischarge(txl) if gaps: hiint = mean(timesincedischarge(txl)) self.y3storage = self.y3storage + [hiint] if len(self.y3storage) >= 10: self.y3storage = self.y3storage[-10:] if self.y3storage: self.y2 = pylab.append(self.y2, mean(self.y3storage)) if self.y < mean(self.y3storage): self.y = mean(self.y3storage) self.p2.set_data(self.t, self.y2) # Update plot 3 - Start to Care careint = intf.getnodevalue(txl, "Care", "interval", uid="name") if careint: scint = careint else: scint = 0 self.y3 = pylab.append(self.y3, scint) if self.y < scint: self.y = scint self.p3.set_data(self.t, self.y3) # Update plot 4 # Update plot showing distribution of well being in the general population. wb = txl.run( "MATCH (a:Agent)-[r:LOCATED]->(n:Node) " "WHERE NOT n.name={node} " "RETURN a.wellbeing", node="Care").values() wb = [val[0] for val in wb] healthy = wb.count("Healthy") / len(wb) at_risk = wb.count("At risk") / len(wb) fallen = wb.count("Fallen") / len(wb) self.y41 = pylab.append(self.y41, healthy) self.p41.set_data(self.t, self.y41) self.y42 = pylab.append(self.y42, at_risk) self.p42.set_data(self.t, self.y42) self.y43 = pylab.append(self.y43, fallen) self.p43.set_data(self.t, self.y43) if max([healthy, at_risk, fallen]) / len(wb) > self.y4: self.y4 = max([healthy, at_risk, fallen]) self.p41.axes.set_ylim(0.0, self.y4 + 1.0) self.p42.axes.set_ylim(0.0, self.y4 + 1.0) self.p43.axes.set_ylim(0.0, self.y4 + 1.0) # Update plot axes if self.x >= self.xmax - 1.00: self.p11.axes.set_xlim(0.0, self.x + 1.0) self.p12.axes.set_xlim(0.0, self.x + 1.0) self.p13.axes.set_xlim(0.0, self.x + 1.0) self.p2.axes.set_xlim(0.0, self.x + 1.0) self.p3.axes.set_xlim(0.0, self.x + 1.0) self.p41.axes.set_xlim(0.0, self.x + 1.0) self.p42.axes.set_xlim(0.0, self.x + 1.0) self.p43.axes.set_xlim(0.0, self.x + 1.0) self.xmax = self.x if self.y > self.ymax1 - 1.0: self.p2.axes.set_ylim(0.0, self.y + 1.0) self.p3.axes.set_ylim(0.0, self.y + 1.0) if self.show: plt.pause(0.0005)
def generate_population(tx, ps): """ Generates the required number of agents and starts them at the Home node. Builds social connections between agents. :param tx: neo4j database write transaction :param ps: population size :return: None """ fa = Patient(None) for j in range(ps // 4): tx.run("CREATE (a:Carer {id:{j_id}, energy:20})", j_id=j) for i in range(ps): fa.generator(tx, [0.8, 0.9, 1, [2, 0, 1, 2], 2, 8]) if npr.random(1) < 0.5: if npr.random(1) < 0.5: samplesize = 2 else: samplesize = 1 newfriends = npr.choice(range(ps // 4), size=samplesize, replace=False) for nf in newfriends: intf.createedge( tx, i, nf, 'Agent', 'Carer', 'SOCIAL', 'created: ' + str(intf.gettime(tx)) + ', usage: ' + str(intf.gettime(tx)) + ', carer: True') intf.createedge(tx, i, nf, 'Agent', 'Carer', 'FRIEND') for i in range(ps): newfriends = npr.choice(range(ps), size=npr.choice(range(3)), replace=False) for nf in newfriends: if not nf == i: intf.createedge( tx, i, nf, 'Agent', 'Agent', 'SOCIAL', 'created: ' + str(intf.gettime(tx)) + ', usage: ' + str(intf.gettime(tx)) + ', carer: False') intf.createedge(tx, i, nf, 'Agent', 'Agent', 'FRIEND')