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 simulate(params, plt=None, hourly=None, xlabel=None, callback=plot_callback, home=None, work=None, positions=None, stopping_i=None, stopping_t=None): """ OU Pandemic simulation :param params: dict of dict as per pandemic.conventions :param plt: Handle to matplotlib plot :param hourly: Bool Set False to speed up, True to see commuting :param xlabel: str Label for plot :param callback: Any function taking home, work, day, params, positions, status (e.g. for plotting, saving etc) :return: None Use the callback """ if stopping_i is None: import math stopping_i = int(math.ceil(0.7 * params['geometry']['i'])) if hourly is None: hourly = params['geometry']['n'] < 50000 # Hack, remove if stopping_t is None: stopping_t = 150 # 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']) if home is None or work is None: home, work = home_and_work_locations(geometry_params=params['geometry'],num=num) if positions is None: positions = nudge(home,w=0.05*params['motion']['w']) status = np.random.permutation([INFECTED]*num_initially_infected +[VULNERABLE]*(num-num_initially_infected)) time_step = 1.0/num_times_of_day day = 0 killed = False while sum( s in [ INFECTED ] for s in status )>=stopping_i and day<stopping_t and not killed: day = day+1 for step_no, day_fraction in enumerate(times_of_day(num_times_of_day)): stationary = [ s in [DECEASED, POSITIVE] for s in status ] attractors = destinations(status, day_fraction, home, work) positions = evolve_positions(positions=positions, motion_params=params['motion'], attractors=attractors, time_step=time_step , 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=time_step ) if callback: signal = callback(day=day, day_fraction=day_fraction, home=home, work=work, positions=positions, status=status, params=params, step_no=step_no, hourly=hourly, plt=plt, xlabel=xlabel) if signal is not None: if 'kill' in signal: killed = True if 'lockdown' in signal: work = [ h for h in home ] if 'params' in signal: params.update(signal['params']) pprint(Counter([list(STATE_DESCRIPTIONS.values())[s] for s in status]))
def step(self): time_step = 1. / self.params['motion']['t'] precision = self.params['geometry']['p'] stationary = [s in [DECEASED, POSITIVE] for s in self.state['status']] self.state['positions'] = evolve_positions( positions=self.state['positions'], motion_params=self.params['motion'], attractors=self.state['attractors'], time_step=time_step, stationary=stationary) exposed = newly_exposed(positions=self.state['positions'], status=self.state['status'], precision=precision) self.state['status'] = contact_progression( status=self.state['status'], health_params=self.params['health'], exposed=exposed) self.state['status'] = individual_progression( self.state['status'], health_params=self.params['health'], day_fraction=time_step)
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)
def simulate(params, plt=None, plot_hourly=None, xlabel=None, callback=plot_callback): """ OU Pandemic simulation :param params: dict of dict as per pandemic.conventions :param plt: Handle to matplotlib plot :param plot_hourly: Bool Set False to speed up, True to see commuting :param xlabel: str Label for plot :param callback: Any function taking home, work, day, params, positions, status (e.g. for plotting, saving etc) :return: None Use the callback """ 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)) time_step = 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] for s in status): day = day + 1 for step_no, day_fraction in enumerate(times_of_day(num_times_of_day)): stationary = [s in [DECEASED, POSITIVE] for s in status] attractors = destinations(status, day_fraction, home, work) positions = evolve_positions(positions=positions, motion_params=params['motion'], attractors=attractors, time_step=time_step, 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=time_step) if callback: callback(day=day, day_fraction=day_fraction, home=home, work=work, positions=positions, status=status, params=params, step_no=step_no, plot_hourly=plot_hourly, plt=plt) pprint(Counter([list(STATE_DESCRIPTIONS.values())[s] for s in status]))