Blocks

This section contains the API documentation for model building blocks.

Block Classes

Block

Base class for all blocks. Provides shared analysis methods, including strategic-relevance analysis (relevance_graph, relies_on).

class skagent.block.Block

Bases: object

agent_populations()

Each agent role, and the entity class whose instances hold it.

An agent name declares a ROLE; it never creates an entity class. The role attaches to the class of the block its control is declared in, and to nothing when that block carries none. A role reported as None is a single agent rather than a population.

Return type:

dict[str, str | None]

calc_reward(vals, agent=None)

Computes the reward for a given set of variable values

Parameters:
  • vals (Mapping[str, Any]) – Values for every variable the reward formulas depend on.

  • agent (str, optional) – If given, compute only the reward variables attributed to this agent. Their sum is that agent’s payoff. Defaults to all reward variables in the block.

Returns:

Mapping from reward variable to value.

Return type:

dict

crossings()

The equations that read out of an entity class, and what they cross.

An equation crosses when it reads an argument whose signature is not contained in its own, which means the argument carries an axis the equation must summarise away. Reading into an entity – an axis-free value used inside a per-instance equation – is an ordinary broadcast and is not reported, since nothing has to be decided about it.

Returns:

Maps a symbol to one entry per crossing argument: the argument’s name, the axes that must be reduced away, and the axes to broadcast along.

Return type:

dict[str, list[tuple[str, frozenset[str], frozenset[str]]]]

deciding_agent(control_sym)

The agent whose payoff a control maximizes.

Read off the control’s agent attribution and checked against the block’s reward owners. None where the control names no agent and the block’s utilities have a single owner, there being nothing to disambiguate.

Raises:

ValueError – If the control names no agent while the block’s utilities have several owners, or if its agent owns no reward variable here.

display(calibration, discount=None)

Displays (as in a notebook) an SVG of the visualized graph of this block.

Base style configuration is in model_visualizatio_config.yaml

Parameters:
  • calibration (dict) – A dictionary of parameters used for calibration. Here, it indicates which symbols are not dynamic.

  • discount (str, optional) – The calibration symbol serving as the discount factor, as in visualize().

Returns:

  • img (matplotlib.image.mpimg)

  • svg_content (str)

display_formulas()

Prints the model’s formulas.

entities()

The entity classes this block tree declares, by name.

Return type:

dict[str, Entity]

formulas(calibration)

Returns a dictionary of string representations of the block’s dynamic formulas.

Parameters:

calibration (dict) – A dictionary of parameters used for calibration. Here, it indicates which symbols are not dynamic.

get_arrival_states(calibration=None)
Return a list of symbols that are:
  • required by the dynamic equations of the block

  • dynamic variables themselves (controlled by dynamic equations)

This is the list of symbols that are implicitly ‘lagged’, or must be provided ‘on arrival’.

Parameters:

calibration (dict, optional) – A dictionary of parameters used for calibration. Here, it indicates which symbols are not dynamic.

get_attributions()

Return the agent assignments of variables as a dict of the form {“agent1” : [“var1”, “var2”, … ], “agent2” : [“var3”, “var4”, …]}

get_control(control_sym)

The Control declared at control_sym.

Raises:

ValueError – If control_sym is not a control of this block.

get_controls()

Returns only the Control variables from the Block dynamics.

relevance_graph(calibration=None)

Return the strategic-relevance graph over this block’s controls.

Nodes are the block’s control (decision) variables; a directed edge d1 -> d2 means decision d1 strategically relies on decision d2 (Koller & Milch s-reachability). See skagent.relevance.

Parameters:

calibration (dict, optional) – Calibration parameters, used only to identify parameter symbols. Relevance is a structural property, so this defaults to an empty dict.

Return type:

skagent.relevance.RelevanceGraph

relies_on(first, second, calibration=None)

Whether control first strategically relies on control second.

first relies on second when, to optimize the decision rule at first, the agent must account for the decision rule at second.

Parameters:
  • first (str) – Control variable names in this block. A name that is not a control raises ValueError.

  • second (str) – Control variable names in this block. A name that is not a control raises ValueError.

  • calibration (dict, optional) – See relevance_graph().

Return type:

bool

shock_roles(calibration=None)

How each shock relates to each control’s information set.

For every control, classifies every shock as observed (the information set accounts for it, so a solver may condition on it), hidden (it must be integrated out inside the maximization), or mixed (partly informed and separately relevant, which needs filtering). See skagent.relevance for the criterion.

Parameters:

calibration (dict, optional) – Calibration parameters, used only to identify parameter symbols. Defaults to empty, as for relevance_graph().

Returns:

{control: {shock: role}}.

Return type:

dict

signatures()

Each symbol’s entity signature: the classes it is an attribute of.

A symbol defined in a block carrying an entity is an attribute of that entity class. A symbol defined in a block carrying none is axis-free, and its signature is empty. An Aggregate shock is axis-free wherever it is declared, so that an economy-wide shock may sit beside the per-instance equations that read it.

Return type:

dict[str, frozenset[str]]

transition(pre, dr, screen=False, until=None, fix=None)
visualize(calibration, title=None, discount=None)

Return a PyDot graph visualization of this block.

Base style configuration is in model_visualization_config.yaml

Parameters:
  • calibration (dict) – A dictionary of parameters used for calibration. Here, it indicates which symbols are not dynamic.

  • title (str, optional) – The graph’s label. Defaults to the block’s name; pass "" to leave the graph unlabelled, which is what a caller supplying its own caption wants.

  • discount (str, optional) – The calibration symbol serving as the discount factor, drawn as such rather than as an ordinary parameter. A block does not know which of its parameters this is, since the choice is made by the period built over it: pass BellmanPeriod.discount_variable.

Returns:

  • graph (pydot.core.Dot) – A PyDot graph representation of the model.

DBlock

class skagent.block.DBlock(name='', description='', shocks=<factory>, dynamics=<factory>, reward=<factory>, entity=None)

Bases: Block

Represents a ‘block’ of model behavior. It prioritizes a representation of the dynamics of the block. Control variables are designated by the appropriate dynamic rule.

Parameters:
  • shocks (dict) –

    A mapping from variable names to Distribution objects, representing exogenous shocks.

    Optionally, the mapping can be to tuples of Distribution constructors and dictionary of input arguments. In this case, the dictionary can map argument names to numbers, or to strings. The strings are parsed as mathematical expressions and evaluated in a scope supplied when the shocks are resolved.

  • dynamics (dict) – An ordered dictionary mapping variable names to mathematical expressions. These expressions can be simple functions, in which case the argument names should match the variable inputs. Or these can be strings, which are parsed into functions. The order of dynamic equations matters, as they are applied sequentially as update rules.

  • reward (dict) – A dictionary mapping variable names to agent role labels. The variable name will almost always appear in ‘dynamics’. The agent role indicates which agent views the variable as a reward to optimize.

  • name (str)

  • description (str)

  • entity (Entity | None)

construct_shocks(calibration, rng=None)

This block’s shocks, resolved against calibration.

A shock declared as a (class, arguments) pair is resolved only against a calibration, which is what lets one block stand for the same model at many of them. The resolved distributions are therefore returned rather than stored: the block keeps the declaration, and a caller wanting a second calibration or a second generator gets it.

A shock whose arguments refer to a symbol the calibration does not assign cannot be resolved here, and raises. Resolving it needs the wider scope of construct_shocks().

Parameters:
  • calibration (dict) – Values for any symbol a shock’s arguments refer to, supplied as the scope those arguments are evaluated in.

  • rng (np.random.Generator, optional) – Generator for the constructed distributions to draw from. Since a distribution is drawn by inverting it at uniforms from its own generator, this fixes the sample path of everything returned here.

Return type:

dict[str, Distribution]

deep_replace(name=None, description=None, shocks=None, dynamics=None, reward=None)

Creates a deep copy of the block with new shocks, dynamics, and rewards dictionaries. These dictionaries will have updated values based on the inputs.

Parameters:
  • name (str | None, optional) – New name for the block. If None, keeps the original name.

  • description (str | None, optional) – New description. If None, keeps the original description.

  • shocks (dict | None, optional) – Dictionary of shocks to merge with existing shocks.

  • dynamics (dict | None, optional) – Dictionary of dynamics to merge with existing dynamics.

  • reward (dict | None, optional) – Dictionary of rewards to merge with existing rewards.

Returns:

A new DBlock instance with the merged values.

Return type:

DBlock

description: str = ''
discretize(disc_params, calibration=None)

Returns a new DBlock which is a copy of this one, but with shock discretized.

Discretizing needs the distributions themselves, so a block whose shocks are still declared as (class, arguments) pairs must be given the calibration to resolve them against.

dynamics: dict
entity: Entity | None = None
get_arrival_value_function(disc_params, dr, continuation, calibration=None)

Returns an arrival value function, which is the value of the states upon arrival into the block.

This involves taking an expectation over shocks (which must first be discretized), a decision rule, and a continuation value function.)

The expectation is over the distributions themselves, so a block whose shocks are still declared as (class, arguments) pairs must be given the calibration to resolve them against.

get_decision_value_function(dr, continuation)

Given a decision rule and a continuation value function, return a function for the value at the decision step/tac, after the shock have been realized.

get_dynamics()
get_shocks()
get_state_rule_value_function_from_continuation(continuation, screen=False)

Given a continuation value function, returns a state-rule value function: the value for each state and decision rule. This value includes both the reward for executing the rule ‘this period’, and the continuation value of the resulting states.

get_vars()

Returns the variables that are created/modified by the Block. Does not include variables that are only used as arguments to the dynamics.

iter_dblocks()

A DBlock is its own leaf.

name: str = ''
reward: dict
shocks: dict

RBlock

class skagent.block.RBlock(name='', description='', blocks=<factory>, entity=None)

Bases: Block

A recursive block.

Parameters:
blocks: List[Block]
construct_shocks(calibration, rng=None)

This recursive block’s shocks, resolved against calibration.

Merged over the sub-blocks, in their order, as get_shocks() does. Returned rather than stored, for the reason DBlock.construct_shocks() gives.

Parameters:
  • calibration (dict) – Values for any symbol a shock’s arguments refer to.

  • rng (np.random.Generator, optional) – Generator for the constructed distributions to draw from.

Return type:

dict[str, Distribution]

description: str = ''
discretize(disc_params, calibration=None)

Recursively discretizes all the blocks. It replaces any DBlocks with new blocks with discretized shocks.

property dynamics

The dynamics of all subblocks, in declaration order.

A composed block has no dynamics of its own; this is the merged view its subblocks give, so that a consumer reaching for block.dynamics sees the same thing on a composed block as on a leaf one.

entity: Entity | None = None
get_dynamics()
get_shocks()
get_vars()
iter_dblocks()

Iterate over all DBlock leaves in this RBlock tree.

name: str = ''
property reward

The reward attributions for all subblocks.

GroundedBlock

A block together with the calibration and generator it is read against. A block is calibration-independent, so a block alone does not fix its shocks; this pair does, and is what solvers, simulators and environments are handed.

class skagent.ground.GroundedBlock(block, calibration, rng=None)

Bases: object

A block together with the calibration and generator it is read against.

Owns the resolution of the block’s shock declarations into distributions: a shock declared as a (class, arguments) pair needs a calibration to resolve, so the resolved distributions belong to this pair rather than to the block.

A calibration is fixed before the model is solved or simulated, so the pair resolves every shock whose arguments refer to calibrated symbols and no others. A shock argument referring to a value known only during a solve or a run is outside what this pair holds and raises; such a shock is resolved by the caller that has the value, against a scope overlaying it on the calibration.

Parameters:
  • block (Block) – The model’s dynamics, shocks and rewards.

  • calibration (dict[str, Any]) – Values for the symbols the block’s declarations and dynamics refer to.

  • rng (Generator | None) – Generator this instance’s shocks are drawn from, however they were declared. Two instances over one block hold separate distributions, so each draws its own path.

block

The underlying block model.

Type:

Block

calibration

The calibration parameters.

Type:

dict[str, Any]

rng

The generator this instance’s shock draws come from.

Type:

numpy.random.Generator | None

draw_shocks(n)

Draw n realizations of each of this instance’s shocks.

Parameters:

n (int) – Number of realizations per shock.

Returns:

A mapping from shock symbol to its draws.

Return type:

dict[str, Any]

shock_distributions()

This instance’s shocks, resolved against its calibration.

The block declares shocks; resolving a declaration needs a calibration, which is what this class supplies. Resolved once and held, so that the generator advances across draws instead of restarting, and so that the block itself is left as its author wrote it. Every resolved shock draws from rng, whether it was declared as a (class, arguments) pair or as a distribution instance.

Return type:

dict[str, Any]

Raises:

KeyError – If a shock’s arguments refer to a symbol the calibration does not assign.

with_rng(rng)

A copy of this pair drawing from rng instead.

A new instance rather than a repointed one, so that a holder currently drawing from this pair keeps its own path: the copy resolves its shocks afresh on first access and therefore shares no distribution with the original. The block, the calibration and anything a subclass adds are carried over unchanged – a generator is a different sample of one model, not a different model.

Parameters:

rng (Generator | None) – Generator the copy’s shocks draw from.

Returns:

Of the same type as self.

Return type:

GroundedBlock

Control

class skagent.block.Control(iset, lower_bound=None, upper_bound=None, agent=None)

Bases: object

Used to designate a variable that is a control variable.

Parameters:
  • iset (list of str) – The labels of the variables that are in the information set of this control.

  • lower_bound (number, callable, or None) – The lower bound of the control variable. A number is treated as a constant bound; a callable is an ‘equation function’ whose parameter names are variables in iset. None leaves the control unbounded below.

  • upper_bound (number, callable, or None) – The upper bound of the control variable, declared the same way as lower_bound. None leaves the control unbounded above.

  • agent (str) – A label identifying the agent role to which this control is attributed.

Aggregate

class skagent.block.Aggregate(dist)

Bases: object

Used to designate a shock as an aggregate shock. If so designated, draws from the shock will be scalar rather than array valued.

Parameters:

dist (Distribution)

Entity

class skagent.block.Entity(name)

Bases: object

A class of things a model describes several of.

An entity carries a name and nothing else. How many instances exist is not a property of the model but of the population it is run over, and is read from the calibration under a key equal to this name: a model declaring Entity("firm") is simulated against a calibration containing {"firm": 3}.

A consequence worth knowing before choosing a name: because the cardinality is looked up by the entity’s name, an entity shares a namespace with the model’s parameters. A model with an entity called "firm" cannot also have an unrelated parameter called "firm".

Parameters:

name (str) – The entity class’s name, e.g. "firm" or "household". Also the calibration key giving how many instances there are.

name: str

Model Utilities

Simulation Dynamics

skagent.block.simulate_dynamics(dynamics, pre, dr, shapes=None)

From the beginning-of-period state (pre), follow the dynamics, including any decision rules, to compute the end-of-period state.

Parameters:

dynamics (Mapping[str, Union[Callable, Control]]) – Maps variable names to functions from variables to values. Can include Controls

preMapping[str, Any]

Bound values for all variables that must be known before beginning the period’s dynamics.

shapesMapping[str, tuple], optional

The shape each symbol’s value must take. An equation may return one value where its shape calls for an array, meaning the same value for every position; where shapes says so, such a value is broadcast as it is produced, so that a later equation reducing over it is handed the array it expects.

drMapping[str, Callable]

Decision rules for all the Control variables in the dynamics.

Parameters:

Shock Construction

skagent.block.construct_shocks(shock_data, scope, rng=None)

Returns a dictionary from shock labels to Distributions.

When the corresponding value in shock_data contains a distribution constructor and input information, any symbolic expressions used in the inputs are evaluated in the provided scope.

Parameters:

shock_data (Mapping(str, Distribution or tuple)) –

A mapping from variable names to Distribution objects, representing exogenous shocks.

Optionally, the mapping can be to tuples of Distribution constructors and dictionary of input arguments. In this case, the dictionary can map argument names to numbers, or to strings. The strings are parsed as mathematical expressions and evaluated in scope.

scope: dict(str, values)

The scope in which those expressions are evaluated: a symbol assigned a value here is available to every shock’s arguments.

A scope is wider than a calibration. A calibration is fixed before a model is solved or simulated, so a shock whose arguments refer only to calibrated symbols can be resolved from the calibration alone – which is what DBlock.construct_shocks() passes. A shock argument may instead refer to a value that is only known during a solve or a run, such as a dynamic variable pinned to a realization; a caller holding such a value overlays it on the calibration and passes the result here. A symbol a shock’s arguments refer to and the scope does not assign raises KeyError.

rng: np.random.Generator, optional

Random number generator to pass to distribution constructors. If provided, distributions created from tuples will use this RNG.

Discretized Shock Distribution

skagent.block.discretized_shock_dstn(shocks, disc_params)

Discretizes a collection of independent shocks and combines them into one DiscreteDistributionLabeled.

Shocks are discretized only if they have a corresponding element of disc_params defined.

Parameters:
  • shocks (dict of Distribution) – A dictionary of Distributions, representing independent exogenous shocks.

  • disc_params (dict of dict) – A dictionary of dictionaries with arguments to Distribution.discretize. Keys of this dictionary should be shared with the shocks argument.