def simulate(params, plt=None, plot_hourly=None, xlabel=None): if plot_hourly is None: plot_hourly = params['geometry']['n'] < 50000 # Hack, remove # Initialize a city's geography and its denizens num, num_initially_infected = int(params['geometry']['n']), int( params['geometry']['i']) num_times_of_day = int(params['motion']['t']) precision = int(params['geometry']['p']) home, work = home_and_work_locations(geometry_params=params['geometry'], num=num) positions = nudge(home, w=0.05 * params['motion']['w']) status = np.random.permutation([INFECTED] * num_initially_infected + [VULNERABLE] * (num - num_initially_infected)) day_fraction = 1.0 / num_times_of_day # Population drifts to work and back, incurring viral load based on proximity to others who are infected day = 0 while any(s in [INFECTED, POSITIVE, SYMPTOMATIC] for s in status): day = day + 1 for step_no, time_of_day in enumerate(times_of_day(num_times_of_day)): stationary = [s in [DECEASED, POSITIVE] for s in status] attractors = destinations(status, time_of_day, home, work) positions = evolve_positions(positions=positions, motion_params=params['motion'], attractors=attractors, day_fraction=day_fraction, stationary=stationary) exposed = newly_exposed(positions=positions, status=status, precision=precision) status = contact_progression(status=status, health_params=params['health'], exposed=exposed) status = individual_progression(status, health_params=params['health'], day_fraction=day_fraction) if plt and (plot_hourly or step_no % 12 == 0): plt.clf() plot_points(plt=plt, positions=positions, status=status, title="Day " + str(day) + ':' + str(time_of_day * num_times_of_day)) b = params['geometry']['b'] plt.axis([-b, b, -b, b]) if xlabel: plt.xlabel(xlabel) plt.show(block=False) plt.pause(0.01) pprint( Counter([list(STATE_DESCRIPTIONS.values())[s] for s in status]))
def homesick(params): """ To illustrate the OU process """ num, num_initially_infected = int(params['geometry']['n']),int(params['geometry']['i']) num_times_of_day = int(params['motion']['t']) day_fraction = 1.0/num_times_of_day home = [(0,0) for _ in range(num) ] w = params['motion']['w'] k = params['motion']['k'] R = w/math.sqrt(2*k) b = params['geometry']['b'] positions = [ (x/2*b,y/2*b) for x,y in [ tuple( np.random.multivariate_normal(mean=[0,0],cov=[[1,0],[0,1]]) ) for _ in range(num)] ] status = [VULNERABLE]*num fig, axs = plt.subplots(nrows=1, ncols=2) population_variances = list() rolling_variances = list() for day in range(10): for step_no, time_of_day in enumerate(times_of_day(num_times_of_day)): stationary = [ s in [DECEASED, POSITIVE] for s in status ] positions = evolve_positions(positions=positions, motion_params=params['motion'], attractors=home, time_step=day_fraction, stationary=stationary) axs[0].clear() plot_points(plt=axs[0], positions=positions, status=status, sizes=[64]*6) b = params['geometry']['b'] axs[0].axis([-b, b, -b, b]) axs[0].set_title('Day '+str(day)) circle_x, circle_y = circle(r=R) axs[0].scatter(x=circle_x,y=circle_y,c='g',s=1) axs[0].figure population_variance = np.mean( [ x*x+y*y for x,y in positions ]) population_variances.append(population_variance) num_lagged = 5+int(0.25*len(rolling_variances)) rolling_mean_variance = np.mean( population_variances[-num_lagged:] ) rolling_variances.append(rolling_mean_variance) axs[1].clear() axs[1].plot([ math.sqrt(v) for v in population_variances],'+') axs[1].plot([ math.sqrt(v) for v in rolling_variances] ) axs[1].plot(list(range(len(rolling_variances))),[R]*len(rolling_variances),'--') axs[1].axis([0,len(rolling_variances),0.75*R,1.5*max(rolling_variances[-1],R)]) axs[1].legend(['Current','Avg last ('+str(num_lagged)+')','Theoretical'],loc='upper left') axs[1].set_title('Root mean square distance') axs[1].figure plt.show(block=False) plt.pause(0.01) if step_no==1 and day==0: plt.pause(20)
def callback(self, day, day_fraction, status, positions, home, work, plt, **ignore_other_kwargs): """ This gets called after each computation cycle (see pandemic/simulation.py) """ from pandemic.metrics import collision_count cc = collision_count(positions=positions, status=status, precision=self.params['geometry']['p']) self.collision_history.append(cc) if day_fraction == 0: # Daily collision TS self.collision_daily_history.append(sum(self.collision_history[-self.params['motion']['t']:])) # Standard plot of infections metrics = status_counts(status=status) self.metric_history.append(metrics) self.time_history.append(day + day_fraction) self.axs[0][0].clear plot_points(plt=self.axs[0][0], positions=positions, status=status, title='Ornstein-Uhlenbeck') self.axs[0][0].figure # Standard plot of vulnerable only metrics = status_counts(status=status) vulnerable_positions = [pos for pos, s in zip(positions, status) if s == 0] vulnerable_status = [s for s in status if s == 0] # Counts plot self.metric_history.append(metrics) self.time_history.append(day + day_fraction) self.axs[1][0].clear() plot_points(plt=self.axs[1][0], positions=vulnerable_positions, status=vulnerable_status, title='Vulnerable') self.axs[1][0].figure # Growth curve infect = [m[1] for m in self.metric_history] self.axs[0][1].plot(self.time_history, infect, color=self.color) # self.axs[0][1].set_yscale('log') self.axs[0][1].set_title('Infected') self.axs[0][1].figure if day > 1: # Collisions curve self.axs[1][1].plot(self.collision_daily_history, color=self.color) self.axs[1][1].set_title('Collisions per day') self.axs[1][1].set_xlabel('Days since ' + str(self.params['geometry']['i']) + ' cases.') self.plt.show(block=False) self.plt.pause(0.1)
def plot(self, plt, positions, status): # Population plot self.axs[0][0].clear() plot_points(plt=self.axs[0][0], positions=positions, status=status) circle_x, circle_y = circle(r=self.R) self.axs[0][0].scatter(x=circle_x, y=circle_y, c='g', s=1) self.axs[0][0].figure # Metrics plots, regular and logarithmic for k in range(2): self.axs[1][k].clear() self.plot_metrics(plt=self.axs[1][k], logarithmic=k) self.axs[1][k].figure # Rates of change self.axs[0][1].clear() self.plot_metrics(plt=self.axs[0][1], logarithmic=False, differences=True) self.axs[0][1].figure plt.show(block=False) plt.pause(0.1)
def callback(self, day, day_fraction, status, positions, home, work, plt, **ignore_other_kwargs): """ This gets called after each computation cycle (see pandemic/simulation.py) """ from pandemic.metrics import collision_count # Unique collisions with Joe from geohash import encode approx_precision = self.params['geometry']['p'] - 2 for joe in range(self.num_joes): joes_hash = encode(positions[joe][0], positions[joe][1], precision=approx_precision) rough_positions = [ (encode(pos[0], pos[1], precision=self.params['geometry']['p'] - 2), j) for j, pos in enumerate(positions) ] joes_collisions = [ j for (h, j) in rough_positions if h == joes_hash and not j == joe ] joes_new_collisions = [ j for j in joes_collisions if not j in self.friends[joe] ] self.friends[joe].update(joes_new_collisions) self.collision_history.append(len(joes_collisions)) self.unique_collions_history.append(len(joes_new_collisions)) num_rolling = 48 if len(self.collision_history) > num_rolling * self.num_joes: rolling_collisions = sum(self.collision_history[-num_rolling * self.num_joes:]) rolling_unique_collisions = sum( self.unique_collions_history[-num_rolling * self.num_joes:]) if day_fraction == 0: self.attenuation_history.append( (0.1 + rolling_unique_collisions) / (0.1 + rolling_collisions)) self.herd_history.append( sum([s in [VULNERABLE] for s in status]) / len(status)) if day_fraction == 0: # Daily collision TS self.collision_daily_history.append( sum(self.collision_history[-self.params['motion']['t']:])) # Standard plot of infections metrics = status_counts(status=status) self.metric_history.append(metrics) self.time_history.append(day + day_fraction) self.axs[0][0].clear plot_points(plt=self.axs[0][0], positions=positions, status=status, title='Ornstein-Uhlenbeck') self.axs[0][0].figure if day > 1: # Collisions curve self.axs[1][0].plot(self.attenuation_history, marker='*') self.axs[1][0].plot(self.herd_history, marker='o') self.axs[1][0].set_title('Novelty versus Herd effect') self.axs[1][0].set_xlabel('Days since ' + str(self.params['geometry']['i']) + ' cases.') self.axs[1][0].set_ylabel('Attenuation') # Growth curve infect = [m[1] for m in self.metric_history] self.axs[0][1].plot(self.time_history, infect, color=self.color) #self.axs[0][1].set_yscale('log') self.axs[0][1].set_title('Infected') self.axs[0][1].figure if day > 1: # Collisions curve self.axs[1][1].plot(self.collision_daily_history, color=self.color, marker='*') self.axs[1][1].set_title('Daily collisions') self.axs[1][1].set_xlabel('Days since ' + str(self.params['geometry']['i']) + ' cases.') self.plt.show(block=False) figManager = plt.get_current_fig_manager() figManager.full_screen_toggle() self.plt.pause(2)
(-10 * np.random.rand(), 0), (0, -10 * np.random.rand())] work_sprawls = list() for center in centers: work_sprawls.append([ (pos[0] + center[0], pos[1] + center[1]) for pos in sprawl(geometry_params=geometry_params, num=num) ]) work = [random.choice(ws) for ws in zip(*work_sprawls)] geometry_params['r'] = 4 * geometry_params['r'] home_sprawls = list() for center in centers: home_sprawls.append([ (pos[0] + center[0], pos[1] + center[1]) for pos in sprawl(geometry_params=geometry_params, num=num) ]) home = [random.choice(hs) for hs in zip(*home_sprawls)] work = [random.choice([w, h]) for w, h in zip(work, home)] # Half stay home return home, work if __name__ == "__main__": import matplotlib.pyplot as plt from pandemic.conventions import EXAMPLE_PARAMETERS from pandemic.plotting import plot_points city = sprawl(geometry_params=EXAMPLE_PARAMETERS['geometry'], num=500) plot_points(plt, city, status=None) plt.show()
def intensity(params): """ To illustrate the OU process with commuting """ num, num_initially_infected = int(params['geometry']['n']), int( params['geometry']['i']) assert num % 2 == 0 HOME_1 = (-1.5, 1.5) HOME_2 = (-1.5, -1.5) HOME_3 = (1.5, 1.5) HOME_4 = (1.5, -1.5) HOMES = [HOME_1, HOME_2, HOME_3, HOME_4] WORK = (0, 0) home = [HOME_1] * int(num / 4) + [HOME_2] * int(num / 4) + [HOME_3] * int( num / 4) + [HOME_4] * int(num / 4) work = [WORK] * num def is_working(time_of_day): return time_of_day > 0.4 and time_of_day < 0.6 def is_home(time_of_day): return time_of_day < 0.0 or time_of_day > 0.75 num, num_initially_infected = int(params['geometry']['n']), int( params['geometry']['i']) num_times_of_day = int(params['motion']['t']) day_fraction = 1.0 / num_times_of_day w = params['motion']['w'] k = params['motion']['k'] R = w / math.sqrt(2 * k) b = params['geometry']['b'] positions = [(x / 2 * b, y / 2 * b) for x, y in [ tuple(np.random.multivariate_normal(mean=[0, 0], cov=[[1, 0], [0, 1]])) for _ in range(num) ]] status = [VULNERABLE] * num fig, axs = plt.subplots(nrows=2, ncols=2) population_variances = list() rolling_variances = list() collisions = list() rolling_mean_collisions = list() for day in range(50): for step_no, time_of_day in enumerate(times_of_day(num_times_of_day)): stationary = [s in [DECEASED, POSITIVE] for s in status] attractors = [w for w in work ] if time_of_day < 0.5 else [h for h in home] positions = evolve_positions(positions=positions, motion_params=params['motion'], attractors=attractors, time_step=day_fraction, stationary=stationary) axs[0][0].clear() plot_points(plt=axs[0][0], positions=positions, status=status, sizes=[16] * 6) b = params['geometry']['b'] axs[0][0].axis([-b, b, -b, b]) axs[0][0].axis([-b, b, -b, b]) axs[0][0].set_title('Day ' + str(day) + ' ' + str(time_of_day)) for offsets in HOMES + [WORK]: circle_x, circle_y = circle(r=R, offset=offsets) axs[0][0].scatter(x=circle_x, y=circle_y, c='g', s=1) axs[0][0].figure # Collision count locations = [ encode(pos[0], pos[1], precision=6) for pos in positions ] num_collisions = num - len(set(locations)) collisions.append(num_collisions) precision = 1.0 / (params['motion']['w']**2) inv_sqs = [ np.mean([ 1. / (0.00001 + sq_dist(positions[j], pos)) for k, pos in enumerate(positions) if not j == k ]) for j in range(num) ] population_variances.append(np.mean(inv_sqs)) num_lagged = 4 * int(num_times_of_day / 30) rolling_mean_variance = np.mean(population_variances[-num_lagged:]) rolling_mean_collisions.append(np.mean(collisions[-num_lagged:])) rolling_variances.append(rolling_mean_variance) axs[0][1].clear() axs[0][1].plot([math.sqrt(v) for v in population_variances], 'x') axs[0][1].plot([math.sqrt(v) for v in rolling_variances]) axs[0][1].plot(list(range(len(rolling_variances))), [R] * len(rolling_variances), '--') axs[0][1].legend( ['Current time step', 'Avg last (' + str(num_lagged) + ')'], loc='lower right') axs[0][1].set_title('Mean inverse squared distance)') axs[0][1].figure axs[1][1].plot(collisions, '+') axs[1][1].plot(rolling_mean_collisions, '--') axs[1][1].set_title('Collisions') axs[1][1].legend( ['Current time step', 'Avg last (' + str(num_lagged) + ')'], loc='upper left') axs[1][1].figure if day > 0: rmc = rolling_mean_collisions[num_times_of_day:] pv = population_variances[num_times_of_day:] axs[1][0].clear() axs[1][0].loglog(pv, rmc, '+') axs[1][0].set_xlabel('Mean inverse squared distance)') axs[1][0].set_ylabel('Mean collisions') b, m = polyfit(pv, rmc, 1) axs[1][0].plot(pv, [b + m * pv_ for pv_ in pv], '-') axs[1][0].set_title('Slope is ' + str(m)) axs[1][0].figure if step_no % 2 == 0: plt.show(block=False) plt.pause(0.5) if step_no == 1 and day == 0: plt.pause(5)
def commute(params): """ To illustrate the OU process with commuting """ num, num_initially_infected = int(params['geometry']['n']), int( params['geometry']['i']) assert num % 2 == 0 HOME_1 = (-1.5, 1.5) HOME_2 = (-1.5, -1.5) WORK = (1.5, 0) home = [HOME_1] * int(num / 2) + [HOME_2] * int(num / 2) work = [WORK] * num def is_working(time_of_day): return time_of_day > 0.4 and time_of_day < 0.6 def is_home(time_of_day): return time_of_day < 0.0 or time_of_day > 0.75 num, num_initially_infected = int(params['geometry']['n']), int( params['geometry']['i']) num_times_of_day = int(params['motion']['t']) day_fraction = 1.0 / num_times_of_day w = params['motion']['w'] k = params['motion']['k'] R = w / math.sqrt(2 * k) b = params['geometry']['b'] positions = [(x / 2 * b, y / 2 * b) for x, y in [ tuple(np.random.multivariate_normal(mean=[0, 0], cov=[[1, 0], [0, 1]])) for _ in range(num) ]] status = [VULNERABLE] * num fig, axs = plt.subplots(nrows=1, ncols=2) population_variances = list() rolling_variances = list() for day in range(10): for step_no, time_of_day in enumerate(times_of_day(num_times_of_day)): stationary = [s in [DECEASED, POSITIVE] for s in status] attractors = [w for w in work ] if time_of_day < 0.5 else [h for h in home] positions = evolve_positions(positions=positions, motion_params=params['motion'], attractors=attractors, day_fraction=day_fraction, stationary=stationary) axs[0].clear() plot_points(plt=axs[0], positions=positions, status=status, sizes=[64] * 6) b = params['geometry']['b'] axs[0].axis([-b, b, -b, b]) axs[0].set_title('Day ' + str(day) + ' ' + str(time_of_day)) for offsets in [HOME_1, HOME_2, WORK]: circle_x, circle_y = circle(r=R, offset=offsets) axs[0].scatter(x=circle_x, y=circle_y, c='g', s=1) axs[0].figure if is_home(time_of_day): population_variance = np.mean( [sq_dist(pos, hm) for pos, hm in zip(positions, home)]) population_variances.append(population_variance) num_lagged = 5 + int(0.25 * len(rolling_variances)) rolling_mean_variance = np.mean( population_variances[-num_lagged:]) rolling_variances.append(rolling_mean_variance) axs[1].clear() axs[1].plot([math.sqrt(v) for v in population_variances], '+') axs[1].plot([math.sqrt(v) for v in rolling_variances]) axs[1].plot(list(range(len(rolling_variances))), [R] * len(rolling_variances), '--') axs[1].axis([ 0, len(rolling_variances), 0.75 * R, 1.5 * max(rolling_variances[-1], R) ]) axs[1].legend([ 'Current', 'Avg last (' + str(num_lagged) + ')', 'Theoretical' ], loc='upper left') axs[1].set_title('RMS Distance to home after 6pm') axs[1].figure plt.show(block=False) plt.pause(0.01) if step_no == 1 and day == 0: plt.pause(20)