-
Notifications
You must be signed in to change notification settings - Fork 12
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
Module exports #332
Module exports #332
Changes from all commits
9df8516
9e20b90
a33b323
5b1bfa9
45ff689
f1d41c2
aad6155
5ffdc7b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -23,6 +23,7 @@ class Parameters(sc.objdict): | |
def __init__(self, **kwargs): | ||
|
||
# Population parameters | ||
self.people = None | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I can't remember, do we also support |
||
self.n_agents = 10e3 # Number of agents | ||
self.total_pop = None # If defined, used for calculating the scale factor | ||
self.pop_scale = None # How much to scale the population | ||
|
@@ -44,14 +45,6 @@ def __init__(self, **kwargs): | |
self.slot_scale = 5 # Random slots will be assigned to newborn agents between min=n_agents and max=slot_scale*n_agents. Choosing a larger value here will reduce the probability of two agents using the same slot (and hence random draws), but increase the number of random numbers that are required. | ||
self.verbose = ss.options.verbose # Whether or not to display information during the run -- options are 0 (silent), 0.1 (some; default), 1 (default), 2 (everything) | ||
|
||
# Plug-ins: demographics, diseases, connectors, networks, analyzers, and interventions | ||
self.demographics = ss.ndict() | ||
self.diseases = ss.ndict() | ||
self.networks = ss.ndict() | ||
self.connectors = ss.ndict() | ||
self.interventions = ss.ndict() | ||
self.analyzers = ss.ndict() | ||
|
||
# Update with any supplied parameter values and generate things that need to be generated | ||
self.update(kwargs) | ||
|
||
|
@@ -60,18 +53,25 @@ def __init__(self, **kwargs): | |
|
||
return | ||
|
||
def update_pars(self, pars=None, create=False, **kwargs): | ||
def update_pars(self, pars=None, create=False, module_types=None, **kwargs): | ||
""" | ||
Update internal dict with new pars. | ||
Args: | ||
pars (dict): the parameters to update (if None, do nothing) | ||
create (bool): if create is False, then raise a KeyNotFoundError if the key does not already exist | ||
module_types (dict): types of parameters to convert to modules | ||
""" | ||
if pars is not None: | ||
if not isinstance(pars, dict): | ||
raise TypeError(f'The pars object must be a dict; you supplied a {type(pars)}') | ||
|
||
pars = sc.mergedicts(pars, kwargs) | ||
|
||
# Initialize modules here?? | ||
for mname, mtype in module_types.items(): | ||
if mname in pars.keys(): | ||
pars[mname] = self.init_module_pars(pars[mname], mtype) | ||
|
||
if not create: | ||
available_keys = list(self.keys()) | ||
mismatches = [key for key in pars.keys() if key not in available_keys] | ||
|
@@ -81,6 +81,68 @@ def update_pars(self, pars=None, create=False, **kwargs): | |
self.update(pars) | ||
return | ||
|
||
def init_module_pars(self, mpar, mtype): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. moved this from sim to parameters, it could also go in the modules themselves as a class method There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hm I kind of like that actually! (having it be in modules) |
||
""" Initialize modules """ | ||
known_modules = {n.__name__.lower():n for n in ss.all_subclasses(mtype)} | ||
processed_m = sc.autolist() | ||
|
||
# Process boolean inputs for demographics | ||
if isinstance(mpar, bool) and mtype==ss.BaseDemographics: | ||
if mpar: | ||
return ss.ndict([ss.Births(), ss.Deaths()], type=mtype) | ||
else: | ||
return ss.ndict(type=mtype) | ||
|
||
# String: convert to a dict | ||
if isinstance(mpar, str): | ||
mpar = {'type': mpar} | ||
|
||
# Dict: check the keys and convert to a class instance | ||
if isinstance(mpar, dict): | ||
|
||
# It might be an already-converted ndict | ||
if all([isinstance(v, mtype) for v in mpar.values()]): | ||
return mpar | ||
|
||
ptype = (mpar.get('type') or mpar.get('name') or '').lower() | ||
name = mpar.get('name') or ptype | ||
|
||
if ptype in known_modules: | ||
# Make an instance of the requested module | ||
module_pars = {k: v for k, v in mpar.items() if k not in ['type', 'name']} | ||
pclass = known_modules[ptype] | ||
module = pclass(name=name, pars=module_pars) # TODO: does this handle par_dists, etc? | ||
else: | ||
errormsg = (f'Could not convert {mpar} to an instance of class {mtype}.' | ||
f'Try specifying it directly rather than as a dictionary.') | ||
raise ValueError(errormsg) | ||
processed_m += module | ||
|
||
# Class instance | ||
elif isinstance(mpar, mtype): | ||
processed_m += mpar | ||
|
||
# Class | ||
elif isinstance(mpar, type) and issubclass(mpar, mtype): | ||
processed_m += mpar() # Convert from a class to an instance of a class | ||
|
||
# Function - only for interventions | ||
elif callable(mpar) and mtype==ss.Intervention: | ||
processed_m += mpar | ||
|
||
# It's a list - iterate | ||
elif isinstance(mpar, list): | ||
for mpar_val in mpar: | ||
processed_m += self.init_module_pars(mpar_val, mtype) | ||
|
||
else: | ||
errormsg = ( | ||
f'{mpar.name.capitalize()} must be provided as either class instances or dictionaries with a ' | ||
f'"name" key corresponding to one of these known subclasses: {known_modules}.') | ||
raise ValueError(errormsg) | ||
|
||
return ss.ndict(processed_m, type=mtype) | ||
|
||
|
||
def make_pars(**kwargs): | ||
return Parameters(**kwargs) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
adapting logic from HPVsim for exporting interventions, but perhaps there would be a nicer way?