Example #1
0
    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)
Example #2
0
    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)
Example #3
0
    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
Example #4
0
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)
Example #5
0
        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)
Example #6
0
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()`:')
Example #7
0
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