def test_basic_get_infection_multiplier(): model = CompartmentalModel(times=[0, 5], compartments=["S", "I", "R"], infectious_compartments=["I"]) model.set_initial_population(distribution={"S": 990, "I": 10}) model._prepare_to_run() model._prepare_time_step(0, model.initial_population) c_src = model.compartments[0] c_dst = model.compartments[1] multiplier = model._get_infection_frequency_multiplier(c_src, c_dst) assert multiplier == 10 / 1000 multiplier = model._get_infection_density_multiplier(c_src, c_dst) assert multiplier == 10
def test_apply_universal_death_flow(): model = CompartmentalModel(times=[0, 5], compartments=["S", "I"], infectious_compartments=["I"]) model.set_initial_population(distribution={"S": 990, "I": 10}) model.add_universal_death_flows("universal_death", 0.1) model._prepare_to_run() actual_flow_rates = model._get_compartment_rates(model.initial_population, 0) expected_flow_rates = np.array([-99, -1]) assert_array_equal(actual_flow_rates, expected_flow_rates)
def test_apply_infect_death_flows(inf_pop, exp_flow): model = CompartmentalModel(times=[0, 5], compartments=["S", "I"], infectious_compartments=["I"]) model.set_initial_population(distribution={"I": inf_pop}) model.add_death_flow("infection_death", 0.1, "I") model._prepare_to_run() actual_flow_rates = model._get_compartment_rates(model.initial_population, 0) # Expect 0.1 * inf_pop = exp_flow expected_flow_rates = np.array([0, -exp_flow]) assert_array_equal(actual_flow_rates, expected_flow_rates)
def test_apply_flows__with_infection_density(inf_pop, sus_pop, exp_flow): """ Use infection density, expect infection multiplier to be proportional to the infectious pop. """ model = CompartmentalModel(times=[0, 5], compartments=["S", "I"], infectious_compartments=["I"]) model.set_initial_population(distribution={"S": sus_pop, "I": inf_pop}) model.add_infection_density_flow("infection", 0.02, "S", "I") model._prepare_to_run() actual_flow_rates = model._get_compartment_rates(model.initial_population, 0) # Expect 0.2 * sus_pop * inf_pop = exp_flow expected_flow_rates = np.array([-exp_flow, exp_flow]) assert_array_equal(actual_flow_rates, expected_flow_rates)
def test_apply_flows__with_transition_flow__expect_flows_applied( inf_pop, sus_pop, exp_flow): """ Expect a flow to occur proportional to the compartment size and parameter. """ model = CompartmentalModel(times=[0, 5], compartments=["S", "I"], infectious_compartments=["I"]) model.set_initial_population(distribution={"S": sus_pop, "I": inf_pop}) model.add_transition_flow("deliberately_infected", 0.1, "S", "I") model._prepare_to_run() actual_flow_rates = model._get_compartment_rates(model.initial_population, 0) # Expect sus_pop * 0.1 = exp_flow expected_flow_rates = np.array([-exp_flow, exp_flow]) assert_array_equal(actual_flow_rates, expected_flow_rates)
def test_apply_flows__with_sojourn_flow__expect_flows_applied( inf_pop, exp_flow): """ Expect a flow to occur proportional to the compartment size and parameter. """ model = CompartmentalModel(times=[0, 5], compartments=["S", "I", "R"], infectious_compartments=["I"]) model.set_initial_population(distribution={"I": inf_pop}) model.add_sojourn_flow("recovery", 10, "I", "R") model._prepare_to_run() actual_flow_rates = model._get_compartment_rates(model.initial_population, 0) # Expect exp_flow = inf_pop * () exp_flow expected_flow_rates = np.array([0, -exp_flow, exp_flow]) assert_array_equal(actual_flow_rates, expected_flow_rates)
def test_apply_flows__with_infection_frequency(inf_pop, sus_pop, exp_flow): """ Use infection frequency, expect infection multiplier to be proportional to the proprotion of infectious to total pop. """ model = CompartmentalModel(times=[0, 5], compartments=["S", "I"], infectious_compartments=["I"]) model.set_initial_population(distribution={"S": sus_pop, "I": inf_pop}) model.add_infection_frequency_flow("infection", 20, "S", "I") model._prepare_to_run() actual_flow_rates = model._get_compartment_rates(model.initial_population, 0) # Expect sus_pop * 20 * (inf_pop / 1000) = exp_flow expected_flow_rates = np.array([-exp_flow, exp_flow]) assert_array_equal(actual_flow_rates, expected_flow_rates)
def test_apply_crude_birth_rate_flow(birth_rate, exp_flow): """ Expect births proportional to the total population and birth rate when the birth approach is "crude birth rate". """ model = CompartmentalModel(times=[0, 5], compartments=["S", "I"], infectious_compartments=["I"]) model.set_initial_population(distribution={"S": 990, "I": 10}) model.add_crude_birth_flow("births", birth_rate, "S") model._prepare_to_run() actual_flow_rates = model._get_compartment_rates(model.initial_population, 0) # Expect birth_rate * total_population = exp_flow expected_flow_rates = np.array([exp_flow, 0]) assert_array_equal(actual_flow_rates, expected_flow_rates)
def test_no_derived_outputs(): model = CompartmentalModel( times=[0, 5], compartments=["S", "I", "R"], infectious_compartments=["I"] ) model.set_initial_population(distribution={"S": 990, "I": 10}) model.run() # 6 timesteps, 3 compartments. assert model.outputs.shape == (6, 3) assert model.derived_outputs == {}
def test_aggregate_derived_outputs(): model = CompartmentalModel( times=[0, 5], compartments=["S", "I", "R"], infectious_compartments=["I"] ) model.set_initial_population(distribution={"S": 990, "I": 10}) model.run() model.outputs = np.array( [ [990, 10, 0], [980, 15, 5], [970, 20, 10], [960, 25, 15], [950, 30, 20], [940, 35, 25], ] ) model.request_output_for_compartments("recovered", ["R"]) model.request_output_for_compartments("not_infected", ["S", "R"]) model.request_output_for_compartments("total_population", ["S", "I", "R"]) model.request_aggregate_output(name="my_aggregate", sources=["recovered", "total_population"]) dos = model._calculate_derived_outputs() assert_array_equal(dos["my_aggregate"], np.array([1000, 1005, 1010, 1015, 1020, 1025]))
def test_strat_get_infection_multiplier__with_age_split_and_simple_mixing(): """ Check FoI when a simple 2-strata stratification applied AND heteregeneous mixing. Unequally split the children and adults. Expect same density as before, different frequency. Note that the mixing matrix has different meanings for density / vs frequency. """ # Create a model model = CompartmentalModel(times=[0, 5], compartments=["S", "I", "R"], infectious_compartments=["I"]) model.set_initial_population(distribution={"S": 990, "I": 10}) strat = Stratification("age", ["child", "adult"], ["S", "I", "R"]) mixing_matrix = np.array([[0.5, 0.5], [0.5, 0.5]]) strat.set_mixing_matrix(mixing_matrix) strat.set_population_split({"child": 0.2, "adult": 0.8}) model.stratify_with(strat) assert model._mixing_categories == [{"age": "child"}, {"age": "adult"}] assert model.compartments == [ C("S", {"age": "child"}), C("S", {"age": "adult"}), C("I", {"age": "child"}), C("I", {"age": "adult"}), C("R", {"age": "child"}), C("R", {"age": "adult"}), ] assert_array_equal(model.initial_population, np.array([198.0, 792.0, 2.0, 8.0, 0.0, 0.0])) # Do pre-run force of infection calcs. model._prepare_to_run() assert_array_equal(model._compartment_infectiousness["default"], np.array([0.0, 0.0, 1.0, 1.0, 0.0, 0.0])) assert_array_equal( model._category_matrix, np.array([[1.0, 0.0, 1.0, 0.0, 1.0, 0.0], [0.0, 1.0, 0.0, 1.0, 0.0, 1.0]]), ) assert model._category_lookup == {0: 0, 1: 1, 2: 0, 3: 1, 4: 0, 5: 1} # Do pre-iteration force of infection calcs model._prepare_time_step(0, model.initial_population) child_density = 5 adult_density = 5 assert child_density == 0.5 * 5 + 0.5 * 5 assert adult_density == 0.5 * 5 + 0.5 * 5 assert_array_equal(model._infection_density["default"], np.array([child_density, adult_density])) child_freq = 0.01 adult_freq = 0.01 assert child_freq == 0.5 * 2 / 200 + 0.5 * 8 / 800 assert adult_freq == 0.5 * 2 / 200 + 0.5 * 8 / 800 assert_array_equal(model._infection_frequency["default"], np.array([child_freq, adult_freq])) # Get multipliers s_child = model.compartments[0] s_adult = model.compartments[1] i_child = model.compartments[2] i_adult = model.compartments[3] assert model._get_infection_density_multiplier(s_child, i_child) == child_density assert model._get_infection_density_multiplier(s_adult, i_adult) == adult_density assert model._get_infection_frequency_multiplier(s_child, i_child) == child_freq assert model._get_infection_frequency_multiplier(s_adult, i_adult) == adult_freq # Santiy check frequency-dependent force of infection assert 200.0 * child_freq + 800.0 * adult_freq == 10
def test_derived_outputs_with_no_save_results(): model = CompartmentalModel( times=[0, 5], compartments=["S", "I", "R"], infectious_compartments=["I"] ) model.set_initial_population(distribution={"S": 990, "I": 10}) model.add_importation_flow("imports", num_imported=2, dest="S") # Expect np.array([0, 2, 2, 2, 2, 2])) model.request_output_for_flow(name="importation", flow_name="imports", save_results=False) # Expect np.array([0, 5, 10, 15, 20, 25])) model.request_output_for_compartments("recovered", ["R"], save_results=False) # Expect np.array([0, 5, 15, 30, 50, 75])) model.request_cumulative_output( name="recovered_cumulative", source="recovered", save_results=False ) # Expect np.array([0, 7, 12, 17, 22, 227])) model.request_aggregate_output( name="some_aggregate", sources=["recovered", "importation"], save_results=False ) # Expect np.array([ 0, 12, 27, 47, 72, 102]) model.request_aggregate_output( name="final_aggregate", sources=["some_aggregate", "recovered_cumulative"] ) model.run() # Override outputs so the test is easier to write model.outputs = np.array( [ [990, 10, 0], [980, 15, 5], [970, 20, 10], [960, 25, 15], [950, 30, 20], [940, 35, 25], ] ) dos = model._calculate_derived_outputs() assert_array_equal(dos["final_aggregate"][1:], np.array([12, 27, 47, 72, 102])) assert "importation" not in dos assert "recovered" not in dos assert "recovered_cumulative" not in dos assert "some_aggregate" not in dos
def test_derived_outputs_whitelist(): model = CompartmentalModel( times=[0, 5], compartments=["S", "I", "R"], infectious_compartments=["I"] ) model.set_initial_population(distribution={"S": 990, "I": 10}) model.run() model.outputs = np.array( [ [990, 10, 0], [980, 15, 5], [970, 20, 10], [960, 25, 15], [950, 30, 20], [940, 35, 25], ] ) model.request_output_for_compartments("recovered", ["R"]) model.request_output_for_compartments("not_infected", ["S", "R"]) model.request_output_for_compartments("infected", ["I"]) model.request_output_for_compartments("total_population", ["S", "I", "R"]) model.request_cumulative_output(name="accum_infected", source="infected") model.set_derived_outputs_whitelist(["recovered", "accum_infected"]) dos = model._calculate_derived_outputs() assert "recovered" in dos # Included coz in whitelist (or dependency of) assert "infected" in dos # Included coz in whitelist (or dependency of) assert "accum_infected" in dos # Included coz in whitelist (or dependency of) assert "not_infected" not in dos # Excluded coz not in whitelist assert "total_population" not in dos # Excluded coz not in whitelist assert_array_equal(dos["recovered"], np.array([0, 5, 10, 15, 20, 25])) assert_array_equal(dos["infected"], np.array([10, 15, 20, 25, 30, 35])) assert_array_equal(dos["accum_infected"], np.array([10, 25, 45, 70, 100, 135]))
def test_strat_get_infection_multiplier__with_double_strat_and_no_mixing(): """ Check FoI when a two 2-strata stratificationz applied and no mixing matrix. Expect the same results as with the basic case. """ model = CompartmentalModel(times=[0, 5], compartments=["S", "I", "R"], infectious_compartments=["I"]) model.set_initial_population(distribution={"S": 990, "I": 10}) strat = Stratification("age", ["child", "adult"], ["S", "I", "R"]) model.stratify_with(strat) strat = Stratification("location", ["work", "home"], ["S", "I", "R"]) model.stratify_with(strat) assert model._mixing_categories == [{}] assert model.compartments == [ C("S", { "age": "child", "location": "work" }), C("S", { "age": "child", "location": "home" }), C("S", { "age": "adult", "location": "work" }), C("S", { "age": "adult", "location": "home" }), C("I", { "age": "child", "location": "work" }), C("I", { "age": "child", "location": "home" }), C("I", { "age": "adult", "location": "work" }), C("I", { "age": "adult", "location": "home" }), C("R", { "age": "child", "location": "work" }), C("R", { "age": "child", "location": "home" }), C("R", { "age": "adult", "location": "work" }), C("R", { "age": "adult", "location": "home" }), ] expected_comp_vals = np.array( [247.5, 247.5, 247.5, 247.5, 2.5, 2.5, 2.5, 2.5, 0, 0, 0, 0]) assert_array_equal(model.initial_population, expected_comp_vals) # Do pre-run force of infection calcs. model._prepare_to_run() expected_compartment_infectiousness = np.array( [0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0]) expected_category_matrix = np.array([[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]]) expected_lookup = {n: 0 for n in range(12)} assert_array_equal(model._compartment_infectiousness["default"], expected_compartment_infectiousness) assert_array_equal(model._category_matrix, expected_category_matrix) assert model._category_lookup == expected_lookup # Do pre-iteration force of infection calcs model._prepare_time_step(0, model.initial_population) assert_array_equal(model._category_populations, np.array([1000])) assert_array_equal(model._infection_density["default"], np.array([10.0])) assert_array_equal(model._infection_frequency["default"], np.array([0.01])) s_child_work = model.compartments[0] s_child_home = model.compartments[1] s_adult_work = model.compartments[2] s_adult_home = model.compartments[3] i_child_work = model.compartments[4] i_child_home = model.compartments[5] i_adult_work = model.compartments[6] i_adult_home = model.compartments[7] assert model._get_infection_density_multiplier(s_child_work, i_child_work) == 10.0 assert model._get_infection_density_multiplier(s_child_home, i_child_home) == 10.0 assert model._get_infection_density_multiplier(s_adult_work, i_adult_work) == 10.0 assert model._get_infection_density_multiplier(s_adult_home, i_adult_home) == 10.0 assert model._get_infection_frequency_multiplier(s_child_work, i_child_work) == 0.01 assert model._get_infection_frequency_multiplier(s_child_home, i_child_home) == 0.01 assert model._get_infection_frequency_multiplier(s_adult_work, i_adult_work) == 0.01 assert model._get_infection_frequency_multiplier(s_adult_home, i_adult_home) == 0.01
def test_functional_derived_outputs(): model = CompartmentalModel( times=[0, 5], compartments=["S", "I", "R"], infectious_compartments=["I"] ) model.set_initial_population(distribution={"S": 990, "I": 10}) model.run() model.outputs = np.array( [ [990, 10, 0], [980, 15, 5], [970, 20, 10], [960, 25, 15], [950, 30, 20], [940, 35, 25], ] ) def get_non_infected(rec, pop): return rec / pop model.request_output_for_compartments("recovered", ["R"]) model.request_output_for_compartments("population", ["S", "I", "R"]) model.request_function_output("recovered_prop", get_non_infected, ["recovered", "population"]) dos = model._calculate_derived_outputs() assert_array_equal(dos["recovered_prop"], np.array([0, 0.005, 0.01, 0.015, 0.020, 0.025]))
def test_apply_many_flows(): """ Expect multiple flows to operate independently and produce the correct final flow rate. """ model = CompartmentalModel(times=[0, 5], compartments=["S", "I", "R"], infectious_compartments=["I"]) model.set_initial_population(distribution={"S": 900, "I": 100}) model.add_death_flow("infection_death", 0.1, "I") model.add_universal_death_flows("universal_death", 0.1) model.add_infection_frequency_flow("infection", 0.2, "S", "I") model.add_sojourn_flow("recovery", 10, "I", "R") model.add_transition_flow("vaccination", 0.1, "S", "R") model.add_crude_birth_flow("births", 0.1, "S") model._prepare_to_run() actual_flow_rates = model._get_compartment_rates(model.initial_population, 0) # Expect the effects of all these flows to be linearly superimposed. infect_death_flows = np.array([0, -10, 0]) universal_death_flows = np.array([-90, -10, 0]) infected = 900 * 0.2 * (100 / 1000) infection_flows = np.array([-infected, infected, 0]) recovery_flows = np.array([0, -10, 10]) vaccination_flows = np.array([-90, 0, 90]) birth_flows = np.array([100, 0, 0]) expected_flow_rates = (infect_death_flows + universal_death_flows + infection_flows + recovery_flows + vaccination_flows + birth_flows) assert_array_equal(actual_flow_rates, expected_flow_rates)
def test_strat_get_infection_multiplier__with_double_strat_and_second_strat_mixing( ): """ Check FoI when a two 2-strata stratification applied and the second stratification has a mixing matrix. """ # Create the model model = CompartmentalModel(times=[0, 5], compartments=["S", "I", "R"], infectious_compartments=["I"]) model.set_initial_population(distribution={"S": 990, "I": 10}) strat = Stratification("age", ["child", "adult"], ["S", "I", "R"]) strat.set_population_split({"child": 0.3, "adult": 0.7}) model.stratify_with(strat) strat = Stratification("location", ["work", "home"], ["S", "I", "R"]) location_mixing = np.array([[2, 3], [5, 7]]) strat.set_mixing_matrix(location_mixing) model.stratify_with(strat) assert model._mixing_categories == [{ "location": "work" }, { "location": "home" }] assert model.compartments == [ C("S", { "age": "child", "location": "work" }), C("S", { "age": "child", "location": "home" }), C("S", { "age": "adult", "location": "work" }), C("S", { "age": "adult", "location": "home" }), C("I", { "age": "child", "location": "work" }), C("I", { "age": "child", "location": "home" }), C("I", { "age": "adult", "location": "work" }), C("I", { "age": "adult", "location": "home" }), C("R", { "age": "child", "location": "work" }), C("R", { "age": "child", "location": "home" }), C("R", { "age": "adult", "location": "work" }), C("R", { "age": "adult", "location": "home" }), ] expected_comp_vals = np.array( [148.5, 148.5, 346.5, 346.5, 1.5, 1.5, 3.5, 3.5, 0, 0, 0, 0]) assert_array_equal(model.initial_population, expected_comp_vals) # Do pre-run force of infection calcs. model._prepare_to_run() exp_lookup = { 0: 0, 1: 1, 2: 0, 3: 1, 4: 0, 5: 1, 6: 0, 7: 1, 8: 0, 9: 1, 10: 0, 11: 1 } exp_matrix = np.array([ [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1], ]) exp_mults = np.array([0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0]) assert_array_equal(model._compartment_infectiousness["default"], exp_mults) assert_array_equal(model._category_matrix, exp_matrix) assert model._category_lookup == exp_lookup # Do pre-iteration force of infection calcs model._prepare_time_step(0, model.initial_population) assert_array_equal(model._category_populations, np.array([500.0, 500.0])) work_density = 25 home_density = 60 assert work_density == 2 * 5 + 3 * 5 assert home_density == 5 * 5 + 7 * 5 assert_array_equal(model._infection_density["default"], np.array([work_density, home_density])) work_freq = 2 * 5 / 500 + 3 * 5 / 500 home_freq = 5 * 5 / 500 + 7 * 5 / 500 assert_array_equal(model._infection_frequency["default"], np.array([work_freq, home_freq])) # Get multipliers s_child_work = model.compartments[0] s_child_home = model.compartments[1] s_adult_work = model.compartments[2] s_adult_home = model.compartments[3] i_child_work = model.compartments[4] i_child_home = model.compartments[5] i_adult_work = model.compartments[6] i_adult_home = model.compartments[7] assert model._get_infection_density_multiplier( s_child_work, i_child_work) == work_density assert model._get_infection_density_multiplier( s_child_home, i_child_home) == home_density assert model._get_infection_density_multiplier( s_adult_work, i_adult_work) == work_density assert model._get_infection_density_multiplier( s_adult_home, i_adult_home) == home_density assert model._get_infection_frequency_multiplier(s_child_work, i_child_work) == work_freq assert model._get_infection_frequency_multiplier(s_child_home, i_child_home) == home_freq assert model._get_infection_frequency_multiplier(s_adult_work, i_adult_work) == work_freq assert model._get_infection_frequency_multiplier(s_adult_home, i_adult_home) == home_freq
def test_strat_get_infection_multiplier__with_age_strat_and_no_mixing(): """ Check FoI when a simple 2-strata stratification applied and no mixing matrix. Expect the same results as with the basic case. """ # Create a model model = CompartmentalModel(times=[0, 5], compartments=["S", "I", "R"], infectious_compartments=["I"]) model.set_initial_population(distribution={"S": 990, "I": 10}) strat = Stratification("age", ["child", "adult"], ["S", "I", "R"]) model.stratify_with(strat) assert model._mixing_categories == [{}] # Do pre-run force of infection calcs. model._prepare_to_run() assert_array_equal(model._compartment_infectiousness["default"], np.array([0.0, 0.0, 1.0, 1.0, 0.0, 0.0])) assert_array_equal(model._category_matrix, np.array([[1.0, 1.0, 1.0, 1.0, 1.0, 1.0]])) assert model._category_lookup == {0: 0, 1: 0, 2: 0, 3: 0, 4: 0, 5: 0} # Do pre-iteration force of infection calcs model._prepare_time_step(0, model.initial_population) assert_array_equal(model._category_populations, np.array([1000])) assert_array_equal(model._infection_density["default"], np.array([10.0])) assert_array_equal(model._infection_frequency["default"], np.array([0.01])) # Get multipliers s_child = model.compartments[0] s_adult = model.compartments[1] i_child = model.compartments[2] i_adult = model.compartments[3] assert model._get_infection_density_multiplier(s_child, i_child) == 10.0 assert model._get_infection_density_multiplier(s_adult, i_adult) == 10.0 assert model._get_infection_frequency_multiplier(s_child, i_child) == 0.01 assert model._get_infection_frequency_multiplier(s_adult, i_adult) == 0.01 # Santiy check frequency-dependent force of infection assert 1000.0 * 0.01 == 10
def test_apply_replace_death_birth_flow(): model = CompartmentalModel(times=[0, 5], compartments=["S", "I"], infectious_compartments=["I"]) model.set_initial_population(distribution={"S": 900, "I": 100}) model.add_death_flow("infection_death", 0.1, "I") model.add_universal_death_flows("universal_death", 0.05) model.add_replacement_birth_flow("births", "S") model._prepare_to_run() actual_flow_rates = model._get_compartment_rates(model.initial_population, 0) # Expect 10 people to die and 10 to be born exp_i_flow_rate = -0.1 * 100 - 0.05 * 100 exp_s_flow_rate = -exp_i_flow_rate # N.B births + deaths in the S compartment should balance. expected_flow_rates = np.array([exp_s_flow_rate, exp_i_flow_rate]) assert_array_equal(actual_flow_rates, expected_flow_rates)
def test_cumulative_derived_outputs(): model = CompartmentalModel( times=[0, 5], compartments=["S", "I", "R"], infectious_compartments=["I"] ) model.set_initial_population(distribution={"S": 990, "I": 10}) model.run() model.outputs = np.array( [ [990, 10, 0], [980, 15, 5], [970, 20, 10], [960, 25, 15], [950, 30, 20], [940, 35, 25], ] ) model.request_output_for_compartments("recovered", ["R"]) model.request_cumulative_output(name="recoved_cumulative", source="recovered") model.request_cumulative_output(name="recoved_cumulative_2", source="recovered", start_time=2) dos = model._calculate_derived_outputs() assert_array_equal(dos["recoved_cumulative"], np.array([0, 5, 15, 30, 50, 75])) assert_array_equal(dos["recoved_cumulative_2"], np.array([0, 0, 10, 25, 45, 70]))
def test_strat_get_infection_multiplier__with_age_strat_and_simple_mixing(): """ Check FoI when a simple 2-strata stratification applied AND heteregeneous mixing. Expect same frequency as before, different density. Note that the mixing matrix has different meanings for density / vs frequency. N.B Mixing matrix format. Columns are the people who are infectors Rows are the people who are infected So matrix has following values child adult child child -> child adult -> child adult child -> adult adult -> adult """ model = CompartmentalModel(times=[0, 5], compartments=["S", "I", "R"], infectious_compartments=["I"]) model.set_initial_population(distribution={"S": 990, "I": 10}) strat = Stratification("age", ["child", "adult"], ["S", "I", "R"]) mixing_matrix = np.array([[0.5, 0.5], [0.5, 0.5]]) strat.set_mixing_matrix(mixing_matrix) model.stratify_with(strat) assert model._mixing_categories == [{"age": "child"}, {"age": "adult"}] assert model.compartments == [ C("S", {"age": "child"}), C("S", {"age": "adult"}), C("I", {"age": "child"}), C("I", {"age": "adult"}), C("R", {"age": "child"}), C("R", {"age": "adult"}), ] assert_array_equal(model.initial_population, np.array([495, 495, 5, 5, 0.0, 0.0])) # Do pre-run force of infection calcs. model._prepare_to_run() assert_array_equal(model._compartment_infectiousness["default"], np.array([0.0, 0.0, 1.0, 1.0, 0.0, 0.0])) assert_array_equal( model._category_matrix, np.array([[1.0, 0.0, 1.0, 0.0, 1.0, 0.0], [0.0, 1.0, 0.0, 1.0, 0.0, 1.0]]), ) assert model._category_lookup == {0: 0, 1: 1, 2: 0, 3: 1, 4: 0, 5: 1} # Do pre-iteration force of infection calcs model._prepare_time_step(0, model.initial_population) child_density = 5 adult_density = 5 assert child_density == 0.5 * 5 + 0.5 * 5 assert adult_density == 0.5 * 5 + 0.5 * 5 assert_array_equal(model._infection_density["default"], np.array([child_density, adult_density])) child_freq = 0.01 adult_freq = 0.01 assert child_freq == child_density / 500 assert adult_freq == adult_density / 500 assert_array_equal(model._infection_frequency["default"], np.array([child_freq, adult_freq])) # Get multipliers s_child = model.compartments[0] s_adult = model.compartments[1] i_child = model.compartments[2] i_adult = model.compartments[3] assert model._get_infection_density_multiplier(s_child, i_child) == child_density assert model._get_infection_density_multiplier(s_adult, i_adult) == adult_density assert model._get_infection_frequency_multiplier(s_child, i_child) == child_freq assert model._get_infection_frequency_multiplier(s_adult, i_adult) == adult_freq # Santiy check frequency-dependent force of infection assert 500.0 * child_freq + 500.0 * adult_freq == 10
def test_strat_get_infection_multiplier__with_double_strat_and_both_strats_mixing( ): """ Check FoI when a two 2-strata stratification applied and both stratifications have a mixing matrix. """ # Create the model model = CompartmentalModel(times=[0, 5], compartments=["S", "I", "R"], infectious_compartments=["I"]) model.set_initial_population(distribution={"S": 990, "I": 10}) strat = Stratification("age", ["child", "adult"], ["S", "I", "R"]) strat.set_population_split({"child": 0.3, "adult": 0.7}) am = np.array([[2, 3], [5, 7]]) strat.set_mixing_matrix(am) model.stratify_with(strat) strat = Stratification("location", ["work", "home"], ["S", "I", "R"]) lm = np.array([[11, 13], [17, 19]]) strat.set_mixing_matrix(lm) model.stratify_with(strat) expected_mixing = np.array([ [2 * 11, 2 * 13, 3 * 11, 3 * 13], [2 * 17, 2 * 19, 3 * 17, 3 * 19], [5 * 11, 5 * 13, 7 * 11, 7 * 13], [5 * 17, 5 * 19, 7 * 17, 7 * 19], ]) assert_array_equal(model._get_mixing_matrix(0), expected_mixing) assert model._mixing_categories == [ { "age": "child", "location": "work" }, { "age": "child", "location": "home" }, { "age": "adult", "location": "work" }, { "age": "adult", "location": "home" }, ] assert model.compartments == [ C("S", { "age": "child", "location": "work" }), C("S", { "age": "child", "location": "home" }), C("S", { "age": "adult", "location": "work" }), C("S", { "age": "adult", "location": "home" }), C("I", { "age": "child", "location": "work" }), C("I", { "age": "child", "location": "home" }), C("I", { "age": "adult", "location": "work" }), C("I", { "age": "adult", "location": "home" }), C("R", { "age": "child", "location": "work" }), C("R", { "age": "child", "location": "home" }), C("R", { "age": "adult", "location": "work" }), C("R", { "age": "adult", "location": "home" }), ] expected_comp_vals = np.array( [148.5, 148.5, 346.5, 346.5, 1.5, 1.5, 3.5, 3.5, 0, 0, 0, 0]) assert_array_equal(model.initial_population, expected_comp_vals) # Do pre-run force of infection calcs. model._prepare_to_run() exp_lookup = { 0: 0, # S age: child location: work -> children at work 1: 1, # S age: child location: home -> children at home 2: 2, # S age: adult location: work -> adults at work 3: 3, # S age: adult location: home -> adults at home 4: 0, # I age: child location: work -> children at work 5: 1, # I age: child location: home -> children at home 6: 2, # I age: adult location: work -> adults at work 7: 3, # I age: adult location: home -> adults at home 8: 0, # R age: child location: work -> children at work 9: 1, # R age: child location: home -> children at home 10: 2, # R age: adult location: work -> adults at work 11: 3, # R age: adult location: home -> adults at home } exp_matrix = np.array([ [1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0], # children at work [0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0], # children at home [0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0], # adults at work [0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1], # adults at home ]) exp_mults = np.array([0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0]) assert_array_equal(model._compartment_infectiousness["default"], exp_mults) assert_array_equal(model._category_matrix, exp_matrix) assert model._category_lookup == exp_lookup # Do pre-iteration force of infection calcs model._prepare_time_step(0, model.initial_population) exp_pops = np.array([ 150, # children at work 150, # children at home 350, # adults at work 350, # adults at home ]) assert_array_equal(model._category_populations, exp_pops) child_work_density = 2 * 11 * 1.5 + 2 * 13 * 1.5 + 3 * 11 * 3.5 + 3 * 13 * 3.5 child_home_density = 2 * 17 * 1.5 + 2 * 19 * 1.5 + 3 * 17 * 3.5 + 3 * 19 * 3.5 adult_work_density = 5 * 11 * 1.5 + 5 * 13 * 1.5 + 7 * 11 * 3.5 + 7 * 13 * 3.5 adult_home_density = 5 * 17 * 1.5 + 5 * 19 * 1.5 + 7 * 17 * 3.5 + 7 * 19 * 3.5 child_work_freq = (2 * 11 * 1.5 / 150 + 2 * 13 * 1.5 / 150 + 3 * 11 * 3.5 / 350 + 3 * 13 * 3.5 / 350) child_home_freq = (2 * 17 * 1.5 / 150 + 2 * 19 * 1.5 / 150 + 3 * 17 * 3.5 / 350 + 3 * 19 * 3.5 / 350) adult_work_freq = (5 * 11 * 1.5 / 150 + 5 * 13 * 1.5 / 150 + 7 * 11 * 3.5 / 350 + 7 * 13 * 3.5 / 350) adult_home_freq = (5 * 17 * 1.5 / 150 + 5 * 19 * 1.5 / 150 + 7 * 17 * 3.5 / 350 + 7 * 19 * 3.5 / 350) exp_density = np.array([ child_work_density, # children at work child_home_density, # children at home adult_work_density, # adults at work adult_home_density, # adults at home ]) exp_frequency = np.array([ child_work_freq, # children at work child_home_freq, # children at home adult_work_freq, # adults at work adult_home_freq, # adults at home ]) assert_array_equal(model._infection_density["default"], exp_density) assert_allclose(model._infection_frequency["default"], exp_frequency, rtol=0, atol=1e-9) # Get multipliers s_child_work = model.compartments[0] s_child_home = model.compartments[1] s_adult_work = model.compartments[2] s_adult_home = model.compartments[3] i_child_work = model.compartments[4] i_child_home = model.compartments[5] i_adult_work = model.compartments[6] i_adult_home = model.compartments[7] assert (abs( model._get_infection_density_multiplier(s_child_work, i_child_work) - child_work_density) <= 1e-9) assert (abs( model._get_infection_density_multiplier(s_child_home, i_child_home) - child_home_density) <= 1e-9) assert (abs( model._get_infection_density_multiplier(s_adult_work, i_adult_work) - adult_work_density) <= 1e-9) assert (abs( model._get_infection_density_multiplier(s_adult_home, i_adult_home) - adult_home_density) <= 1e-9) assert (abs( model._get_infection_frequency_multiplier(s_child_work, i_child_work) - child_work_freq) <= 1e-9) assert (abs( model._get_infection_frequency_multiplier(s_child_home, i_child_home) - child_home_freq) <= 1e-9) assert (abs( model._get_infection_frequency_multiplier(s_adult_work, i_adult_work) - adult_work_freq) <= 1e-9) assert (abs( model._get_infection_frequency_multiplier(s_adult_home, i_adult_home) - adult_home_freq) <= 1e-9)
def test_apply_flows__with_function_flow__expect_flows_applied( inf_pop, sus_pop, exp_flow): """ Expect a flow to occur proportional to the result of `get_flow_rate`. """ def get_flow_rate(flow, comps, comp_vals, flows, flow_rates, time): _, i_pop, _ = comp_vals i_flow, _ = flow_rates return i_pop + i_flow model = CompartmentalModel(times=[0, 5], compartments=["S", "I", "R"], infectious_compartments=["I"]) model.set_initial_population(distribution={"S": sus_pop, "I": inf_pop}) model.add_transition_flow("infection", 0.1, "S", "I") model.add_function_flow("treatment", get_flow_rate, "I", "S") model._prepare_to_run() actual_flow_rates = model._get_compartment_rates(model.initial_population, 0) expected_infected = sus_pop * 0.1 expected_flow_rates = np.array([ exp_flow - expected_infected, expected_infected - exp_flow, 0, ]) assert_array_equal(actual_flow_rates, expected_flow_rates)
def create_simple_model(): model = CompartmentalModel(times=[0, 5], compartments=["S", "I"], infectious_compartments=["I"]) model.set_initial_population(distribution={"S": 990, "I": 10}) return model
def test_flow_derived_outputs(): model = CompartmentalModel( times=[0, 5], compartments=["S", "I", "R"], infectious_compartments=["I"] ) model.set_initial_population(distribution={"S": 900, "I": 100}) # Constant entry. model.add_importation_flow("imports", num_imported=2, dest="S") model.request_output_for_flow(name="importation", flow_name="imports") model.request_output_for_flow(name="importation_raw", flow_name="imports", raw_results=True) # Linear entry. model.add_importation_flow("imports_land", num_imported=lambda t: 3 * t, dest="S") model.request_output_for_flow(name="importation_land", flow_name="imports_land") # Quadratic entry. model.add_importation_flow("imports_air", num_imported=lambda t: t ** 2, dest="S") model.request_output_for_flow(name="importation_air", flow_name="imports_air") # Fractional transition flow model.add_transition_flow("recovery", 0.1, "I", "R") model.request_output_for_flow(name="recovery_raw", flow_name="recovery", raw_results=True) model.request_output_for_flow(name="recovery_delta", flow_name="recovery", raw_results=False) model.run() dos = model.derived_outputs # Raw outputs are the instantaneous flow rate at a given time. assert_allclose(dos["recovery_raw"], model.outputs[:, 1] * 0.1, rtol=0.01) # Post-processed outputs reflect changes in compartment size. recovered_count = np.zeros_like(model.outputs[:, 2]) recovered_count[1:] = np.diff(model.outputs[:, 2]) assert_allclose(dos["recovery_delta"][1:], recovered_count[1:], rtol=0.01) # Good match for constant assert_array_equal(dos["importation"][1:], np.array([2, 2, 2, 2, 2])) assert_array_equal(dos["importation_raw"], np.array([2, 2, 2, 2, 2, 2])) # Good match for linear assert_allclose(dos["importation_land"][1:], np.array([1.5, 4.5, 7.5, 10.5, 13.5])) # So-so match for quadratic assert_allclose(dos["importation_air"][1:], np.array([0.5, 2.5, 6.5, 12.5, 20.5]), rtol=0.1)
def test_strat_get_infection_multiplier__with_age_strat_and_mixing(): """ Check FoI when a simple 2-strata stratification applied AND heteregeneous mixing. Use a non-uniform mixing matrix """ # Create a model model = CompartmentalModel(times=[0, 5], compartments=["S", "I", "R"], infectious_compartments=["I"]) model.set_initial_population(distribution={"S": 990, "I": 10}) strat = Stratification("age", ["child", "adult"], ["S", "I", "R"]) mixing_matrix = np.array([[2, 3], [5, 7]]) strat.set_mixing_matrix(mixing_matrix) strat.set_population_split({"child": 0.2, "adult": 0.8}) model.stratify_with(strat) assert model._mixing_categories == [{"age": "child"}, {"age": "adult"}] assert model.compartments == [ C("S", {"age": "child"}), C("S", {"age": "adult"}), C("I", {"age": "child"}), C("I", {"age": "adult"}), C("R", {"age": "child"}), C("R", {"age": "adult"}), ] assert_array_equal(model.initial_population, np.array([198.0, 792.0, 2.0, 8.0, 0.0, 0.0])) # Do pre-run force of infection calcs. model._prepare_to_run() assert_array_equal(model._compartment_infectiousness["default"], np.array([0.0, 0.0, 1.0, 1.0, 0.0, 0.0])) assert_array_equal( model._category_matrix, np.array([[1.0, 0.0, 1.0, 0.0, 1.0, 0.0], [0.0, 1.0, 0.0, 1.0, 0.0, 1.0]]), ) assert model._category_lookup == {0: 0, 1: 1, 2: 0, 3: 1, 4: 0, 5: 1} # Do pre-iteration force of infection calcs model._prepare_time_step(0, model.initial_population) assert_array_equal(model._category_populations, np.array([200.0, 800.0])) child_density = 28 adult_density = 66 assert child_density == 2 * 2.0 + 3 * 8.0 assert adult_density == 5 * 2.0 + 7 * 8.0 assert_array_equal(model._infection_density["default"], np.array([child_density, adult_density])) child_freq = 0.05 adult_freq = 0.12000000000000001 assert child_freq == 2 * 2.0 / 200 + 3 * 8.0 / 800 assert adult_freq == 5 * 2.0 / 200 + 7 * 8.0 / 800 assert_array_equal(model._infection_frequency["default"], np.array([child_freq, adult_freq])) # Get multipliers s_child = model.compartments[0] s_adult = model.compartments[1] i_child = model.compartments[2] i_adult = model.compartments[3] assert model._get_infection_density_multiplier(s_child, i_child) == child_density assert model._get_infection_density_multiplier(s_adult, i_adult) == adult_density assert model._get_infection_frequency_multiplier(s_child, i_child) == child_freq assert model._get_infection_frequency_multiplier(s_adult, i_adult) == adult_freq