From df264d430550876d109086c0a6d626faf3687e5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnus=20Kv=C3=A5le=20Helliesen?= Date: Mon, 4 Mar 2024 16:30:22 +0100 Subject: [PATCH 1/7] Test for number of equations and endogenous variables --- src/model_solver/model_solver.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/model_solver/model_solver.py b/src/model_solver/model_solver.py index 06dffcc..2376311 100644 --- a/src/model_solver/model_solver.py +++ b/src/model_solver/model_solver.py @@ -163,17 +163,17 @@ def last_solution(self): @root_tolerance.setter def root_tolerance(self, val: float): if isinstance(val, float) is False: - raise ValueError('Tolerance for termination must be of type float') + raise ValueError('tolerance for termination must be of type float') if val <= 0: - raise ValueError('Tolerance for termination must be positive') + raise ValueError('tolerance for termination must be positive') self._root_tolerance = val @max_iter.setter def max_iter(self, val: int): if isinstance(val, int) is False: - raise ValueError('Maximum number of iterations must be an integer') + raise ValueError('maximum number of iterations must be an integer') if val < 0: - raise ValueError('Maximum number of iterations cannot be negative') + raise ValueError('maximum number of iterations cannot be negative') self._max_iter = val @@ -184,16 +184,20 @@ def _init_model(self, eqns: list, endo_vars: list): for i, eqn in enumerate(eqns): if eqn.strip() == '': self._some_error = True - raise ValueError('There are empty elements in equation list') + raise ValueError('there are empty elements in equation list') eqns[i] = eqns[i].lower() print('* Importing endogenous variables') for endo_var in endo_vars: if endo_var.strip() == '': self._some_error = True - raise ValueError('There are empty elements in endogenous variable list') + raise ValueError('there are empty elements in endogenous variable list') endo_vars[i] = endo_vars[i].lower() + if len(eqns) != len(endo_vars): + self._some_error = True + raise ValueError('there is a different number of equations and endogenous variables') + return tuple(eqns), tuple(endo_vars) From 9cefd2b2138f0dbdf58fde2485957ab32908d0f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnus=20Kv=C3=A5le=20Helliesen?= Date: Mon, 4 Mar 2024 16:34:23 +0100 Subject: [PATCH 2/7] Fixed docstring --- src/model_solver/model_solver.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/model_solver/model_solver.py b/src/model_solver/model_solver.py index 2376311..7959b8e 100644 --- a/src/model_solver/model_solver.py +++ b/src/model_solver/model_solver.py @@ -719,7 +719,7 @@ def show_block(self, i: int): def solve_model(self, input_df: pd.DataFrame, jit=True) -> pd.DataFrame: """ - Solves the model for a given DataFrame. + Solves the model subject to a given DataFrame. Parameters: ----------- @@ -1253,7 +1253,7 @@ def get_var_info(vars): def sensitivity(self, i: int, period_index: int, method='std', exog_subset=None): """ - Analyzes sensitivity of endogenous variables to exogenous variables for a specific period. + Analyses sensitivity of endogenous variables to exogenous variables for a specific period. Parameters: ----------- @@ -1265,11 +1265,12 @@ def sensitivity(self, i: int, period_index: int, method='std', exog_subset=None) method : str, optional Method for sensitivity analysis. Default is 'std'. - - 'std': Adjusts variables by adding their standard deviation + 1. - - 'pct': Adjusts variables by adding 1% of their value + 1. + - '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. exog_subset : list or None, optional - List of exogenous variables to be analysed. If None, all relevant exogenous vairbales will be analysed. + List of exogenous variables to be analysed. If None, all relevant exogenous variables will be analysed. Returns: -------- From 81425b5b5ad18c5f9e3eb5abdbfb2477f88f7f34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnus=20Kv=C3=A5le=20Helliesen?= Date: Mon, 4 Mar 2024 16:46:07 +0100 Subject: [PATCH 3/7] Fixed some nasty code (yikes!!!) --- src/model_solver/model_solver.py | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/src/model_solver/model_solver.py b/src/model_solver/model_solver.py index 7959b8e..77a3ac7 100644 --- a/src/model_solver/model_solver.py +++ b/src/model_solver/model_solver.py @@ -181,24 +181,20 @@ def max_iter(self, val: int): # Checks that there are no blank lines, sets everything to lowercase and returns as tuples def _init_model(self, eqns: list, endo_vars: list): print('* Importing equations') - for i, eqn in enumerate(eqns): - if eqn.strip() == '': - self._some_error = True - raise ValueError('there are empty elements in equation list') - eqns[i] = eqns[i].lower() + if any(x.strip() == '' for x in eqns): + self._some_error = True + raise ValueError('there are empty elements in equation list') print('* Importing endogenous variables') - for endo_var in endo_vars: - if endo_var.strip() == '': - self._some_error = True - raise ValueError('there are empty elements in endogenous variable list') - endo_vars[i] = endo_vars[i].lower() + if any(x.strip() == '' for x in endo_vars): + self._some_error = True + raise ValueError('there are empty elements in endogenous variable list') if len(eqns) != len(endo_vars): self._some_error = True raise ValueError('there is a different number of equations and endogenous variables') - return tuple(eqns), tuple(endo_vars) + return tuple(x.lower() for x in eqns), tuple(x.lower() for x in endo_vars) # Analyzes the equations of the model From a542ae13043acc44ac9dbefb1efbb2ec272e061f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnus=20Kv=C3=A5le=20Helliesen?= Date: Tue, 5 Mar 2024 08:45:57 +0100 Subject: [PATCH 4/7] Bump --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 42f024b..7521d85 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "ssb-model-solver" -version = "1.1.19" +version = "1.1.20" description = "Class to define, block analyse and solve dynamic and algebraic models numerically" authors = ["Magnus Helliesen "] license = "MIT" From fa0d1073f1882bf691b84cd79f8de98c231dec71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnus=20Kv=C3=A5le=20Helliesen?= Date: Tue, 5 Mar 2024 08:54:09 +0100 Subject: [PATCH 5/7] Small changes --- src/model_solver/model_solver.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/model_solver/model_solver.py b/src/model_solver/model_solver.py index 77a3ac7..39dcf66 100644 --- a/src/model_solver/model_solver.py +++ b/src/model_solver/model_solver.py @@ -115,7 +115,12 @@ def __init__(self, eqns: list[str], endo_vars: list[str]): self._eqns_analyzed, self._var_mapping, self._lag_mapping = self._analyze_eqns() # Perform block analysis and ordering of equations - self._eqns_endo_vars_match, self._condenced_model_digraph, self._augmented_condenced_model_digraph, self._node_varlist_mapping = self._block_analyze_model() + ( + self._eqns_endo_vars_match, + self._condenced_model_digraph, + self._augmented_condenced_model_digraph, + self._node_varlist_mapping + ) = self._block_analyze_model() # Generating everything needed to simulate model self._sim_code, self._blocks = self._gen_sim_code_and_blocks() @@ -402,7 +407,8 @@ def _gen_sim_code_and_blocks(self): if self._some_error: return - print('\t* Generating simulation code (i.e. block-wise symbolic objective function, symbolic Jacobian matrix and lists of endogenous and exogenous variables)') + print('\t* Generating simulation code (i.e. block-wise symbolic objective function,', + 'symbolic Jacobian matrix and lists of endogenous and exogenous variables)') sim_code, blocks = {}, {} for i, node in enumerate(reversed(tuple(self._condenced_model_digraph.nodes()))): From f922773ffdd1017c062e58bad0670601c72df5d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnus=20Kv=C3=A5le=20Helliesen?= Date: Tue, 5 Mar 2024 08:56:13 +0100 Subject: [PATCH 6/7] Tiny change --- src/model_solver/model_solver.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/model_solver/model_solver.py b/src/model_solver/model_solver.py index 39dcf66..b4a0019 100644 --- a/src/model_solver/model_solver.py +++ b/src/model_solver/model_solver.py @@ -529,7 +529,7 @@ def switch_endo_vars(self, old_endo_vars: list[str], new_endo_vars: list[str]): raise RuntimeError('some variables in new_endo_vars are endogenous') print('Analyzing model...') - self._endo_vars = (*[x for x in self._endo_vars if x not in old_endo_vars], *new_endo_vars) + self._endo_vars = *[x for x in self._endo_vars if x not in old_endo_vars], *new_endo_vars, ( self._eqns_endo_vars_match, From f23f2513759c9c2d217555ae165a2364b1d88006 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnus=20Kv=C3=A5le=20Helliesen?= Date: Tue, 5 Mar 2024 09:28:47 +0100 Subject: [PATCH 7/7] Small change to make things prettier --- src/model_solver/model_solver.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/model_solver/model_solver.py b/src/model_solver/model_solver.py index b4a0019..ad1a1aa 100644 --- a/src/model_solver/model_solver.py +++ b/src/model_solver/model_solver.py @@ -523,9 +523,9 @@ def switch_endo_vars(self, old_endo_vars: list[str], new_endo_vars: list[str]): >>> model.switch_endo_vars(['var1', 'var2'], ['var3', 'var4']) """ - if all([x in self.endo_vars for x in old_endo_vars]) is False: + if all(x in self.endo_vars for x in old_endo_vars) is False: raise RuntimeError('all variables in old_endo_vars are not endogenous') - if any([x in self.endo_vars for x in new_endo_vars]): + if any(x in self.endo_vars for x in new_endo_vars): raise RuntimeError('some variables in new_endo_vars are endogenous') print('Analyzing model...')