Пример #1
0
def test_samples(doplot=False):
    sc.heading('Samples distribution')

    n = 10000
    nbins = 40

    # Warning, must match utils.py!
    choices = [
        'uniform',
        'normal',
        'lognormal',
        'normal_pos',
        'normal_int',
        'lognormal_int',
        'neg_binomial'
        ]

    if doplot:
        pl.figure(figsize=(20,14))

    # Run the samples
    nchoices = len(choices)
    nsqr = np.ceil(np.sqrt(nchoices))
    results = {}
    for c,choice in enumerate(choices):
        if choice == 'neg_binomial':
            par1 = 10
            par2 = 0.5
        elif choice in ['lognormal', 'lognormal_int']:
            par1 = 1
            par2 = 0.5
        else:
            par1 = 0
            par2 = 5
        results[choice] = cova.sample(dist=choice, par1=par1, par2=par2, size=n)

        if doplot:
            pl.subplot(nsqr, nsqr, c+1)
            pl.hist(x=results[choice], bins=nbins)
            pl.title(f'dist={choice}, par1={par1}, par2={par2}')

    with pytest.raises(NotImplementedError):
        cova.sample(dist='not_found')

    return results
Пример #2
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_people(
                            max_ind=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_people_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_people(max_ind=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
Пример #3
0
def test_samples(do_plot=False, verbose=True):
    sc.heading('Samples distribution')

    n = 200_000
    nbins = 100

    # Warning, must match utils.py!
    choices = [
        'uniform', 'normal', 'lognormal', 'normal_pos', 'normal_int',
        'lognormal_int', 'poisson', 'neg_binomial'
    ]

    if do_plot:
        pl.figure(figsize=(20, 14))

    # Run the samples
    nchoices = len(choices)
    nsqr, _ = sc.get_rows_cols(nchoices)
    results = sc.objdict()
    mean = 11
    std = 7
    low = 3
    high = 9
    normal_dists = [
        'normal', 'normal_pos', 'normal_int', 'lognormal', 'lognormal_int'
    ]
    for c, choice in enumerate(choices):
        kw = {}
        if choice in normal_dists:
            par1 = mean
            par2 = std
        elif choice == 'neg_binomial':
            par1 = mean
            par2 = 1.2
            kw['step'] = 0.1
        elif choice == 'poisson':
            par1 = mean
            par2 = 0
        elif choice == 'uniform':
            par1 = low
            par2 = high
        else:
            errormsg = f'Choice "{choice}" not implemented'
            raise NotImplementedError(errormsg)

        # Compute
        results[choice] = cv.sample(dist=choice,
                                    par1=par1,
                                    par2=par2,
                                    size=n,
                                    **kw)

        # Optionally plot
        if do_plot:
            pl.subplot(nsqr, nsqr, c + 1)
            plotbins = np.unique(results[choice]) if (
                choice == 'poisson' or '_int' in choice) else nbins
            pl.hist(x=results[choice], bins=plotbins, width=0.8)
            pl.title(f'dist={choice}, par1={par1}, par2={par2}')

    with pytest.raises(NotImplementedError):
        cv.sample(dist='not_found')

    # Do statistical tests
    tol = 1 / np.sqrt(
        n / 50 / len(choices)
    )  # Define acceptable tolerance -- broad to avoid false positives

    def isclose(choice, tol=tol, **kwargs):
        key = list(kwargs.keys())[0]
        ref = list(kwargs.values())[0]
        npfunc = getattr(np, key)
        value = npfunc(results[choice])
        msg = f'Test for {choice:14s}: expecting {key:4s} = {ref:8.4f} ± {tol*ref:8.4f} and got {value:8.4f}'
        if verbose:
            print(msg)
        assert np.isclose(value, ref, rtol=tol), msg
        return True

    # Normal
    for choice in normal_dists:
        isclose(choice, mean=mean)
        if all([k not in choice
                for k in ['_pos', '_int']]):  # These change the variance
            isclose(choice, std=std)

    # Negative binomial
    isclose('neg_binomial', mean=mean)

    # Poisson
    isclose('poisson', mean=mean)
    isclose('poisson', var=mean)

    # Uniform
    isclose('uniform', mean=(low + high) / 2)

    return results
Пример #4
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

        # 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 t == person.date_infectious:
                        # print(type(person.uid))
                        if int(person.uid) != 0:
                            # give 50% chance of quaratine?
                            q_per = .5
                            quar = np.random.choice([True, False],
                                                    p=[q_per, 1 - q_per])
                            #person.quarantine = np.copy(quar)
                            person.quarantine = False
                        else:
                            person.quarantine = False

                # If infectious, check if anyone gets infected
                if person.infectious:
                    # print(person)
                    # 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

                        cnum = 11
                        topull = np.random.poisson(cnum)

                        s1_cons = person.contacts['s1']
                        s2_cons = person.contacts['s2']
                        s3_cons = person.contacts['s3']

                        # Draw the numbergit  of Poisson contacts for this person
                        # this should instead initiate the random contacts?
                        rng = np.random.default_rng()

                        shuffled = np.array(range(len(self.people)))
                        rng.shuffle(shuffled)

                        c_contacts = np.array(
                            shuffled)[:topull]  # Choose people at random

                        contypes = np.concatenate(
                            (["s1" for _ in range(len(s2_cons))
                              ], ["s2" for _ in range(len(s1_cons))
                                  ], ["s3" for _ in range(len(s3_cons))],
                             ["c" for _ in range(topull)]))

                        #contact_inds = np.concatenate((s1_cons, s2_cons, s3_cons, c_contacts))
                        # uncomment above to add back community contacts
                        contact_inds = np.concatenate(
                            (s1_cons, s2_cons, s3_cons))

                        z = -1
                        for contact_ind in contact_inds:
                            if person.quarantine == False:
                                exposure = bt(
                                    self['r_contact']
                                )  # Check for exposure per person
                                z += 1
                                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.exposed_type = contypes[
                                            z]

                                        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