atomica.programs

Classes for implementing Programs

This module principally defines the Program and ProgramSet classes, which are used to define a single program/modality (e.g., FSW programs) and a set of programs, respectively.

Classes

Covout(par, pop, progs[, cov_interaction, …]) Store and compute program outcomes
Program(name[, label, target_pops, …]) Representation of a single program
ProgramInstructions(start_year[, stop_year, …]) Store instructions for applying programs
ProgramSet([name, framework, data, tvec]) Representation of a single program
class atomica.programs.Covout(par, pop, progs, cov_interaction=None, imp_interaction=None, uncertainty=0.0, baseline=0.0)[source]

Store and compute program outcomes

The Covout object is responsible for storing the

Parameters:
  • par (str) – string with the code name of the parameter being overwritten
  • pop (str) – string with the code name of the population being overwritten
  • progs (dict) – a dict containing {prog_name:outcome} with the single program outcomes
  • cov_interaction (Optional[str]) – one of ‘additive’, ‘random’, ‘nested’
  • imp_interaction (Optional[str]) – a parsable string like 'Prog1+Prog2=10,Prog2+Prog3=20' with the interaction outcomes
  • uncertainty – a scalar standard deviation for the outcomes
  • baseline – the zero coverage baseline value
compute_impact_interaction(progs)[source]

Return the output for a given combination of programs

The outcome for various combinations of programs is cached prior to running the model. This function retrieves the appropriate delta given a boolean array flagging which programs are active

Parameters:progs (<built-in function array>) – A numpy boolean array, with length equal to the number of programs
Return type:float
Returns:The delta value corresponding to the specified combination of programs
get_outcome(prop_covered)[source]

Return parameter value given program coverages

The Covout object contains a set of programs and outcomes. The Covout.get_outcome() method returns the outcome value associated for coverage of each program. Don’t forget that any given Covout instance is already specific to a (par,pop) combination

Parameters:prop_covered – A dict with {prog_name:coverage} containing at least all of the programs in self.progs. Note that coverage is expected to be a np.array (such that that generated by ProgramSet.get_prop_coverage()). However, because the modality calculations only work for scalars, only the first entry in the array will be used.
Returns:A scalar outcome (of type np.double or similar i.e. _not_ an array)
n_progs

Return the number of programs

Return type:int
Returns:The number of programs with defined outcomes (usually this is a subset of all available programs)
sample()[source]

Perturb the values entered in the databook

The Covout instance is modified in-place. Note that the program outcomes are scalars that do not vary over time - therefore, Covout.sample() does not have a constant argument.

Return type:None
update_outcomes()[source]

Update cache when outcomes change

This method should be called whenever the baseline, program outcomes, or interaction outcomes change. It updates the internal cache so that get_outcome() uses the correct values. It’s responsible for

  1. Sorting the programs by outcome value
  2. Compute the deltas relative to baseline
  3. Pre-compute the outcomes associated with every possible combination of programs
Return type:None
class atomica.programs.Program(name, label=None, target_pops=None, target_comps=None, currency='$')[source]

Representation of a single program

A Program object is instantiated for every program listed on the ‘Program Targeting’ sheet in the program book. The Program object contains

  • Collections of targeted populations and compartments from the targeting sheet in the program book
  • The time-dependent program properties on the spending data sheet (such as total spend and unit cost)
Parameters:
  • name – Short name of the program
  • label – Full name of the program
  • target_pops – List of population code names for pops targeted by the program
  • target_comps – List of compartment code names for compartments targeted by the program
  • currency – The currency to use (for display purposes only) - normally this would be set to ProgramSet.currency by ProgramSet.add_program()
  • name – Short name of the program
  • label – Full name of the program
  • target_pops – List of population code names for pops targeted by the program
  • target_comps – List of compartment code names for compartments targeted by the program
  • currency – The currency to use (for display purposes only) - normally this would be set to ProgramSet.currency by ProgramSet.add_program()
baseline_spend = None

A TimeSeries with any baseline spending data - currently not exposed in progbook

capacity_constraint = None

TimeSeries with capacity constraint for the program

coverage = None

TimeSeries with capacity of program - optional - if not supplied, cost function is assumed to be linear

get_capacity(tvec, spending, dt)[source]

Return timestep capacity

This method returns the number of people covered at each timestep. For one-off programs, this means the annual capacity is multiplied by the timestep size. Whether timestep scaling takes place or not is determined based on the units of the unit cost ($/person or $/person/year where the former is used for one-off programs).

The method takes in the spending value to support overwrites in spending value by program instructions (in which case, spending should be drawn from the instructions rather than the program’s spending data). This is handled in ProgramSet.get_capacities()

This method returns the program’s timestep capacity - that is, the capacity of the program per timestep, at specified points in time. The spending and capacity constraint are automatically adjusted depending on the units of the unit cost and capacity constraint such that the calculation returns capacity in units of ‘people’ (not ‘people/year’)

Parameters:
  • tvec – A scalar, list, or array of times
  • spending – A vector of spending values (in units of ‘$/year’), the same size as tvec
  • dt – The time step size (required because the number covered at each time step potentially depends on the spending per-timestep)
Returns:

Array the same size as tvec, with capacity in units of ‘people’

get_prop_covered(tvec, capacity, eligible)[source]

Return proportion of people covered

The time vector tvec is required to interpolate the saturation values. The capacity and eligible variables are assumed to correspond to the same time points and thus the array sizes should match the size of the time array.

Parameters:
  • tvec – An array of times
  • capacity – An array of number of people covered (e.g. the output of Program.get_capacity()) This should be in units of ‘people’, rather than ‘people/year’
  • eligible – The number of people eligible for the program (computed from a model object or a Result)
Returns:

The fractional coverage (used to compute outcomes)

get_spend(year=None, total=False)[source]

Retrieve program spending

Parameters:
  • year – Scalar, list, or array of years to retrieve spending in
  • total (bool) – If True, the baseline spend will be added to the spending data
Return type:

<built-in function array>

Returns:

Array of spending values

label = None

Full name of the program

name = None

Short name of program

sample(constant)[source]

Perturb program values based on uncertainties

Calling this function will perturb the original values based on their uncertainties. The values will change in-place. Normally, this method would be called by ProgramSet.sample() which will copy the Program instance first.

Parameters:constant (bool) – If True, time series will be perturbed by a single constant offset. If False, an different perturbation will be applied to each time specific value independently.
Return type:None
saturation = None

TimeSeries with saturation constraint that is applied to fractional coverage

spend_data = None

TimeSeries with spending data for the program

target_comps = None

Compartments targeted by the program - used for calculating coverage denominators

target_pops = None

List of populations targeted by the program

unit_cost = None

TimeSeries with unit cost of the program

class atomica.programs.ProgramInstructions(start_year, stop_year=None, alloc=None, coverage=None, capacity=None)[source]

Store instructions for applying programs

A ProgramSet contains a Python representation of the program book, with a collection of programs, their effects, and quantities like historical spending. However, to run a simulation with programs, additional information is required - for example, which year to switch from databook parameters to program-computed parameters. This type of information is specific to the simulation being run, rather than being an intrinsic property of a set of programs. Therefore, that information is stored in a ProgramInstructions instance.

At minimum, the ProgramInstructions must contain the year to turn on programs. It can also optionally contain the year to turn off programs. In addition to the start/stop years, the ProgramInstructions also contains any overwrites that should be applied to

  • Spending (in units of people/year)
  • Capacity (in units of people/year)
  • Fraction/proportion coverage

which thus provides the underlying implementation for program-related scenarios. The ProgramSet and Program methods access and use the ProgramInstructions instances. The overwrites for spending in particular are widely used - for example, budget optimization is a mapping from one set of ProgramInstructions to another, and thus during optimization, ProgramInstructions instances are used to test different allocations.

Note that the program calculation proceeds by

  1. Using spending and unit cost to compute capacity
  2. Using capacity and the compartment sizes to compute fractional coverage
  3. Using fractional coverage to compute program outcomes

Overwrites to each quantity (spending, capacity, coverage) are applied at their respective stages, so if the ProgramInstructions contains more than one type of overwrite, they will be applied in this same order (and later stages will take precedence e.g. if both capacity and coverage are overwritten, it will be the coverage overwrite that impacts the final parameter value).

Finally, for simplicity, if an overwrite is provided, it entirely replaces the values in the program book, in contrast to parameter scenarios, which contain more complex logic for interpolating between databook and scenario values.

Parameters:
  • start_year (float) – Year to switch to program-calculated parameters
  • stop_year (Optional[float]) – Year to switch back to databook parameters
  • alloc

    The allocation. It can be - A dict keyed by program name, containing a scalar spend, or a TimeSeries of spending values. If the spend is

    scalar, it will be assigned to the start year
    • A ProgramSet instance, in which case an allocation will be assigned by interpolating the ProgramSet’s spending onto the program start year. This is a shortcut to ensure that budget scenarios and optimizations where spending is specified in future years ramp correctly from the program start year (and not the last year that data was entered for)
  • capacity (Optional[dict]) – Overwrites to capacity. This is a dict keyed by program name, containing a scalar capacity or a TimeSeries of capacity values For convenience, the capacity overwrite should be in units of ‘people/year’ and it will be automatically converted to a per-timestep value based on the units for the program’s unit cost.
  • coverage (Optional[dict]) – Overwrites to proportion coverage. This is a dict keyed by program name, containing a scalar coverage or a TimeSeries of coverage values
scale_alloc(scale_factor)[source]

Scale allocation by a constant

This method multiplies the budget by a constant to scale the total budget up or down. The scale factor is applied at all time points.

Parameters:scale_factor (float) – Multiplicative factor for spending
Return type:None
Returns:A new, scaled copy of the instructions
class atomica.programs.ProgramSet(name='default', framework=None, data=None, tvec=None)[source]

Representation of a single program

A ProgramSet object is the code representation of a program book. It provides an interface for reading and writing the program book, as well as retrieving program outcomes (due to interactions between programs, they must be computed using the entire collection of programs).

Parameters:
  • name – Optionally specify the name of the ProgramSet
  • tvec – Optionally specify the years for data entry
_get_code_name(name)[source]

Return code name given code or full name

This function converts the input name to a code name, thereby allowing operations like removing pops/comps/pars/progs to be performed by code name or by full name

Code names are unique because they key the program dictionary. Full names are likely unique but may not be. Code names will be returned as-is so take precedence over full names. For example, if you have a program with a particular code name, and other programs with that same name used as the full name, this method will return the program with the matching code name.

Parameters:name (str) – A code name or full name
Return type:str
Returns:A code name
static _normalize_inputs(framework, data, project)[source]

Normalize constructor inputs

A ProjectFramework is constructed against a particular framework and data. For convenience, these can be specified by passing in a Project containing the framework and data. This function takes in all of the inputs provided to the constructor (whether making a blank instance or reading a spreadsheet) and returns the framework and data. The order of precedence is

  • If separate framework or data is provided, it will be used
  • Otherwise, they will be drawn from the project

So for example, a project and data could be provided, in which case the framework would come from the project and the data would come from the explicit argument (even if the project also contained data).

Parameters:
  • framework – Optionally a ProjectFramework instance
  • data – Optionally a ProjectData instance
  • project – Optionally a Project instance
Return type:

tuple

Returns:

Tuple containing (framework,data)

_read_effects(sheet, framework, data)[source]
_read_spending(sheet, _allow_missing_data=False)[source]

Internal method to read the spending data

Parameters:
  • sheet
  • _allow_missing_data – If False, an error will be raised if unit costs or total spending is missing. However, the FE requires loading in a progset with missing spending data. In that case, having these values missing is be allowed
Returns:

_read_targeting(sheet)[source]
Return type:None
_write_effects()[source]
_write_spending()[source]
_write_targeting()[source]
add_comp(code_name, full_name, pop_type=None)[source]

Add a compartment

The primary use case would be when an existing project has a change made to the framework and it’s desired to update an already filled out program book.

Parameters:
  • code_name (str) – Code name of the compartment to add
  • full_name (str) – Full name of the compartment to add
Return type:

None

add_par(code_name, full_name, pop_type=None)[source]

Add a parameter

The primary use case would be when an existing project has a change made to the framework and it’s desired to update an already filled out program book.

Parameters:
  • code_name (str) – Code name of the parameter to add
  • full_name (str) – Full name of the parameter to add
  • pop_type (Optional[str]) – Code name of the population type
Return type:

None

add_pop(code_name, full_name, pop_type=None)[source]

Add a population to the ProgramSet

At this level, we can add any arbitrarily named population. When the progbook is loaded in, the populations contained in the ProgramSet will be validated against the specified ProjectData instance.

Parameters:
  • code_name (str) – The code name of the new population
  • full_name (str) – The full name of the new population
Return type:

None

add_program(code_name, full_name)[source]

Add a program to the ProgramSet

Parameters:
  • code_name (str) – The code name of the new program
  • full_name (str) – The full name of the new program
Return type:

None

static from_spreadsheet(spreadsheet=None, framework=None, data=None, project=None, name=None, _allow_missing_data=False)[source]

Instantiate a ProgramSet from a spreadsheet

To load a spreadsheet, need to either pass in

  • A Project containing a framework and data
  • ProjectFramework and ProjectData instances without a project
Parameters:
  • spreadsheet – A string file path, or an sc.Spreadsheet
  • framework – A ProjectFramework instance
  • data – A ProjectData instance
  • project – A Project instance
  • name – Optionally specify the name of the ProgramSet to create
  • _allow_missing_data – Internal only - optionally allow missing unit costs and spending (used for loading template progbook files)
Returns:

A ProgramSet

get_alloc(tvec, instructions=None)[source]

Return the spending allocation for each program

This method fuses the spending data entered in the program book with any overwrites that are present in the instructions. The spending values returned by this method thus reflect any budget scenarios that may be present.

Parameters:
  • tvec – array of times (in years) - this is required to interpolate time-varying spending values
  • instructions – optionally specify instructions, which can supply a spending overwrite
Return type:

dict

Returns:

Dict like {prog_name: np.array()} with spending on each program (in units of ‘$/year’ or currency equivalent)

get_capacities(tvec, dt, instructions=None)[source]

Return timestep capacity for all programs

For convenience, this method automatically calls ProgramSet.get_alloc() to retrieve the spending values that are used to compute capacity. Thus, this method fuses the program capacity computed with the inclusion of any budget scenarios, with any capacity overwrites that are present in the instructions.

Parameters:
  • tvec – array of times (in years) - this is required to interpolate time-varying unit costs and capacity_constraint constraints
  • dt – scalar timestep size - this is required to adjust spending on incidence-type programs
  • instructions – optionally specify instructions, which can supply a spending overwrite
Return type:

dict

Returns:

Dict like {prog_name: np.array()} with program capacity at each timestep (in units of people)

get_outcomes(prop_coverage)[source]

Get program outcomes given fractional coverage

Since the modality interactions in Covout.get_outcome() assume that the coverage is scalar, this function will also only work for scalar coverage. Therefore, the prop coverage would normally come from ProgramSet.get_prop_coverage(tvec,…) where tvec was only one year.

Note that this function is mainly aimed at internal usage. Typically, the program-provided parameter values would be best accessed by examining the appropriate output in the Result. For example, if the programs system overwrites the screening rate screen then it would normally be easiest to run a simulation and then use Result.get_variable(popname,'screen')

For computational efficiency, this method returns a flat dictionary keyed by parameter-population pairs that then gets inserted into the appropriate integration objects by Model.update_pars().

Parameters:prop_coverage (dict) – dict with coverage values {prog_name:val}
Return type:dict
Returns:dict {(par,pop):val} containing parameter value overwrites
get_prop_coverage(tvec, capacities, num_eligible, instructions=None)[source]

Return fractional coverage

Note that this function is primarily for internal usage (i.e. during model integration or reconciliation). Since the proportion covered depends on the number of people eligible for the program (the coverage denominator), retrieving fractional coverage after running the model is best accomplished via Result.get_coverage('fraction') whereas this method is called automatically during integration.

Evaluating the proportion coverage for a ProgramSet is not a straight division because

  • instructions can override the coverage (for coverage scenarios)
  • Programs can contain saturation constraints
Parameters:
  • tvec – array of times (in years) - this is required to interpolate time-varying saturation values
  • capacities – dict of program coverages, should match the available programs (typically the output of ProgramSet.get_capacities()) Note that since the capacity and eligible compartment sizes are being compared here, the capacity needs to be in units of ‘people’ (not ‘people/year’) at this point
  • num_eligible – dict of number of people covered by each program, computed externally and with one entry for each program
  • instructions – optionally specify instructions, which can supply a coverage overwrite
Return type:

dict

Returns:

Dict like {prog_name: np.array()} with fractional coverage values (dimensionless)

static new(name=None, tvec=None, progs=None, project=None, framework=None, data=None)[source]

Generate a new progset with blank data

Parameters:
  • name – the name for the progset
  • tvec – an np.array() with the time values to write
  • progs – This can be - A number of progs - An odict of {code_name:display name} programs
  • project – specify a project to use the project’s framework and data to initialize the comps, pars, and pops
  • framework – specify a framework to use the framework’s comps and pars
  • data – specify a data to use the data’s pops
Returns:

A new ProgramSet instances

remove_comp(name)[source]

Remove a compartment

The primary use case would be when an existing project has a change made to the framework and it’s desired to update an already filled out program book.

Note that removing a compartment also requires removing it as a target compartment from all programs, which is automatically handled by this method.

Parameters:code_name – Code name or full name of the compartment to remove
Return type:None
remove_par(name)[source]

Remove a parameter

The primary use case would be when an existing project has a change made to the framework and it’s desired to update an already filled out program book.

Note that removing a parameter also requires removing all of the Covout instances associated with it.

Parameters:name (str) – Code name or full name of the parameter to remove
Return type:None
remove_pop(name)[source]

Remove a population from the ProgramSet

This method will remove the population from the ProgramSet, as well as from all program target pops and will also remove the covout objects for the affected population.

Parameters:name (str) – Code name or full name of the population to remove
Return type:None
remove_program(name)[source]

Remove a program from the ProgramSet

Parameters:name (str) – The code name or full name of the program to remove
Return type:None
sample(constant=True)[source]

Perturb programs based on uncertainties

The covout objects contained within the ProgramSet cache the program outcomes. At construction, the cache corresponds to the values entered in the databook. Calling this function will perturb the caches based on the sigma values. A simulation subsequently run using the ProgramSet will use the perturbed outcomes.

Parameters:constant (bool) – If True, time series will be perturbed by a single constant offset. If False, an different perturbation will be applied to each time specific value independently.
Returns:A new ProgramSet with values perturbed by sampling
save(filename=None, folder=None)[source]
Return type:str
to_spreadsheet()[source]

Write the contents of a program set to a spreadsheet.

validate()[source]

Perform basic validation checks

Raises:Exception if anything is invalid
Return type:None