Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rethink Sim API #488

Merged
merged 171 commits into from
May 14, 2024
Merged

Rethink Sim API #488

merged 171 commits into from
May 14, 2024

Conversation

cliffckerr
Copy link
Contributor

@cliffckerr cliffckerr commented May 4, 2024

Description

Summary

All inputs to the sim and modules now use a ss.Pars() class, which handles updating and validation. It is now not necessary to ever use pars= (although you still can if you want), so what was previously:
sim = ss.Sim(pars=dict(diseases='sir', networks='random'))
is now just:
sim = ss.Sim(diseases='sir', networks='random')

Updates happen recursively, so distributions etc. can be flexibly updated.

This has significantly changed how modules are initialized; what was previously

def __init__(self, pars=None, **kwargs):

    pars = ss.omergeleft(pars,
        dur_inf = 6,
        init_prev = 0.01,
        p_death = 0.01,
        beta = 0.5,
    )

    par_dists = ss.omergeleft(par_dists,
        dur_inf = ss.lognorm_ex,
        init_prev = ss.bernoulli,
        p_death = ss.bernoulli,
    )

    super().__init__(pars=pars, par_dists=par_dists, *args, **kwargs)

is now:

def __init__(self, pars=None, **kwargs):
    super().__init__()
    self.default_pars(
        beta = 0.5,
        init_prev = ss.bernoulli(0.01),
        dur_inf = ss.lognorm_ex(6),
        p_death = ss.bernoulli(0.01),
    )
    self.update_pars(pars, **kwargs)

Parameter changes

  • Added a ss.Pars class (and a ss.SimPars subclass) that handles parameter creation, updates, and validation.
  • Initialization has been moved from sim.py to parameters.py; ss.Sim.convert_plugins() has been replaced by ss.SimPars.convert_modules().
  • The key method is ss.Pars.update(), which performs all necessary validation on the parameters being updated.

Module changes

  • Whereas modules previously initialized a dict of parameters and then called super().__init__(pars, **kwargs), they now call super().__init__() first, then self.default_pars(par1=x, par2=y), then finally self.update_pars(pars, **kwargs).
  • What was previously e.g. ss.Module(pars=dict(par=x)) is now ss.Module(par=x).
  • par_dists has been removed; instead, distributions are specified in the default parameters, and are updated via the Pars object.
  • ss.module_map() maps different module types to their location in the sim.
  • ss.find_modules() finds all available modules (including subclasses) in Starsim.
  • Removed ss.dictmerge() and ss.dictmergeleft (now handled by ss.Pars.update()).
  • Removed ss.get_subclasses() and ss.all_subclasses() (now handled by ss.find_modules()).
  • Modules now contain a link back to the Sim object.
  • Added to_json() and plot() methods to Module.
  • Removed connectors.py; connectors still exist but as an empty subclass of Module.

People and network changes

  • Time parameters (ti, dt, etc.) have been removed from People. Use sim.ti, sim.dt etc. instead. One consequence of this is that people.request_death() now requires a sim argument. Another is that network methods (e.g. add_pairs()) now take sim arguments instead of people arguments.
  • SexualNetwork is now a subclass of DynamicNetwork.
  • Removed ss.Networks (now just an ss.ndict).
  • Network connectors have been removed.
  • Person has been implemented as a slice of sim.people[i].
  • The default maximum age if none is specified is 50 instead of 100.
  • Agents do not age if no demographics modules are supplied.

Other changes

  • All inputs to a sim are now copied by default. To disable, use ss.Sim(..., copy_inputs=False).
  • There is a new Plugin class, which contains shared logic for Interventions and Analyzers. It has a from_func(), which will generate an intervention/analyzer from a function.
  • Diseases no longer have a default value of beta=1 assigned; beta must be defined explicitly if being used.
  • Individual diseases can now be plotted via either e.g. sim.plot('hiv') or sim.diseases.hiv.plot().
  • Distributions can be created from dicts via ss.make_dist().
  • A new function ss.check_sims_match() will check if the results of two or more simulations match.
  • Merged test_dcp.py and test_base.py into test_other.py.
  • Renamed test_simple.py to test_sim.py.
  • Renamed test_dists.py to test_randomness.py.

Checklist

  • Code commented & docstrings added
  • New tests were needed and have been added
  • A new version number was needed & changelog has been updated
  • A new PyPI version needs to be released

Closes #301, #178, #412, #440

@cliffckerr cliffckerr mentioned this pull request May 4, 2024
@cliffckerr
Copy link
Contributor Author

@daniel-klein I made it so you can do ss.uids(BoolArr) instead of BoolArr.uids ... I would personally discourage this usage, since ss.uids() isn't valid on anything other than a BoolArr, so I think the property makes it clearer that tis related specifically to that array. In particular, the previous implementation of ss.true() would just give indices, which would match UIDs if and only if (a) the input array matched the length the raw data, or (b) the user then did a subsequent transformation. In practice one of these two things was usually true, but sometimes not, leading to subtle bugs! In contrast, with arr.uids, it's guaranteed to be correct.

@cliffckerr cliffckerr merged commit cb23f50 into main May 14, 2024
3 checks passed
@cliffckerr cliffckerr deleted the rethink-sim-api branch May 14, 2024 13:50
This was referenced May 20, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Rethink Starsim parameters and API
7 participants