Skip to main content

connectors

connectors

Connectors, which link modules as the sim runs: e.g. one disease making another more likely

Most links between diseases need no connector, since diseases can read each other’s states (e.g. in a rate). A connector is for the rest, e.g. seasonal forcing or the effect of a coinfection. The one most used is ss.modify(), which changes a named quantity while a condition holds, e.g. a seasonal ss.modify(sir.rel_trans, multiply=ss.seasonal(amplitude=0.2, peak='January')).

Used with: agents and compartments; declared and coded modules

Classes

Name Description
Connector Base class for connectors, which link diseases (or other modules) to each other
Due A modifier’s when= as a function of the timeline: true on the steps its schedule is due (see parse_when()), as ss.schedule() itself works them out, once for the sim
Target A reference to a quantity of a module that is not a parameter, e.g. sir.rel_sus; used as the target of ss.modify()
modify Change a named quantity while a condition holds: scale it, shift it, or set it

Connector

connectors.Connector(name=None, label=None, **kwargs)

Base class for connectors, which link diseases (or other modules) to each other

A connector can do anything, so it has no fixed structure: its step() says what it does.

Due

connectors.Due(schedule, sim)

A modifier’s when= as a function of the timeline: true on the steps its schedule is due (see parse_when()), as ss.schedule() itself works them out, once for the sim

Target

connectors.Target(module, kind, name=None)

A reference to a quantity of a module that is not a parameter, e.g. sir.rel_sus; used as the target of ss.modify()

Parameters

Name Type Description Default
module Module / str the module the quantity belongs to, or its name required
kind str the kind of quantity, e.g. 'rel_sus', 'transition', or 'branch' required
name str the name of the quantity, if the kind has several None

modify

connectors.modify(
    target,
    multiply=None,
    add=None,
    set=None,
    efficacy=None,
    frac=None,
    network=None,
    who=None,
    when=None,
    until=None,
    start=None,
    stop=None,
    name=None,
    **kwargs,
)

Change a named quantity while a condition holds: scale it, shift it, or set it

A modifier never overwrites the quantity it changes: the base value is kept, and the value used is (set value if any, else base) × multipliers + addends over the modifiers that are currently active. It can be passed to a sim in connectors (interactions that are part of the model) or interventions (policy), or be a member of a declared disease, as part of its natural history.

The target can be any of these (for a modifier that is a member of a disease, its own, e.g. beta, recovery, or 'rel_trans'):

  • a parameter, e.g. sir.pars.beta, including a parameter of any module written as code
  • a transition, e.g. sir.recovery, which scales its rate (or its probability, if it is a branch with p=)
  • sir.rel_sus or sir.rel_trans, each person’s susceptibility or infectiousness
  • the contacts of a network, as the network itself (e.g. ss.contacts('Kenya', layer='school'), matched by its name) or as a path ('school.pars.n_contacts')
  • the path of any of these as a string, e.g. 'sir.pars.beta'

For a transition with several destinations (e.g. ss.Transition(Tx, {R: 0.8, I: None}, rate=...)), the transition scales its rate (or its dwell time), and a destination ('tb.treatment.R') scales the probability of that destination. With an outcome for each strain carried (per_strain='each', with the probability of the first destination), that is the probability for the whole set of strains a person carries (e.g. that all are cured: Π c_k), not each strain’s.

Note: CRN safe.

Parameters

Name Type Description Default
target par / str the quantity to change (see above) required
multiply float multiply the quantity by this; like add and set, it can also be ss.by(...), a function of the sim, or an ss.timeseries, for changes at given times None
add float add this to the quantity: a parameter, or an inflow (a transition from ss.outside, e.g. CFUs shed into a reservoir), to which it adds an amount per unit time from outside the model None
set float replace the quantity with this (a parameter only) None
efficacy float for protection such as a condom or a needle program, the fraction of the risk it removes: the same as multiply=1 - efficacy (give one of them) None
frac float with multiply= or efficacy=, the fraction of acts (or, on a route without acts, of contacts) it acts on: the hazard is multiplied by 1 − frac·(1 − multiply), exactly, since hazards add; a number, an ss.timeseries, an ss.by, or (with network=, as agents) a function of the network returning one value per edge (e.g. lambda net: net.edges.condoms) None
network str / list for a modifier of a transmission, the network or networks (by name, as in the sim’s networks=, including mixing pools) it applies on; by default, every one. On a parameter (e.g. tb.pars.beta), it acts on every transmission the parameter scales (e.g. an infection and its reinfections). A disease written as code has no transmission to name, so on a network the target is its rel_sus (of the people who would be infected over it) None
who Cond / str / func who it applies to: a condition (e.g. sir.vaccinated, or a state by its path, who='sir.I', as ss.ref('sir.I')), a condition on a count (e.g. sir.I.n > 1000, worked out at each step; in compartments, as part of the rates where it can be, so the run stays in the kernels), or a function of the sim (default: everyone); with network=, the people who would be infected None
start date / float / dur when it starts to apply (default: the start of the sim): a date, a bare number (a calendar year if the sim has a calendar, else a time in its unit from 0), or a duration, that long after the start of the sim; the same as when=ss.schedule(start=...) None
stop date / float / dur when it stops applying, likewise (default: never; a stop after the end of the sim never stops it) None
when schedule / func when it applies, on time alone, which can be read before the run: ss.schedule(start=..., stop=...) for a window (a stop after the end of the sim never stops it; a bare number is a calendar year if the sim has a calendar, else a time in its unit from 0), ss.schedule() with times or how often for those steps only, or a function of the date of each step, true on the steps it applies (e.g. lambda now: now.month in [6, 7, 8], in a sim with a calendar); a condition on anything else goes in who= None
until Cond / func / dur how long it stays on once who= (or when=) selects someone: until a condition holds, as for a lockdown that starts at one level of cases and ends at a lower one (with who=; while both hold, who= wins); or a duration, at least this long, as a rule that is lifted only after a while (a bare number is in the sim’s unit) None
name str the name of the modifier None

Examples

S, I, R = ss.states(3)
sir = ss.Disease(infection=S >> I @ 0.3*I, recovery=ss.Transition(I, R, dur=10), init_prev=0.01)
masks = ss.modify(sir.pars.beta, multiply=0.6, start=30)
sim = ss.Sim(sir, interventions=masks, stop=100).run()

# Schools closed from day 200 to day 260 (in a sim with a calendar, that long after its start)
closure = ss.modify(sir.pars.beta, multiply=0.7, start=ss.days(200), stop=ss.days(260))

# Changes at given times, each holding until the next; before the first, the quantity has its own value
lockdown = ss.modify(sir.pars.beta, multiply=ss.timeseries({30: 0.5, 60: 1.0}), start=30)

# Each place on its own: contacts cut by 40% for at least 14 days once a week's cases pass 200 per 100,000
npi = ss.modify(sir.pars.beta, multiply=0.6, who=sir.infection.incidence(over=ss.days(7), by='patch') > 200e-5, until=ss.days(14))

# A lockdown that starts when 2% of people are infectious and ends when 1% are
lockdown = ss.modify(sir.pars.beta, multiply=0.5, who=sir.I.frac >= 0.02, until=sir.I.frac <= 0.01)

# Condoms, 90% effective against gonorrhea, used in a fraction of acts that grows over time, on one network
use = ss.timeseries({2000: 0.05, 2020: 0.3})
condoms = ss.modify(ng.infection, efficacy=0.9, frac=use, network='sexualnet')

# A member of a disease, with the rest of its natural history
class Measles(ss.Disease):
    S, I, R = ss.states('S I R')
    infection = S >> I @ 0.5*I
    recovery = I >> R @ ss.days(10)
    fading = ss.modify('rel_trans', multiply=0.5**(I.time_in/ss.days(3)), who=I)

# Summer only, every year
summer = ss.modify(sir.pars.beta, multiply=1.2, when=lambda now: now.month in [6, 7, 8])

Attributes

Name Description
active Whether the modifier applies on the current step
constant_amount Whether the amount and frac are the same at every step: numbers, or expressions of parameters with plain values
fixed_amount Whether the amount depends at most on time: not a function of the sim, nor an expression that reads states (e.g. 1 - 0.7*b.R.frac), which is worked out at each step
holding Whether it stays on for people after who= selects them: for a while, or until a condition holds (until=)
per_edge Whether its fraction is a function of the network, one value per edge
tabulated Whether the engines can work out the modifier for every step before a run (see ss.solvers.Compartmental.tables()): one that depends only on time, and on who it applies to only through states and stratifications
target The target, as a path
timed Whether the modifier depends only on time (not on who, or on a function of the sim), so can be worked out for every step before a run

Methods

Name Description
apply Apply the modifier to a value, for the people it applies to
current The amount on this step: a scalar, or one value per agent or group; with frac=, the multiplier on the hazard, 1 − frac·(1 − multiply); given a mask, a clock is read only for the agents in it
describe One line saying what the modifier does
finalize_results Parameters are back to their base values after the run
frac_of The fraction of acts (or contacts) it acts on this step: a scalar, or one value per agent or group; or, given a network, a function of it gives one per edge
held_mask With until=: who the modifier applies to, including those it still holds for
init_post A parameter of a module written as code with no engine of its own (an ss.Module or ss.Connector), whose code reads it from self.pars: written there at the start of each step, as the engine of a disease written as code does
init_pre Find the quantity this modifies, by name if it was given as a string, and register the modifier with the sim
marks When the modifier acts, for marking on plots (see sim.plot()): its start and stop on the results’ time axis, if it has either and no steps within them (when=); else none
nan_sources What in the amount can have no value for some people, and how to give it one
own Become a member of a declared module, whose states, parameters, and transitions have their names now
start_step Write the modified parameters of a module written as code, if any
step Nothing: a modifier acts when the quantity it changes is read (see combine())
apply
connectors.modify.apply(value, engine)

Apply the modifier to a value, for the people it applies to

current
connectors.modify.current(engine, frac=True, mask=None)

The amount on this step: a scalar, or one value per agent or group; with frac=, the multiplier on the hazard, 1 − frac·(1 − multiply); given a mask, a clock is read only for the agents in it

describe
connectors.modify.describe()

One line saying what the modifier does

finalize_results
connectors.modify.finalize_results()

Parameters are back to their base values after the run

frac_of
connectors.modify.frac_of(engine, net=None)

The fraction of acts (or contacts) it acts on this step: a scalar, or one value per agent or group; or, given a network, a function of it gives one per edge

held_mask
connectors.modify.held_mask(mask, engine)

With until=: who the modifier applies to, including those it still holds for

init_post
connectors.modify.init_post()

A parameter of a module written as code with no engine of its own (an ss.Module or ss.Connector), whose code reads it from self.pars: written there at the start of each step, as the engine of a disease written as code does

init_pre
connectors.modify.init_pre(sim)

Find the quantity this modifies, by name if it was given as a string, and register the modifier with the sim

Parameters
Name Type Description Default
sim Sim the sim being initialized required
marks
connectors.modify.marks()

When the modifier acts, for marking on plots (see sim.plot()): its start and stop on the results’ time axis, if it has either and no steps within them (when=); else none

nan_sources
connectors.modify.nan_sources()

What in the amount can have no value for some people, and how to give it one

own
connectors.modify.own(module, name)

Become a member of a declared module, whose states, parameters, and transitions have their names now

Parameters
Name Type Description Default
module Module the module required
name str the name of the modifier in it required
start_step
connectors.modify.start_step()

Write the modified parameters of a module written as code, if any

step
connectors.modify.step()

Nothing: a modifier acts when the quantity it changes is read (see combine())

Functions

Name Description
check_applied Raise a ValueError for any modifier or efficacy of a product that nothing in the sim would apply, naming it and why
combine Apply the modifiers that are active on a quantity: set, then multiply, then add
contact_mult The multiplier on the contacts of a network from the modifiers of its n_contacts, for an engine (see Engine.contact_mult())
describe_mod What a modifier is, for a message: ss.modify(), a product’s efficacy, or another’s (e.g. a strain’s immunity)
parse_target Work out what a modifier changes: returns the module (or its name), the kind of quantity, and its name
parse_when The when= of a modifier, as (start, stop, schedule): a window, and an ss.schedule() of the steps within it it applies on, if not all;
reached Whether a time on the timeline is at or after another, allowing for rounding: the timeline keeps its times to 9 decimals, so e.g. ss.months(7) is a hair after step 7 of monthly steps
route_mult The multiplier on a transmission over one route to the people who would be infected (trg), 1 if there are none: from the modifiers
scaled_transmissions The transmissions (by name) whose rates a parameter scales, for a modifier of it on a route (ss.modify(sir.pars.beta, network=...)): those
to_steps The number of steps in a duration (a bare number is in the sim’s unit), at least one
to_ti The first step that starts at or after a time, which must be within the sim
to_time Convert a time (a date, a number, or a duration) to the same kind of time as the sim’s timeline
unapplied Why a declared module (its graph and name) would not apply the modifiers (mods) on one of its quantities (kind and name, as keyed in sim.modifiers), or None if it would

check_applied

connectors.check_applied(sim)

Raise a ValueError for any modifier or efficacy of a product that nothing in the sim would apply, naming it and why

Called once by sim.init(), after every module has registered its modifiers in sim.modifiers (by module, kind of quantity, and name). For each quantity modified, the module it belongs to says whether it reads it: a declared module from its graph (unapplied()), and a module written as code from what its own code reads (ss.solvers.Effects.unapplied_par(), and for a disease ss.solvers.Undeclared.unapplied()). A network’s contacts are read by every transmission over it. It costs one pass over the quantities modified.

combine

connectors.combine(mods, value, engine)

Apply the modifiers that are active on a quantity: set, then multiply, then add

contact_mult

connectors.contact_mult(engine, name, mods)

The multiplier on the contacts of a network from the modifiers of its n_contacts, for an engine (see Engine.contact_mult())

describe_mod

connectors.describe_mod(m)

What a modifier is, for a message: ss.modify(), a product’s efficacy, or another’s (e.g. a strain’s immunity)

parse_target

connectors.parse_target(target)

Work out what a modifier changes: returns the module (or its name), the kind of quantity, and its name

parse_when

connectors.parse_when(when, start=None, stop=None)

The when= of a modifier, as (start, stop, schedule): a window, and an ss.schedule() of the steps within it it applies on, if not all; a function of the date is the schedule of the steps it is true on. start= and stop= of ss.modify() are the window, as in ss.schedule()

reached

connectors.reached(now, time)

Whether a time on the timeline is at or after another, allowing for rounding: the timeline keeps its times to 9 decimals, so e.g. ss.months(7) is a hair after step 7 of monthly steps

route_mult

connectors.route_mult(engine, name, route, inds, trg)

The multiplier on a transmission over one route to the people who would be infected (trg), 1 if there are none: from the modifiers of the route’s contacts (n_contacts), and from those given the network (ss.modify(..., network=)), each 1 − frac·(1 − multiply) for the people it applies to (who=, of the people who would be infected)

Parameters

Name Type Description Default
engine ss.solvers.Transmission the engine of the disease, as agents required
name str the name the modifiers on the route are under: a declared disease’s transmission, or rel_sus for a disease written as code required
route Route the route required
inds array the edges of a network (Ellipsis for all), for a fraction given per edge; None for a route with no edges required
trg array the people who would be infected, by UID (or a slice of every agent) required

scaled_transmissions

connectors.scaled_transmissions(graph, par, what)

The transmissions (by name) whose rates a parameter scales, for a modifier of it on a route (ss.modify(sir.pars.beta, network=...)): those in which it is a factor of the rate from every source of infection (e.g. beta in beta*(k*A + S)), so that scaling the transmission on the route is scaling the parameter

Parameters

Name Type Description Default
graph ss.graph.Graph the graph of the disease required
par ss.par the parameter required
what str the modifier, for the errors required

to_steps

connectors.to_steps(sim, dur)

The number of steps in a duration (a bare number is in the sim’s unit), at least one

to_ti

connectors.to_ti(sim, val, name='time', after=False)

The first step that starts at or after a time, which must be within the sim

Step ti runs from time point ti to the next (see ss.loop.Loop), so a time at the start of the sim is step 0.

Parameters

Name Type Description Default
sim Sim the sim required
val date / dur / float the time; see to_time() required
name str what the time is, for the error message (e.g. ‘the start of “masks”’) 'time'
after bool whether a time after the end is allowed (e.g. for a stop, meaning it never stops) False

to_time

connectors.to_time(sim, val)

Convert a time (a date, a number, or a duration) to the same kind of time as the sim’s timeline

How a bare number is read is decided once, from the sim’s start (see ss.timeline.calendar_of()): if the sim has a calendar (its start is a date or a year), a number is a calendar year (2005 is the start of 2005, 2005.5 the middle of it); otherwise, it is a time in the sim’s unit from 0. A duration is that long after the sim’s start if the sim has a calendar (ss.days(30) is 30 days in), and with none a point on the sim’s own axis, counted from 0 as sim.t.tvec is (ss.days(30) is day 30, the same when the start is 0); see ss.Timeline.to_time().

unapplied

connectors.unapplied(graph, modname, kind, name, mods)

Why a declared module (its graph and name) would not apply the modifiers (mods) on one of its quantities (kind and name, as keyed in sim.modifiers), or None if it would