コード例 #1
0
ファイル: test_fsic_tools.py プロジェクト: ChrisThoung/fsic
    def test_symbols_to_sympy(self):
        result = fsic.tools.symbols_to_sympy(fsic.parse_model('Y = C + G'))
        expected = {
            sympy.Symbol('Y'): sympy.Eq(sympy.Symbol('Y'), sympy.sympify('C + G')),
        }

        self.assertEqual(result, expected)
コード例 #2
0
ファイル: test_fsic_tools.py プロジェクト: ChrisThoung/fsic
    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)
コード例 #3
0
ファイル: test_fsic_tools.py プロジェクト: ChrisThoung/fsic
    def test_symbols_to_sympy_singleton_and_imaginary(self):
        # Also test (forced) conversion of 'I' to a SymPy Symbol (rather
        # than an imaginary number)
        result = fsic.tools.symbols_to_sympy(fsic.parse_model('I = S'))
        expected = {
            sympy.Symbol('I'): sympy.Eq(sympy.Symbol('I'), sympy.Symbol('S')),
        }

        self.assertEqual(result, expected)
コード例 #4
0
ファイル: test_fsic_tools.py プロジェクト: ChrisThoung/fsic
    def test_symbols_to_sympy_singleton(self):
        # Test (forced) conversion of 'S' to a SymPy Symbol (rather than a
        # Singleton)
        result = fsic.tools.symbols_to_sympy(fsic.parse_model('S = YD - C'))
        expected = {
            sympy.Symbol('S'): sympy.Eq(sympy.Symbol('S'), sympy.sympify('YD - C')),
        }

        self.assertEqual(result, expected)
コード例 #5
0
ファイル: test_fsic_tools.py プロジェクト: ChrisThoung/fsic
class TestNetworkXFunctions(unittest.TestCase):

    SYMBOLS = fsic.parse_model('Y = C + G')

    def test_symbols_to_graph(self):
        result = fsic.tools.symbols_to_graph(self.SYMBOLS)

        expected = nx.DiGraph()
        expected.add_nodes_from(['Y[t]'], equation='Y[t] = C[t] + G[t]')
        expected.add_edge('C[t]', 'Y[t]')
        expected.add_edge('G[t]', 'Y[t]')

        self.assertEqual(result.nodes, expected.nodes)
        self.assertEqual(result.edges, expected.edges)
コード例 #6
0
ファイル: test_fsic_fortran.py プロジェクト: ChrisThoung/fsic
class TestCompile(FortranTestWrapper, unittest.TestCase):

    TEST_MODULE_NAME = 'fsic_test_fortran_testcompile'

    # Define a large number of variables of the same type (here, arbitrarily,
    # exogenous variables) to check that index numbers in the resulting Fortran
    # code are line-wrapped correctly
    SCRIPT = ('RESULT = A + B + C + D + E + F + G + H + I + J + '
                       'K + L + M + N + O + P + Q + R + S + T + '
                       'U + V + W + X + Y + Z')
    SYMBOLS = fsic.parse_model(SCRIPT)

    def test_continuation_lines(self):
        # Test line wrapping for long lines
        # No code here: Check that the code successfully compiles during
        # `setUp()`
        pass
コード例 #7
0
ファイル: test_fsic_tools.py プロジェクト: ChrisThoung/fsic
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)
コード例 #8
0
ファイル: aliasing.py プロジェクト: ChrisThoung/fsic
        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)
コード例 #9
0
ファイル: test_fsic_fortran.py プロジェクト: ChrisThoung/fsic
class TestBuildAndSolve(FortranTestWrapper, unittest.TestCase):

    TEST_MODULE_NAME = 'fsic_test_fortran_testbuildandsolve'

    # Definition of a stripped-down Model *SIM* from Chapter 3 of Godley and
    # Lavoie (2007)
    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)

    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 test_initialisation_error(self):
        # Check that the Fortran class catches a missing (unlinked) Fortran
        # module
        with self.assertRaises(fsic.fortran.InitialisationError):
            fsic.fortran.FortranEngine(range(10))

    def test_evaluate(self):
        # Check Python- and Fortran-based evaluate functions generate the same
        # results

        # Python
        self.model_python.G = 20
        self.model_python.theta = 0.2

        for i in self.model_python.span[3:10]:
            for _ in range(100):
                self.model_python._evaluate(i)

        for _ in range(100):
            self.model_python._evaluate(-10)

        # Fortran
        self.model_fortran.G = 20
        self.model_fortran.theta = 0.2

        for i in self.model_fortran.span[3:10]:
            for _ in range(100):
                self.model_fortran._evaluate(i)

        for _ in range(100):
            self.model_fortran._evaluate(-10)

        # Comparison
        self.assertEqual(self.model_python.values.shape, self.model_fortran.values.shape)
        self.assertTrue(np.allclose(self.model_python.values, self.model_fortran.values))

    def test_evaluate_index_errors(self):
        # Check model instances correctly throw errors if period (`t`)
        # parameters are out of bounds
        with self.assertRaises(IndexError):
            self.model_python._evaluate(100)

        with self.assertRaises(IndexError):
            self.model_fortran._evaluate(100)

        with self.assertRaises(IndexError):
            self.model_python._evaluate(-100)

        with self.assertRaises(IndexError):
            self.model_fortran._evaluate(-100)

    def test_solve_t_max_iter(self):
        # Check that the Fortran code returns the correct number of iterations
        # if it reaches `max_iter`
        # (In Fortran, a loop that completes seems to leave the counter 1
        # higher than the loop limit. This isn't the case if the loop exits
        # early.)

        # Python
        self.model_python.G = 20
        self.model_python.theta = 0.2

        self.model_python.solve(max_iter=2, failures='ignore')
        self.assertTrue(np.all(self.model_python.iterations[1:] == 2))

        # Fortran
        self.model_fortran.G = 20
        self.model_fortran.theta = 0.2

        self.model_fortran.solve(max_iter=2, failures='ignore')
        self.assertTrue(np.all(self.model_fortran.iterations[1:] == 2))

    def test_solve_t_errors_ignore(self):
        # Check that the `errors='ignore'` solve option ignores NaNs properly

        # Python
        self.model_python.G = 20
        self.model_python.theta = 0.2
        self.model_python.C[2] = np.NaN

        # Solution should halt because of the pre-existing NaN
        with self.assertRaises(fsic.exceptions.SolutionError):
            self.model_python.solve()

        self.model_python.solve(errors='ignore')

        # Fortran
        self.model_fortran.G = 20
        self.model_fortran.theta = 0.2
        self.model_fortran.C[2] = np.NaN

        # Solution should halt because of the pre-existing NaN
        with self.assertRaises(fsic.exceptions.SolutionError):
            self.model_fortran.solve()

        self.model_fortran.solve(errors='ignore')

        # Comparison
        self.assertEqual(self.model_python.values.shape, self.model_fortran.values.shape)
        self.assertTrue(np.allclose(self.model_python.values, self.model_fortran.values))

    def test_solve(self):
        # Check Python- and Fortran-based solve functions generate the same
        # results

        # Python
        self.model_python.G = 20
        self.model_python.theta = 0.2

        self.model_python.solve()

        # Fortran
        self.model_fortran.G = 20
        self.model_fortran.theta = 0.2

        self.model_fortran.solve()

        # Comparison
        self.assertEqual(self.model_python.values.shape, self.model_fortran.values.shape)
        self.assertTrue(np.allclose(self.model_python.values, self.model_fortran.values))

    def test_solve_offset(self):
        # Check Python- and Fortran-based solve functions generate the same
        # results with the `offset` keyword argument

        # Python
        self.model_python.G = 20
        self.model_python.theta = 0.2

        self.model_python.solve(offset=-1)

        # Fortran
        self.model_fortran.G = 20
        self.model_fortran.theta = 0.2

        self.model_fortran.solve(offset=-1)

        # Comparison
        self.assertEqual(self.model_python.values.shape, self.model_fortran.values.shape)
        self.assertTrue(np.allclose(self.model_python.values, self.model_fortran.values))

    def test_solve_max_iter_non_convergence_error(self):
        # Check Python- and Fortran-based solve functions generate the same
        # error if the model fails to solve

        # Python
        self.model_python.G = 20
        self.model_python.theta = 0.2

        with self.assertRaises(fsic.exceptions.NonConvergenceError):
            self.model_python.solve(max_iter=5)

        # Fortran
        self.model_fortran.G = 20
        self.model_fortran.theta = 0.2

        with self.assertRaises(fsic.exceptions.NonConvergenceError):
            self.model_fortran.solve(max_iter=5)

        # Comparison
        self.assertEqual(self.model_python.values.shape, self.model_fortran.values.shape)
        self.assertTrue(np.allclose(self.model_python.values, self.model_fortran.values))
コード例 #10
0
ファイル: progress_bar.py プロジェクト: ChrisThoung/fsic
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()`:')