distributions
Define random-number-safe distributions.
Classes
| Name | Description |
|---|---|
| Dist | Base class for tracking one random number generator associated with one distribution, |
| Dists | Class for managing a collection of Dist objects |
| multi_random | A class for holding two or more ss.random() distributions, and generating |
| scale_types | Define how distributions scale |
Dist
distributions.Dist(
dist=None,
distname=None,
name=None,
unit=None,
seed=None,
offset=None,
strict=True,
auto=True,
sim=None,
module=None,
mock=False,
debug=False,
**kwargs,
)Base class for tracking one random number generator associated with one distribution, i.e. one decision per timestep.
See ss.dist_list for a full list of supported distributions. Parameter inputs tend to follow SciPy’s, rather than NumPy’s, definitions (although in most cases they’re the same). See also ss.distributions.scale_types for more information on how different distributions scale by time.
Note: by default, ss.Dist is initialized with an ss.Sim object to ensure random number reproducibility. You can override this with either ss.Dist(strict=False) on creation, or dist.init(force=True) after creation.
Although it’s possible in theory to define a custom distribution (i.e., not one from NumPy or SciPy), in practice this is difficult. The distribution needs to have both a way to return random variates (easy), as well as the probability point function (inverse CDF). In addition, the distribution must be able to take a NumPy RNG as its bit generator. It’s easier to just use a default Dist (e.g., ss.random()), and then take its output as input (i.e., quantiles) for whatever custom distribution you want to create.
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| dist | rv_generic |
optional; a scipy.stats distribution (frozen or not) to get the ppf from |
None |
| distname | str | the name for this class of distribution (e.g. “uniform”) | None |
| name | str | the name for this particular distribution (e.g. “age_at_death”) | None |
| unit | str/ss.TimePar |
if provided, convert the output of the distribution to a timepar (e.g. rate or duration); can also be inferred from distribution parameters (see examples below) | None |
| seed | int | the user-chosen random seed (e.g. 3) | None |
| offset | int | the seed offset; will be automatically assigned (based on hashing the name) if None | None |
| strict | bool | if True, require initialization and invalidate after each call to rvs() | True |
| auto | bool | whether to auto-reset the state after each draw | True |
| sim | Sim | usually determined on initialization; the sim to use as input to callable parameters | None |
| module | Module | usually determined on initialization; the module to use as input to callable parameters | None |
| mock | int | if provided, then initialize with a mock Sim object (of size mock) for debugging purposes |
False |
| debug | bool | print out additional detail | False |
| kwargs | dict | parameters of the distribution | {} |
Examples:
# Create a Bernoulli distribution
p_death = ss.bernoulli(p=0.1).init(force=True)
p_death.rvs(50) # Create 50 draws
# Create a normal distribution that's also a timepar
dur_infection = ss.normal(loc=12, scale=2, unit='years')
dur_infection = ss.years(ss.normal(loc=12, scale=2)) # Same as above
dur_infection = ss.normal(loc=ss.years(12), scale=2)) # Same as above
dur_infection = ss.normal(loc=ss.years(12), scale=ss.months(24)) # Same as above, perform time unit conversion internally
dur_infection.init(force=True).plot_hist() # Show results
# Create a distribution manually
dist = ss.Dist(dist=sps.norm, loc=3).init(force=True)
dist.rvs(10) # Return 10 normally distributed random numbers
Attributes
| Name | Description |
|---|---|
| state | Get the current state |
| state_int | Get the integer corresponding to the current state |
Methods
| Name | Description |
|---|---|
| call_par | Check if this parameter needs to be called to be turned into an array; not for the user |
| call_pars | Check if any parameters need to be called to be turned into arrays; not for the user |
| convert_callable | Method to handle how callable parameters are processed; not for the user |
| convert_timepars | Convert time parameters (durations and rates) to scalars |
| disp | Return full display of object |
| get_state | Return a copy of the state |
| init | Calculate the starting seed and create the RNG |
| jump | Advance the RNG, e.g. to timestep “to”, by jumping |
| jump_dt | Automatically jump on the next value of dt |
| link_module | Shortcut for linking the module |
| link_sim | Shortcut for linking the sim, only overwriting an existing one if overwrite=True; not for the user |
| make_history | Store the current state in history |
| make_rvs | Return default random numbers for scalar parameters; not for the user |
| mock | Create a distribution using a mock sim for testing purposes |
| plot_hist | Plot the current state of the RNG as a histogram |
| ppf | Return default random numbers for array parameters; not for the user |
| process_dist | Ensure the distribution works; not for the user |
| process_pars | Ensure the supplied dist and parameters are valid, and initialize them; not for the user |
| process_seed | Obtain the seed offset by hashing the path to this distribution; not for the user |
| process_size | Handle an input of either size or UIDs and calculate size, UIDs, and slots; not for the user |
| rand | Simple way to get simple random numbers |
| randround | Round the values up or down to an integer stochastically; usually called via dist.rvs(round=True) |
| reset | Restore state, allowing the same numbers to be resampled |
| rvs | Get random variates – use this! |
| set | Set (change) the distribution type, or one or more parameters of the distribution |
| show_state | Show the state of the object |
| shrink | Shrink the size of the module for saving to disk |
| sync_pars | Perform any necessary synchronizations or transformations on distribution parameters; not for the user |
| to_json | Return a dictionary representation of the Dist |
| update_dist_pars | Update SciPy distribution parameters; not for the user |
| validate_pars | Check if parameters are valid; only used for non-SciPy distributions |
call_par
distributions.Dist.call_par(key, val, size, uids)Check if this parameter needs to be called to be turned into an array; not for the user
call_pars
distributions.Dist.call_pars()Check if any parameters need to be called to be turned into arrays; not for the user
convert_callable
distributions.Dist.convert_callable(parkey, func, size, uids)Method to handle how callable parameters are processed; not for the user
convert_timepars
distributions.Dist.convert_timepars()Convert time parameters (durations and rates) to scalars
This function converts time parameters into bare numbers that will be returned by rvs() depending on the timestep of the parent module for this Dist. The conversion for these types is
- Durations are divided by
dt(so the result will be a number of timesteps) - Rates are multiplied by
dt(so the result will be a number of events, or else the equivalent multiplicate value for the timestep)
disp
distributions.Dist.disp()Return full display of object
get_state
distributions.Dist.get_state()Return a copy of the state
init
distributions.Dist.init(
trace=None,
seed=None,
module=None,
sim=None,
slots=None,
force=False,
)Calculate the starting seed and create the RNG
Typically this is not invoked by the user, although the user can call it with force=True to initialize a distribution manually independently of a ss.Sim object (which is equivalent to setting strict=False when creating the dist).
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| trace | str | the distribution’s location within the sim | None |
| seed | int | the base random number seed that other random number seeds will be generated from | None |
| module | ss.Module |
the parent module | None |
| sim | ss.Sim |
the parent sim | None |
| slots | array | the agent slots of the parent sim | None |
| force | bool | whether to skip validation (if the dist has already been initialized, and if any inputs are None) | False |
jump
distributions.Dist.jump(to=None, delta=1, force=False)Advance the RNG, e.g. to timestep “to”, by jumping
jump_dt
distributions.Dist.jump_dt(ti=None, force=False)Automatically jump on the next value of dt
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| ti | int | if specified, jump to this timestep (default: current module timestep plus one) | None |
link_module
distributions.Dist.link_module(module=None, overwrite=False)Shortcut for linking the module
link_sim
distributions.Dist.link_sim(sim=None, overwrite=False)Shortcut for linking the sim, only overwriting an existing one if overwrite=True; not for the user
make_history
distributions.Dist.make_history(reset=False)Store the current state in history
make_rvs
distributions.Dist.make_rvs()Return default random numbers for scalar parameters; not for the user
mock
distributions.Dist.mock(trace='mock', **kwargs)Create a distribution using a mock sim for testing purposes
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| trace | str | the “trace” of the distribution (normally, where it would be located in the sim) | 'mock' |
| **kwargs | dict | passed to ss.mock_sim() as well as ss.mock_module() (typically time args, e.g. dt) |
{} |
Example:
dist = ss.normal(3, 2, unit='years').mock(dt=ss.days(1))
dist.rvs(10)
plot_hist
distributions.Dist.plot_hist(n=1000, bins=None, fig_kw=None, hist_kw=None)Plot the current state of the RNG as a histogram
ppf
distributions.Dist.ppf(rands)Return default random numbers for array parameters; not for the user
process_dist
distributions.Dist.process_dist()Ensure the distribution works; not for the user
process_pars
distributions.Dist.process_pars(call=True)Ensure the supplied dist and parameters are valid, and initialize them; not for the user
process_seed
distributions.Dist.process_seed(trace=None, seed=None)Obtain the seed offset by hashing the path to this distribution; not for the user
process_size
distributions.Dist.process_size(n=1)Handle an input of either size or UIDs and calculate size, UIDs, and slots; not for the user
rand
distributions.Dist.rand(size)Simple way to get simple random numbers
randround
distributions.Dist.randround(rvs)Round the values up or down to an integer stochastically; usually called via dist.rvs(round=True)
reset
distributions.Dist.reset(state=0)Restore state, allowing the same numbers to be resampled
Use 0 for original state, -1 for most recent state.
Example:
dist = ss.random(seed=5).init()
r1 = dist(5)
r2 = dist(5)
dist.reset(-1)
r3 = dist(5)
dist.reset(0)
r4 = dist(5)
assert all(r1 != r2)
assert all(r2 == r3)
assert all(r4 == r1)
rvs
distributions.Dist.rvs(n=1, round=False, reset=False)Get random variates – use this!
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| n | int / tuple / arr |
if an int or tuple, return this many random variates; if an array, treat as UIDs | 1 |
| round | bool | if True, randomly round up or down based on how close the value is | False |
| reset | bool | whether to automatically reset the random number distribution state after being called | False |
set
distributions.Dist.set(*args, dist=None, **kwargs)Set (change) the distribution type, or one or more parameters of the distribution
show_state
distributions.Dist.show_state(output=False)Show the state of the object
shrink
distributions.Dist.shrink()Shrink the size of the module for saving to disk
sync_pars
distributions.Dist.sync_pars()Perform any necessary synchronizations or transformations on distribution parameters; not for the user
to_json
distributions.Dist.to_json()Return a dictionary representation of the Dist
update_dist_pars
distributions.Dist.update_dist_pars(pars=None)Update SciPy distribution parameters; not for the user
validate_pars
distributions.Dist.validate_pars()Check if parameters are valid; only used for non-SciPy distributions
Dists
distributions.Dists(obj=None, *args, base_seed=None, sim=None)Class for managing a collection of Dist objects
Methods
| Name | Description |
|---|---|
| check_seeds | Check that no two distributions share the same seed |
| copy_to_module | Copy the Sim’s Dists object to the specified module |
| init | Set the base seed, find and initialize all distributions in an object |
| jump | Advance all RNGs, e.g. to call “to”, by jumping |
| jump_dt | Advance all RNGs to the next timestep |
| reset | Reset each RNG |
check_seeds
distributions.Dists.check_seeds()Check that no two distributions share the same seed
copy_to_module
distributions.Dists.copy_to_module(module)Copy the Sim’s Dists object to the specified module
init
distributions.Dists.init(obj=None, base_seed=None, sim=None, force=False)Set the base seed, find and initialize all distributions in an object
In practice, the object is usually a Sim, but can be anything.
jump
distributions.Dists.jump(to=None, delta=1, force=False)Advance all RNGs, e.g. to call “to”, by jumping
jump_dt
distributions.Dists.jump_dt(ti=None, force=False)Advance all RNGs to the next timestep
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| ti | int | if specified, jump to this timestep (default: current sim timestep) | None |
reset
distributions.Dists.reset()Reset each RNG
multi_random
distributions.multi_random(names, *args, **kwargs)A class for holding two or more ss.random() distributions, and generating random numbers linked to each of them. Useful for e.g. pairwise transmission probabilities.
See ss.combine_rands() for the manual version; in almost all cases this class should be used instead.
Usage
multi = ss.multi_random(‘source’, ‘target’) rvs = multi.rvs(source_uids, target_uids)
Methods
| Name | Description |
|---|---|
| combine_rvs | Combine inputs into one number |
| init | Not usually needed since each dist will handle this automatically; for completeness only |
| jump | Not usually needed since each dist will handle this automatically; for completeness only |
| reset | Not usually needed since each dist will handle this automatically; for completeness only |
| rvs | Get random variates from each of the underlying distributions and combine them efficiently |
combine_rvs
distributions.multi_random.combine_rvs(rvs_list, int_type, int_max)Combine inputs into one number
init
distributions.multi_random.init(*args, **kwargs)Not usually needed since each dist will handle this automatically; for completeness only
jump
distributions.multi_random.jump(*args, **kwargs)Not usually needed since each dist will handle this automatically; for completeness only
reset
distributions.multi_random.reset(*args, **kwargs)Not usually needed since each dist will handle this automatically; for completeness only
rvs
distributions.multi_random.rvs(*args)Get random variates from each of the underlying distributions and combine them efficiently
scale_types
distributions.scale_types()Define how distributions scale
Distributions scale in different ways, such as converting between time units. Some distributions can’t be scaled at all (e.g. ss.beta_dist() or ss.choice()). For distributions that can be scaled, some distributions can only be (linearly) scaled before the random numbers are generated (called “predraw”), some can only be scaled after (called “postdraw”), and some can be scaled in either way (“both”).
For example, a normal distribution is “both” since 2Normal(a, b) = Normal(2a, 2b). A Poisson distribution is “predraw” since 2Poisson(λ) ≠ Poisson(2λ), and there is no way to get the correct shape of a different Poisson distribution once the numbers have been drawn. Finally, distributions with unitless shape parameters as well as parameter that can have units (e.g. a gamma distribution with shape and scale parameters) are referred to as “postdraw” since scaling all input parameters is invalid (i.e. 2Gamma(shape, scale) ≠ Gamma(2shape, 2scale)), but they can still be scaled (i.e. 2Gamma(shape, scale) = Gamma(1shape, 2*scale)).
To summarize, options for dist.scaling are:
- 'postdraw' (after the random numbers are drawn, e.g. `ss.weibull()`)
- 'predraw' (before the draw, e.g. `ss.poisson()`)
- 'both' (either pre or post draw, e.g. `ss.normal()`)
- False (not at all, e.g. `ss.beta_dist()`)
Use ss.distributions.scale_types.show() to show how each distribution scales with time.
Methods
| Name | Description |
|---|---|
| check_postdraw | Check if the supplied distribution supports post-draw (results) scaling |
| check_predraw | Check if the supplied distribution supports pre-draw (parameter) scaling |
| show | Show which distributions have which scale types |
check_postdraw
distributions.scale_types.check_postdraw(dist)Check if the supplied distribution supports post-draw (results) scaling
check_predraw
distributions.scale_types.check_predraw(dist)Check if the supplied distribution supports pre-draw (parameter) scaling
show
distributions.scale_types.show(to_df=False)Show which distributions have which scale types
Functions
| Name | Description |
|---|---|
| link_dists | Link distributions to the sim and the module; used in module.init() and people.init() |
| make_dist | Make a distribution from a dictionary |
link_dists
distributions.link_dists(
obj,
sim,
module=None,
overwrite=False,
init=False,
**kwargs,
)Link distributions to the sim and the module; used in module.init() and people.init()
make_dist
distributions.make_dist(pars=None, **kwargs)Make a distribution from a dictionary