def test_quadratic_processes(self): S, E, I, R = sympy.symbols("S E I R") epi = SymbolicEpiModel([S, E, I, R]) quadratic_rates = [I * S] quadratic_events = [Matrix([[-1, +1, 0, 0]])] epi.add_transmission_processes([ (S, I, 1, I, E), ]) for r0, r1 in zip(quadratic_rates, epi.quadratic_rate_functions): assert (r0 == r1) for e0, e1 in zip(quadratic_events, epi.quadratic_event_updates): assert (all([_e0 == _e1 for _e0, _e1 in zip(e0, e1)]))
def symbolic_simulation_temporally_forced(): from epipack import SymbolicEpiModel import sympy as sy from epipack.plottools import plot import numpy as np S, I, R, eta, rho, omega, t, T = \ sy.symbols("S I R eta rho omega t T") N = 1000 SIRS = SymbolicEpiModel([S,I,R],N)\ .set_processes([ (S, I, 3+sy.cos(2*sy.pi*t/T), I, I), (I, rho, R), (R, omega, S), ]) SIRS.set_parameter_values({ rho: 1, omega: 1 / 14, T: 100, }) SIRS.set_initial_conditions({S: N - 100, I: 100}) _t = np.linspace(0, 150, 1000) result = SIRS.integrate(_t) t_sim, result_sim = SIRS.simulate(max(_t)) ax = plot(_t, result) plot(t_sim, result_sim, ax=ax) ax.get_figure().savefig('symbolic_model_time_varying_rate.png', dpi=300)
def test_stochastic_well_mixed(self): S, E, I, R = sympy.symbols("S E I R") N = 75000 tmax = 100 model = SymbolicEpiModel([S, E, I, R], N) model.set_processes([ (S, I, 2, E, I), (I, 1, R), (E, 1, I), ]) model.set_initial_conditions({S: N - 100, I: 100}) tt = np.linspace(0, tmax, 10) result_int = model.integrate(tt) t, result_sim = model.simulate(tmax, sampling_dt=1, return_compartments=[S, R]) for c, res in result_sim.items(): #print(c, np.abs(1-res[-1]/result_int[c][-1])) #print(c, np.abs(1-res[-1]/result_sim[c][-1])) assert (np.abs(1 - res[-1] / result_int[c][-1]) < 0.05)
def test_linear_rates(self): S, E, I, R = sympy.symbols("S E I R") epi = SymbolicEpiModel([S, E, I, R]) epi.add_transition_processes([ (E, 1, I), (I, 1, R), ]) linear_rates = [E, I] linear_events = [Matrix([[0, -1, +1, 0]]), Matrix([[0, 0, -1, +1]])] for r0, r1 in zip(linear_rates, epi.linear_rate_functions): assert (r0 == r1) for e0, e1 in zip(linear_events, epi.linear_event_updates): assert (all([_e0 == _e1 for _e0, _e1 in zip(e0, e1)]))
def test_time_dependent_rates(self): B, t = sympy.symbols("B t") epi = SymbolicEpiModel([B]) epi.add_fission_processes([ (B, t, B, B), ]) epi.set_initial_conditions({B: 1}) result = epi.integrate([0, 3], adopt_final_state=True) assert (np.isclose(epi.y0[0], np.exp(3**2 / 2))) epi.set_initial_conditions({B: 1}) result = epi.integrate(np.linspace(0, 3, 10000), integrator='euler', adopt_final_state=True) eul = epi.y0[0] real = np.exp(3**2 / 2) assert (np.abs(1 - eul / real) < 1e-2)
def test_interactive_integrator(self): S, I, R, R0, tau, omega = sympy.symbols("S I R R_0 tau omega") I0 = 0.01 model = SymbolicEpiModel([S,I,R])\ .set_processes([ (S, I, R0/tau, I, I), (I, 1/tau, R), (R, omega, S), ])\ .set_initial_conditions({S:1-I0, I:I0}) parameters = { R0: LogRange(min=0.1, max=10, step_count=1000), tau: Range(min=0.1, max=10, value=8.0), omega: 1 / 14 } t = np.linspace(0.01, 200, 1000) integrator = InteractiveIntegrator(model, parameters, t, figsize=(3, 4), return_compartments=[S, I]) keys = sorted([str(k) for k in integrator.sliders.keys()]) assert (all([a == b for a, b in zip(sorted(['R_0', 'tau']), keys)])) integrator.update_parameters() keys = sorted([str(k) for k in integrator.lines.keys()]) assert (all([a == b for a, b in zip(sorted(['I', 'S']), keys)])) class change(): new = True integrator.update_xscale(change()) assert (integrator.ax.get_xscale() == 'log') assert (integrator.ax.get_yscale() == 'linear') integrator.update_yscale(change()) assert (integrator.ax.get_xscale() == 'log') assert (integrator.ax.get_yscale() == 'log') integrator = InteractiveIntegrator(model, parameters, t, figsize=(3, 4)) keys = sorted([str(k) for k in integrator.lines.keys()]) assert (all([ a == b for a, b in zip(sorted([str(C) for C in model.compartments]), keys) ]))
def test_basic_analytics(self): S, I, R, eta, rho, omega, t = symbols("S I R eta rho omega, t") SIRS = SymbolicEpiModel([S, I, R]) SIRS.set_processes([ #### transmission process #### # S + I (eta)-> I + I (S, I, eta, I, I), #### transition processes #### # I (rho)-> R # R (omega)-> S (I, rho, R), (R, omega, S), ]) odes = SIRS.ODEs() expected = [ Eq(Derivative(S, t), -I * S * eta + R * omega), Eq(Derivative(I, t), I * (S * eta - rho)), Eq(Derivative(R, t), I * rho - R * omega) ] assert (all([got == exp for got, exp in zip(odes, expected)])) fixed_points = SIRS.find_fixed_points() expected = FiniteSet((S, 0, 0), (rho / eta, R * omega / rho, R)) assert (all([got == exp for got, exp in zip(fixed_points, expected)])) J = SIRS.jacobian() expected = Matrix([[-I * eta, -S * eta, omega], [I * eta, S * eta - rho, 0], [0, rho, -omega]]) N = SIRS.N_comp assert (all( [J[i, j] == expected[i, j] for i in range(N) for j in range(N)])) eig = SIRS.get_eigenvalues_at_disease_free_state() expected = {-omega: 1, eta - rho: 1, 0: 1} assert (all([v == expected[k] for k, v in eig.items()]))
def test_exceptions(self): B, mu, t = sympy.symbols("B mu t") epi = SymbolicEpiModel([B]) epi.add_fission_processes([ (B, mu, B, B), ]) epi.set_initial_conditions({B: 1}) self.assertRaises(ValueError, epi.integrate, [0, 1]) self.assertRaises(ValueError, SymbolicEpiModel, [t]) self.assertRaises(ValueError, epi.get_eigenvalues_at_disease_free_state)
def test_adding_quadratic_processes(self): S, E, I, A, R, rho = sympy.symbols("S E I A R rho") epi = SymbolicEpiModel([S, E, I, A, R]) epi.set_processes([ (S, I, rho, I, E), ]) epi.add_transmission_processes([ (S, A, rho, A, E), ]) quadratic_rates = [I * S * rho, S * A * rho] quadratic_events = [ Matrix([[-1, +1, 0, 0, 0]]), Matrix([[-1, +1, 0, 0, 0]]) ] for r0, r1 in zip(quadratic_rates, epi.quadratic_rate_functions): assert (r0 == r1) for e0, e1 in zip(quadratic_events, epi.quadratic_event_updates): assert (all([_e0 == _e1 for _e0, _e1 in zip(e0, e1)]))
def test_changing_population_size(self): A, B, C, t = sympy.symbols("A B C t") epi = SymbolicEpiModel([A, B, C], 10, correct_for_dynamical_population_size=True) epi.set_initial_conditions({A: 5, B: 5}) epi.set_processes([ (A, B, 1, C), ], allow_nonzero_column_sums=True) dydt = epi.dydt() assert (dydt[0] == -1 * A * B / (A + B + C)) _, res = epi.simulate(1e9) assert (res[C][-1] == 5) epi.set_processes([ (None, 1 + sympy.log(1 + t), A), (A, 1 + sympy.log(1 + t), B), (B, 1 + sympy.log(1 + t), None), ], allow_nonzero_column_sums=True) rates, comp_changes = epi.get_numerical_event_and_rate_functions() _, res = epi.simulate(200, sampling_dt=0.05) vals = np.concatenate([res[A][_ > 10], res[B][_ > 10]]) rv = poisson(vals.mean()) measured, bins = np.histogram(vals, bins=np.arange(10) - 0.5, density=True) theory = [ rv.pmf(i) for i in range(0, len(bins) - 1) if measured[i] > 0 ] experi = [ measured[i] for i in range(0, len(bins) - 1) if measured[i] > 0 ] # make sure the kullback-leibler divergence is below some threshold assert (entropy(theory, experi) < 2e-3) assert (np.median(res[A]) == 1)
def test_compartments(self): comps = sympy.symbols("S E I R") epi = SymbolicEpiModel(comps) assert (all( [i == epi.get_compartment_id(C) for i, C in enumerate(comps)]))
print() print(epi.ODEs()) print(epi.find_fixed_points()) omega = sympy.symbols("omega") epi = SymbolicSIRSModel(eta, rho, omega) print() print(epi.ODEs()) print(epi.find_fixed_points()) import sympy from epipack import SymbolicEpiModel S, I, eta, rho = sympy.symbols("S I eta rho") SIS = SymbolicEpiModel([S, I]) SIS.add_transmission_processes([ (I, S, eta, I, I), ]) SIS.add_transition_processes([ (I, rho, S), ]) print(SIS.find_fixed_points()) print(SIS.get_eigenvalues_at_fixed_point({S: 1})) print("==========") SIS = SymbolicEpiModel([S, I]) SIS.set_processes([ (I, S, eta / (1 - I), I, I),