Reference

model_solver package

model_solver.model_solver module

class ModelSolver(eqns, endo_vars)

Bases: object

ModelSolver is designed to handle and solve mathematical models represented by a system of equations.

It supports various mathematical functions such as min, max, log, and exp. This class allows you to initialize a model with a list of equations and endogenous variables. It subsequently solves the model using input data stored in a Pandas DataFrame.

Usage Example:

Let equations and endogenous be lists containing equations and endogenous variables, respectively, stored as strings, e.g.,

equations = [
    'x + y = A',
    'x / y = B'
]
endogenous = [
    'x',
    'y'
]

where ‘A’ and ‘B’ are exogenous variables.

To initialize a ModelSolver instance, use:

model = ModelSolver(equations, endogenous)

This reads in the equations and endogenous variables, performs block analysis and ordering, and generates simulation code.

To solve the model using input data in a Pandas DataFrame, let’s assume you have a DataFrame named “input_df” containing data on ‘A’ and ‘B’ as well as initial values for ‘x’ and ‘y’. You can solve the model by invoking:

solution_df = model.solve_model(input_df)

Now, solution_df is a Pandas DataFrame with the same dimensions as input_df, but with the endogenous variables replaced by the solutions to the model. The last solution is also stored in model.last_solution.

Parameters:
  • eqns (list[str])

  • endo_vars (list[str])

__init__(eqns, endo_vars)

Reads in equations and endogenous variables and does a number of operations, e.g. analyzing block structure using graph theory.

Parameters:
  • eqns (list[str]) – A list of equations in string format

  • endo_vars (list[str]) – A list of endogenous variables

Return type:

None

Example

>>> equations = ["x1 = a1", "x2 = a2", "0.2*x1+0.7*x2 = 0.1*ca+0.8*cb+0.3*i1", "0.8*x1+0.3*x2 = 0.9*ca+0.2*cb+0.1*i2", "k1 = k1(-1)+i1", "k2 = k2(-1)+i2"]
>>> endogenous = ["x1", "x2", "ca", "cb", "k1", "k2"]
>>> model = ModelSolver(equations, endogenous)
----------------------------------------------------------------------------------------------------
Initializing model
* Importing equations
* Importing endogenous variables
* Analyzing model
        * Analyzing equation strings
        * Generating bipartite graph (BiGraph) connecting equations and endogenous variables
        * Finding maximum bipartite match (MBM) (i.e. associating every equation with exactly one endogenus variable)
        * Generating directed graph (DiGraph) connecting endogenous variables using bipartite graph and MBM
        * Finding condensation of DiGraph (i.e. determining minimal blocks of systems of simulataneous equations)
        * Generating simulation code (i.e. block-wise symbolic objective function, symbolic Jacobian matrix and lists of endogenous and exogenous variables)
Finished
----------------------------------------------------------------------------------------------------
describe()

Display a summary of the model’s characteristics.

Prints information about the model, including the number of equations, blocks, simple definition blocks, and the distribution of equation counts in the blocks.

Return type:

None

draw_blockwise_graph(variable, max_ancs_gens=5, max_desc_gens=5, max_nodes=50, figsize=(7.5, 7.5))

Draws a directed graph of a block containing the given variable with a limited number of ancestors and descendants.

Parameters:
  • variable (str) – The variable for which the blockwise graph will be drawn.

  • max_ancs_gens (int) – Maximum number of generations of ancestors to include in the graph.

  • max_desc_gens (int) – Maximum number of generations of descendants to include in the graph.

  • max_nodes (int) – Maximum number of nodes to include in the graph. If the graph has more nodes, it won’t be plotted.

  • figsize (tuple[float, float]) – A tuple specifying the width and height of the figure for the graph.

Return type:

None

Example

Draws a directed graph of the block containing ‘var1’ with up to 3 generations of ancestors and 2 generations of descendants.

model = ModelSolver(equations, endogenous)
model.draw_blockwise_graph('var1', max_ancs_gens=3, max_desc_gens=2, max_nodes=30, figsize=(10, 10))
property endo_vars: tuple[str, ...]

Return the endogenous variables in the model.

property eqns: tuple[str, ...]

Return the equations in the model.

property exog_vars: tuple[str, ...]

Return the exogenous variables in the model.

find_endo_var(endo_var, noisy=False)

Find the block that solves the specified endogenous variable.

Note

This function searches for the specified endogenous variable in the model’s blocks and returns the block number of the block that solves it. If the endogenous variable is not found in any block, it returns None.

Parameters:
  • endo_var (str) – The endogenous variable to be found.

  • noisy (bool) – Whether output should be printed or returned.

Return type:

int | None

Returns:

The block number of the block that solves the specified endogenous variable. Returns None if the endogenous variable is not found in any block.

Raises:

IndexError – If endo_var is not endogenous in model.

gen_get_var_info(var_col_index)

Function that returns function that returns names, columns and lags for variables.

Return type:

Callable[[Iterable[str]], tuple[list[str], ndarray[Any, dtype[int64]], ndarray[Any, dtype[int64]]]]

Parameters:

var_col_index (dict[str, int])

property last_solution: DataFrame

Returns the last found solution in the model.

Returns:

The last found solution as a dataframe.

Raises:

AttributeError – If no solution is found.

property max_iter: int

Return maximum number of iterations.

property max_lag: int

Return max_lag in the model.

property root_tolerance: float

Return root tolerance.

sensitivity(i, period_index, method='std', exog_subset=None)

Analyses sensitivity of endogenous variables to exogenous variables for a specific period.

Parameters:
  • i (int) – The index of the block for which variable values will be displayed.

  • period_index (int) – The index of the period for which variable values will be shown.

  • exog_subset (list[str] | None) – List of exogenous variables to be analysed. If None, all relevant exogenous variables will be analysed.

  • method (str) –

    Method for sensitivity analysis. Default is ‘std’.

    • ’std’: Adjusts variables by adding their standard deviation.

    • ’pct’: Adjusts variables by adding 1% of their value.

    • ’one’: Adjusts variables by adding 1 to their value.

Return type:

DataFrame

Returns:

DataFrame showing the sensitivity of endogenous variables to exogenous variables.

Raises:
  • RuntimeError – If no solution is found.

  • ValueError – If method is not std, pct or one.

Example

model = ModelSolver(equations, endogenous)
sensitivity_df = model.sensitivity(1, 3, method='pct', exog_subset=['exog_var1', 'exog_var2'])
print(sensitivity_df)

With output:

           | endog_var1 | endog_var2 |
--------------------------------------
exog_var1  |    0.23    |    0.12    |
exog_var2  |    0.45    |    0.56    |
show_block(i)

Prints endogenous and exogenous variables and equations for a given block.

Parameters:

i (int) – The index of the block to display.

Raises:

IndexError – If block i is not in model.

Return type:

None

Example

model = ModelSolver(equations, endogenous)
model.show_block(1)

A block consists of an equation or a system of equations:

5 endogenous variables:
- var1
- var2
- var3
- var4
- var5

3 predetermined variables:
- pred_var1
- pred_var2
- pred_var3

4 equations:
- eqn1: var1 = pred_var1 + pred_var2
- eqn2: var2 = var1 + pred_var2
- eqn3: var3 = pred_var2 + pred_var3
- eqn4: var4 = var3 + pred_var1
show_block_vals(i, period_index, noisy=True)

Prints the values of endogenous and predetermined variables in a given block for a specific period.

Parameters:
  • i (int) – The index of the block for which variable values will be displayed.

  • period_index (int) – The index of the period for which variable values will be shown.

  • noisy (bool) – Whether output should be printed or returned.

Return type:

tuple[Series, Series] | tuple[None, None]

Returns:

Two pd.Series of endogenous and predetermined values, or None, None.

Raises:

RuntimeError – If no solution is found.

Example

model = ModelSolver(equations, endogenous)
model.show_block_vals(1, 3)

With output:

Block 1 has endogenous variables in 2023-01-04 that evaluate to:
var1=10.5
var2=15.2
...
Block 1 has predetermined variables in 2023-01-04 that evaluate to:
pred_var1=8.1
pred_var2=9.7
show_blocks()

Prints endogenous and exogenous variables and equations for every block in the model.

Iterates through all blocks in the model and calls the show_block function to display their details.

Return type:

None

Example

model = ModelSolver(equations, endogenous)
model.show_blocks()

The output is like this:

--------------------------------------------------
Block 1
--------------------------------------------------
Endogenous Variables:
- var1
- var2

Exogenous Variables:
- exog_var1
- exog_var2

Equations:
- eqn1: var1 = exog_var1 + exog_var2
- eqn2: var2 = var1 + exog_var2

--------------------------------------------------
Block 2
--------------------------------------------------
Endogenous Variables:
- var3
- var4

Exogenous Variables:
- exog_var3
- exog_var4

Equations:
- eqn3: var3 = exog_var3 + exog_var4
- eqn4: var4 = var3 + exog_var4

...

--------------------------------------------------
Block n
--------------------------------------------------
Endogenous Variables:
- var_n1
- var_n2

Exogenous Variables:
- exog_var_n1
- exog_var_n2

Equations:
- eqn_n1: var_n1 = exog_var_n1 + exog_var_n2
- eqn_n2: var_n2 = var_n1 + exog_var_n2
solve_model(input_df, jit=True)

Solves the model subject to a given DataFrame.

Parameters:
  • input_df (DataFrame) – A DataFrame containing input data for the model.

  • jit (bool) – Flag indicating whether to use just-in-time (JIT) compilation for solving equations.

Return type:

DataFrame

Returns:

A DataFrame containing the model’s output data.

Raises:

TypeError – If any column in input_df is not of numeric data type.

Example

>>> equations = ["x1 = a1", "x2 = a2", "0.2*x1+0.7*x2 = 0.1*ca+0.8*cb+0.3*i1", "0.8*x1+0.3*x2 = 0.9*ca+0.2*cb+0.1*i2", "k1 = k1(-1)+i1", "k2 = k2(-1)+i2"]
>>> endogenous = ["x1", "x2", "ca", "cb", "k1", "k2"]
>>> model = ModelSolver(equations, endogenous)
----------------------------------------------------------------------------------------------------
Initializing model
* Importing equations
* Importing endogenous variables
* Analyzing model
        * Analyzing equation strings
        * Generating bipartite graph (BiGraph) connecting equations and endogenous variables
        * Finding maximum bipartite match (MBM) (i.e. associating every equation with exactly one endogenus variable)
        * Generating directed graph (DiGraph) connecting endogenous variables using bipartite graph and MBM
        * Finding condensation of DiGraph (i.e. determining minimal blocks of systems of simulataneous equations)
        * Generating simulation code (i.e. block-wise symbolic objective function, symbolic Jacobian matrix and lists of endogenous and exogenous variables)
Finished
----------------------------------------------------------------------------------------------------
>>> input_data = pd.DataFrame({"x1": [2, 4, 1, 2], "x2": [2, 1, 2, 3], "ca": [1, 3, 4, 1], "cb": [1, 2, 1, 4], "k1": [1, 3, 4, 1], "k2": [1, 2, 1, 4], "a1": [1, 2, 4, 4], "a2": [3, 2, 3, 4], "i1": [1, 2, 4, 4], "i2": [3, 2, 3, 4]})
>>> output_data = model.solve_model(input_data)
----------------------------------------------------------------------------------------------------
Solving model
        First period: 1, last period: 3
        Solving
        |   |
         ...
Finished
----------------------------------------------------------------------------------------------------
switch_endo_vars(old_endo_vars, new_endo_vars)

Sets old_endo_vars as exogenous and new_endo_vars as endogenous and performs block analysis.

Note

This function switches the endogenous and exogenous status of variables and performs block analysis on the model.

Example

model = ModelSolver(equations, endogenous)
model.switch_endo_vars(['var1', 'var2'], ['var3', 'var4'])
Parameters:
  • old_endo_vars (list[str]) – List of old endogenous variables to be switched to exogenous.

  • new_endo_vars (list[str]) – List of new endogenous variables to be switched from exogenous.

Raises:

ValueError – If any variable in old_endo_vars is not in the current list of endogenous variables or if any variable in new_endo_vars is already in the list of endogenous variables.

Return type:

None

trace_to_exog_vals(i, period_index, noisy=True)

Traces the given block back to exogenous values and prints those values.

Parameters:
  • i (int) – The index of the block for which variable values will be displayed.

  • period_index (int) – The index of the period for which exogenous values will be traced.

  • noisy (bool) – Whether output should be printed or returned.

Return type:

Series | None

Returns:

A pd.Series of exogenous values, or None.

Raises:

RuntimeError – If no solution is found.

Example

model = ModelSolver(equations, endogenous)
model.trace_to_exog_vals(1, 3)

With output:

Block 1 traces back to the following exogenous variable values in 2023-01-04:
exog_var1=12.5
exog_var2=8.2
exog_var3=10.0
trace_to_exog_vars(i, noisy=True)

Prints all exogenous variables that are ancestors to the given block.

Parameters:
  • i (int) – The index of the block for which variable values will be displayed.

  • noisy (bool) – Whether output should be printed or returned.

Return type:

list[str] | None

Returns:

A list of exogenous variables that are ancestors to the given block, or None.

Example

model = ModelSolver(equations, endogenous)
model.trace_to_exog_vars(1)