diff --git a/caimira/models.py b/caimira/models.py index f8ea29e1..6df9740d 100644 --- a/caimira/models.py +++ b/caimira/models.py @@ -1700,41 +1700,11 @@ class ExposureModel: else: return 0 - def dynamic_total_probability_rule(self) -> _VectorisedFloat: - if (self.geographical_data.geographic_population != 0 and self.geographical_data.geographic_cases != 0): - total_probability_rule_list = [] - population_change_times = self.population_state_change_times() - for start, stop in zip(population_change_times[:-1], population_change_times[1:]): - sum_probability = 0.0 - exposed_present = self.exposed.people_present(stop) - infected_present = self.concentration_model.infected.people_present(stop) - - # Create an equivalent exposure model but changing the number of infected cases. - total_people = exposed_present + infected_present - max_num_infected = (total_people if total_people < 10 else 10) - # The influence of a higher number of simultainious infected people (> 4 - 5) yields an almost negligible contirbution to the total probability. - # To be on the safe side, a hard coded limit with a safety margin of 2x was set. - # Therefore we decided a hard limit of 10 infected people. - for num_infected in range(1, max_num_infected + 1): - exposure_model = nested_replace( - self, {'concentration_model.infected.number': - IntPiecewiseConstant((start, stop), (num_infected,)), - } - ) - prob_ind = exposure_model.infection_probability().mean() / 100 - - n = total_people - num_infected - # By means of the total probability rule - prob_at_least_one_infected = 1 - (1 - prob_ind)**n - sum_probability += (prob_at_least_one_infected * - self.geographical_data.probability_meet_infected_person(self.concentration_model.infected.virus, num_infected, total_people)) - total_probability_rule_list.append(sum_probability) - return (1 - np.prod([(1 - prob) for prob in total_probability_rule_list], axis = 0)) * 100 - else: - return 0 - - def expected_new_cases(self) -> _VectorisedFloat: + if (isinstance(self.concentration_model.infected.number, IntPiecewiseConstant) or + isinstance(self.exposed.number, IntPiecewiseConstant)): + raise NotImplementedError("Cannot compute expected new cases " + "with dynamic occupancy") # Create an equivalent exposure model without short-range interactions, if any. if (len(self.short_range) == 0): exposure_model = nested_replace(self, {'short_range': ()}) @@ -1750,13 +1720,19 @@ class ExposureModel: cases directly generated by one infected case in a population. """ + if (isinstance(self.concentration_model.infected.number, IntPiecewiseConstant) or + isinstance(self.exposed.number, IntPiecewiseConstant)): + raise NotImplementedError("Cannot compute reproduction number " + "with dynamic occupancy") + if self.concentration_model.infected.number == 1: return self.expected_new_cases() # Create an equivalent exposure model but with precisely # one infected case. single_exposure_model = nested_replace( - self, {'concentration_model.infected.number': 1} + self, { + 'concentration_model.infected.number': 1} ) return single_exposure_model.expected_new_cases() diff --git a/caimira/tests/models/test_dynamic_population.py b/caimira/tests/models/test_dynamic_population.py index 3a03bebb..abc8272c 100644 --- a/caimira/tests/models/test_dynamic_population.py +++ b/caimira/tests/models/test_dynamic_population.py @@ -32,11 +32,7 @@ def full_exposure_model(): activity=models.Activity.types['Seated'], host_immunity=0. ), - geographical_data=models.Cases( - geographic_population=50_000, - geographic_cases=52, - ascertainment_bias=1, - ), + geographical_data=(), ) @@ -213,19 +209,35 @@ def test_infection_probability( npt.assert_almost_equal(base_infection_probability, dynamic_population_exposure_model.infection_probability()) -# def test_dynamic_total_probability_rule( -# full_exposure_model: models.ExposureModel, -# dynamic_infected_single_exposure_model: models.ExposureModel, -# dynamic_exposed_single_exposure_model: models.ExposureModel, -# dynamic_population_exposure_model: models.ExposureModel): +def test_dynamic_total_probability_rule( + dynamic_infected_single_exposure_model: models.ExposureModel, + dynamic_exposed_single_exposure_model: models.ExposureModel, + dynamic_population_exposure_model: models.ExposureModel): -# full_model_total_prob_rule = full_exposure_model.total_probability_rule() -# npt.assert_almost_equal(full_model_total_prob_rule, -# dynamic_population_exposure_model.dynamic_total_probability_rule()) + with pytest.raises(NotImplementedError, match=re.escape("Cannot compute total probability " + "(including incidence rate) with dynamic occupancy")): + dynamic_infected_single_exposure_model.total_probability_rule() + dynamic_exposed_single_exposure_model.total_probability_rule() + dynamic_population_exposure_model.total_probability_rule() -# npt.assert_almost_equal(full_model_total_prob_rule, -# dynamic_infected_single_exposure_model.dynamic_total_probability_rule()) +def test_dynamic_expected_new_cases( + dynamic_infected_single_exposure_model: models.ExposureModel, + dynamic_exposed_single_exposure_model: models.ExposureModel, + dynamic_population_exposure_model: models.ExposureModel): -# npt.assert_almost_equal(full_model_total_prob_rule, -# dynamic_exposed_single_exposure_model.dynamic_total_probability_rule()) - \ No newline at end of file + with pytest.raises(NotImplementedError, match=re.escape("Cannot compute expected new cases " + "with dynamic occupancy")): + dynamic_infected_single_exposure_model.expected_new_cases() + dynamic_exposed_single_exposure_model.expected_new_cases() + dynamic_population_exposure_model.expected_new_cases() + +def test_dynamic_reproduction_number( + dynamic_infected_single_exposure_model: models.ExposureModel, + dynamic_exposed_single_exposure_model: models.ExposureModel, + dynamic_population_exposure_model: models.ExposureModel): + + with pytest.raises(NotImplementedError, match=re.escape("Cannot compute reproduction number " + "with dynamic occupancy")): + dynamic_infected_single_exposure_model.reproduction_number() + dynamic_exposed_single_exposure_model.reproduction_number() + dynamic_population_exposure_model.reproduction_number()