Changing the recovery rate of SIR model - python

import networkx as nx
from collections import defaultdict
from collections import Counter
def test_transmission(u, v, p):
return random.random()<p
def discrete_SIR(G,w,initial_infecteds,beta,Vl,duration):
if G.has_node(initial_infecteds):
initial_infecteds=[initial_infecteds]
N=G.order()
#t = [tmin]
S = [N-len(initial_infecteds)]
#I = [len(initial_infecteds)]
R = [0]
V = [0]
susceptible = defaultdict(lambda: True)
#above line is equivalent to u.susceptible=True for all nodes.
for u in initial_infecteds:
susceptible[u] = False
infecteds = [{}]*duration #bunch of empty sets
infecteds[0] = set(initial_infecteds)
I = [sum(map(len, infecteds))] #set I[0] to be the total number of infections
while I[-1]>0 :
new_infecteds = set()
vaccinated= set()
for u in infecteds:
for v in G.neighbors(u):
if len(vaccinated)+V[-1]< (Vl*N) : #check if vaccination over or not
if susceptible[v] and test_transmission(u, v, w):
vaccinated.add(v)
susceptible[v] = False
# print('transmitting vaccination')
elif susceptible[v] and test_transmission(u,v,beta):
new_infecteds.add(v)
susceptible[v]=False
# print('transmitting infection')
else:
# print("BYE")
if susceptible[v] and test_transmission(u, v,beta):
new_infecteds.add(v)
susceptible[v] = False
#infector[v] = [u]
recovering_nodes = infecteds.pop()
infecteds.insert(0,new_infecteds)
infecteds = new_infecteds
I.append(sum(map(len, infecteds)))
R.append(R[-1]+I[-1])
V.append(len(vaccinated)+V[-1])
S.append(N-V[-1]-I[-1]-R[-1])
return scipy.array(S),scipy.array(V), scipy.array(I),scipy.array(R)
m=100
w=0.2
#ran=nx.gnp_random_graph(100,0.003)
G=nx.grid_2d_graph(m,m,periodic=True)
initial_infections = [(u,v) for (u,v) in G if u==int(m/2) and v==int(m/2)]
S, V, I, R = discrete_SIR(G,w,initial_infecteds=initial_infections,beta=0.5,Vl=1,duration=8)
This is a code of SIR model but this is for recovery rate 1. I want to change this code to include a variable parameter recovery rate and not the default which is 1 in this case. I have tried to change the code to include that. The basic code is of a SIR model.
I added the changes as made from Joels post in my modified SIR model.
For book keeping-
next_time = t[-1]+1
if next_time <= tmax:
for i in infecteds:
for u in i:
node_history[u][0].append(next_time+duration)
node_history[u][1].append('R')
for j in new_infecteds:
for v in j:
node_history[v][0].append(next_time)
node_history[v][1].append('I')

Let infecteds be a list of sets, such that infecteds[T] are those just infected, infecteds[T-1] are those that have been infected for 1 time step, etc. Then pop off infecteds[0] [e.g., recovering_nodes = infecteds.pop(0)] and append the newly infected nodes to the list.
Then for each time step, just iterate through all the sets in infecteds.
Here's some relevant pseudocode:
duration = 8
infecteds = [{}]*duration #bunch of empty sets
infecteds[0] = {1,2,3}
I = [sum(map(len, infecteds))] #set I[0] to be the total number of infections
while I[-1] >0:
new_infecteds = {}
for infected_set in infecteds:
for infected_node in infected_set:
Do some stuff with the node and its neighbors.
new_infecteds gets some things added to it.
recovering_nodes = infecteds.pop()
infecteds.insert(0,new_infecteds)
for node in recovering_nodes:
update status and do any bookkeeping.
I.append(sum(map(len, infecteds)))
Be careful about your use of the word "rate". A higher rate should mean faster recovery, and thus shorter duration (duration is like 1/rate). Your comment seems to use the word "rate" to mean "duration", so that for you a higher "rate" is actually a longer "duration". This is the inverse of what most people would understand you to mean.

Related

CSP Recursive Calls Fail with with Range(a,b) but not Explicit Range

Sudoku is a well known CSP and, in this case, LC problem. I don't need the solution, which follows.
My question is why does self.DOMAIN = "123456789" (line #4) work, whereas self.DOMAIN = map(str, range(1, 10)) (line #5) does not? Are they not equivalent?
class Solution:
def __init__(self):
self.SIZE = 9
# self.DOMAIN = map(str, range(1, self.SIZE + 1)) # THIS DES NOT WORK NOT WORK
self.DOMAIN = "123456789" # THIS WORKS
self.EMPTY = "."
from collections import defaultdict
self.rows = defaultdict(set) # {row:set}
self.cols = defaultdict(set) # {col:set}
self.boxs = defaultdict(set) # {box:set}
self.row_idx = lambda cell: cell // self.SIZE # determines row from cell num
self.col_idx = lambda cell: cell % self.SIZE # determins col from cell num
self.box_idx = lambda r, c: (r // 3) * 3 + c // 3 # determines box from row, col
def solveSudoku(self, board: List[List[str]]) -> None:
"""
Do not return anything, modify board in-place instead.
"""
assert(len(board) == self.SIZE and len(board[0]) == self.SIZE), \
"Sudoku board must be 9x9 in dimensions"
# Initialize board state
self.board = board
for r in range(self.SIZE):
for c in range(self.SIZE):
val = self.board[r][c]
if val != self.EMPTY:
self.rows[r].add(val)
self.cols[c].add(val)
self.boxs[self.box_idx(r, c)].add(val)
# Begin backtracking search from the first cell
self.backtrack(0)
def backtrack(self, cell):
if cell == 81:
# all values have been set and we are outside the board range
return True
r, c = self.row_idx(cell), self.col_idx(cell)
if self.board[r][c] != self.EMPTY:
# nothing to do, continue search
return self.backtrack(cell + 1)
# explore values in the domain for this cell
for candidate_val in self.DOMAIN:
if self.is_valid(r, c, candidate_val):
# place candidate
self.board[r][c] = candidate_val
self.rows[r].add(candidate_val)
self.cols[c].add(candidate_val)
self.boxs[self.box_idx(r, c)].add(candidate_val)
# continue search
if self.backtrack(cell + 1):
return True
# remove candidate and backtrack
self.board[r][c] = self.EMPTY
self.rows[r].remove(candidate_val)
self.cols[c].remove(candidate_val)
self.boxs[self.box_idx(r, c)].remove(candidate_val)
# no solution found for all values in the domain for this cell
return False
def is_valid(self, r, c, val):
""" Returns whether a value can be placed in board[r][c] in the current game state """
if val in self.rows[r]:
return False
if val in self.cols[c]:
return False
if val in self.boxs[self.box_idx(r, c)]:
return False
return True
You are suffering confusion between "generator" and "container".
Consult type( ... ) to tell the difference between them.
Python generators are "lazy", for excellent technical reasons.
Sometimes we can get away with just evaluating
the first K items of an infinite generator.
In your method you want everything greedily pulled out,
and then stored in a container.
The conventional way to phrase this is to wrap map with list:
self.DOMAIN = list(map(str, range(1, 10)))
or perhaps in your case you would prefer that .join pull them out:
self.DOMAIN = ''.join(map(str, range(1, 10)))

Solving Dijkstra's algorithm - passing costs / parents with two edges

I have a graph like this:
# graph table
graph = {}
graph['start'] = {}
graph['start']['a'] = 5
graph['start']['b'] = 2
graph['a'] = {}
graph['a']['c'] = 4
graph['a']['d'] = 2
graph['b'] = {}
graph['b']['a'] = 8
graph['b']['d'] = 7
graph['c'] = {}
graph['c']['d'] = 6
graph['c']['finish'] = 3
graph['d'] = {}
graph['d']['finish'] = 1
graph['finish'] = {}
And I am trying to find the fastest way from S to F.
In the first example in the book only one edge was connected to one node, in this example for example, node D has 3 weights and a cost table was used:
costs = {}
infinity = float('inf')
costs['a'] = 5
costs['b'] = 2
costs['c'] = 4
costs['d'] = # there is 3 costs to node D, which one to select?
costs['finish'] = infinity
And a parents table:
parents = {}
parents['a'] = 'start' # why not start and b since both `S` and `B` can be `A` nodes parent?
parents['b'] = 'start'
parents['c'] = 'a'
parents['d'] = # node D can have 3 parents
parents['finish'] = None
But this also works, by works I mean no error is thrown, so do I only have to name the parents from the first node S?
parents = {}
parents['a'] = 'start'
parents['b'] = 'start'
parents['finish'] = None
The code:
processed = []
def find_lowest_cost_node(costs):
lowest_cost = float('inf')
lowest_cost_node = None
for node in costs:
cost = costs[node]
if cost < lowest_cost and node not in processed:
lowest_cost = cost
lowest_cost_node = node
return lowest_cost_node
node = find_lowest_cost_node(costs)
while node is not None:
cost = costs[node]
neighbors = graph[node]
for n in neighbors.keys():
new_cost = cost + neighbors[n]
if costs[n] > new_cost:
costs[n] = new_cost
parents[n] = node
processed.append(node)
node = find_lowest_cost_node(costs)
def find_path(parents, finish):
path = []
node = finish
while node:
path.insert(0, node)
if parents.__contains__(node):
node = parents[node]
else:
node = None
return path
path = find_path(parents, 'finish')
distance = costs['finish']
print(f'Path is: {path}')
print(f'Distance from start to finish is: {distance}')
I get:
Path is: ['finish']
Distance from start to finish is: inf
Where is my mistake and how should I add cost and parent to a node which can be visited from more than 1 node?
Edit
I do believe this is not the best approach for this problem, the best in practice solution / recommendations are welcome.
You do not need to initialise the cost table with more than costs['start'] = 0 or the parents dictionary with more than parents = {}. That is what your algorithm is going to create for you!
The only other change you need to make is to your while loop. It just needs to check whether the new node has already been detected before. If so then we check to see whether the new path is shorter and update as required; if not then we establish the new path.
while node is not None:
cost = costs[node]
neighbors = graph[node]
for n in neighbors.keys():
new_cost = cost + neighbors[n]
if n in costs:
if costs[n] > new_cost:
costs[n] = new_cost
parents[n] = node
else:
costs[n] = new_cost
parents[n] = node
processed.append(node)
node = find_lowest_cost_node(costs)
I think there are much neater ways to deal with graphs but this is the minimal change needed to make your code work as required. Hope it's helpful!

Python genetic algorithm is slow

I made a genetic algorithm that writes the target phrase, but i'm feeling that it takes too long on each iteration, so I'd like to know if you have any idea on how to speed it up.
Thanks
from random import randint, random
from time import time
def rescale(X,A,B,C,D,force_float=False):
retval = ((float(X - A) / (B - A)) * (D - C)) + C
if not force_float and all(map(lambda x: type(x) == int, [X,A,B,C,D])):
return int(round(retval))
return retval
Here i create a list of random characters that form the phrase
class DNA:
def __init__(self,num):
self.genes=[]
for i in range(num):
self.genes.append((chr(randint(32,126))))
Transform the genes in a string
def getPhrase(self):
return(''.join(self.genes))
Calculating the fitness(similarity to the target)
def fitness(self,target):
score=0
for i in range(len(self.genes)):
if (self.genes[i] == target[i]):
score+=1
return score*score/(len(target)*len(target))
Mixing two genes (getting some character from one and some from another)
def crossover(self,partner):
child = DNA(len(self.genes))
midpoint = randint(0,len(self.genes))
for i in range(len(self.genes)):
if (i > midpoint):
child.genes[i] = self.genes[i]
else:
child.genes[i] = partner.genes[i]
return child
Mutating a gene(adding some random characters)
def mutate(self,mutationRate):
for i in range(len(self.genes)):
if (random() < mutationRate):
self.genes[i] = chr(randint(32,126))
A list of DNA objects
class Population:
def __init__(self,target,mutationRate,num):
self.population=[]
for i in range(num):
self.population.append(DNA(len(target)))
self.mutationRate=mutationRate
self.calcFitness(target)
self.generations=0
Making a list with the fitness of each gene
def calcFitness(self,target):
self.fitness=[]
for i in range(len(self.population)):
self.fitness.append(self.population[i].fitness(target))
Making a list to the crossover function, with entries proportional to the fitness
def naturalSelection(self):
global index, x
self.matingPool=[]
self.maxFitness = 0
for i in range(len(self.population)):
if (self.fitness[i] > self.maxFitness):
self.maxFitness = self.fitness[i]
index=i
print(DNA.getPhrase(population.population[index]),' generation: ',self.generations)
if (DNA.getPhrase(population.population[index]))==target:
x=False
for i in range(len(self.population)):
fitness = rescale(self.fitness[i],0,float(self.maxFitness),0,1)
n = (fitness * 100)
for j in range(int(n)):
self.matingPool.append(population.population[i])
Updating the population
def generate(self):
for i in range(len(self.population)):
a = randint(0,len(self.matingPool)-1)
b = randint(0,len(self.matingPool)-1)
partnerA = self.matingPool[a]
partnerB = self.matingPool[b]
child = partnerA.crossover(partnerB)
child.mutate(self.mutationRate)
population.population[i] = child
self.generations+=1
start=time()
target= input("Target: ")
population = Population(target, 0.05,300)
x=True
while x==True:
population.naturalSelection()
population.generate()
population.calcFitness(target)
end=time()
print(end-start)

Tripartite Matching: Graph Algorithm, Run Time Error

I am practicing problems on Graphs on Hacker Rank. Below is the link for the problem
https://www.hackerrank.com/challenges/tripartite-matching
I wrote a piece of code and the code seems to run on the test case that is being given. I created some test cases of my own and manually calculated the output and then ran my program and it works. However, for most of the other test cases on Hacker Rank. I get a Run Time Error. I am not sure which Run Time Error it is.
I think my logic is breaking for large input sizes. But I am not sure how.
Below is my code.
'''
Input Sequence
3
2
1 2
2 3
3
1 2
1 3
2 3
2
1 3
2 3
'''
#Stores the data of the graph
class Graph:
def __init__(self,parent_node,child_node):
self.node_map = {}
self.node_map[parent_node] = [child_node]
self.node_map[child_node] = [parent_node]
def getNodeMap(self):
return self.node_map
def deleteKey(self, parent_node):
del self.node_map[parent_node]
def printGraph(self):
print self.node_map
def getChildren(self, parent_node):
return self.node_map[parent_node]
def getAllParents(self):
return self.node_map.keys()
def add(self,parent_node,child_node):
#Adding the Node and Edge Details
# Adding Parent and Child
if (parent_node in self.node_map):
child_list = []
child_list = self.node_map[parent_node]
child_list.append(child_node)
self.node_map[parent_node] = child_list
else:
self.node_map[parent_node] = [child_node]
if (child_node in self.node_map):
parent_list = self.node_map[child_node]
parent_list.append(parent_node)
self.node_map[child_node] = parent_list
else:
self.node_map[child_node] = [parent_node]
def runDFS(parent,seq,graph_num):
#print seq
if(graph_num == len(all_graphs)):
all_sequences.append(seq)
return
else:
if(parent not in all_graphs[graph_num].getAllParents()):
return
children = all_graphs[graph_num].getChildren(parent)
for child in children:
new_set = copy.deepcopy(s)
new_seq = copy.deepcopy(seq)
new_seq.append(child)
runDFS(child,new_seq,graph_num+1)
import sys
import fileinput
import copy
import sys
all_graphs = []
all_sequences = []
list_sequences = []
graphs = raw_input()
#print 'Graphs are', graphs
g = [Graph for i in range(int(graphs))]
for i in range(0,int(graphs)):
g[i] = Graph(-999*i,-999*i)
#print 'Read no. of Edges'
edges = raw_input()
#print edges
for j in range(0,int(edges)):
#print 'Read Edge Info'
edge_info = raw_input()
parent_node = edge_info[0]
child_node = edge_info[2]
g[i].add(parent_node, child_node)
g[i].deleteKey(-999*i)
all_graphs.append(g[i])
#for i in range(0,len(all_graphs)):
# print all_graphs[i].getNodeMap()
graph_num = 0
all_sequences = []
sys.setrecursionlimit(20000)
for item in all_graphs[graph_num].getAllParents():
s = set()
s.add(item)
seq = list()
seq.append(item)
runDFS(item,seq,graph_num)
#print all_sequences
counter = 0
for item in all_sequences:
#print item
end_node = item[len(item)-1]
start_node = item[0]
if(end_node == start_node):
counter = counter + 1
print counter
Would really appreciate some help. Is there a bug in my logic or is my assumption right?
If the code is not working for larger input sizes than what is the best way to optimize it?
Instead of running DFS, I used the following approach. But the same thing happens here as well. I think there is some problem with my IO. I am not able to figure out what... :(
for itemA in all_graphs[0].getAllParents():
for itemB in all_graphs[0].getChildren(itemA):
for itemC in all_graphs[1].getChildren(itemB):
if(itemA in all_graphs[2].getChildren(itemC)):
counter = counter + 1
print counter

Python - Dijsktra's Algorithm Distance Problem

I've run into a problem with my code, i'm not able to calculate the distance to a node from the starting node. I have a text file of the form:
1,2,3,4,5,6,7,8,9
1,2,3,4,5,6,7,8,9
This represents the node distances in the graph. Here is my code, unfortunately, despite trying a few different methods I still keep coming up with various error messages.
infinity = 1000000
invalid_node = -1
startNode = 0
class Node:
distFromSource = infinity
previous = invalid_node
visited = False
def populateNodeTable():
nodeTable = []
index =0
f = open('route.txt', 'r')
for line in f:
node = map(int, line.split(','))
nodeTable.append(Node())
print nodeTable[index].previous
print nodeTable[index].distFromSource
index +=1
nodeTable[startNode].distFromSource = 0
return nodeTable
def tentativeDistance(currentNode, nodeTable):
nearestNeighbour = []
for currentNode in nodeTable:
# if Node[currentNode].distFromSource + currentDistance = startNode + currentNode
# currentDistance = currentNode.distFromSource + nodeTable.currentNode
currentNode.previous = currentNode
currentNode.length = currentDistance
currentNode.visited = True
currentNode +=1
nearestNeighbour.append(currentNode)
print nearestNeighbour
return nearestNeighbour
def shortestPath (nearestNeighbour)
shortestPath = []
f = open ('spf.txt', 'r')
f.close()
currentNode = startNode
if __name__ == "__main__":
populateNodeTable()
tentativeDistance(currentNode,populateNodeTable())
The lines starting with '#' in my tentativeDistance function is the section giving me trouble. I've looked at some other implementations on the web though they confuse me
I have been programming the Dijkstra's Algorithm in Python a few months ago; its tested and it should work:
def dijkstra(u,graph):
n = graph.numNodes
l = { u : 0 } ; W = graph.V()
F = [] ; k = {}
for i in range(0,n):
lv,v = min([ (l[lk],lk) for lk in l.keys() if lk in W ])
W.remove(v)
if v!=u: F.append(k[v])
for v1 in [ v2 for v2 in graph.G(v) if v2 in W ]:
if v1 not in l or l[v]+graph.w(v,v1) < l[v1]:
l[v1] = l[v] + graph.w(v,v1)
k[v1] = (v,v1)
return l,F
You need a class Graph with Method V() (which yields the graphs nodes), w(v1,v2) (which yields the weight of the edge (v1,v2)), remove (which removes an edge from a graph) and attribute numNodes (which yields the number of nodes in the graph) and G(v) which yields the neighborhood of node v.

Categories