Ejemplo n.º 1
0
    def apply(self, sim, t):
        ''' Perform testing '''

        if self.n_tests[t]:
            # Compute weights for people who would test positive or negative
            positive_tests = np.zeros((sim.n, ))
            for i, person in enumerate(sim.people.values()):
                if person.infectious:
                    positive_tests[i] = 1
            negative_tests = 1 - positive_tests

            # Select the people to test in each category
            positive_inds = cv.choose_weighted(probs=positive_tests,
                                               n=min(sum(positive_tests),
                                                     self.n_positive[t]),
                                               normalize=True)
            negative_inds = cv.choose_weighted(
                probs=negative_tests,
                n=min(sum(negative_tests),
                      self.n_tests[t] - len(positive_inds)),
                normalize=True)

            # Todo - assess performance and optimize e.g. to reduce dict indexing
            for ind in positive_inds:
                person = sim.get_person(ind)
                person.test(
                    t, test_sensitivity=1.0
                )  # Sensitivity is 1 because the person is guaranteed to test positive
                self.results['n_tested'][t] += 1
                self.results['n_diagnosed'][t] += 1

            for ind in negative_inds:
                person = sim.get_person(ind)
                person.test(t, test_sensitivity=1.0)
                self.results['n_tested'][t] += 1
Ejemplo n.º 2
0
def test_choose():
    sc.heading('Choose people')
    x1 = cova.choose(10, 5)
    with pytest.raises(Exception):
        cova.choose_weighted(10, 5) # Requesting mroe people than are available
    print(f'Uniform sample from 0-9: {x1}')
    return x1
Ejemplo n.º 3
0
    def apply(self, sim):

        t = sim.t

        # Process daily tests -- has to be here rather than init so have access to the sim object
        if isinstance(self.daily_tests, (pd.Series, pd.DataFrame)):
            start_date = sim['start_day']
            end_date = self.daily_tests.index[-1]
            dateindex = pd.date_range(start_date, end_date)
            self.daily_tests = self.daily_tests.reindex(dateindex, fill_value=0).to_numpy()

        # Check that there are still tests
        if t < len(self.daily_tests):
            n_tests = self.daily_tests[t]  # Number of tests for this day
            if not (n_tests and pl.isfinite(n_tests)): # If there are no tests today, abort early
                return
            else:
                sim.results['new_tests'][t] += n_tests
        else:
            return

        test_probs = np.ones(sim.n)
        new_diagnoses = 0

        for i,person in enumerate(sim.people):

            new_diagnoses += person.check_diagnosed(t)

            # Adjust testing probability based on what's happened to the person
            # NB, these need to be separate if statements, because a person can be both diagnosed and infectious/symptomatic
            if person.symptomatic:
                test_probs[i] *= self.sympt_test  # They're symptomatic
            if person.quarantine:
                test_probs[i] *= self.quar_test  # They're in quarantine
            if person.diagnosed:
                test_probs[i] = 0.0

        test_inds = cv.choose_weighted(probs=test_probs, n=n_tests, normalize=True, unique=False)
        sim.results['new_diagnoses'][t] += new_diagnoses

        for test_ind in test_inds:
            person = sim.people[test_ind]
            person.test(t, self.sensitivity, test_delay=self.test_delay)

        return
Ejemplo n.º 4
0
    def apply(self, sim):

        t = sim.t

        # Check that there are still tests
        if t < len(self.daily_tests):
            n_tests = self.daily_tests[t]  # Number of tests for this day
            sim.results['new_tests'][t] += n_tests
        else:
            return

        # If there are no tests today, abort early
        if not (n_tests and pl.isfinite(n_tests)):
            return

        test_probs = np.ones(sim.n)
        new_diagnoses = 0

        for i, person in enumerate(sim.people):

            new_diagnoses += person.check_diagnosed(t)

            # Adjust testing probability based on what's happened to the person
            # NB, these need to be separate if statements, because a person can be both diagnosed and infectious/symptomatic
            if person.symptomatic:
                test_probs[i] *= self.sympt_test  # They're symptomatic
            if person.known_contact:
                test_probs[
                    i] *= self.trace_test  # They've had contact with a known positive
            if person.diagnosed:
                test_probs[i] = 0.0

        test_inds = cv.choose_weighted(probs=test_probs,
                                       n=n_tests,
                                       normalize=True)
        sim.results['new_diagnoses'][t] += new_diagnoses

        for test_ind in test_inds:
            person = sim.people[test_ind]
            person.test(t, self.sensitivity, test_delay=self.test_delay)

        return
Ejemplo n.º 5
0
def test_choose_weighted():
    sc.heading('Choose weighted people')
    n = 100
    samples = 5
    lin = np.arange(n)
    lin = lin/lin.sum()
    x0 = cova.choose_weighted([0.01]*n, samples)
    x1 = cova.choose_weighted(lin, samples)
    x2 = cova.choose_weighted([1, 0, 0, 0, 0], 1)
    x3 = cova.choose_weighted([0.5, 0.5, 0, 0, 0], 1)
    assert x2[0] == 0
    assert x3[0] in [0,1]
    assert len(x0) == len(x1) == samples
    with pytest.raises(Exception):
        cova.choose_weighted([0.5, 0, 0, 0, 0], 1) # Probabilities don't sum to 1
    with pytest.raises(Exception):
        cova.choose_weighted([0.5, 0.5], 10) # Requesting mroe people than are available
    print(f'Uniform sample 0-99: x0 = {x0}, mean {x0.mean()}')
    print(f'Weighted sample 0-99: x1 = {x1}, mean {x1.mean()}')
    print(f'All weight on 0: x2 = {x2}')
    print(f'All weight on 0 or 1: x3 = {x3}')
    return x1
Ejemplo n.º 6
0
    def run(self,
            seed_infections=1,
            verbose=None,
            calc_likelihood=False,
            do_plot=False,
            **kwargs):
        ''' Run the simulation '''

        T = sc.tic()

        # Reset settings and results
        if verbose is None:
            verbose = self['verbose']
        self.init_results()
        self.init_people(
            seed_infections=seed_infections)  # Actually create the people
        daily_tests = self.data[
            'new_tests']  # Number of tests each day, from the data
        evacuated = self.data['evacuated']  # Number of people evacuated

        # Main simulation loop
        for t in range(self.npts):

            # Print progress
            if verbose >= 1:
                string = f'  Running day {t:0.0f} of {self["n_days"]}...'
                if verbose >= 2:
                    sc.heading(string)
                else:
                    print(string)

            test_probs = {
            }  # Store the probability of each person getting tested

            # Update each person
            for person in self.people.values():

                # Count susceptibles
                if person.susceptible:
                    self.results['n_susceptible'][t] += 1
                    continue  # Don't bother with the rest of the loop

                # Handle testing probability
                if person.infectious:
                    test_probs[person.uid] = self[
                        'symptomatic']  # They're infectious: high probability of testing
                else:
                    test_probs[person.uid] = 1.0

                # If exposed, check if the person becomes infectious
                if person.exposed:
                    self.results['n_exposed'][t] += 1
                    if not person.infectious and t >= person.date_infectious:  # It's the day they become infectious
                        person.infectious = True
                        if verbose >= 2:
                            print(
                                f'      Person {person.uid} became infectious!'
                            )

                # If infectious, check if anyone gets infected
                if person.infectious:
                    # First, check for recovery
                    if person.date_recovered and t >= person.date_recovered:  # It's the day they become infectious
                        person.exposed = False
                        person.infectious = False
                        person.recovered = True
                        self.results['recoveries'][t] += 1
                    else:
                        self.results['n_infectious'][
                            t] += 1  # Count this person as infectious
                        n_contacts = cv.pt(
                            person.contacts
                        )  # Draw the number of Poisson contacts for this person
                        contact_inds = cv.choose(
                            max_n=len(self.people),
                            n=n_contacts)  # Choose people at random
                        for contact_ind in contact_inds:
                            exposure = cv.bt(self['r_contact']
                                             )  # Check for exposure per person
                            if exposure:
                                target_person = self.people[contact_ind]
                                if target_person.susceptible:  # Skip people who are not susceptible
                                    self.results['infections'][t] += 1
                                    target_person.susceptible = False
                                    target_person.exposed = True
                                    target_person.date_exposed = t
                                    incub_pars = dict(dist='normal_int',
                                                      par1=self['incub'],
                                                      par2=self['incub_std'])
                                    dur_pars = dict(dist='normal_int',
                                                    par1=self['dur'],
                                                    par2=self['dur_std'])
                                    incub_dist = cv.sample(**incub_pars)
                                    dur_dist = cv.sample(**dur_pars)

                                    target_person.date_infectious = t + incub_dist
                                    target_person.date_recovered = target_person.date_infectious + dur_dist
                                    if verbose >= 2:
                                        print(
                                            f'        Person {person.uid} infected person {target_person.uid}!'
                                        )

                # Count people who recovered
                if person.recovered:
                    self.results['n_recovered'][t] += 1

            # Implement testing -- this is outside of the loop over people, but inside the loop over time
            if t < len(
                    daily_tests
            ):  # Don't know how long the data is, ensure we don't go past the end
                n_tests = daily_tests.iloc[t]  # Number of tests for this day
                if n_tests and not pl.isnan(
                        n_tests):  # There are tests this day
                    self.results['tests'][
                        t] = n_tests  # Store the number of tests
                    test_probs = pl.array(list(test_probs.values()))
                    test_probs /= test_probs.sum()
                    test_inds = cv.choose_weighted(probs=test_probs, n=n_tests)
                    uids_to_pop = []
                    for test_ind in test_inds:
                        tested_person = self.people[test_ind]
                        if tested_person.infectious and cv.bt(
                                self['sensitivity']
                        ):  # Person was tested and is true-positive
                            self.results['diagnoses'][t] += 1
                            tested_person.diagnosed = True
                            if self['evac_positives']:
                                uids_to_pop.append(tested_person.uid)
                            if verbose >= 2:
                                print(
                                    f'          Person {person.uid} was diagnosed!'
                                )
                    for uid in uids_to_pop:  # Remove people from the ship once they're diagnosed
                        self.off_ship[uid] = self.people.pop(uid)

            # Implement quarantine
            if t == self['quarantine']:
                if verbose >= 1:
                    print(f'Implementing quarantine on day {t}...')
                for person in self.people.values():
                    if 'quarantine_eff' in self.pars.keys():
                        quarantine_eff = self['quarantine_eff']  # Both
                    else:
                        if person.crew:
                            quarantine_eff = self['quarantine_eff_c']  # Crew
                        else:
                            quarantine_eff = self['quarantine_eff_g']  # Guests
                    person.contacts *= quarantine_eff

            # Implement testing change
            if t == self['testing_change']:
                if verbose >= 1:
                    print(f'Implementing testing change on day {t}...')
                self['symptomatic'] *= self[
                    'testing_symptoms']  # Reduce the proportion of symptomatic testing

            # Implement evacuations
            if t < len(evacuated):
                n_evacuated = evacuated.iloc[
                    t]  # Number of evacuees for this day
                if n_evacuated and not pl.isnan(
                        n_evacuated
                ):  # There are evacuees this day # TODO -- refactor with n_tests
                    if verbose >= 1:
                        print(f'Implementing evacuation on day {t}')
                    evac_inds = cv.choose(max_n=len(self.people),
                                          n=n_evacuated)
                    uids_to_pop = []
                    for evac_ind in evac_inds:
                        evac_person = self.people[evac_ind]
                        if evac_person.infectious and cv.bt(
                                self['sensitivity']):
                            self.results['evac_diagnoses'][t] += 1
                        uids_to_pop.append(evac_person.uid)
                    for uid in uids_to_pop:  # Remove people from the ship once they're diagnosed
                        self.off_ship[uid] = self.people.pop(uid)

        # Compute cumulative results
        self.results['cum_exposed'] = pl.cumsum(self.results['infections'])
        self.results['cum_tested'] = pl.cumsum(self.results['tests'])
        self.results['cum_diagnosed'] = pl.cumsum(self.results['diagnoses'])

        # Compute likelihood
        if calc_likelihood:
            self.likelihood()

        # Tidy up
        self.results['ready'] = True
        elapsed = sc.toc(T, output=True)
        if verbose >= 1:
            print(f'\nRun finished after {elapsed:0.1f} s.\n')
            summary = self.summary_stats()
            print(f"""Summary:
     {summary['n_susceptible']:5.0f} susceptible
     {summary['n_exposed']:5.0f} exposed
     {summary['n_infectious']:5.0f} infectious
               """)

        if do_plot:
            self.plot(**kwargs)

        return self.results