def test_linker_to_dataframes_core(self): Submodel = fsic.build_model(fsic.parse_model('Y = C + I + G + X - M')) model = fsic.BaseLinker({ 'A': Submodel(range(1990, 2005 + 1)), 'B': Submodel(range(1990, 2005 + 1)), 'C': Submodel(range(1990, 2005 + 1)), }, name='test') model.add_variable('D', 0.0) results = model.to_dataframes() pd.testing.assert_frame_equal(results['test'], pd.DataFrame({'D': 0.0, 'status': '-', 'iterations': -1, }, index=range(1990, 2005 + 1))) expected = pd.DataFrame({x: 0.0 for x in 'YCIGXM'}, index=range(1990, 2005 + 1)) expected['status'] = '-' expected['iterations'] = -1 for name, submodel in model.submodels.items(): with self.subTest(submodel=name): pd.testing.assert_frame_equal(results[name], expected)
def setUp(self): super().setUp() PythonClass = fsic.build_model(self.SYMBOLS) FortranClass = self.Model # Instantiate a Python and a corresponding Fortran instance of the # model self.model_python = PythonClass(range(100), alpha_1=0.6, alpha_2=0.4) self.model_fortran = FortranClass(range(100), alpha_1=0.6, alpha_2=0.4)
def build_model(symbols, test_module_name): # Switch the working directory to the same one that contains this test # script original_working_directory = os.getcwd() os.chdir(os.path.split(__file__)[0]) # Write out a file of Fortran code fortran_definition = fsic.fortran.build_fortran_definition(symbols) with open('{}.f95'.format(test_module_name), 'w') as f: f.write(fortran_definition) # Compile the code output = subprocess.run(['f2py', '-c', '{}.f95'.format(test_module_name), '-m', test_module_name], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) # Print output on failed compile try: output.check_returncode() except subprocess.CalledProcessError as e: print(e.stdout.decode()) raise # Construct the class PythonClass = fsic.build_model(symbols) class FortranClass(fsic.fortran.FortranEngine, PythonClass): if imported_as_package: ENGINE = importlib.import_module('.{}'.format(test_module_name), os.path.split(os.path.split(__file__)[0])[1]) else: ENGINE = importlib.import_module(test_module_name) # Switch back to the original directory os.chdir(original_working_directory) return FortranClass
class TestPandasFunctions(unittest.TestCase): SYMBOLS = fsic.parse_model('Y = C + G') MODEL = fsic.build_model(SYMBOLS) def test_symbols_to_dataframe(self): result = fsic.tools.symbols_to_dataframe(self.SYMBOLS) expected = pd.DataFrame({ 'name': ['Y', 'C', 'G'], 'type': [fsic.parser.Type.ENDOGENOUS, fsic.parser.Type.EXOGENOUS, fsic.parser.Type.EXOGENOUS], 'lags': 0, 'leads': 0, 'equation': ['Y[t] = C[t] + G[t]', None, None], 'code': ['self._Y[t] = self._C[t] + self._G[t]', None, None], }) pd.testing.assert_frame_equal(result, expected) def test_dataframe_to_symbols(self): # Check by way of a roundtrip: symbols -> DataFrame -> symbols result = fsic.tools.symbols_to_dataframe(self.SYMBOLS) expected = pd.DataFrame({ 'name': ['Y', 'C', 'G'], 'type': [fsic.parser.Type.ENDOGENOUS, fsic.parser.Type.EXOGENOUS, fsic.parser.Type.EXOGENOUS], 'lags': 0, 'leads': 0, 'equation': ['Y[t] = C[t] + G[t]', None, None], 'code': ['self._Y[t] = self._C[t] + self._G[t]', None, None], }) # Initial (i.e. pre-)check only pd.testing.assert_frame_equal(result, expected) # Check by value self.assertEqual(fsic.tools.dataframe_to_symbols(result), self.SYMBOLS) self.assertEqual(fsic.tools.dataframe_to_symbols(expected), self.SYMBOLS) # Check by string representation for before, after in zip(self.SYMBOLS, fsic.tools.dataframe_to_symbols(result)): with self.subTest(symbol=before): self.assertEqual(str(before), str(after)) self.assertEqual(repr(before), repr(after)) def test_model_to_dataframe(self): model = self.MODEL(range(5)) # Check list of names is unchanged self.assertEqual(model.names, model.NAMES) result = fsic.tools.model_to_dataframe(model) expected = pd.DataFrame({ 'Y': 0.0, 'C': 0.0, 'G': 0.0, 'status': '-', 'iterations': -1 }, index=range(5)) pd.testing.assert_frame_equal(result, expected) def test_model_to_dataframe_no_status(self): model = self.MODEL(range(5)) # Check list of names is unchanged self.assertEqual(model.names, model.NAMES) expected = fsic.tools.model_to_dataframe(model).drop('status', axis='columns') result = fsic.tools.model_to_dataframe(model, status=False) pd.testing.assert_frame_equal(result, expected) def test_model_to_dataframe_no_iterations(self): model = self.MODEL(range(5)) # Check list of names is unchanged self.assertEqual(model.names, model.NAMES) expected = fsic.tools.model_to_dataframe(model).drop('iterations', axis='columns') result = fsic.tools.model_to_dataframe(model, iterations=False) pd.testing.assert_frame_equal(result, expected) def test_model_to_dataframe_no_status_or_iterations(self): model = self.MODEL(range(5)) # Check list of names is unchanged self.assertEqual(model.names, model.NAMES) expected = fsic.tools.model_to_dataframe(model).drop(['status', 'iterations'], axis='columns') result = fsic.tools.model_to_dataframe(model, status=False, iterations=False) pd.testing.assert_frame_equal(result, expected) def test_model_to_dataframe_additional_variables(self): # Check that extending the model with extra variables carries through # to the results DataFrame (including preserving variable types) model = self.MODEL(range(5)) # Check list of names is unchanged self.assertEqual(model.names, model.NAMES) model.add_variable('I', 0, dtype=int) model.add_variable('J', 0) model.add_variable('K', 0, dtype=float) model.add_variable('L', False, dtype=bool) # Check list of names is now changed self.assertEqual(model.names, model.NAMES + ['I', 'J', 'K', 'L']) result = fsic.tools.model_to_dataframe(model) expected = pd.DataFrame({ 'Y': 0.0, 'C': 0.0, 'G': 0.0, 'I': 0, # int 'J': 0.0, # float 'K': 0.0, # float (forced) 'L': False, # bool 'status': '-', 'iterations': -1 }, index=range(5)) pd.testing.assert_frame_equal(result, expected) def test_model_to_dataframe_core(self): model = self.MODEL(range(5)) # Check list of names is unchanged self.assertEqual(model.names, model.NAMES) result = model.to_dataframe() expected = pd.DataFrame({ 'Y': 0.0, 'C': 0.0, 'G': 0.0, 'status': '-', 'iterations': -1 }, index=range(5)) pd.testing.assert_frame_equal(result, expected) def test_model_to_dataframe_core_no_status(self): model = self.MODEL(range(5)) # Check list of names is unchanged self.assertEqual(model.names, model.NAMES) result = model.to_dataframe(status=False) expected = pd.DataFrame({ 'Y': 0.0, 'C': 0.0, 'G': 0.0, 'iterations': -1 }, index=range(5)) pd.testing.assert_frame_equal(result, expected) def test_model_to_dataframe_core_no_iterations(self): model = self.MODEL(range(5)) # Check list of names is unchanged self.assertEqual(model.names, model.NAMES) result = model.to_dataframe(iterations=False) expected = pd.DataFrame({ 'Y': 0.0, 'C': 0.0, 'G': 0.0, 'status': '-', }, index=range(5)) pd.testing.assert_frame_equal(result, expected) def test_model_to_dataframe_core_no_status_or_iterations(self): model = self.MODEL(range(5)) # Check list of names is unchanged self.assertEqual(model.names, model.NAMES) result = model.to_dataframe(status=False, iterations=False) expected = pd.DataFrame({ 'Y': 0.0, 'C': 0.0, 'G': 0.0, }, index=range(5)) pd.testing.assert_frame_equal(result, expected) def test_model_to_dataframe_additional_variables_core(self): # Check that extending the model with extra variables carries through # to the results DataFrame (including preserving variable types) model = self.MODEL(range(5)) # Check list of names is unchanged self.assertEqual(model.names, model.NAMES) model.add_variable('I', 0, dtype=int) model.add_variable('J', 0) model.add_variable('K', 0, dtype=float) model.add_variable('L', False, dtype=bool) # Check list of names is now changed self.assertEqual(model.names, model.NAMES + ['I', 'J', 'K', 'L']) result = model.to_dataframe() expected = pd.DataFrame({ 'Y': 0.0, 'C': 0.0, 'G': 0.0, 'I': 0, # int 'J': 0.0, # float 'K': 0.0, # float (forced) 'L': False, # bool 'status': '-', 'iterations': -1 }, index=range(5)) pd.testing.assert_frame_equal(result, expected) def test_linker_to_dataframes(self): Submodel = fsic.build_model(fsic.parse_model('Y = C + I + G + X - M')) model = fsic.BaseLinker({ 'A': Submodel(range(1990, 2005 + 1)), 'B': Submodel(range(1990, 2005 + 1)), 'C': Submodel(range(1990, 2005 + 1)), }, name='test') model.add_variable('D', 0.0) results = fsic.tools.linker_to_dataframes(model) pd.testing.assert_frame_equal(results['test'], pd.DataFrame({'D': 0.0, 'status': '-', 'iterations': -1, }, index=range(1990, 2005 + 1))) expected = pd.DataFrame({x: 0.0 for x in 'YCIGXM'}, index=range(1990, 2005 + 1)) expected['status'] = '-' expected['iterations'] = -1 for name, submodel in model.submodels.items(): with self.subTest(submodel=name): pd.testing.assert_frame_equal(results[name], expected) def test_linker_to_dataframes_core(self): Submodel = fsic.build_model(fsic.parse_model('Y = C + I + G + X - M')) model = fsic.BaseLinker({ 'A': Submodel(range(1990, 2005 + 1)), 'B': Submodel(range(1990, 2005 + 1)), 'C': Submodel(range(1990, 2005 + 1)), }, name='test') model.add_variable('D', 0.0) results = model.to_dataframes() pd.testing.assert_frame_equal(results['test'], pd.DataFrame({'D': 0.0, 'status': '-', 'iterations': -1, }, index=range(1990, 2005 + 1))) expected = pd.DataFrame({x: 0.0 for x in 'YCIGXM'}, index=range(1990, 2005 + 1)) expected['status'] = '-' expected['iterations'] = -1 for name, submodel in model.submodels.items(): with self.subTest(submodel=name): pd.testing.assert_frame_equal(results[name], expected)
if use_aliases: df = df.rename(columns={v: k for k, v in self.aliases.items()}) return df script = ''' C = {alpha_1} * YD + {alpha_2} * H[-1] YD = Y - T Y = C + G T = {theta} * Y H = H[-1] + YD - C ''' symbols = fsic.parse_model(script) SIM = fsic.build_model(symbols) class SIMAlias(AliasMixin, SIM): ALIASES = { 'GDP': 'Y', 'mpc_income': 'alpha_1', 'mpc_wealth': 'alpha_2', 'income_tax_rate': 'theta', } if __name__ == '__main__': # Run Model *SIM* as usual ----------------------------------------------- model = SIM(range(1945, 2010 + 1), alpha_1=0.6, alpha_2=0.4)
from tqdm import tqdm import fsic # Define the example model script = ''' C = {alpha_1} * YD + {alpha_2} * H[-1] YD = Y - T Y = C + G T = {theta} * Y H = H[-1] + YD - C ''' # Parse the script and generate a class definition SIM = fsic.build_model(fsic.parse_model(script)) if __name__ == '__main__': # Initialise a model instance (which will be copied in each example) base = SIM(range(1945, 2010 + 1), alpha_1=0.6, alpha_2=0.4, G=20, theta=0.2) # ------------------------------------------------------------------------- # 1. Call `iter_periods()` directly each time, wrapping the iterator with # `tqdm` (two ways shown below) # 1a. Wrap `iter_periods()` print('1a. Wrap `iter_periods()`:')
Hh = V - Bh # 6.O.15 / 6.O.16 Bh = V * ({lambda_0} + {lambda_1} * r) - {lambda_2} * YD # 6.O.17A / 6.O.18A Bs = Bs[-1] + (G + r[-1] * Bs[-1]) - (T + r[-1] * Bcb[-1]) # 6.O.19A / 6.O.20A Bcb = Bs - Bh # 6.O.21 / 6.O.22 # Note the underscore in 'or_' to avoid collisions with the Python `or` # keyword. (Any potential collisions raise an Exception.) or_ = or_[-1] + ((Hs - Hs[-1]) - (Bcb - Bcb[-1])) / p_g # 6.O.23A / 6.O.24A Hs = Hh # 6.O.25 / 6.O.26 r = r_bar # 6.O.30 / 6.O.31 ''' symbols = fsic.parse_model(script) Country = fsic.build_model(symbols) # Define the linker that connects the country models -------------------------- class OPEN(fsic.BaseLinker): """Model *OPEN*: A two-country economy.""" # Core variables (not specific to any individual submodel) work the same # way as models derived from the `fsic` `BaseModel` class ENDOGENOUS = ['xr'] EXOGENOUS = ['xr_bar', 'p_g_bar'] NAMES = ENDOGENOUS + EXOGENOUS CHECK = ENDOGENOUS # Not strictly needed but using the `__slots__` attribute like this