├── quantecon ├── util │ ├── tests │ │ ├── __init__.py │ │ ├── test_file.md │ │ ├── test_array.py │ │ ├── test_notebooks.py │ │ ├── test_timing.py │ │ ├── test_combinatorics.py │ │ └── test_numba.py │ ├── __init__.py │ ├── common_messages.py │ ├── random.py │ ├── array.py │ ├── combinatorics.py │ ├── numba.py │ └── notebooks.py ├── markov │ ├── tests │ │ ├── __init__.py │ │ ├── test_utilities.py │ │ ├── test_estimate.py │ │ └── test_approximation.py │ ├── __init__.py │ ├── estimate.py │ └── utilities.py ├── optimize │ ├── tests │ │ ├── __init__.py │ │ ├── test_minmax.py │ │ ├── test_scalar_max.py │ │ └── test_root_finding.py │ ├── __init__.py │ └── minmax.py ├── random │ ├── tests │ │ ├── __init__.py │ │ └── test_utilities.py │ └── __init__.py ├── game_theory │ ├── tests │ │ ├── __init__.py │ │ ├── test_utilities.py │ │ ├── test_repeated_game.py │ │ ├── test_logitdyn.py │ │ ├── test_localint.py │ │ ├── test_brd.py │ │ ├── test_pure_nash.py │ │ ├── test_support_enumeration.py │ │ ├── test_random.py │ │ └── test_fictplay.py │ ├── game_generators │ │ ├── tests │ │ │ └── __init__.py │ │ └── __init__.py │ ├── __init__.py │ ├── pure_nash.py │ └── utilities.py ├── tests │ ├── data │ │ └── matlab_quad.mat │ ├── __init__.py │ ├── test_lyapunov.py │ ├── test_filter.py │ ├── test_matrix_eqn.py │ ├── test_distributions.py │ ├── test_arma.py │ ├── test_lae.py │ ├── test_rank_nullspace.py │ ├── test_quadsum.py │ ├── test_ecdf.py │ ├── util.py │ ├── test_discrete_rv.py │ ├── test_ricatti.py │ ├── test_kalman.py │ ├── test_lss.py │ ├── test_robustlq.py │ ├── test_estspec.py │ ├── test_lqnash.py │ └── test_dle.py ├── build.sh ├── bld.bat ├── arma.py ├── ivp.py ├── lae.py ├── dle.py ├── ecdf.py ├── kalman.py ├── lqnash.py ├── filter.py ├── robustlq.py ├── lss.py ├── lqcontrol.py ├── estspec.py ├── quadsums.py ├── ce_util.py ├── rank_nullspace.py ├── discrete_rv.py ├── compute_fp.py ├── graph_tools.py ├── inequality.py ├── matrix_eqn.py ├── gridtools.py ├── _ecdf.py ├── meta.yaml ├── _filter.py ├── __init__.py ├── _discrete_rv.py ├── _lae.py ├── _quadsums.py ├── _rank_nullspace.py ├── _ce_util.py ├── distributions.py └── _inequality.py ├── MANIFEST.in ├── pytest.ini ├── docs ├── source │ ├── random.rst │ ├── tools │ │ ├── arma.rst │ │ ├── dle.rst │ │ ├── ecdf.rst │ │ ├── ivp.rst │ │ ├── lae.rst │ │ ├── lss.rst │ │ ├── quad.rst │ │ ├── filter.rst │ │ ├── kalman.rst │ │ ├── lqnash.rst │ │ ├── ce_util.rst │ │ ├── estspec.rst │ │ ├── gridtools.rst │ │ ├── lqcontrol.rst │ │ ├── quadsums.rst │ │ ├── robustlq.rst │ │ ├── compute_fp.rst │ │ ├── inequality.rst │ │ ├── matrix_eqn.rst │ │ ├── discrete_rv.rst │ │ ├── graph_tools.rst │ │ ├── distributions.rst │ │ └── rank_nullspace.rst │ ├── markov │ │ ├── ddp.rst │ │ ├── core.rst │ │ ├── random.rst │ │ ├── estimate.rst │ │ ├── gth_solve.rst │ │ ├── utilities.rst │ │ └── approximation.rst │ ├── util │ │ ├── array.rst │ │ ├── numba.rst │ │ ├── random.rst │ │ ├── timing.rst │ │ ├── notebooks.rst │ │ ├── combinatorics.rst │ │ └── common_messages.rst │ ├── game_theory │ │ ├── brd.rst │ │ ├── random.rst │ │ ├── fictplay.rst │ │ ├── localint.rst │ │ ├── logitdyn.rst │ │ ├── pure_nash.rst │ │ ├── utilities.rst │ │ ├── lemke_howson.rst │ │ ├── repeated_game.rst │ │ ├── mclennan_tourky.rst │ │ ├── normal_form_game.rst │ │ ├── vertex_enumeration.rst │ │ ├── support_enumeration.rst │ │ └── game_generators │ │ │ └── bimatrix_generators.rst │ ├── optimize │ │ ├── minmax.rst │ │ ├── pivoting.rst │ │ ├── nelder_mead.rst │ │ ├── root_finding.rst │ │ ├── linprog_simplex.rst │ │ └── scalar_maximization.rst │ ├── random │ │ └── utilities.rst │ ├── markov.rst │ ├── util.rst │ ├── optimize.rst │ ├── game_theory.rst │ ├── tools.rst │ └── index.rst ├── rtd-requirements.txt ├── README.md └── sphinxext │ ├── only_directives.py │ └── ipython_console_highlighting.py ├── Makefile ├── .gitignore ├── environment.yml ├── readthedocs.yml ├── .coveragerc ├── LICENSE ├── pyproject.toml ├── .github └── workflows │ └── ci.yml └── README.md /quantecon/util/tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /quantecon/markov/tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /quantecon/optimize/tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /quantecon/random/tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /quantecon/game_theory/tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /quantecon/game_theory/game_generators/tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /quantecon/util/tests/test_file.md: -------------------------------------------------------------------------------- 1 | This is a Simple Test File for test_notebooks.py -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.md 2 | include LICENSE.txt 3 | recursive-include quantecon/tests/data * -------------------------------------------------------------------------------- /pytest.ini: -------------------------------------------------------------------------------- 1 | [pytest] 2 | markers = 3 | slow: marks tests as slow (deselect with '-m "not slow"') 4 | -------------------------------------------------------------------------------- /docs/source/random.rst: -------------------------------------------------------------------------------- 1 | Random 2 | ====== 3 | 4 | .. toctree:: 5 | :maxdepth: 2 6 | 7 | random/utilities 8 | -------------------------------------------------------------------------------- /quantecon/tests/data/matlab_quad.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jborovicka/QuantEcon.py/master/quantecon/tests/data/matlab_quad.mat -------------------------------------------------------------------------------- /docs/source/tools/arma.rst: -------------------------------------------------------------------------------- 1 | arma 2 | ==== 3 | 4 | .. automodule:: quantecon.arma 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/tools/dle.rst: -------------------------------------------------------------------------------- 1 | dle 2 | === 3 | 4 | .. automodule:: quantecon.dle 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/tools/ecdf.rst: -------------------------------------------------------------------------------- 1 | ecdf 2 | ==== 3 | 4 | .. automodule:: quantecon.ecdf 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/tools/ivp.rst: -------------------------------------------------------------------------------- 1 | ivp 2 | === 3 | 4 | .. automodule:: quantecon.ivp 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/tools/lae.rst: -------------------------------------------------------------------------------- 1 | lae 2 | === 3 | 4 | .. automodule:: quantecon.lae 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/tools/lss.rst: -------------------------------------------------------------------------------- 1 | lss 2 | === 3 | 4 | .. automodule:: quantecon.lss 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/tools/quad.rst: -------------------------------------------------------------------------------- 1 | quad 2 | ==== 3 | 4 | .. automodule:: quantecon.quad 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/rtd-requirements.txt: -------------------------------------------------------------------------------- 1 | sphinx 2 | ipython 3 | numpydoc 4 | numba>=0.38 5 | numpy>=1.17 6 | sympy 7 | scipy>=1.5 8 | requests 9 | matplotlib 10 | -------------------------------------------------------------------------------- /docs/source/markov/ddp.rst: -------------------------------------------------------------------------------- 1 | ddp 2 | === 3 | 4 | .. automodule:: quantecon.markov.ddp 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/markov/core.rst: -------------------------------------------------------------------------------- 1 | core 2 | ==== 3 | 4 | .. automodule:: quantecon.markov.core 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/tools/filter.rst: -------------------------------------------------------------------------------- 1 | filter 2 | ====== 3 | 4 | .. automodule:: quantecon.filter 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/tools/kalman.rst: -------------------------------------------------------------------------------- 1 | kalman 2 | ====== 3 | 4 | .. automodule:: quantecon.kalman 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/tools/lqnash.rst: -------------------------------------------------------------------------------- 1 | lqnash 2 | ====== 3 | 4 | .. automodule:: quantecon.lqnash 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/util/array.rst: -------------------------------------------------------------------------------- 1 | array 2 | ===== 3 | 4 | .. automodule:: quantecon.util.array 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/util/numba.rst: -------------------------------------------------------------------------------- 1 | numba 2 | ===== 3 | 4 | .. automodule:: quantecon.util.numba 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /quantecon/game_theory/game_generators/__init__.py: -------------------------------------------------------------------------------- 1 | # flake8: noqa 2 | """ 3 | game_theory.game_generators 4 | 5 | """ 6 | from .bimatrix_generators import * 7 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # QuantEcon.py Makefile 2 | 3 | install: 4 | python setup.py install 5 | 6 | test: 7 | @echo "Running nosetests on test suite ..." 8 | nosetests -v 9 | -------------------------------------------------------------------------------- /docs/source/game_theory/brd.rst: -------------------------------------------------------------------------------- 1 | brd 2 | === 3 | 4 | .. automodule:: quantecon.game_theory.brd 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/tools/ce_util.rst: -------------------------------------------------------------------------------- 1 | ce_util 2 | ======= 3 | 4 | .. automodule:: quantecon.ce_util 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/tools/estspec.rst: -------------------------------------------------------------------------------- 1 | estspec 2 | ======= 3 | 4 | .. automodule:: quantecon.estspec 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/util/random.rst: -------------------------------------------------------------------------------- 1 | random 2 | ====== 3 | 4 | .. automodule:: quantecon.util.random 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/util/timing.rst: -------------------------------------------------------------------------------- 1 | timing 2 | ====== 3 | 4 | .. automodule:: quantecon.util.timing 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/markov/random.rst: -------------------------------------------------------------------------------- 1 | random 2 | ====== 3 | 4 | .. automodule:: quantecon.markov.random 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/optimize/minmax.rst: -------------------------------------------------------------------------------- 1 | minmax 2 | ====== 3 | 4 | .. automodule:: quantecon.optimize.minmax 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/tools/gridtools.rst: -------------------------------------------------------------------------------- 1 | gridtools 2 | ========= 3 | 4 | .. automodule:: quantecon.gridtools 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/tools/lqcontrol.rst: -------------------------------------------------------------------------------- 1 | lqcontrol 2 | ========= 3 | 4 | .. automodule:: quantecon.lqcontrol 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/tools/quadsums.rst: -------------------------------------------------------------------------------- 1 | quadsums 2 | ======== 3 | 4 | .. automodule:: quantecon.quadsums 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/tools/robustlq.rst: -------------------------------------------------------------------------------- 1 | robustlq 2 | ======== 3 | 4 | .. automodule:: quantecon.robustlq 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/markov/estimate.rst: -------------------------------------------------------------------------------- 1 | estimate 2 | ======== 3 | 4 | .. automodule:: quantecon.markov.estimate 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/tools/compute_fp.rst: -------------------------------------------------------------------------------- 1 | compute_fp 2 | ========== 3 | 4 | .. automodule:: quantecon.compute_fp 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/tools/inequality.rst: -------------------------------------------------------------------------------- 1 | inequality 2 | ========== 3 | 4 | .. automodule:: quantecon.inequality 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/tools/matrix_eqn.rst: -------------------------------------------------------------------------------- 1 | matrix_eqn 2 | ========== 3 | 4 | .. automodule:: quantecon.matrix_eqn 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/util/notebooks.rst: -------------------------------------------------------------------------------- 1 | notebooks 2 | ========= 3 | 4 | .. automodule:: quantecon.util.notebooks 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/game_theory/random.rst: -------------------------------------------------------------------------------- 1 | random 2 | ====== 3 | 4 | .. automodule:: quantecon.game_theory.random 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/markov/gth_solve.rst: -------------------------------------------------------------------------------- 1 | gth_solve 2 | ========= 3 | 4 | .. automodule:: quantecon.markov.gth_solve 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/markov/utilities.rst: -------------------------------------------------------------------------------- 1 | utilities 2 | ========= 3 | 4 | .. automodule:: quantecon.markov.utilities 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/optimize/pivoting.rst: -------------------------------------------------------------------------------- 1 | pivoting 2 | ======== 3 | 4 | .. automodule:: quantecon.optimize.pivoting 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/random/utilities.rst: -------------------------------------------------------------------------------- 1 | utilities 2 | ========= 3 | 4 | .. automodule:: quantecon.random.utilities 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/tools/discrete_rv.rst: -------------------------------------------------------------------------------- 1 | discrete_rv 2 | =========== 3 | 4 | .. automodule:: quantecon.discrete_rv 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/tools/graph_tools.rst: -------------------------------------------------------------------------------- 1 | graph_tools 2 | =========== 3 | 4 | .. automodule:: quantecon.graph_tools 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/game_theory/fictplay.rst: -------------------------------------------------------------------------------- 1 | fictplay 2 | ======== 3 | 4 | .. automodule:: quantecon.game_theory.fictplay 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/game_theory/localint.rst: -------------------------------------------------------------------------------- 1 | localint 2 | ======== 3 | 4 | .. automodule:: quantecon.game_theory.localint 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/game_theory/logitdyn.rst: -------------------------------------------------------------------------------- 1 | logitdyn 2 | ======== 3 | 4 | .. automodule:: quantecon.game_theory.logitdyn 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/game_theory/pure_nash.rst: -------------------------------------------------------------------------------- 1 | pure_nash 2 | ========= 3 | 4 | .. automodule:: quantecon.game_theory.pure_nash 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/game_theory/utilities.rst: -------------------------------------------------------------------------------- 1 | utilities 2 | ========= 3 | 4 | .. automodule:: quantecon.game_theory.utilities 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/optimize/nelder_mead.rst: -------------------------------------------------------------------------------- 1 | nelder_mead 2 | =========== 3 | 4 | .. automodule:: quantecon.optimize.nelder_mead 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/tools/distributions.rst: -------------------------------------------------------------------------------- 1 | distributions 2 | ============= 3 | 4 | .. automodule:: quantecon.distributions 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/tools/rank_nullspace.rst: -------------------------------------------------------------------------------- 1 | rank_nullspace 2 | ============== 3 | 4 | .. automodule:: quantecon.rank_nullspace 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/util/combinatorics.rst: -------------------------------------------------------------------------------- 1 | combinatorics 2 | ============= 3 | 4 | .. automodule:: quantecon.util.combinatorics 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/markov/approximation.rst: -------------------------------------------------------------------------------- 1 | approximation 2 | ============= 3 | 4 | .. automodule:: quantecon.markov.approximation 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/optimize/root_finding.rst: -------------------------------------------------------------------------------- 1 | root_finding 2 | ============ 3 | 4 | .. automodule:: quantecon.optimize.root_finding 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/game_theory/lemke_howson.rst: -------------------------------------------------------------------------------- 1 | lemke_howson 2 | ============ 3 | 4 | .. automodule:: quantecon.game_theory.lemke_howson 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/util/common_messages.rst: -------------------------------------------------------------------------------- 1 | common_messages 2 | =============== 3 | 4 | .. automodule:: quantecon.util.common_messages 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/game_theory/repeated_game.rst: -------------------------------------------------------------------------------- 1 | repeated_game 2 | ============= 3 | 4 | .. automodule:: quantecon.game_theory.repeated_game 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/optimize/linprog_simplex.rst: -------------------------------------------------------------------------------- 1 | linprog_simplex 2 | =============== 3 | 4 | .. automodule:: quantecon.optimize.linprog_simplex 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/game_theory/mclennan_tourky.rst: -------------------------------------------------------------------------------- 1 | mclennan_tourky 2 | =============== 3 | 4 | .. automodule:: quantecon.game_theory.mclennan_tourky 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/game_theory/normal_form_game.rst: -------------------------------------------------------------------------------- 1 | normal_form_game 2 | ================ 3 | 4 | .. automodule:: quantecon.game_theory.normal_form_game 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/game_theory/vertex_enumeration.rst: -------------------------------------------------------------------------------- 1 | vertex_enumeration 2 | ================== 3 | 4 | .. automodule:: quantecon.game_theory.vertex_enumeration 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/optimize/scalar_maximization.rst: -------------------------------------------------------------------------------- 1 | scalar_maximization 2 | =================== 3 | 4 | .. automodule:: quantecon.optimize.scalar_maximization 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/game_theory/support_enumeration.rst: -------------------------------------------------------------------------------- 1 | support_enumeration 2 | =================== 3 | 4 | .. automodule:: quantecon.game_theory.support_enumeration 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /quantecon/tests/__init__.py: -------------------------------------------------------------------------------- 1 | # flake8: noqa 2 | """ 3 | namespace for quantecon.tests 4 | 5 | @author : Spencer Lyon 6 | @date : 2014-08-01 13:13:59 7 | 8 | """ 9 | from . util import capture, get_data_dir, max_abs_diff 10 | -------------------------------------------------------------------------------- /docs/source/game_theory/game_generators/bimatrix_generators.rst: -------------------------------------------------------------------------------- 1 | bimatrix_generators 2 | =================== 3 | 4 | .. automodule:: quantecon.game_theory.game_generators.bimatrix_generators 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/markov.rst: -------------------------------------------------------------------------------- 1 | Markov 2 | ====== 3 | 4 | .. toctree:: 5 | :maxdepth: 2 6 | 7 | markov/approximation 8 | markov/core 9 | markov/ddp 10 | markov/estimate 11 | markov/gth_solve 12 | markov/random 13 | markov/utilities 14 | -------------------------------------------------------------------------------- /docs/source/util.rst: -------------------------------------------------------------------------------- 1 | Utilities 2 | ========= 3 | 4 | .. toctree:: 5 | :maxdepth: 2 6 | 7 | util/array 8 | util/combinatorics 9 | util/common_messages 10 | util/notebooks 11 | util/numba 12 | util/random 13 | util/timing 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.ipynb_checkpoints* 3 | 4 | build/ 5 | /dist/ 6 | quantecon.egg-info/ 7 | *.DS_Store 8 | *.TODO 9 | *.noseids 10 | *.coverage 11 | *.h5 12 | *.lcov 13 | 14 | # Numba cache files 15 | *.nbc 16 | *.nbi 17 | 18 | # Coverage 19 | 20 | coverage.xml 21 | -------------------------------------------------------------------------------- /docs/source/optimize.rst: -------------------------------------------------------------------------------- 1 | Optimize 2 | ======== 3 | 4 | .. toctree:: 5 | :maxdepth: 2 6 | 7 | optimize/linprog_simplex 8 | optimize/minmax 9 | optimize/nelder_mead 10 | optimize/pivoting 11 | optimize/root_finding 12 | optimize/scalar_maximization 13 | -------------------------------------------------------------------------------- /quantecon/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | $PYTHON setup.py install 4 | 5 | # Add more build steps here, if they are necessary. 6 | 7 | # See 8 | # http://docs.continuum.io/conda/build.html 9 | # for a list of environment variables that are set during the build process. 10 | -------------------------------------------------------------------------------- /quantecon/bld.bat: -------------------------------------------------------------------------------- 1 | "%PYTHON%" setup.py install 2 | if errorlevel 1 exit 1 3 | 4 | :: Add more build steps here, if they are necessary. 5 | 6 | :: See 7 | :: http://docs.continuum.io/conda/build.html 8 | :: for a list of environment variables that are set during the build process. 9 | -------------------------------------------------------------------------------- /quantecon/util/__init__.py: -------------------------------------------------------------------------------- 1 | # flake8: noqa 2 | """ 3 | API for QuantEcon Utilities 4 | """ 5 | 6 | from .array import searchsorted 7 | from .notebooks import fetch_nb_dependencies 8 | from .random import check_random_state, rng_integers 9 | from .timing import tic, tac, toc, loop_timer 10 | -------------------------------------------------------------------------------- /environment.yml: -------------------------------------------------------------------------------- 1 | name: qe 2 | channels: 3 | - conda-forge 4 | - defaults 5 | dependencies: 6 | - coverage 7 | - numpy 8 | - scipy 9 | - pandas 10 | - numba 11 | - sympy 12 | - ipython 13 | - flake8 14 | - requests 15 | - flit 16 | - chardet # python>3.9,osx 17 | - pytest 18 | -------------------------------------------------------------------------------- /quantecon/random/__init__.py: -------------------------------------------------------------------------------- 1 | # flake8: noqa 2 | """ 3 | Random SubPackage 4 | ================= 5 | 6 | Utilities 7 | --------- 8 | Utilities to Support Generation of Random Arrays or Matrices 9 | 1. probvec 10 | 2. sample_without_replacement 11 | 12 | .. Future Work 13 | ----------- 14 | 1. AR1 Function 15 | """ 16 | 17 | from .utilities import probvec, sample_without_replacement, draw 18 | -------------------------------------------------------------------------------- /quantecon/optimize/__init__.py: -------------------------------------------------------------------------------- 1 | # flake8: noqa 2 | """ 3 | Initialization of the optimize subpackage 4 | """ 5 | from .linprog_simplex import ( 6 | linprog_simplex, solve_tableau, get_solution, PivOptions 7 | ) 8 | from .minmax import minmax 9 | from .scalar_maximization import brent_max 10 | from .nelder_mead import nelder_mead 11 | from .root_finding import newton, newton_halley, newton_secant, bisect, brentq 12 | -------------------------------------------------------------------------------- /quantecon/markov/__init__.py: -------------------------------------------------------------------------------- 1 | # flake8: noqa 2 | """ 3 | Markov Chain SubPackge API 4 | """ 5 | 6 | from .core import MarkovChain 7 | from .core import mc_compute_stationary, mc_sample_path #-Future Deprecation-# 8 | from .gth_solve import gth_solve 9 | from .random import random_markov_chain, random_stochastic_matrix, \ 10 | random_discrete_dp 11 | from .approximation import tauchen, rouwenhorst 12 | from .ddp import DiscreteDP, backward_induction 13 | from .utilities import sa_indices 14 | from .estimate import estimate_mc, fit_discrete_mc 15 | -------------------------------------------------------------------------------- /quantecon/util/common_messages.py: -------------------------------------------------------------------------------- 1 | """ 2 | Warnings Module 3 | =============== 4 | 5 | Contains a collection of warning messages for consistent package wide notifications 6 | 7 | """ 8 | 9 | #-Numba-# 10 | numba_import_fail_message = ("Numba import failed. Falling back to non-optimized routines.\n" 11 | "This will reduce the overall performance of this package.\n" 12 | "To install please use the anaconda distribution.\n" 13 | "http://continuum.io/downloads") 14 | -------------------------------------------------------------------------------- /docs/source/game_theory.rst: -------------------------------------------------------------------------------- 1 | Game theory 2 | =========== 3 | 4 | .. toctree:: 5 | :maxdepth: 2 6 | 7 | game_theory/brd 8 | game_theory/fictplay 9 | game_theory/lemke_howson 10 | game_theory/localint 11 | game_theory/logitdyn 12 | game_theory/mclennan_tourky 13 | game_theory/normal_form_game 14 | game_theory/pure_nash 15 | game_theory/random 16 | game_theory/repeated_game 17 | game_theory/support_enumeration 18 | game_theory/utilities 19 | game_theory/vertex_enumeration 20 | game_theory/game_generators/bimatrix_generators 21 | -------------------------------------------------------------------------------- /quantecon/markov/tests/test_utilities.py: -------------------------------------------------------------------------------- 1 | """ 2 | Tests for markov/utilities.py 3 | 4 | """ 5 | from numpy.testing import assert_array_equal 6 | from quantecon.markov import sa_indices 7 | 8 | 9 | def test_sa_indices(): 10 | num_states, num_actions = 3, 4 11 | s_expected = [0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2] 12 | a_expected = [0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3] 13 | 14 | s, a = sa_indices(num_states, num_actions) 15 | 16 | for indices, indices_expected in zip([s, a], [s_expected, a_expected]): 17 | assert_array_equal(indices, indices_expected) 18 | -------------------------------------------------------------------------------- /quantecon/tests/test_lyapunov.py: -------------------------------------------------------------------------------- 1 | """ 2 | Tests for ricatti.py 3 | 4 | """ 5 | import numpy as np 6 | from numpy.testing import assert_allclose 7 | from quantecon import solve_discrete_lyapunov 8 | 9 | 10 | def test_dlyap_simple_ones(): 11 | A = np.zeros((4, 4)) 12 | B = np.ones((4, 4)) 13 | 14 | sol = solve_discrete_lyapunov(A, B) 15 | 16 | assert_allclose(sol, np.ones((4, 4))) 17 | 18 | 19 | def test_dlyap_scalar(): 20 | a = .5 21 | b = .75 22 | 23 | sol = solve_discrete_lyapunov(a, b) 24 | 25 | assert_allclose(sol, np.ones((1, 1))) 26 | -------------------------------------------------------------------------------- /docs/source/tools.rst: -------------------------------------------------------------------------------- 1 | Tools 2 | ===== 3 | 4 | .. toctree:: 5 | :maxdepth: 2 6 | 7 | tools/arma 8 | tools/ce_util 9 | tools/compute_fp 10 | tools/discrete_rv 11 | tools/distributions 12 | tools/dle 13 | tools/ecdf 14 | tools/estspec 15 | tools/filter 16 | tools/graph_tools 17 | tools/gridtools 18 | tools/inequality 19 | tools/ivp 20 | tools/kalman 21 | tools/lae 22 | tools/lqcontrol 23 | tools/lqnash 24 | tools/lss 25 | tools/matrix_eqn 26 | tools/quad 27 | tools/quadsums 28 | tools/rank_nullspace 29 | tools/robustlq 30 | -------------------------------------------------------------------------------- /readthedocs.yml: -------------------------------------------------------------------------------- 1 | # .readthedocs.yaml 2 | # Read the Docs configuration file 3 | # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details 4 | 5 | # Required 6 | version: 2 7 | 8 | # Set the version of Python and other tools you might need 9 | build: 10 | os: ubuntu-20.04 11 | tools: 12 | python: "3.9" 13 | 14 | # Build documentation in the docs/ directory with Sphinx 15 | sphinx: 16 | configuration: docs/source/conf.py 17 | 18 | # Optionally declare the Python requirements required to build your docs 19 | python: 20 | install: 21 | - requirements: docs/rtd-requirements.txt 22 | - method: pip 23 | path: . 24 | -------------------------------------------------------------------------------- /docs/source/index.rst: -------------------------------------------------------------------------------- 1 | ======================= 2 | QuantEcon documentation 3 | ======================= 4 | 5 | The `quantecon` python library consists of a number of modules which 6 | includes game theory (game_theory), markov chains (markov), random 7 | generation utilities (random), a collection of tools (tools), 8 | and other utilities (util) which are 9 | mainly used by developers internal to the package. 10 | 11 | .. toctree:: 12 | :maxdepth: 2 13 | 14 | game_theory 15 | markov 16 | optimize 17 | random 18 | tools 19 | util 20 | 21 | Indices and tables 22 | ================== 23 | 24 | * :ref:`genindex` 25 | * :ref:`modindex` 26 | * :ref:`search` 27 | -------------------------------------------------------------------------------- /quantecon/util/tests/test_array.py: -------------------------------------------------------------------------------- 1 | """ 2 | Tests for Array Utilities 3 | 4 | Functions 5 | --------- 6 | searchsorted 7 | 8 | """ 9 | import numpy as np 10 | from numpy.testing import assert_ 11 | from quantecon.util import searchsorted 12 | 13 | 14 | def test_searchsorted(): 15 | a = np.array([0.2, 0.4, 1.0]) 16 | assert_(searchsorted(a, 0.1) == 0) 17 | assert_(searchsorted(a, 0.4) == 2) 18 | assert_(searchsorted(a, 2) == 3) 19 | 20 | a = np.ones(0) 21 | for (v, i) in zip([0, 1, 2], [0, 0, 0]): 22 | assert_(searchsorted(a, v) == i) 23 | 24 | a = np.ones(1) 25 | for (v, i) in zip([0, 1, 2], [0, 1, 1]): 26 | assert_(searchsorted(a, v) == i) 27 | 28 | a = np.ones(2) 29 | for (v, i) in zip([0, 1, 2], [0, 2, 2]): 30 | assert_(searchsorted(a, v) == i) 31 | -------------------------------------------------------------------------------- /quantecon/arma.py: -------------------------------------------------------------------------------- 1 | # This file is not meant for public use and will be removed v0.8.0. 2 | # Use the `quantecon` namespace for importing the objects 3 | # included below. 4 | 5 | import warnings 6 | from . import _arma 7 | 8 | 9 | __all__ = ['ARMA'] 10 | 11 | 12 | def __dir__(): 13 | return __all__ 14 | 15 | 16 | def __getattr__(name): 17 | if name not in __all__: 18 | raise AttributeError( 19 | f"`quantecon.arma` is deprecated and has no attribute '{name}'." 20 | ) 21 | 22 | warnings.warn(f"Please use `{name}` from the `quantecon` namespace, " 23 | "the `quantecon.arma` namespace is deprecated. You can use " 24 | f"the following instead:\n `from quantecon import {name}`.", 25 | category=DeprecationWarning, stacklevel=2) 26 | 27 | return getattr(_arma, name) 28 | -------------------------------------------------------------------------------- /quantecon/ivp.py: -------------------------------------------------------------------------------- 1 | # This file is not meant for public use and will be removed v0.8.0. 2 | # Use the `quantecon` namespace for importing the objects 3 | # included below. 4 | 5 | import warnings 6 | from . import _ivp 7 | 8 | 9 | __all__ = ['IVP'] 10 | 11 | 12 | def __dir__(): 13 | return __all__ 14 | 15 | 16 | def __getattr__(name): 17 | if name not in __all__: 18 | raise AttributeError( 19 | "`quantecon.ivp` is deprecated and has no attribute " 20 | f"'{name}'." 21 | ) 22 | 23 | warnings.warn(f"Please use `{name}` from the `quantecon` namespace, the" 24 | "`quantecon.ivp` namespace is deprecated. You can use" 25 | f" the following instead:\n `from quantecon import {name}`.", 26 | category=DeprecationWarning, stacklevel=2) 27 | 28 | return getattr(_ivp, name) 29 | -------------------------------------------------------------------------------- /quantecon/lae.py: -------------------------------------------------------------------------------- 1 | # This file is not meant for public use and will be removed v0.8.0. 2 | # Use the `quantecon` namespace for importing the objects 3 | # included below. 4 | 5 | import warnings 6 | from . import _lae 7 | 8 | 9 | __all__ = ['LAE'] 10 | 11 | 12 | def __dir__(): 13 | return __all__ 14 | 15 | 16 | def __getattr__(name): 17 | if name not in __all__: 18 | raise AttributeError( 19 | "`quantecon.lae` is deprecated and has no attribute " 20 | f"'{name}'." 21 | ) 22 | 23 | warnings.warn(f"Please use `{name}` from the `quantecon` namespace, the" 24 | "`quantecon.lae` namespace is deprecated. You can use" 25 | f" the following instead:\n `from quantecon import {name}`.", 26 | category=DeprecationWarning, stacklevel=2) 27 | 28 | return getattr(_lae, name) 29 | -------------------------------------------------------------------------------- /quantecon/dle.py: -------------------------------------------------------------------------------- 1 | # This file is not meant for public use and will be removed v0.8.0. 2 | # Use the `quantecon` namespace for importing the objects 3 | # included below. 4 | 5 | import warnings 6 | from . import _dle 7 | 8 | 9 | __all__ = ['DLE'] 10 | 11 | 12 | def __dir__(): 13 | return __all__ 14 | 15 | 16 | def __getattr__(name): 17 | if name not in __all__: 18 | raise AttributeError( 19 | "`quantecon.dle` is deprecated and has no attribute " 20 | f"'{name}'." 21 | ) 22 | 23 | warnings.warn(f"Please use `{name}` from the `quantecon` namespace, " 24 | "the `quantecon.dle` namespace is deprecated. You can use " 25 | f"the following instead:\n `from quantecon import {name}`.", 26 | category=DeprecationWarning, stacklevel=2) 27 | 28 | return getattr(_dle, name) 29 | -------------------------------------------------------------------------------- /quantecon/ecdf.py: -------------------------------------------------------------------------------- 1 | # This file is not meant for public use and will be removed v0.8.0. 2 | # Use the `quantecon` namespace for importing the objects 3 | # included below. 4 | 5 | import warnings 6 | from . import _ecdf 7 | 8 | 9 | __all__ = ['ECDF'] 10 | 11 | 12 | def __dir__(): 13 | return __all__ 14 | 15 | 16 | def __getattr__(name): 17 | if name not in __all__: 18 | raise AttributeError( 19 | "`quantecon.ecdf` is deprecated and has no attribute " 20 | f"'{name}'." 21 | ) 22 | 23 | warnings.warn(f"Please use `{name}` from the `quantecon` namespace, " 24 | "the `quantecon.ecdf` namespace is deprecated. You can use " 25 | f"the following instead:\n `from quantecon import {name}`.", 26 | category=DeprecationWarning, stacklevel=2) 27 | 28 | return getattr(_ecdf, name) 29 | -------------------------------------------------------------------------------- /quantecon/kalman.py: -------------------------------------------------------------------------------- 1 | # This file is not meant for public use and will be removed v0.8.0. 2 | # Use the `quantecon` namespace for importing the objects 3 | # included below. 4 | 5 | import warnings 6 | from . import _kalman 7 | 8 | 9 | __all__ = ['Kalman'] 10 | 11 | 12 | def __dir__(): 13 | return __all__ 14 | 15 | 16 | def __getattr__(name): 17 | if name not in __all__: 18 | raise AttributeError( 19 | "`quantecon.kalman` is deprecated and has no attribute " 20 | f"'{name}'." 21 | ) 22 | 23 | warnings.warn(f"Please use `{name}` from the `quantecon` namespace, the" 24 | "`quantecon.kalman` namespace is deprecated. You can use" 25 | f" the following instead:\n `from quantecon import {name}`.", 26 | category=DeprecationWarning, stacklevel=2) 27 | 28 | return getattr(_kalman, name) 29 | -------------------------------------------------------------------------------- /quantecon/lqnash.py: -------------------------------------------------------------------------------- 1 | # This file is not meant for public use and will be removed v0.8.0. 2 | # Use the `quantecon` namespace for importing the objects 3 | # included below. 4 | 5 | import warnings 6 | from . import _lqnash 7 | 8 | 9 | __all__ = ['nnash'] 10 | 11 | 12 | def __dir__(): 13 | return __all__ 14 | 15 | 16 | def __getattr__(name): 17 | if name not in __all__: 18 | raise AttributeError( 19 | "`quantecon.lqnash` is deprecated and has no attribute " 20 | f"'{name}'." 21 | ) 22 | 23 | warnings.warn(f"Please use `{name}` from the `quantecon` namespace, the" 24 | "`quantecon.lqnash` namespace is deprecated. You can use" 25 | f" the following instead:\n `from quantecon import {name}`.", 26 | category=DeprecationWarning, stacklevel=2) 27 | 28 | return getattr(_lqnash, name) 29 | -------------------------------------------------------------------------------- /quantecon/filter.py: -------------------------------------------------------------------------------- 1 | # This file is not meant for public use and will be removed v0.8.0. 2 | # Use the `quantecon` namespace for importing the objects 3 | # included below. 4 | 5 | import warnings 6 | from . import _filter 7 | 8 | 9 | __all__ = ['hamilton_filter'] 10 | 11 | 12 | def __dir__(): 13 | return __all__ 14 | 15 | 16 | def __getattr__(name): 17 | if name not in __all__: 18 | raise AttributeError( 19 | "`quantecon.filter` is deprecated and has no attribute " 20 | f"'{name}'." 21 | ) 22 | 23 | warnings.warn(f"Please use `{name}` from the `quantecon` namespace, " 24 | "the `quantecon.filter` namespace is deprecated. You can use" 25 | f" the following instead:\n `from quantecon import {name}`.", 26 | category=DeprecationWarning, stacklevel=2) 27 | 28 | return getattr(_filter, name) 29 | -------------------------------------------------------------------------------- /quantecon/robustlq.py: -------------------------------------------------------------------------------- 1 | # This file is not meant for public use and will be removed v0.8.0. 2 | # Use the `quantecon` namespace for importing the objects 3 | # included below. 4 | 5 | import warnings 6 | from . import _robustlq 7 | 8 | 9 | __all__ = ['RBLQ'] 10 | 11 | 12 | def __dir__(): 13 | return __all__ 14 | 15 | 16 | def __getattr__(name): 17 | if name not in __all__: 18 | raise AttributeError( 19 | "`quantecon.robustlq` is deprecated and has no attribute " 20 | f"'{name}'." 21 | ) 22 | 23 | warnings.warn(f"Please use `{name}` from the `quantecon` namespace, the" 24 | "`quantecon.robustlq` namespace is deprecated. You can use" 25 | f" the following instead:\n `from quantecon import {name}`.", 26 | category=DeprecationWarning, stacklevel=2) 27 | 28 | return getattr(_robustlq, name) 29 | -------------------------------------------------------------------------------- /quantecon/lss.py: -------------------------------------------------------------------------------- 1 | # This file is not meant for public use and will be removed v0.8.0. 2 | # Use the `quantecon` namespace for importing the objects 3 | # included below. 4 | 5 | import warnings 6 | from . import _lss 7 | 8 | 9 | __all__ = ['LinearStateSpace', 'simulate_linear_model'] 10 | 11 | 12 | def __dir__(): 13 | return __all__ 14 | 15 | 16 | def __getattr__(name): 17 | if name not in __all__: 18 | raise AttributeError( 19 | "`quantecon.lss` is deprecated and has no attribute " 20 | f"'{name}'." 21 | ) 22 | 23 | warnings.warn(f"Please use `{name}` from the `quantecon` namespace, the" 24 | "`quantecon.lss` namespace is deprecated. You can use" 25 | f" the following instead:\n `from quantecon import {name}`.", 26 | category=DeprecationWarning, stacklevel=2) 27 | 28 | return getattr(_lss, name) 29 | -------------------------------------------------------------------------------- /quantecon/lqcontrol.py: -------------------------------------------------------------------------------- 1 | # This file is not meant for public use and will be removed v0.8.0. 2 | # Use the `quantecon` namespace for importing the objects 3 | # included below. 4 | 5 | import warnings 6 | from . import _lqcontrol 7 | 8 | 9 | __all__ = ['LQ', 'LQMarkov'] 10 | 11 | 12 | def __dir__(): 13 | return __all__ 14 | 15 | 16 | def __getattr__(name): 17 | if name not in __all__: 18 | raise AttributeError( 19 | "`quantecon.lqcontrol` is deprecated and has no attribute " 20 | f"'{name}'." 21 | ) 22 | 23 | warnings.warn(f"Please use `{name}` from the `quantecon` namespace, the " 24 | "`quantecon.lqcontrol` namespace is deprecated. You can use" 25 | f" the following instead:\n `from quantecon import {name}`.", 26 | category=DeprecationWarning, stacklevel=2) 27 | 28 | return getattr(_lqcontrol, name) 29 | -------------------------------------------------------------------------------- /quantecon/estspec.py: -------------------------------------------------------------------------------- 1 | # This file is not meant for public use and will be removed v0.8.0. 2 | # Use the `quantecon` namespace for importing the objects 3 | # included below. 4 | 5 | import warnings 6 | from . import _estspec 7 | 8 | 9 | __all__ = ['smooth', 'periodogram', 'ar_periodogram'] 10 | 11 | 12 | def __dir__(): 13 | return __all__ 14 | 15 | 16 | def __getattr__(name): 17 | if name not in __all__: 18 | raise AttributeError( 19 | "`quantecon.estspec` is deprecated and has no attribute " 20 | f"'{name}'." 21 | ) 22 | 23 | warnings.warn(f"Please use `{name}` from the `quantecon` namespace, " 24 | "the `quantecon.estspec` namespace is deprecated. You can use" 25 | f" the following instead:\n `from quantecon import {name}`.", 26 | category=DeprecationWarning, stacklevel=2) 27 | 28 | return getattr(_estspec, name) 29 | -------------------------------------------------------------------------------- /quantecon/quadsums.py: -------------------------------------------------------------------------------- 1 | # This file is not meant for public use and will be removed v0.8.0. 2 | # Use the `quantecon` namespace for importing the objects 3 | # included below. 4 | 5 | import warnings 6 | from . import _quadsums 7 | 8 | 9 | __all__ = ['var_quadratic_sum', 'm_quadratic_sum'] 10 | 11 | 12 | def __dir__(): 13 | return __all__ 14 | 15 | 16 | def __getattr__(name): 17 | if name not in __all__: 18 | raise AttributeError( 19 | "`quantecon.quadsums` is deprecated and has no attribute " 20 | f"'{name}'." 21 | ) 22 | 23 | warnings.warn(f"Please use `{name}` from the `quantecon` namespace, the" 24 | "`quantecon.quadsums` namespace is deprecated. You can use" 25 | f" the following instead:\n `from quantecon import {name}`.", 26 | category=DeprecationWarning, stacklevel=2) 27 | 28 | return getattr(_quadsums, name) 29 | -------------------------------------------------------------------------------- /quantecon/ce_util.py: -------------------------------------------------------------------------------- 1 | # This file is not meant for public use and will be removed v0.8.0. 2 | # Use the `quantecon` namespace for importing the objects 3 | # included below. 4 | 5 | import warnings 6 | from . import _ce_util 7 | 8 | 9 | __all__ = ['ckron', 'gridmake'] 10 | 11 | 12 | def __dir__(): 13 | return __all__ 14 | 15 | 16 | def __getattr__(name): 17 | if name not in __all__: 18 | raise AttributeError( 19 | "`quantecon.ce_util` is deprecated and has no attribute " 20 | f"'{name}'." 21 | ) 22 | 23 | warnings.warn(f"Please use `{name}` from the `quantecon` namespace, " 24 | "the `quantecon.ce_util` namespace is deprecated. You can " 25 | "use the following instead:\n " 26 | f"`from quantecon import {name}`.", 27 | category=DeprecationWarning, stacklevel=2) 28 | 29 | return getattr(_ce_util, name) 30 | -------------------------------------------------------------------------------- /quantecon/rank_nullspace.py: -------------------------------------------------------------------------------- 1 | # This file is not meant for public use and will be removed v0.8.0. 2 | # Use the `quantecon` namespace for importing the objects 3 | # included below. 4 | 5 | import warnings 6 | from . import _rank_nullspace 7 | 8 | 9 | __all__ = ['rank_est', 'nullspace'] 10 | 11 | 12 | def __dir__(): 13 | return __all__ 14 | 15 | 16 | def __getattr__(name): 17 | if name not in __all__: 18 | raise AttributeError( 19 | "`quantecon.rank_nullspace` is deprecated and has no attribute" 20 | f" '{name}'." 21 | ) 22 | 23 | warnings.warn(f"Please use `{name}` from the `quantecon` namespace, the" 24 | "`quantecon.rank_nullspace` namespace is deprecated. You can" 25 | f" use following instead:\n `from quantecon import {name}`.", 26 | category=DeprecationWarning, stacklevel=2) 27 | 28 | return getattr(_rank_nullspace, name) 29 | -------------------------------------------------------------------------------- /quantecon/discrete_rv.py: -------------------------------------------------------------------------------- 1 | # This file is not meant for public use and will be removed v0.8.0. 2 | # Use the `quantecon` namespace for importing the objects 3 | # included below. 4 | 5 | import warnings 6 | from . import _discrete_rv 7 | 8 | 9 | __all__ = ['DiscreteRV'] 10 | 11 | 12 | def __dir__(): 13 | return __all__ 14 | 15 | 16 | def __getattr__(name): 17 | if name not in __all__: 18 | raise AttributeError( 19 | "`quantecon.discrete_rv` is deprecated and has no attribute " 20 | f"'{name}'." 21 | ) 22 | 23 | warnings.warn(f"Please use `{name}` from the `quantecon` namespace, " 24 | "the `quantecon.discrete_rv` namespace is deprecated. You " 25 | "can use the following instead:\n " 26 | f"`from quantecon import {name}`.", 27 | category=DeprecationWarning, stacklevel=2) 28 | 29 | return getattr(_discrete_rv, name) 30 | -------------------------------------------------------------------------------- /quantecon/compute_fp.py: -------------------------------------------------------------------------------- 1 | # This file is not meant for public use and will be removed v0.8.0. 2 | # Use the `quantecon` namespace for importing the objects 3 | # included below. 4 | 5 | import warnings 6 | from . import _compute_fp 7 | 8 | 9 | __all__ = ['compute_fixed_point'] 10 | 11 | 12 | def __dir__(): 13 | return __all__ 14 | 15 | 16 | def __getattr__(name): 17 | if name not in __all__: 18 | raise AttributeError( 19 | "`quantecon.compute_fp` is deprecated and has no attribute " 20 | f"'{name}'." 21 | ) 22 | 23 | warnings.warn(f"Please use `{name}` from the `quantecon` namespace, " 24 | "the `quantecon.compute_fp` namespace is deprecated. You " 25 | "can use the following instead:\n " 26 | f"`from quantecon import {name}`.", 27 | category=DeprecationWarning, stacklevel=2) 28 | 29 | return getattr(_compute_fp, name) 30 | -------------------------------------------------------------------------------- /quantecon/graph_tools.py: -------------------------------------------------------------------------------- 1 | # This file is not meant for public use and will be removed v0.8.0. 2 | # Use the `quantecon` namespace for importing the objects 3 | # included below. 4 | 5 | import warnings 6 | from . import _graph_tools 7 | 8 | 9 | __all__ = ['DiGraph', 'random_tournament_graph', 'annotate_nodes'] 10 | 11 | 12 | def __dir__(): 13 | return __all__ 14 | 15 | 16 | def __getattr__(name): 17 | if name not in __all__: 18 | raise AttributeError( 19 | "`quantecon.graph_tools` is deprecated and has no attribute " 20 | f"'{name}'." 21 | ) 22 | 23 | warnings.warn(f"Please use `{name}` from the `quantecon` namespace, the" 24 | "`quantecon.graph_tools` namespace is deprecated. You can use" 25 | f" the following instead:\n `from quantecon import {name}`.", 26 | category=DeprecationWarning, stacklevel=2) 27 | 28 | return getattr(_graph_tools, name) 29 | -------------------------------------------------------------------------------- /quantecon/inequality.py: -------------------------------------------------------------------------------- 1 | # This file is not meant for public use and will be removed v0.8.0. 2 | # Use the `quantecon` namespace for importing the objects 3 | # included below. 4 | 5 | import warnings 6 | from . import _inequality 7 | 8 | 9 | __all__ = ['lorenz_curve', 'gini_coefficient', 'shorrocks_index', 'rank_size'] 10 | 11 | 12 | def __dir__(): 13 | return __all__ 14 | 15 | 16 | def __getattr__(name): 17 | if name not in __all__: 18 | raise AttributeError( 19 | "`quantecon.inequality` is deprecated and has no attribute " 20 | f"'{name}'." 21 | ) 22 | 23 | warnings.warn(f"Please use `{name}` from the `quantecon` namespace, the" 24 | "`quantecon.inequality` namespace is deprecated. You can use" 25 | f" the following instead:\n `from quantecon import {name}`.", 26 | category=DeprecationWarning, stacklevel=2) 27 | 28 | return getattr(_inequality, name) 29 | -------------------------------------------------------------------------------- /quantecon/game_theory/__init__.py: -------------------------------------------------------------------------------- 1 | # flake8: noqa 2 | """ 3 | Game Theory SubPackage 4 | 5 | """ 6 | from .normal_form_game import Player, NormalFormGame 7 | from .normal_form_game import pure2mixed, best_response_2p 8 | from .random import ( 9 | random_game, covariance_game, random_pure_actions, random_mixed_actions 10 | ) 11 | from .pure_nash import pure_nash_brute, pure_nash_brute_gen 12 | from .support_enumeration import support_enumeration, support_enumeration_gen 13 | from .lemke_howson import lemke_howson 14 | from .mclennan_tourky import mclennan_tourky 15 | from .vertex_enumeration import vertex_enumeration, vertex_enumeration_gen 16 | from .game_generators import ( 17 | blotto_game, ranking_game, sgc_game, tournament_game, unit_vector_game 18 | ) 19 | from .repeated_game import RepeatedGame 20 | from .fictplay import FictitiousPlay, StochasticFictitiousPlay 21 | from .localint import LocalInteraction 22 | from .brd import BRD, KMR, SamplingBRD 23 | from .logitdyn import LogitDynamics 24 | -------------------------------------------------------------------------------- /quantecon/matrix_eqn.py: -------------------------------------------------------------------------------- 1 | # This file is not meant for public use and will be removed v0.8.0. 2 | # Use the `quantecon` namespace for importing the objects 3 | # included below. 4 | 5 | import warnings 6 | from . import _matrix_eqn 7 | 8 | 9 | __all__ = ['solve_discrete_lyapunov', 'solve_discrete_riccati', 10 | 'solve_discrete_riccati_system'] 11 | 12 | 13 | def __dir__(): 14 | return __all__ 15 | 16 | 17 | def __getattr__(name): 18 | if name not in __all__: 19 | raise AttributeError( 20 | "`quantecon.matrix_eqn` is deprecated and has no attribute " 21 | f"'{name}'." 22 | ) 23 | 24 | warnings.warn(f"Please use `{name}` from the `quantecon` namespace, the" 25 | "`quantecon.matrix_eqn` namespace is deprecated. You can use" 26 | f" the following instead:\n `from quantecon import {name}`.", 27 | category=DeprecationWarning, stacklevel=2) 28 | 29 | return getattr(_matrix_eqn, name) 30 | -------------------------------------------------------------------------------- /quantecon/tests/test_filter.py: -------------------------------------------------------------------------------- 1 | """ 2 | Tests for filter.py. 3 | Using the data of original paper. 4 | 5 | """ 6 | 7 | import os 8 | import pandas as pd 9 | import numpy as np 10 | from numpy.testing import assert_allclose 11 | from quantecon import hamilton_filter 12 | from quantecon.tests.util import get_data_dir 13 | 14 | def test_hamilton_filter(): 15 | # read data 16 | data_dir = get_data_dir() 17 | data = pd.read_csv(os.path.join(data_dir, "employment.csv"), 18 | names = ['year', 'employment', 'matlab_cyc', 'matlab_cyc_rw']) 19 | 20 | data['hamilton_cyc'], data['hamilton_trend'] = hamilton_filter( 21 | 100*np.log(data['employment']), 8, 4) 22 | data['hamilton_cyc_rw'], data['hamilton_trend_rw'] = hamilton_filter( 23 | 100*np.log(data['employment']), 8) 24 | assert_allclose(data['matlab_cyc'], data['hamilton_cyc'], 25 | rtol = 1e-07, atol = 1e-07) 26 | assert_allclose(data['matlab_cyc_rw'], data['hamilton_cyc_rw']) 27 | -------------------------------------------------------------------------------- /quantecon/gridtools.py: -------------------------------------------------------------------------------- 1 | # This file is not meant for public use and will be removed v0.8.0. 2 | # Use the `quantecon` namespace for importing the objects 3 | # included below. 4 | 5 | import warnings 6 | from . import _gridtools 7 | 8 | 9 | __all__ = ['cartesian', 'mlinspace', 'cartesian_nearest_index', 'simplex_grid', 10 | 'simplex_index', 'num_compositions', 'num_compositions_jit'] 11 | 12 | 13 | def __dir__(): 14 | return __all__ 15 | 16 | 17 | def __getattr__(name): 18 | if name not in __all__: 19 | raise AttributeError( 20 | "`quantecon.gridtools` is deprecated and has no attribute " 21 | f"'{name}'." 22 | ) 23 | 24 | warnings.warn(f"Please use `{name}` from the `quantecon` namespace, the" 25 | "`quantecon.gridtools` namespace is deprecated. You can use" 26 | f" the following instead:\n `from quantecon import {name}`.", 27 | category=DeprecationWarning, stacklevel=2) 28 | 29 | return getattr(_gridtools, name) 30 | -------------------------------------------------------------------------------- /quantecon/tests/test_matrix_eqn.py: -------------------------------------------------------------------------------- 1 | """ 2 | Tests for quantecon.util 3 | 4 | """ 5 | import numpy as np 6 | from numpy.testing import assert_allclose 7 | from quantecon import _matrix_eqn as qme 8 | 9 | 10 | def test_solve_discrete_lyapunov_zero(): 11 | 'Simple test where X is all zeros' 12 | A = np.eye(4) * .95 13 | B = np.zeros((4, 4)) 14 | 15 | X = qme.solve_discrete_lyapunov(A, B) 16 | 17 | assert_allclose(X, np.zeros((4, 4))) 18 | 19 | 20 | def test_solve_discrete_lyapunov_B(): 21 | 'Simple test where X is same as B' 22 | A = np.ones((2, 2)) * .5 23 | B = np.array([[.5, -.5], [-.5, .5]]) 24 | 25 | X = qme.solve_discrete_lyapunov(A, B) 26 | 27 | assert_allclose(B, X) 28 | 29 | def test_solve_discrete_lyapunov_complex(): 30 | 'Complex test, A is companion matrix' 31 | A = np.array([[0.5 + 0.3j, 0.1 + 0.1j], 32 | [ 1, 0]]) 33 | B = np.eye(2) 34 | 35 | X = qme.solve_discrete_lyapunov(A, B) 36 | 37 | assert_allclose(np.dot(np.dot(A, X), A.conj().transpose()) - X, -B, 38 | atol=1e-15) 39 | -------------------------------------------------------------------------------- /.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | # relative_files = True is necessary as per documentation of 3 | # https://github.com/AndreMiras/coveralls-python-action 4 | relative_files = True 5 | source = quantecon 6 | omit = 7 | */python?.?/* 8 | */lib-python/?.?/*.py 9 | */lib_pypy/_*.py 10 | */site-packages/ordereddict.py 11 | */site-packages/nose/* 12 | */unittest2/* 13 | 14 | [report] 15 | show_missing = True 16 | # Regexes for lines to exclude from consideration 17 | exclude_lines = 18 | # Have to re-enable the standard pragma 19 | pragma: no cover 20 | 21 | # Don't complain about missing debug-only code: 22 | def __repr__ 23 | if self\.debug 24 | 25 | # Don't complain if tests don't hit defensive assertion code: 26 | raise AssertionError 27 | raise NotImplementedError 28 | 29 | # Don't complain if non-runnable code isn't run: 30 | if 0: 31 | if __name__ == .__main__.: 32 | 33 | # Don't count Numba nopython=True jit functions 34 | @jit 35 | @jit\(.*nopython=True 36 | @njit 37 | @generated_jit\(.*nopython=True 38 | @guvectorize\(.*nopython=True 39 | -------------------------------------------------------------------------------- /quantecon/tests/test_distributions.py: -------------------------------------------------------------------------------- 1 | """ 2 | Tests for distributions.py 3 | 4 | """ 5 | import numpy as np 6 | from numpy.testing import assert_allclose, assert_ 7 | from math import sqrt 8 | from quantecon.distributions import BetaBinomial 9 | 10 | 11 | class TestBetaBinomial: 12 | def setup_method(self): 13 | self.n = 100 14 | self.a = 5 15 | self.b = 5 16 | self.test_obj = BetaBinomial(self.n, self.a, self.b) 17 | 18 | def test_init(self): 19 | assert_(self.test_obj.n == self.n) 20 | assert_(self.test_obj.a == self.a) 21 | assert_(self.test_obj.b == self.b) 22 | 23 | def test_mean(self): 24 | assert_(self.test_obj.mean == 50) 25 | 26 | def test_std(self): 27 | assert_(self.test_obj.std == sqrt(250)) 28 | 29 | def test_var(self): 30 | assert_(self.test_obj.var == 250) 31 | 32 | def test_skew(self): 33 | assert_(self.test_obj.skew == 0) 34 | 35 | def test_pdf(self): 36 | n = 9 37 | a = 1 38 | b = 1 39 | test_obj = BetaBinomial(n, a, b) 40 | assert_allclose(test_obj.pdf(), np.full(n+1, 1/(n+1))) 41 | -------------------------------------------------------------------------------- /quantecon/tests/test_arma.py: -------------------------------------------------------------------------------- 1 | """ 2 | Tests for arma.py file. Most of this testing can be considered 3 | covered by the numpy tests since we rely on much of their code. 4 | 5 | """ 6 | import numpy as np 7 | from numpy.testing import assert_array_equal, assert_ 8 | from quantecon import ARMA 9 | 10 | 11 | class TestARMA(): 12 | def setup_method(self): 13 | # Initial Values 14 | phi = np.array([.95, -.4, -.4]) 15 | theta = np.zeros(3) 16 | sigma = .15 17 | 18 | 19 | self.lp = ARMA(phi, theta, sigma) 20 | 21 | def teardown_method(self): 22 | del self.lp 23 | 24 | def test_simulate(self): 25 | lp = self.lp 26 | 27 | sim = lp.simulation(ts_length=250) 28 | 29 | assert_(sim.size == 250) 30 | 31 | def test_simulate_with_seed(self): 32 | lp = self.lp 33 | seed = 5 34 | sim0 = lp.simulation(ts_length=10, random_state=seed) 35 | sim1 = lp.simulation(ts_length=10, random_state=seed) 36 | 37 | assert_array_equal(sim0, sim1) 38 | 39 | def test_impulse_response(self): 40 | lp = self.lp 41 | 42 | imp_resp = lp.impulse_response(impulse_length=75) 43 | 44 | assert_(imp_resp.size == 75) 45 | -------------------------------------------------------------------------------- /quantecon/tests/test_lae.py: -------------------------------------------------------------------------------- 1 | """ 2 | Tests for lae.py 3 | 4 | TODO: write (economically) meaningful tests for this module 5 | 6 | """ 7 | import numpy as np 8 | from numpy.testing import assert_ 9 | from scipy.stats import lognorm 10 | from quantecon import LAE 11 | 12 | # copied from the lae lecture 13 | s = 0.2 14 | delta = 0.1 15 | a_sigma = 0.4 # A = exp(B) where B ~ N(0, a_sigma) 16 | alpha = 0.4 # We set f(k) = k**alpha 17 | phi = lognorm(a_sigma) 18 | 19 | 20 | def p(x, y): 21 | d = s * x**alpha 22 | return phi.pdf((y - (1 - delta) * x) / d) / d 23 | 24 | # other data 25 | n_a, n_b, n_y = 50, (5, 5), 20 26 | a = np.random.rand(n_a) + 0.01 27 | b = np.random.rand(*n_b) + 0.01 28 | 29 | y = np.linspace(0, 10, 20) 30 | 31 | lae_a = LAE(p, a) 32 | lae_b = LAE(p, b) 33 | 34 | 35 | def test_x_flattened(): 36 | "lae: is x flattened and reshaped" 37 | # should have a trailing singleton dimension 38 | assert_(lae_b.X.shape[-1] == 1) 39 | assert_(lae_a.X.shape[-1] == 1) 40 | 41 | 42 | def test_x_2d(): 43 | "lae: is x 2d" 44 | assert_(lae_a.X.ndim == 2) 45 | assert_(lae_b.X.ndim == 2) 46 | 47 | 48 | def test_call_shapes(): 49 | "lae: shape of call to lae" 50 | assert_(lae_a(y).shape == (n_y,)) 51 | assert_(lae_b(y).shape == (n_y,)) 52 | -------------------------------------------------------------------------------- /quantecon/util/tests/test_notebooks.py: -------------------------------------------------------------------------------- 1 | """ 2 | Tests for Notebook Utilities 3 | 4 | Functions 5 | --------- 6 | fetch_nb_dependencies 7 | 8 | """ 9 | 10 | from quantecon.util import fetch_nb_dependencies 11 | import os 12 | 13 | FILES = ['test_file.md'] 14 | REPO = "https://github.com/QuantEcon/QuantEcon.py" 15 | RAW = "raw" 16 | BRANCH = "main" 17 | FOLDER = "quantecon/util/tests/" 18 | 19 | 20 | class TestNotebookUtils: 21 | 22 | def test_fetch_nb_dependencies(self): 23 | """ 24 | Run First and Test Download 25 | """ 26 | status = fetch_nb_dependencies( 27 | files=FILES, repo=REPO, raw=RAW, branch=BRANCH, folder=FOLDER) 28 | assert(not (False in status)) 29 | 30 | def test_fetch_nb_dependencies_overwrite(self): 31 | """ 32 | Run Second and Ensure file is skipped by checking a False is found in status 33 | """ 34 | status = fetch_nb_dependencies( 35 | files=FILES, repo=REPO, raw=RAW, branch=BRANCH, folder=FOLDER) #First will succeed 36 | status = fetch_nb_dependencies( 37 | files=FILES, repo=REPO, raw=RAW, branch=BRANCH, folder=FOLDER) #Second should skip 38 | assert(False in status) 39 | 40 | def teardown_method(self): 41 | os.remove("test_file.md") 42 | -------------------------------------------------------------------------------- /quantecon/optimize/tests/test_minmax.py: -------------------------------------------------------------------------------- 1 | """ 2 | Tests for minmax 3 | 4 | """ 5 | import numpy as np 6 | from numpy.testing import assert_, assert_allclose 7 | 8 | from quantecon.optimize import minmax 9 | from quantecon.game_theory import NormalFormGame, Player, lemke_howson 10 | 11 | 12 | class TestMinmax: 13 | def test_RPS(self): 14 | A = np.array( 15 | [[0, 1, -1], 16 | [-1, 0, 1], 17 | [1, -1, 0], 18 | [-1, -1, -1]] 19 | ) 20 | v_expected = 0 21 | x_expected = [1/3, 1/3, 1/3, 0] 22 | y_expected = [1/3, 1/3, 1/3] 23 | 24 | v, x, y = minmax(A) 25 | 26 | assert_allclose(v, v_expected) 27 | assert_allclose(x, x_expected) 28 | assert_allclose(y, y_expected) 29 | 30 | def test_random_matrix(self): 31 | seed = 12345 32 | rng = np.random.default_rng(seed) 33 | size = (10, 15) 34 | A = rng.normal(size=size) 35 | v, x, y = minmax(A) 36 | 37 | for z in [x, y]: 38 | assert_((z >= 0).all()) 39 | assert_allclose(z.sum(), 1) 40 | 41 | g = NormalFormGame((Player(A), Player(-A.T))) 42 | NE = lemke_howson(g) 43 | assert_allclose(v, NE[0] @ A @ NE[1]) 44 | assert_(g.is_nash((x, y))) 45 | -------------------------------------------------------------------------------- /quantecon/tests/test_rank_nullspace.py: -------------------------------------------------------------------------------- 1 | """ 2 | Tests for rank_nullspace.py 3 | 4 | """ 5 | import numpy as np 6 | from numpy.linalg import matrix_rank as np_rank 7 | from quantecon import rank_est, nullspace 8 | 9 | 10 | class TestRankNullspace: 11 | 12 | def setup_method(self): 13 | self.A1 = np.eye(6) 14 | self.A2 = np.array([[1., 0, 0], [0., 1., 0], [1., 1., 0.]]) 15 | self.A3 = np.zeros((3, 3)) 16 | 17 | def teardown_method(self): 18 | del self.A1 19 | del self.A2 20 | del self.A3 21 | 22 | def testRankwithNumpy(self): 23 | A1, A2, A3 = self.A1, self.A2, self.A3 24 | qe_A1 = rank_est(A1) 25 | qe_A2 = rank_est(A2) 26 | qe_A3 = rank_est(A3) 27 | 28 | np_A1 = np_rank(A1) 29 | np_A2 = np_rank(A2) 30 | np_A3 = np_rank(A3) 31 | 32 | assert(qe_A1 == np_A1 and qe_A2 == np_A2 and qe_A3 == np_A3) 33 | 34 | 35 | def testNullspacewithPaper(self): 36 | A1, A2, A3 = self.A1, self.A2, self.A3 37 | ns_A1 = nullspace(A1).squeeze() 38 | ns_A2 = nullspace(A2).squeeze() 39 | ns_A3 = nullspace(A3).squeeze() 40 | 41 | assert(np.allclose(ns_A1, np.array([])) and 42 | np.allclose(ns_A2, np.array([0, 0, 1])) and 43 | np.allclose(ns_A3, np.eye(3))) 44 | -------------------------------------------------------------------------------- /quantecon/tests/test_quadsum.py: -------------------------------------------------------------------------------- 1 | """ 2 | Tests for quadsums.py 3 | 4 | """ 5 | import numpy as np 6 | from numpy.testing import assert_allclose 7 | from quantecon import var_quadratic_sum, m_quadratic_sum 8 | 9 | 10 | def test_var_simplesum(): 11 | beta = .95 12 | A = 1. 13 | C = 0. 14 | H = 1. 15 | x0 = 1. 16 | 17 | val = var_quadratic_sum(A, C, H, beta, x0) 18 | 19 | assert abs(val-20) < 1e-10 20 | 21 | 22 | def test_var_identitysum(): 23 | beta = .95 24 | A = np.eye(3) 25 | C = np.zeros((3, 3)) 26 | H = np.eye(3) 27 | x0 = np.ones(3) 28 | 29 | val = var_quadratic_sum(A, C, H, beta, x0) 30 | 31 | assert(abs(val-60) < 1e-10) 32 | 33 | 34 | def test_m_simplesum(): 35 | a = np.sqrt(.95) 36 | b = 1 37 | 38 | retval = m_quadratic_sum(a, b) 39 | 40 | assert(abs(retval - 20) < 1e-8) 41 | 42 | 43 | def test_m_matsum(): 44 | 45 | a = np.eye(3) * .99 46 | b = np.eye(3) 47 | 48 | retval = m_quadratic_sum(a, b) 49 | 50 | summedval = np.zeros_like(a) 51 | 52 | for i in range(5000): 53 | summedval = summedval + a**i * b * a.T**i 54 | 55 | assert_allclose(retval, summedval, atol=1e-5, rtol=0) 56 | 57 | 58 | 59 | if __name__ == '__main__': 60 | test_simplesum() 61 | test_identitysum() 62 | test_m_simplesum() 63 | test_m_identitysum 64 | -------------------------------------------------------------------------------- /quantecon/_ecdf.py: -------------------------------------------------------------------------------- 1 | """ 2 | Implements the empirical cumulative distribution function given an array 3 | of observations. 4 | 5 | """ 6 | 7 | import numpy as np 8 | 9 | 10 | class ECDF: 11 | """ 12 | One-dimensional empirical distribution function given a vector of 13 | observations. 14 | 15 | Parameters 16 | ---------- 17 | observations : array_like 18 | An array of observations 19 | 20 | Attributes 21 | ---------- 22 | observations : see Parameters 23 | 24 | """ 25 | 26 | def __init__(self, observations): 27 | self.observations = np.asarray(observations) 28 | 29 | def __repr__(self): 30 | return self.__str__() 31 | 32 | def __str__(self): 33 | m = "Empirical CDF:\n - number of observations: {n}" 34 | return m.format(n=self.observations.size) 35 | 36 | def __call__(self, x): 37 | """ 38 | Evaluates the ecdf at x 39 | 40 | Parameters 41 | ---------- 42 | x : scalar(float) 43 | The x at which the ecdf is evaluated 44 | 45 | Returns 46 | ------- 47 | scalar(float) 48 | Fraction of the sample less than x 49 | 50 | """ 51 | def f(a): 52 | return np.mean(self.observations <= a) 53 | vf = np.frompyfunc(f, 1, 1) 54 | return vf(x).astype(float) 55 | -------------------------------------------------------------------------------- /quantecon/tests/test_ecdf.py: -------------------------------------------------------------------------------- 1 | """ 2 | Tests for ecdf.py 3 | 4 | """ 5 | import numpy as np 6 | from numpy.testing import assert_, assert_allclose 7 | from quantecon import ECDF 8 | 9 | 10 | class TestECDF: 11 | 12 | @classmethod 13 | def setup_method(cls): 14 | cls.obs = np.random.rand(40) # observations defining dist 15 | cls.ecdf = ECDF(cls.obs) 16 | 17 | def test_call_high(self): 18 | "ecdf: x above all obs give 1.0" 19 | # all of self.obs <= 1 so ecdf(1.1) should be 1 20 | assert_allclose(self.ecdf(1.1), 1.0) 21 | 22 | def test_call_low(self): 23 | "ecdf: x below all obs give 0.0" 24 | # all of self.obs <= 1 so ecdf(1.1) should be 1 25 | assert_allclose(self.ecdf(-0.1), 0.0) 26 | 27 | def test_ascending(self): 28 | "ecdf: larger values should return F(x) at least as big" 29 | x = np.random.rand() 30 | F_1 = self.ecdf(x) 31 | F_2 = self.ecdf(1.1 * x) 32 | assert_(F_2 >= F_1) 33 | 34 | def test_vectorized(self): 35 | "ecdf: testing vectorized __call__ method" 36 | t = np.linspace(-1, 1, 100) 37 | e = self.ecdf(t) 38 | assert_(t.shape == e.shape) 39 | assert_(e.dtype == float) 40 | t = np.linspace(-1, 1, 100).reshape(2, 2, 25) 41 | e = self.ecdf(t) 42 | assert_(t.shape == e.shape) 43 | assert_(e.dtype == float) 44 | -------------------------------------------------------------------------------- /quantecon/util/random.py: -------------------------------------------------------------------------------- 1 | """ 2 | Utilities to Support Random State Infrastructure 3 | """ 4 | 5 | import numpy as np 6 | import numbers 7 | 8 | # To be called as util.rng_integers 9 | from scipy._lib._util import rng_integers # noqa: F401 10 | 11 | 12 | # Random States 13 | 14 | def check_random_state(seed): 15 | """ 16 | Turn `seed` into a `np.random.RandomState` instance. 17 | 18 | Parameters 19 | ---------- 20 | seed : None, int, `np.random.RandomState`, or `np.random.Generator` 21 | If None, the `np.random.RandomState` singleton is returned. If 22 | `seed` is an int, a new ``RandomState`` instance seeded with 23 | `seed` is returned. If `seed` is already a `RandomState` or 24 | `Generator` instance, then that instance is returned. 25 | 26 | Returns 27 | ------- 28 | `np.random.RandomState` or `np.random.Generator` 29 | Random number generator. 30 | 31 | Notes 32 | ----- 33 | This code was originally sourced from scikit-learn. 34 | 35 | """ 36 | if seed is None or seed is np.random: 37 | return np.random.mtrand._rand 38 | if isinstance(seed, (numbers.Integral, np.integer)): 39 | return np.random.RandomState(seed) 40 | if isinstance(seed, (np.random.RandomState, np.random.Generator)): 41 | return seed 42 | raise ValueError('%r cannot be used to seed a numpy.random.RandomState' 43 | ' instance' % seed) 44 | -------------------------------------------------------------------------------- /quantecon/tests/util.py: -------------------------------------------------------------------------------- 1 | """ 2 | Utilities for testing within quantecon 3 | 4 | """ 5 | import sys 6 | import os 7 | from contextlib import contextmanager 8 | import numpy as np 9 | 10 | if sys.version_info[0] == 2: 11 | from cStringIO import StringIO 12 | else: # python 3 13 | from io import StringIO 14 | 15 | 16 | @contextmanager 17 | def capture(command, *args, **kwargs): 18 | """ 19 | A context manager to capture std out, so we can write tests that 20 | depend on messages that are printed to stdout 21 | 22 | References 23 | ---------- 24 | http://schinckel.net/2013/04/15/capture-and-test-sys.stdout-sys. 25 | stderr-in-unittest.testcase/ 26 | 27 | Examples 28 | -------- 29 | class FooTest(unittest.TestCase): 30 | def test_printed_msg(self): 31 | with capture(func, *args, **kwargs) as output: 32 | self.assertRegexpMatches(output, 'should be in print msg') 33 | 34 | """ 35 | out, sys.stdout = sys.stdout, StringIO() 36 | command(*args, **kwargs) 37 | sys.stdout.seek(0) 38 | yield sys.stdout.read() 39 | sys.stdout = out 40 | 41 | 42 | def get_data_dir(): 43 | "Return directory where data is stored" 44 | this_dir = os.path.dirname(__file__) 45 | data_dir = os.path.join(this_dir, "data") 46 | return data_dir 47 | 48 | 49 | def max_abs_diff(a1, a2): 50 | "return max absolute difference between two arrays" 51 | return np.max(np.abs(a1 - a2)) 52 | -------------------------------------------------------------------------------- /quantecon/util/array.py: -------------------------------------------------------------------------------- 1 | """ 2 | Array Utilities 3 | =============== 4 | 5 | Array 6 | ----- 7 | searchsorted 8 | 9 | """ 10 | 11 | from numba import jit 12 | 13 | # ----------------- # 14 | # -ARRAY UTILITIES- # 15 | # ----------------- # 16 | 17 | @jit(nopython=True) 18 | def searchsorted(a, v): 19 | """ 20 | Custom version of np.searchsorted. Return the largest index `i` such 21 | that `a[i-1] <= v < a[i]` (for `i = 0`, `v < a[0]`); if `v[n-1] <= 22 | v`, return `n`, where `n = len(a)`. 23 | 24 | Parameters 25 | ---------- 26 | a : ndarray(float, ndim=1) 27 | Input array. Must be sorted in ascending order. 28 | 29 | v : scalar(float) 30 | Value to be compared with elements of `a`. 31 | 32 | Returns 33 | ------- 34 | scalar(int) 35 | Largest index `i` such that `a[i-1] <= v < a[i]`, or len(a) if 36 | no such index exists. 37 | 38 | Notes 39 | ----- 40 | This routine is jit-complied if the module Numba is vailable; if 41 | not, it is an alias of np.searchsorted(a, v, side='right'). 42 | 43 | Examples 44 | -------- 45 | >>> a = np.array([0.2, 0.4, 1.0]) 46 | >>> searchsorted(a, 0.1) 47 | 0 48 | >>> searchsorted(a, 0.4) 49 | 2 50 | >>> searchsorted(a, 2) 51 | 3 52 | 53 | """ 54 | lo = -1 55 | hi = len(a) 56 | while(lo < hi-1): 57 | m = (lo + hi) // 2 58 | if v < a[m]: 59 | hi = m 60 | else: 61 | lo = m 62 | return hi 63 | -------------------------------------------------------------------------------- /quantecon/game_theory/tests/test_utilities.py: -------------------------------------------------------------------------------- 1 | """ 2 | Tests for game_theory/utilities.py 3 | 4 | """ 5 | import numpy as np 6 | from numpy.testing import assert_array_equal 7 | from quantecon.game_theory.utilities import ( 8 | _copy_action_to, _copy_action_profile_to 9 | ) 10 | 11 | 12 | def test_copy_action_to(): 13 | pure_action = 1 14 | n = 3 15 | mixed_action = np.zeros(n) 16 | mixed_action[pure_action] = 1 17 | dst = np.empty(n) 18 | _copy_action_to(dst, pure_action) 19 | assert_array_equal(dst, mixed_action) 20 | 21 | mixed_action = np.array([0.2, 0.3, 0.5]) 22 | n = len(mixed_action) 23 | dst = np.empty(n) 24 | _copy_action_to(dst, mixed_action) 25 | assert_array_equal(dst, mixed_action) 26 | 27 | 28 | def test_copy_action_profile_to(): 29 | pure_action = 1 30 | n0 = 3 31 | mixed_action0 = np.zeros(n0) 32 | mixed_action0[pure_action] = 1 33 | 34 | action_profile = (pure_action, np.array([0.2, 0.3, 0.5]), [0.4, 0.6]) 35 | mixed_actions = (mixed_action0,) + action_profile[1:] 36 | nums_actions = tuple(len(x) for x in mixed_actions) 37 | dst = tuple(np.empty(n) for n in nums_actions) 38 | 39 | _copy_action_profile_to(dst, mixed_actions) 40 | for x, y in zip(dst, mixed_actions): 41 | assert_array_equal(x, y) 42 | 43 | 44 | if __name__ == '__main__': 45 | import sys 46 | import nose 47 | 48 | argv = sys.argv[:] 49 | argv.append('--verbose') 50 | argv.append('--nocapture') 51 | nose.main(argv=argv, defaultTest=__file__) 52 | -------------------------------------------------------------------------------- /quantecon/optimize/tests/test_scalar_max.py: -------------------------------------------------------------------------------- 1 | """ 2 | Tests for scalar maximization. 3 | 4 | """ 5 | import numpy as np 6 | from numpy.testing import assert_almost_equal, assert_raises 7 | from numba import njit 8 | 9 | from quantecon.optimize import brent_max 10 | 11 | 12 | @njit 13 | def f(x): 14 | """ 15 | A function for testing on. 16 | """ 17 | return -(x + 2.0)**2 + 1.0 18 | 19 | 20 | def test_f(): 21 | """ 22 | Uses the function f defined above to test the scalar maximization 23 | routine. 24 | """ 25 | true_fval = 1.0 26 | true_xf = -2.0 27 | xf, fval, info = brent_max(f, -2, 2) 28 | assert_almost_equal(true_fval, fval, decimal=4) 29 | assert_almost_equal(true_xf, xf, decimal=4) 30 | 31 | 32 | @njit 33 | def g(x, y): 34 | """ 35 | A multivariate function for testing on. 36 | """ 37 | return -x**2 + y 38 | 39 | 40 | def test_g(): 41 | """ 42 | Uses the function g defined above to test the scalar maximization 43 | routine. 44 | """ 45 | y = 5 46 | true_fval = 5.0 47 | true_xf = -0.0 48 | xf, fval, info = brent_max(g, -10, 10, args=(y,)) 49 | assert_almost_equal(true_fval, fval, decimal=4) 50 | assert_almost_equal(true_xf, xf, decimal=4) 51 | 52 | 53 | def test_invalid_a_brent_max(): 54 | assert_raises(ValueError, brent_max, f, -np.inf, 2) 55 | 56 | 57 | def test_invalid_b_brent_max(): 58 | assert_raises(ValueError, brent_max, f, -2, -np.inf) 59 | 60 | 61 | def test_invalid_a_b_brent_max(): 62 | assert_raises(ValueError, brent_max, f, 1, 0) 63 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Copyright © 2013-2021 Thomas J. Sargent and John Stachurski: BSD-3 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright 12 | notice, this list of conditions and the following disclaimer in the 13 | documentation and/or other materials provided with the distribution. 14 | 15 | 3. Neither the name of the copyright holder nor the names of its 16 | contributors may be used to endorse or promote products derived from 17 | this software without specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 24 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 25 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 26 | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 27 | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY 29 | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 | POSSIBILITY OF SUCH DAMAGE. 31 | -------------------------------------------------------------------------------- /quantecon/util/tests/test_timing.py: -------------------------------------------------------------------------------- 1 | """ 2 | Tests for timing.py 3 | 4 | """ 5 | 6 | import time 7 | from numpy.testing import assert_allclose, assert_ 8 | from quantecon.util import tic, tac, toc, loop_timer 9 | 10 | 11 | class TestTicTacToc: 12 | def setup_method(self): 13 | self.h = 0.1 14 | self.digits = 10 15 | 16 | def test_timer(self): 17 | 18 | tic() 19 | 20 | time.sleep(self.h) 21 | tm1 = tac() 22 | 23 | time.sleep(self.h) 24 | tm2 = tac() 25 | 26 | time.sleep(self.h) 27 | tm3 = toc() 28 | 29 | rtol = 2 30 | atol = 0.05 31 | 32 | for actual, desired in zip([tm1, tm2, tm3], 33 | [self.h, self.h, self.h*3]): 34 | assert_allclose(actual, desired, atol=atol, rtol=rtol) 35 | 36 | def test_loop(self): 37 | 38 | def test_function_one_arg(n): 39 | return time.sleep(n) 40 | 41 | def test_function_two_arg(n, a): 42 | return time.sleep(n) 43 | 44 | test_one_arg = \ 45 | loop_timer(5, test_function_one_arg, self.h, digits=10) 46 | test_two_arg = \ 47 | loop_timer(5, test_function_two_arg, [self.h, 1], digits=10) 48 | 49 | rtol = 2 50 | atol = 0.05 51 | 52 | for tm in test_one_arg: 53 | assert_allclose(tm, self.h, atol=atol, rtol=rtol) 54 | for tm in test_two_arg: 55 | assert_allclose(tm, self.h, atol=atol, rtol=rtol) 56 | 57 | for (average_time, average_of_best) in [test_one_arg, test_two_arg]: 58 | assert_(average_time >= average_of_best) 59 | -------------------------------------------------------------------------------- /quantecon/meta.yaml: -------------------------------------------------------------------------------- 1 | package: 2 | name: quantecon 3 | version: !!str 0.1.2 4 | 5 | source: 6 | fn: quantecon-0.1.2.tar.gz 7 | url: https://pypi.python.org/packages/source/q/quantecon/quantecon-0.1.2.tar.gz 8 | md5: 245bcef5f18d1afe858cf95453c28934 9 | # patches: 10 | # List any patch files here 11 | # - fix.patch 12 | 13 | # build: 14 | #preserve_egg_dir: True 15 | #entry_points: 16 | # Put any entry points (scripts to be generated automatically) here. The 17 | # syntax is module:function. For example 18 | # 19 | # - quantecon = quantecon:main 20 | # 21 | # Would create an entry point called quantecon that calls quantecon.main() 22 | 23 | 24 | # If this is a new build for the same version, increment the build 25 | # number. If you do not include this key, it defaults to 0. 26 | # number: 1 27 | 28 | requirements: 29 | build: 30 | - python 31 | - setuptools 32 | # - numpy 33 | # - scipy 34 | # - pandas 35 | 36 | run: 37 | - python 38 | - numpy 39 | - scipy 40 | - pandas 41 | 42 | test: 43 | # Python imports 44 | imports: 45 | - quantecon 46 | 47 | #commands: 48 | # You can put test commands to be run here. Use this to test that the 49 | # entry points work. 50 | 51 | 52 | # You can also put a file called run_test.py in the recipe that will be run 53 | # at test time. 54 | 55 | # requires: 56 | # Put any additional test requirements here. For example 57 | # - nose 58 | 59 | about: 60 | home: https://github.com/jstac/quant-econ 61 | license: BSD 62 | summary: 'Code for quant-econ.net' 63 | 64 | # See 65 | # http://docs.continuum.io/conda/build.html for 66 | # more information about meta.yaml 67 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # QuantEcon documentation 2 | 3 | This is the main directory for the documentation for the `quantecon` python library. 4 | 5 | ## Dependencies 6 | 7 | The documentation requires a few dependencies beyond those necessary for the quantecon library. These dependencies are (warning, this may be an incomplete list): 8 | 9 | * sphinx 10 | * numpydoc 11 | * sphinx_rtd_theme 12 | * mock 13 | 14 | You can install these by executing 15 | 16 | ``` 17 | conda install sphinx numpydoc sphinx_rtd_theme mock 18 | ``` 19 | 20 | ## Building the docs 21 | 22 | In order to generate the documentation, follow these steps: 23 | 24 | 1. Install the `quantecon` python library locally. Do to this enter the commands below: 25 | ``` 26 | cd .. 27 | python setup.py install 28 | cd docs 29 | ``` 30 | 2. From this directory, execute the local file `qe_apidoc.py` (for an explanation of what the file does, see the module level docstring in the file) 31 | ``` 32 | python qe_apidoc.py 33 | ``` 34 | 3. Run sphinx using the Makefile (this is the command for unix based system -- sorry windows users, you will have to google how to do this) 35 | ``` 36 | make html 37 | ``` 38 | 4. Open the file `build/html/index.html`. 39 | 40 | I have added a couple utility commands to the make file: 41 | 42 | ``` 43 | srcclean: 44 | rm -rf source/modules* 45 | rm -rf source/models* 46 | rm -rf source/tools* 47 | rm -f source/index.rst 48 | rm -f source/models.rst 49 | rm -f source/tools.rst 50 | 51 | myhtml: 52 | make srcclean 53 | cd .. && python setup.py install && cd docs 54 | python qe_apidoc.py 55 | make html 56 | ``` 57 | 58 | Notice that we can automate steps 1-3 (and make sure we get a clean build) above by simply running `make myhtml` 59 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["flit_core >=3.5,<4"] 3 | build-backend = "flit_core.buildapi" 4 | 5 | [project] 6 | name = "quantecon" 7 | authors = [{name = "QuantEcon Project", email = "admin@quantecon.org"}] 8 | classifiers = [ 9 | 'Development Status :: 4 - Beta', 10 | 'Operating System :: OS Independent', 11 | 'Intended Audience :: Science/Research', 12 | 'Programming Language :: Python', 13 | 'Programming Language :: Python :: 3', 14 | 'Programming Language :: Python :: 3.5', 15 | 'Topic :: Scientific/Engineering', 16 | ] 17 | keywords = [ 18 | 'quantitative', 19 | 'economics' 20 | ] 21 | dynamic = ["description", "version"] 22 | requires-python = ">=3.7" 23 | dependencies = [ 24 | 'numba', 25 | 'numpy>=1.17.0', 26 | 'requests', 27 | 'scipy>=1.5.0', 28 | 'sympy', 29 | ] 30 | 31 | [project.optional-dependencies] 32 | testing = [ 33 | "pytest", 34 | "coverage", 35 | "flake8", 36 | "numpy", 37 | "scipy", 38 | "pandas", 39 | "numba", 40 | "sympy", 41 | ] 42 | 43 | 44 | [project.license] 45 | file = "LICENSE" 46 | 47 | [project.readme] 48 | file = "README.md" 49 | content-type = "text/markdown" 50 | 51 | [project.urls] 52 | Homepage = "https://quantecon.org/quantecon-py/" 53 | Documentation = "http://quanteconpy.readthedocs.org/en/latest/" 54 | Funding = "https://quantecon.org" 55 | Source = "https://github.com/quantecon/QuantEcon.py" 56 | Tracker = "https://github.com/quantecon/QuantEcon.py/issues" 57 | 58 | [tool.flit.module] 59 | name = "quantecon" 60 | 61 | [tool.flit.sdist] 62 | exclude = [ 63 | ".*", # Any hidden folders or files 64 | "docs/", 65 | "quantecon/tests/", 66 | "quantecon/util/tests", 67 | "Makefile", 68 | "environment.yml", 69 | "readthedocs.yml", 70 | ] -------------------------------------------------------------------------------- /quantecon/util/tests/test_combinatorics.py: -------------------------------------------------------------------------------- 1 | """ 2 | Tests for util/combinatorics.py 3 | 4 | """ 5 | import numpy as np 6 | from numpy.testing import assert_array_equal, assert_ 7 | import scipy.special 8 | from quantecon.util.combinatorics import ( 9 | next_k_array, k_array_rank, k_array_rank_jit 10 | ) 11 | 12 | 13 | class TestKArray: 14 | def setup_method(self): 15 | self.k_arrays = np.array( 16 | [[0, 1, 2], 17 | [0, 1, 3], 18 | [0, 2, 3], 19 | [1, 2, 3], 20 | [0, 1, 4], 21 | [0, 2, 4], 22 | [1, 2, 4], 23 | [0, 3, 4], 24 | [1, 3, 4], 25 | [2, 3, 4], 26 | [0, 1, 5], 27 | [0, 2, 5], 28 | [1, 2, 5], 29 | [0, 3, 5], 30 | [1, 3, 5], 31 | [2, 3, 5], 32 | [0, 4, 5], 33 | [1, 4, 5], 34 | [2, 4, 5], 35 | [3, 4, 5]] 36 | ) 37 | self.L, self.k = self.k_arrays.shape 38 | 39 | def test_next_k_array(self): 40 | k_arrays_computed = np.empty((self.L, self.k), dtype=int) 41 | k_arrays_computed[0] = np.arange(self.k) 42 | for i in range(1, self.L): 43 | k_arrays_computed[i] = k_arrays_computed[i-1] 44 | next_k_array(k_arrays_computed[i]) 45 | assert_array_equal(k_arrays_computed, self.k_arrays) 46 | 47 | def test_k_array_rank(self): 48 | for i in range(self.L): 49 | assert_(k_array_rank(self.k_arrays[i]) == i) 50 | 51 | def test_k_array_rank_jit(self): 52 | for i in range(self.L): 53 | assert_(k_array_rank_jit(self.k_arrays[i]) == i) 54 | 55 | 56 | def test_k_array_rank_arbitrary_precision(): 57 | n, k = 100, 50 58 | a = np.arange(n-k, n) 59 | assert_array_equal(k_array_rank(a), 60 | scipy.special.comb(n, k, exact=True)-1) 61 | -------------------------------------------------------------------------------- /quantecon/_filter.py: -------------------------------------------------------------------------------- 1 | """ 2 | 3 | function for filtering 4 | 5 | """ 6 | import numpy as np 7 | 8 | 9 | def hamilton_filter(data, h, p=None): 10 | r""" 11 | This function applies "Hamilton filter" to the data 12 | 13 | http://econweb.ucsd.edu/~jhamilto/hp.pdf 14 | 15 | Parameters 16 | ---------- 17 | data : array or dataframe 18 | h : integer 19 | Time horizon that we are likely to predict incorrectly. 20 | Original paper recommends 2 for annual data, 8 for quarterly data, 21 | 24 for monthly data. 22 | p : integer (optional) 23 | If supplied, it is p in the paper. Number of lags in regression. 24 | If not supplied, random walk process is assumed. 25 | 26 | Returns 27 | ------- 28 | cycle : array of cyclical component 29 | trend : trend component 30 | 31 | Notes 32 | ----- 33 | For seasonal data, it's desirable for p and h to be integer multiples of 34 | the number of obsevations in a year. E.g. for quarterly data, h = 8 and p = 35 | 4 are recommended. 36 | 37 | """ 38 | # transform data to array 39 | y = np.asarray(data, float) 40 | # sample size 41 | T = len(y) 42 | 43 | if p is not None: # if p is supplied 44 | # construct X matrix of lags 45 | X = np.ones((T-p-h+1, p+1)) 46 | for j in range(1, p+1): 47 | X[:, j] = y[p-j:T-h-j+1:1] 48 | 49 | # do OLS regression 50 | b = np.linalg.solve(X.transpose()@X, X.transpose()@y[p+h-1:T]) 51 | # trend component (`nan` for the first p+h-1 period) 52 | trend = np.append(np.zeros(p+h-1)+np.nan, X@b) 53 | # cyclical component 54 | cycle = y - trend 55 | else: # if p is not supplied (random walk) 56 | cycle = np.append(np.zeros(h)+np.nan, y[h:T] - y[0:T-h]) 57 | trend = y - cycle 58 | return cycle, trend 59 | -------------------------------------------------------------------------------- /quantecon/tests/test_discrete_rv.py: -------------------------------------------------------------------------------- 1 | """ 2 | Tests for discrete_rv.py 3 | 4 | """ 5 | 6 | import numpy as np 7 | from numpy.testing import assert_allclose, assert_ 8 | import pytest 9 | from quantecon import DiscreteRV 10 | 11 | 12 | class TestDiscreteRV: 13 | 14 | @classmethod 15 | def setup_method(cls): 16 | x = np.random.rand(10) 17 | x /= x.sum() 18 | # make sure it sums to 1 19 | cls.x = x 20 | cls.drv = DiscreteRV(cls.x) 21 | 22 | def test_Q_updates(self): 23 | "discrete_rv: Q attributes updates on q change?" 24 | Q_init = np.copy(self.drv.Q) 25 | 26 | # change q, see if Q updates 27 | x = np.random.rand(10) 28 | x /= x.sum() 29 | self.drv.q = x 30 | Q_after = self.drv.Q 31 | 32 | # should be different 33 | assert_(not np.allclose(Q_init, Q_after)) 34 | 35 | # clean up: reset values 36 | self.drv.q = self.x 37 | 38 | # now we should have our original Q back 39 | assert_allclose(Q_init, self.drv.Q) 40 | 41 | def test_Q_end_1(self): 42 | "discrete_rv: Q sums to 1" 43 | assert_(self.drv.Q[-1] - 1.0 < 1e-10) 44 | 45 | @pytest.mark.slow 46 | def test_draw_lln(self): 47 | "discrete_rv: lln satisfied?" 48 | draws = self.drv.draw(1000000) 49 | bins = np.arange(self.drv.q.size+1) 50 | freqs, _ = np.histogram(draws, bins=bins, density=True) 51 | assert_allclose(freqs, self.drv.q, atol=1e-2) 52 | 53 | def test_draw_with_seed(self): 54 | x = np.array([0.03326189, 0.60713005, 0.84514831, 0.28493183, 55 | 0.12393182, 0.35308009, 0.70371579, 0.81728178, 56 | 0.21294538, 0.05358209]) 57 | draws = DiscreteRV(x).draw(k=10, random_state=5) 58 | 59 | expected_output = np.array([1, 2, 1, 2, 1, 1, 2, 1, 1, 1]) 60 | 61 | assert_allclose(draws, expected_output) 62 | -------------------------------------------------------------------------------- /quantecon/game_theory/pure_nash.py: -------------------------------------------------------------------------------- 1 | """ 2 | Methods for computing pure Nash equilibria of a normal form game. 3 | (For now, only brute force method is supported) 4 | 5 | """ 6 | 7 | import numpy as np 8 | 9 | 10 | def pure_nash_brute(g, tol=None): 11 | """ 12 | Find all pure Nash equilibria of a normal form game by brute force. 13 | 14 | Parameters 15 | ---------- 16 | g : NormalFormGame 17 | tol : scalar(float), optional(default=None) 18 | Tolerance level used in determining best responses. If None, 19 | default to the value of the `tol` attribute of `g`. 20 | 21 | Returns 22 | ------- 23 | NEs : list(tuple(int)) 24 | List of tuples of Nash equilibrium pure actions. 25 | If no pure Nash equilibrium is found, return empty list. 26 | 27 | Examples 28 | -------- 29 | Consider the "Prisoners' Dilemma" game: 30 | 31 | >>> PD_bimatrix = [[(1, 1), (-2, 3)], 32 | ... [(3, -2), (0, 0)]] 33 | >>> g_PD = NormalFormGame(PD_bimatrix) 34 | >>> pure_nash_brute(g_PD) 35 | [(1, 1)] 36 | 37 | If we consider the "Matching Pennies" game, which has no pure nash 38 | equilibrium: 39 | 40 | >>> MP_bimatrix = [[(1, -1), (-1, 1)], 41 | ... [(-1, 1), (1, -1)]] 42 | >>> g_MP = NormalFormGame(MP_bimatrix) 43 | >>> pure_nash_brute(g_MP) 44 | [] 45 | 46 | """ 47 | return list(pure_nash_brute_gen(g, tol=tol)) 48 | 49 | 50 | def pure_nash_brute_gen(g, tol=None): 51 | """ 52 | Generator version of `pure_nash_brute`. 53 | 54 | Parameters 55 | ---------- 56 | g : NormalFormGame 57 | tol : scalar(float), optional(default=None) 58 | Tolerance level used in determining best responses. If None, 59 | default to the value of the `tol` attribute of `g`. 60 | 61 | Yields 62 | ------ 63 | out : tuple(int) 64 | Tuple of Nash equilibrium pure actions. 65 | 66 | """ 67 | for a in np.ndindex(*g.nums_actions): 68 | if g.is_nash(a, tol=tol): 69 | yield a 70 | -------------------------------------------------------------------------------- /quantecon/game_theory/tests/test_repeated_game.py: -------------------------------------------------------------------------------- 1 | """ 2 | Tests for repeated_game.py 3 | 4 | """ 5 | import numpy as np 6 | from numpy.testing import assert_allclose 7 | from quantecon.game_theory import NormalFormGame, RepeatedGame 8 | 9 | 10 | class TestAS(): 11 | def setup_method(self): 12 | self.game_dicts = [] 13 | 14 | # Example from Abreu and Sannikov (2014) 15 | bimatrix = [[(16, 9), (3, 13), (0, 3)], 16 | [(21, 1), (10, 4), (-1, 0)], 17 | [(9, 0), (5, -4), (-5, -15)]] 18 | vertices = np.array([[7.33770472e+00, 1.09826253e+01], 19 | [1.12568240e+00, 2.80000000e+00], 20 | [7.33770472e+00, 4.44089210e-16], 21 | [7.86308964e+00, 4.44089210e-16], 22 | [1.97917977e+01, 2.80000000e+00], 23 | [1.55630896e+01, 9.10000000e+00]]) 24 | d = {'sg': NormalFormGame(bimatrix), 25 | 'delta': 0.3, 26 | 'vertices': vertices, 27 | 'u': np.zeros(2)} 28 | self.game_dicts.append(d) 29 | 30 | # Prisoner's dilemma 31 | bimatrix = [[(9, 9), (1, 10)], 32 | [(10, 1), (3, 3)]] 33 | vertices = np.array([[3. , 3. ], 34 | [9.75, 3. ], 35 | [9. , 9. ], 36 | [3. , 9.75]]) 37 | d = {'sg': NormalFormGame(bimatrix), 38 | 'delta': 0.9, 39 | 'vertices': vertices, 40 | 'u': np.array([3., 3.])} 41 | self.game_dicts.append(d) 42 | 43 | def test_abreu_sannikov(self): 44 | for d in self.game_dicts: 45 | rpg = RepeatedGame(d['sg'], d['delta']) 46 | for method in ('abreu_sannikov', 'AS'): 47 | hull = rpg.equilibrium_payoffs(method=method, 48 | options={'u_init': d['u']}) 49 | assert_allclose(hull.points[hull.vertices], d['vertices']) 50 | -------------------------------------------------------------------------------- /quantecon/__init__.py: -------------------------------------------------------------------------------- 1 | # flake8: noqa 2 | """ 3 | Import the main names to top level. 4 | """ 5 | 6 | __version__ = '0.6.0' 7 | 8 | try: 9 | import numba 10 | except: 11 | raise ImportError( 12 | "Cannot import numba from current anaconda distribution. \ 13 | Please run `conda install numba` to install the latest version.") 14 | 15 | #-Modules-# 16 | from . import distributions 17 | from . import game_theory 18 | from . import quad 19 | from . import random 20 | from . import optimize 21 | 22 | #-Objects-# 23 | from ._compute_fp import compute_fixed_point 24 | from ._discrete_rv import DiscreteRV 25 | from ._dle import DLE 26 | from ._ecdf import ECDF 27 | from ._estspec import smooth, periodogram, ar_periodogram 28 | # from .game_theory import #Place Holder if we wish to promote any general objects to the qe namespace. 29 | from ._graph_tools import DiGraph, random_tournament_graph 30 | from ._gridtools import ( 31 | cartesian, mlinspace, cartesian_nearest_index, simplex_grid, simplex_index, 32 | num_compositions 33 | ) 34 | from ._inequality import lorenz_curve, gini_coefficient, shorrocks_index, \ 35 | rank_size 36 | from ._kalman import Kalman 37 | from ._lae import LAE 38 | from ._arma import ARMA 39 | from ._lqcontrol import LQ, LQMarkov 40 | from ._filter import hamilton_filter 41 | from ._lqnash import nnash 42 | from ._ivp import IVP 43 | from ._lss import LinearStateSpace 44 | from ._matrix_eqn import solve_discrete_lyapunov, solve_discrete_riccati 45 | from ._quadsums import var_quadratic_sum, m_quadratic_sum 46 | #->Propose Delete From Top Level 47 | #Promote to keep current examples working 48 | from .markov import MarkovChain, random_markov_chain, random_stochastic_matrix, \ 49 | gth_solve, tauchen, rouwenhorst, estimate_mc 50 | #Imports that Should be Deprecated with markov package 51 | from .markov import mc_compute_stationary, mc_sample_path 52 | #<- 53 | from ._rank_nullspace import rank_est, nullspace 54 | from ._robustlq import RBLQ 55 | from .util import searchsorted, fetch_nb_dependencies, tic, tac, toc 56 | -------------------------------------------------------------------------------- /quantecon/game_theory/tests/test_logitdyn.py: -------------------------------------------------------------------------------- 1 | """ 2 | Filename: test_logitdyn.py 3 | 4 | Tests for logitdyn.py 5 | 6 | """ 7 | import numpy as np 8 | from numpy.testing import assert_array_equal, assert_array_almost_equal_nulp 9 | 10 | from quantecon.game_theory import NormalFormGame, LogitDynamics 11 | 12 | 13 | class TestLogitDynamics: 14 | '''Test the methods of LogitDynamics''' 15 | 16 | def setup_method(self): 17 | '''Setup a LogitDynamics instance''' 18 | # symmetric 2x2 coordination game 19 | payoff_matrix = [[4, 0], 20 | [3, 2]] 21 | beta = 4.0 22 | data = NormalFormGame(payoff_matrix) 23 | self.ld = LogitDynamics(data, beta=beta) 24 | 25 | def test_play(self): 26 | seed = 76240103339929371127372784081282227092 27 | x = [self.ld.play(init_actions=(0, 0), 28 | random_state=np.random.default_rng(seed)) 29 | for i in range(2)] 30 | assert_array_equal(x[0], x[1]) 31 | 32 | def test_time_series(self): 33 | seed = 165570719993771384215214311194249493239 34 | series = [self.ld.time_series(ts_length=10, init_actions=(0, 0), 35 | random_state=np.random.default_rng(seed)) 36 | for i in range(2)] 37 | assert_array_equal(series[0], series[1]) 38 | 39 | 40 | def test_set_choice_probs_with_asymmetric_payoff_matrix(): 41 | bimatrix = np.array([[(4, 4), (1, 1), (0, 3)], 42 | [(3, 0), (1, 1), (2, 2)]]) 43 | beta = 1.0 44 | data = NormalFormGame(bimatrix) 45 | ld = LogitDynamics(data, beta=beta) 46 | 47 | # (Normalized) CDFs of logit choice 48 | cdfs = np.ones((bimatrix.shape[1], bimatrix.shape[0])) 49 | cdfs[:, 0] = 1 / (1 + np.exp(beta*(bimatrix[1, :, 0]-bimatrix[0, :, 0]))) 50 | 51 | # self.ld.players[0].logit_choice_cdfs: unnormalized 52 | cdfs_computed = ld.players[0].logit_choice_cdfs 53 | cdfs_computed = cdfs_computed / cdfs_computed[..., [-1]] # Normalized 54 | 55 | assert_array_almost_equal_nulp(cdfs_computed, cdfs) 56 | -------------------------------------------------------------------------------- /quantecon/game_theory/tests/test_localint.py: -------------------------------------------------------------------------------- 1 | """ 2 | Filename: test_localint.py 3 | 4 | Tests for localint.py 5 | 6 | """ 7 | import numpy as np 8 | from numpy.testing import assert_array_equal, assert_equal 9 | 10 | from quantecon.game_theory import LocalInteraction 11 | 12 | 13 | class TestLocalInteraction: 14 | '''Test the methods of LocalInteraction''' 15 | 16 | def setup_method(self): 17 | '''Setup a LocalInteraction instance''' 18 | payoff_matrix = np.asarray([[4, 0], [2, 3]]) 19 | adj_matrix = np.asarray([[0, 1, 3], 20 | [2, 0, 1], 21 | [3, 2, 0]]) 22 | self.li = LocalInteraction(payoff_matrix, adj_matrix) 23 | 24 | def test_play(self): 25 | init_actions = (0, 0, 1) 26 | x = (1, 0, 0) 27 | assert_equal(self.li.play(actions=init_actions), x) 28 | 29 | def test_time_series_simultaneous_revision(self): 30 | init_actions = (0, 0, 1) 31 | x = [[0, 0, 1], 32 | [1, 0, 0], 33 | [0, 1, 1]] 34 | assert_array_equal(self.li.time_series(ts_length=3, 35 | actions=init_actions), x) 36 | 37 | def test_time_series_asynchronous_revision(self): 38 | seed = 21388225457580135037104913043871211454 39 | init_actions = (0, 0, 1) 40 | x = [self.li.time_series(ts_length=3, 41 | revision='asynchronous', 42 | actions=init_actions, 43 | random_state=np.random.default_rng(seed)) 44 | for i in range(2)] 45 | assert_array_equal(x[0], x[1]) 46 | 47 | def test_time_series_asynchronous_revision_with_player_index(self): 48 | init_actions = (0, 0, 1) 49 | player_ind_seq = [0, 1, 2] 50 | x = [[0, 0, 1], 51 | [1, 0, 1], 52 | [1, 1, 1]] 53 | assert_array_equal(self.li.time_series(ts_length=3, 54 | revision='asynchronous', 55 | actions=init_actions, 56 | player_ind_seq=player_ind_seq), 57 | x) 58 | -------------------------------------------------------------------------------- /quantecon/_discrete_rv.py: -------------------------------------------------------------------------------- 1 | """ 2 | Generates an array of draws from a discrete random variable with a 3 | specified vector of probabilities. 4 | 5 | """ 6 | 7 | import numpy as np 8 | from .util import check_random_state 9 | 10 | 11 | class DiscreteRV: 12 | """ 13 | Generates an array of draws from a discrete random variable with 14 | vector of probabilities given by q. 15 | 16 | Parameters 17 | ---------- 18 | q : array_like(float) 19 | Nonnegative numbers that sum to 1. 20 | 21 | Attributes 22 | ---------- 23 | q : see Parameters. 24 | Q : array_like(float) 25 | The cumulative sum of q. 26 | 27 | """ 28 | 29 | def __init__(self, q): 30 | self._q = np.asarray(q) 31 | self.Q = np.cumsum(q) 32 | 33 | def __repr__(self): 34 | return "DiscreteRV with {n} elements".format(n=self._q.size) 35 | 36 | def __str__(self): 37 | return self.__repr__() 38 | 39 | @property 40 | def q(self): 41 | """ 42 | Getter method for q. 43 | 44 | """ 45 | return self._q 46 | 47 | @q.setter 48 | def q(self, val): 49 | """ 50 | Setter method for q. 51 | 52 | """ 53 | self._q = np.asarray(val) 54 | self.Q = np.cumsum(val) 55 | 56 | def draw(self, k=1, random_state=None): 57 | """ 58 | Returns k draws from q. 59 | 60 | For each such draw, the value i is returned with probability 61 | q[i]. 62 | 63 | Parameters 64 | ---------- 65 | k : scalar(int), optional 66 | Number of draws to be returned 67 | 68 | random_state : int or np.random.RandomState/Generator, optional 69 | Random seed (integer) or np.random.RandomState or Generator 70 | instance to set the initial state of the random number 71 | generator for reproducibility. If None, a randomly 72 | initialized RandomState is used. 73 | 74 | Returns 75 | ------- 76 | array_like(int) 77 | An array of k independent draws from q 78 | 79 | """ 80 | random_state = check_random_state(random_state) 81 | 82 | return self.Q.searchsorted(random_state.uniform(0, 1, size=k), 83 | side='right') 84 | -------------------------------------------------------------------------------- /quantecon/game_theory/tests/test_brd.py: -------------------------------------------------------------------------------- 1 | """ 2 | Filename: test_brd.py 3 | 4 | Tests for brd.py 5 | 6 | """ 7 | import numpy as np 8 | from numpy.testing import assert_array_equal 9 | 10 | from quantecon.game_theory import BRD, KMR, SamplingBRD 11 | 12 | 13 | class TestBRD: 14 | '''Test the methods of BRD''' 15 | 16 | def setup_method(self): 17 | '''Setup a BRD instance''' 18 | # 2x2 coordination game with action 1 risk-dominant 19 | payoff_matrix = [[4, 0], 20 | [3, 2]] 21 | self.N = 4 # 4 players 22 | self.brd = BRD(payoff_matrix, self.N) 23 | 24 | def test_time_series_1(self): 25 | assert_array_equal( 26 | self.brd.time_series(ts_length=3, init_action_dist=[4, 0]), 27 | [[4, 0], 28 | [4, 0], 29 | [4, 0]] 30 | ) 31 | 32 | def test_time_series_2(self): 33 | seed = 329478856717593533176523622896549543480 34 | x = [self.brd.time_series(ts_length=3, init_action_dist=[2, 2], 35 | random_state=np.random.default_rng(seed)) 36 | for i in range(2)] 37 | assert_array_equal(x[0], x[1]) 38 | 39 | 40 | class TestKMR: 41 | '''Test the methods of KMR''' 42 | 43 | def setup_method(self): 44 | payoff_matrix = [[4, 0], 45 | [3, 2]] 46 | self.N = 4 47 | self.kmr = KMR(payoff_matrix, self.N) 48 | 49 | def test_time_series(self): 50 | seed = 21519527815966711149598801341951879349 51 | x = [self.kmr.time_series(ts_length=3, init_action_dist=[2, 2], 52 | random_state=np.random.default_rng(seed)) 53 | for i in range(2)] 54 | assert_array_equal(x[0], x[1]) 55 | 56 | 57 | class TestSamplingBRD: 58 | '''Test the methods of SamplingBRD''' 59 | 60 | def setup_method(self): 61 | payoff_matrix = [[4, 0], 62 | [3, 2]] 63 | self.N = 4 64 | self.sbrd = SamplingBRD(payoff_matrix, self.N) 65 | 66 | def test_time_series(self): 67 | seed = 205165126657120054758393970887343077472 68 | x = [self.sbrd.time_series(ts_length=3, init_action_dist=[2, 2], 69 | random_state=np.random.default_rng(seed)) 70 | for i in range(2)] 71 | assert_array_equal(x[0], x[1]) 72 | -------------------------------------------------------------------------------- /quantecon/_lae.py: -------------------------------------------------------------------------------- 1 | r""" 2 | Computes a sequence of marginal densities for a continuous state space 3 | Markov chain :math:`X_t` where the transition probabilities can be represented 4 | as densities. The estimate of the marginal density of :math:`X_t` is 5 | 6 | .. math:: 7 | 8 | \frac{1}{n} \sum_{i=0}^n p(X_{t-1}^i, y) 9 | 10 | This is a density in :math:`y`. 11 | 12 | References 13 | ---------- 14 | 15 | https://lectures.quantecon.org/py/stationary_densities.html 16 | 17 | """ 18 | from textwrap import dedent 19 | import numpy as np 20 | 21 | 22 | class LAE: 23 | """ 24 | An instance is a representation of a look ahead estimator associated 25 | with a given stochastic kernel p and a vector of observations X. 26 | 27 | Parameters 28 | ---------- 29 | p : function 30 | The stochastic kernel. A function p(x, y) that is vectorized in 31 | both x and y 32 | X : array_like(float) 33 | A vector containing observations 34 | 35 | Attributes 36 | ---------- 37 | p, X : see Parameters 38 | 39 | Examples 40 | -------- 41 | >>> psi = LAE(p, X) 42 | >>> y = np.linspace(0, 1, 100) 43 | >>> psi(y) # Evaluate look ahead estimate at grid of points y 44 | 45 | """ 46 | 47 | def __init__(self, p, X): 48 | X = X.flatten() # So we know what we're dealing with 49 | n = len(X) 50 | self.p, self.X = p, X.reshape((n, 1)) 51 | 52 | def __repr__(self): 53 | return self.__str__() 54 | 55 | def __str__(self): 56 | m = """\ 57 | Look ahead estimator 58 | - number of observations : {n} 59 | """ 60 | return dedent(m.format(n=self.X.size)) 61 | 62 | def __call__(self, y): 63 | """ 64 | A vectorized function that returns the value of the look ahead 65 | estimate at the values in the array y. 66 | 67 | Parameters 68 | ---------- 69 | y : array_like(float) 70 | A vector of points at which we wish to evaluate the look- 71 | ahead estimator 72 | 73 | Returns 74 | ------- 75 | psi_vals : array_like(float) 76 | The values of the density estimate at the points in y 77 | 78 | """ 79 | k = len(y) 80 | v = self.p(self.X, y.reshape((1, k))) 81 | psi_vals = np.mean(v, axis=0) # Take mean along each row 82 | 83 | return psi_vals.flatten() 84 | -------------------------------------------------------------------------------- /quantecon/game_theory/tests/test_pure_nash.py: -------------------------------------------------------------------------------- 1 | """ 2 | Tests for pure_nash.py 3 | 4 | """ 5 | 6 | import numpy as np 7 | import itertools 8 | from numpy.testing import assert_ 9 | from quantecon.game_theory import NormalFormGame, pure_nash_brute 10 | 11 | 12 | class TestPureNashBruteForce(): 13 | def setup_method(self): 14 | self.game_dicts = [] 15 | 16 | # Matching Pennies game with no pure nash equilibrium 17 | MP_bimatrix = [[(1, -1), (-1, 1)], 18 | [(-1, 1), (1, -1)]] 19 | MP_NE = [] 20 | 21 | # Prisoners' Dilemma game with one pure nash equilibrium 22 | PD_bimatrix = [[(1, 1), (-2, 3)], 23 | [(3, -2), (0, 0)]] 24 | PD_NE = [(1, 1)] 25 | 26 | # Battle of the Sexes game with two pure nash equilibria 27 | BoS_bimatrix = [[(3, 2), (1, 0)], 28 | [(0, 1), (2, 3)]] 29 | BoS_NE = [(0, 0), (1, 1)] 30 | 31 | # Unanimity Game with more than two players 32 | N = 4 33 | a, b = 1, 2 34 | g_Unanimity = NormalFormGame((2,)*N) 35 | g_Unanimity[(0,)*N] = (a,)*N 36 | g_Unanimity[(1,)*N] = (b,)*N 37 | 38 | Unanimity_NE = [(0,)*N] 39 | for k in range(2, N-2+1): 40 | for ind in itertools.combinations(range(N), k): 41 | a = np.ones(N, dtype=int) 42 | a[list(ind)] = 0 43 | Unanimity_NE.append(tuple(a)) 44 | Unanimity_NE.append((1,)*N) 45 | 46 | for bimatrix, NE in zip([MP_bimatrix, PD_bimatrix, BoS_bimatrix], 47 | [MP_NE, PD_NE, BoS_NE]): 48 | d = {'g': NormalFormGame(bimatrix), 49 | 'NEs': NE} 50 | self.game_dicts.append(d) 51 | self.game_dicts.append({'g': g_Unanimity, 52 | 'NEs': Unanimity_NE}) 53 | 54 | def test_brute_force(self): 55 | for d in self.game_dicts: 56 | assert_(sorted(pure_nash_brute(d['g'])) == sorted(d['NEs'])) 57 | 58 | def test_tol(self): 59 | # Prisoners' Dilemma game with one NE and one epsilon NE 60 | epsilon = 1e-08 61 | 62 | PD_bimatrix = [[(1, 1), (-2, 1 + epsilon)], 63 | [(1 + epsilon, -2), (0, 0)]] 64 | 65 | NEs = [(1, 1)] 66 | epsilon_NEs = [(1, 1), (0, 0)] 67 | 68 | g = NormalFormGame(PD_bimatrix) 69 | for tol, answer in zip([0, epsilon], [NEs, epsilon_NEs]): 70 | assert_(sorted(pure_nash_brute(g, tol=tol)) == sorted(answer)) 71 | -------------------------------------------------------------------------------- /quantecon/game_theory/utilities.py: -------------------------------------------------------------------------------- 1 | """ 2 | Utility routines for the game_theory submodule 3 | 4 | """ 5 | import numbers 6 | import numpy as np 7 | 8 | 9 | class NashResult(dict): 10 | """ 11 | Contain the information about the result of Nash equilibrium 12 | computation. 13 | 14 | Attributes 15 | ---------- 16 | NE : tuple(ndarray(float, ndim=1)) 17 | Computed Nash equilibrium. 18 | 19 | converged : bool 20 | Whether the routine has converged. 21 | 22 | num_iter : int 23 | Number of iterations. 24 | 25 | max_iter : int 26 | Maximum number of iterations. 27 | 28 | init : scalar or array_like 29 | Initial condition used. 30 | 31 | Notes 32 | ----- 33 | This is sourced from `sicpy.optimize.OptimizeResult`. 34 | 35 | There may be additional attributes not listed above depending of the 36 | routine. 37 | 38 | """ 39 | def __getattr__(self, name): 40 | try: 41 | return self[name] 42 | except KeyError: 43 | raise AttributeError(name) 44 | 45 | __setattr__ = dict.__setitem__ 46 | __delattr__ = dict.__delitem__ 47 | 48 | def __repr__(self): 49 | if self.keys(): 50 | m = max(map(len, list(self.keys()))) + 1 51 | return '\n'.join([k.rjust(m) + ': ' + repr(v) 52 | for k, v in sorted(self.items())]) 53 | else: 54 | return self.__class__.__name__ + "()" 55 | 56 | def __dir__(self): 57 | return self.keys() 58 | 59 | 60 | # _copy_action_to, _copy_action_profile_to 61 | 62 | def _copy_action_to(dst, src): 63 | """ 64 | Copy the pure action (int) or mixed action (array_like) in `src` to 65 | the empty ndarray `dst`. 66 | 67 | Parameters 68 | ---------- 69 | dst : ndarray(float, ndim=1) 70 | 71 | src : scalar(int) or array_like(float, ndim=1) 72 | 73 | """ 74 | if isinstance(src, numbers.Integral): # pure action 75 | dst[:] = 0 76 | dst[src] = 1 77 | else: # mixed action 78 | np.copyto(dst, src) 79 | 80 | 81 | def _copy_action_profile_to(dst, src): 82 | """ 83 | Copy the pure actions (int) or mixed actions (array_like) in the 84 | N-array_like `src` to the empty ndarrays in the N-array_like `dst`. 85 | 86 | Parameters 87 | ---------- 88 | dst : array_like(ndarray(float, ndim=1)) 89 | 90 | src : array_like(int or array_like(float, ndim=1)) 91 | 92 | """ 93 | N = len(dst) 94 | for i in range(N): 95 | _copy_action_to(dst[i], src[i]) 96 | -------------------------------------------------------------------------------- /quantecon/tests/test_ricatti.py: -------------------------------------------------------------------------------- 1 | """ 2 | Tests for solve_discrete_riccati in matrix_eqn.py file 3 | 4 | """ 5 | import numpy as np 6 | from numpy.testing import assert_allclose, assert_raises 7 | from quantecon import solve_discrete_riccati 8 | import pytest 9 | 10 | 11 | def dare_golden_num_float(method): 12 | val = solve_discrete_riccati(1.0, 1.0, 1.0, 1.0, method=method) 13 | gold_ratio = (1 + np.sqrt(5)) / 2. 14 | assert_allclose(val, gold_ratio) 15 | 16 | 17 | def dare_golden_num_2d(method): 18 | A, B, R, Q = np.eye(2), np.eye(2), np.eye(2), np.eye(2) 19 | gold_diag = np.eye(2) * (1 + np.sqrt(5)) / 2. 20 | val = solve_discrete_riccati(A, B, R, Q, method=method) 21 | assert_allclose(val, gold_diag) 22 | 23 | 24 | def dare_tjm_1(method): 25 | A = [[0.0, 0.1, 0.0], 26 | [0.0, 0.0, 0.1], 27 | [0.0, 0.0, 0.0]] 28 | B = [[1.0, 0.0], 29 | [0.0, 0.0], 30 | [0.0, 1.0]] 31 | Q = [[10**5, 0.0, 0.0], 32 | [0.0, 10**3, 0.0], 33 | [0.0, 0.0, -10.0]] 34 | R = [[0.0, 0.0], 35 | [0.0, 1.0]] 36 | X = solve_discrete_riccati(A, B, Q, R, method=method) 37 | Y = np.diag((1e5, 1e3, 0.0)) 38 | assert_allclose(X, Y, atol=1e-07) 39 | 40 | 41 | def dare_tjm_2(method): 42 | A = [[0, -1], 43 | [0, 2]] 44 | B = [[1, 0], 45 | [1, 1]] 46 | Q = [[1, 0], 47 | [0, 0]] 48 | R = [[4, 2], 49 | [2, 1]] 50 | X = solve_discrete_riccati(A, B, Q, R, method=method) 51 | Y = np.zeros((2, 2)) 52 | Y[0, 0] = 1 53 | assert_allclose(X, Y, atol=1e-07) 54 | 55 | 56 | def dare_tjm_3(method): 57 | r = 0.5 58 | I = np.identity(2) 59 | A = [[2 + r**2, 0], 60 | [0, 0]] 61 | A = np.array(A) 62 | B = I 63 | R = [[1, r], 64 | [r, r*r]] 65 | Q = I - np.dot(A.T, A) + np.dot(A.T, np.linalg.solve(R + I, A)) 66 | X = solve_discrete_riccati(A, B, Q, R, method=method) 67 | Y = np.identity(2) 68 | assert_allclose(X, Y, atol=1e-07) 69 | 70 | 71 | _test_funcs = [ 72 | dare_golden_num_float, dare_golden_num_2d, 73 | dare_tjm_1, dare_tjm_2, dare_tjm_3 74 | ] 75 | 76 | 77 | _test_methods = [ 78 | 'doubling', 'qz' 79 | ] 80 | 81 | 82 | @pytest.mark.parametrize("test_func", _test_funcs) 83 | @pytest.mark.parametrize("test_method", _test_methods) 84 | def test_solve_discrete_riccati(test_func, test_method): 85 | test_func(test_method) 86 | 87 | 88 | def test_solve_discrete_riccati_invalid_method(): 89 | method = 'invalid_method' 90 | assert_raises(ValueError, _test_funcs[0], method) 91 | -------------------------------------------------------------------------------- /quantecon/util/tests/test_numba.py: -------------------------------------------------------------------------------- 1 | """ 2 | Tests for Numba support utilities 3 | 4 | """ 5 | import numpy as np 6 | from numpy.testing import assert_array_equal, assert_ 7 | from numba import jit 8 | from quantecon.util.numba import _numba_linalg_solve, comb_jit 9 | 10 | 11 | @jit(nopython=True) 12 | def numba_linalg_solve_orig(a, b): 13 | return np.linalg.solve(a, b) 14 | 15 | 16 | class TestNumbaLinalgSolve: 17 | def setup_method(self): 18 | self.dtypes = [np.float32, np.float64] 19 | self.a = np.array([[3, 2, 0], [1, -1, 0], [0, 5, 1]]) 20 | self.b_1dim = np.array([2, 4, -1]) 21 | self.b_2dim = np.array([[2, 3], [4, 1], [-1, 0]]) 22 | self.a_singular = np.array([[0, 1, 2], [3, 4, 5], [3, 5, 7]]) 23 | 24 | def test_b_1dim(self): 25 | for dtype in self.dtypes: 26 | a = np.asfortranarray(self.a, dtype=dtype) 27 | b = np.asfortranarray(self.b_1dim, dtype=dtype) 28 | sol_orig = numba_linalg_solve_orig(a, b) 29 | r = _numba_linalg_solve(a, b) 30 | assert_(r == 0) 31 | assert_array_equal(b, sol_orig) 32 | 33 | def test_b_2dim(self): 34 | for dtype in self.dtypes: 35 | a = np.asfortranarray(self.a, dtype=dtype) 36 | b = np.asfortranarray(self.b_2dim, dtype=dtype) 37 | sol_orig = numba_linalg_solve_orig(a, b) 38 | r = _numba_linalg_solve(a, b) 39 | assert_(r == 0) 40 | assert_array_equal(b, sol_orig) 41 | 42 | def test_singular_a(self): 43 | for b in [self.b_1dim, self.b_2dim]: 44 | for dtype in self.dtypes: 45 | a = np.asfortranarray(self.a_singular, dtype=dtype) 46 | b = np.asfortranarray(b, dtype=dtype) 47 | r = _numba_linalg_solve(a, b) 48 | assert_(r != 0) 49 | 50 | 51 | class TestCombJit: 52 | def setup_method(self): 53 | self.MAX_INTP = np.iinfo(np.intp).max 54 | 55 | def test_comb(self): 56 | N, k = 10, 3 57 | N_choose_k = 120 58 | assert_(comb_jit(N, k) == N_choose_k) 59 | 60 | def test_comb_zeros(self): 61 | assert_(comb_jit(2, 3) == 0) 62 | assert_(comb_jit(-1, 3) == 0) 63 | assert_(comb_jit(2, -1) == 0) 64 | 65 | assert_(comb_jit(self.MAX_INTP, 2) == 0) 66 | 67 | N = np.intp(self.MAX_INTP**0.5 * 2**0.5) + 1 68 | assert_(comb_jit(N, 2) == 0) 69 | 70 | def test_max_intp(self): 71 | assert_(comb_jit(self.MAX_INTP, 0) == 1) 72 | assert_(comb_jit(self.MAX_INTP, 1) == self.MAX_INTP) 73 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: conda-build 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | tags: 8 | - 'v*' 9 | pull_request: 10 | branches: 11 | - main 12 | 13 | 14 | jobs: 15 | tests: 16 | runs-on: ${{ matrix.os }} 17 | strategy: 18 | matrix: 19 | os: [windows-latest, ubuntu-latest, macos-latest] 20 | python-version: [3.8, 3.9, '3.10'] 21 | exclude: 22 | - os: windows-latest 23 | python-version: 3.8 24 | 25 | steps: 26 | - uses: actions/checkout@v2 27 | with: 28 | fetch-depth: 0 29 | 30 | - name: Cache conda 31 | uses: actions/cache@v2 32 | env: 33 | CACHE_NUMBER: 0 34 | with: 35 | path: ~/conda_pkgs_dir 36 | key: 37 | ${{ runner.os }}-${{ matrix.python-version }}-conda-${{ env.CACHE_NUMBER }}-${{ hashFiles('environment.yml') }} 38 | 39 | - uses: conda-incubator/setup-miniconda@v2 40 | with: 41 | auto-update-conda: true 42 | environment-file: environment.yml 43 | python-version: ${{ matrix.python-version }} 44 | auto-activate-base: false 45 | use-only-tar-bz2: true 46 | 47 | - name: Conda info 48 | shell: bash -l {0} 49 | run: | 50 | conda info 51 | conda list 52 | 53 | - name: Setup QuantEcon 54 | shell: bash -l {0} 55 | run: | 56 | pip install -U pip 57 | pip install .[testing] 58 | 59 | - name: flake8 Tests 60 | shell: bash -l {0} 61 | run: | 62 | flake8 --select F401, F405, E231 quantecon 63 | 64 | - name: Run Tests (pytest) 65 | shell: bash -l {0} 66 | run: | 67 | coverage run -m pytest quantecon 68 | coverage lcov 69 | 70 | - name: Coveralls 71 | uses: coverallsapp/github-action@master 72 | if: runner.os == 'Linux' 73 | with: 74 | github-token: ${{ secrets.GITHUB_TOKEN }} 75 | path-to-lcov: coverage.lcov 76 | 77 | publish: 78 | 79 | name: Publish to PyPi 80 | needs: [tests] 81 | if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags') 82 | runs-on: ubuntu-latest 83 | steps: 84 | - name: Checkout source 85 | uses: actions/checkout@v3 86 | - name: Set up Python 3.8 87 | uses: actions/setup-python@v3 88 | with: 89 | python-version: 3.8 90 | - name: Install flit 91 | run: | 92 | pip install flit~=3.6 93 | - name: Build and publish 94 | run: | 95 | flit publish 96 | env: 97 | FLIT_USERNAME: __token__ 98 | FLIT_PASSWORD: ${{ secrets.PYPI_TOKEN }} 99 | -------------------------------------------------------------------------------- /quantecon/game_theory/tests/test_support_enumeration.py: -------------------------------------------------------------------------------- 1 | """ 2 | Tests for support_enumeration.py 3 | 4 | """ 5 | import numpy as np 6 | from numpy.testing import assert_allclose, assert_, assert_raises 7 | from quantecon.util import check_random_state 8 | from quantecon.game_theory import Player, NormalFormGame, support_enumeration 9 | 10 | 11 | def random_skew_sym(n, m=None, random_state=None): 12 | """ 13 | Generate a random skew symmetric zero-sum NormalFormGame of the form 14 | O B 15 | -B.T O 16 | where B is an n x m matrix. 17 | 18 | """ 19 | if m is None: 20 | m = n 21 | random_state = check_random_state(random_state) 22 | B = random_state.random((n, m)) 23 | A = np.empty((n+m, n+m)) 24 | A[:n, :n] = 0 25 | A[n:, n:] = 0 26 | A[:n, n:] = B 27 | A[n:, :n] = -B.T 28 | return NormalFormGame([Player(A) for i in range(2)]) 29 | 30 | 31 | class TestSupportEnumeration(): 32 | def setup_method(self): 33 | self.game_dicts = [] 34 | 35 | # From von Stengel 2007 in Algorithmic Game Theory 36 | bimatrix = [[(3, 3), (3, 2)], 37 | [(2, 2), (5, 6)], 38 | [(0, 3), (6, 1)]] 39 | d = {'g': NormalFormGame(bimatrix), 40 | 'NEs': [([1, 0, 0], [1, 0]), 41 | ([4/5, 1/5, 0], [2/3, 1/3]), 42 | ([0, 1/3, 2/3], [1/3, 2/3])]} 43 | self.game_dicts.append(d) 44 | 45 | # Degenerate game 46 | # NEs ([0, p, 1-p], [1/2, 1/2]), 0 <= p <= 1, are not detected. 47 | bimatrix = [[(1, 1), (-1, 0)], 48 | [(-1, 0), (1, 0)], 49 | [(0, 0), (0, 0)]] 50 | d = {'g': NormalFormGame(bimatrix), 51 | 'NEs': [([1, 0, 0], [1, 0]), 52 | ([0, 1, 0], [0, 1])]} 53 | self.game_dicts.append(d) 54 | 55 | def test_support_enumeration(self): 56 | for d in self.game_dicts: 57 | NEs_computed = support_enumeration(d['g']) 58 | assert_(len(NEs_computed) == len(d['NEs'])) 59 | for actions_computed, actions in zip(NEs_computed, d['NEs']): 60 | for action_computed, action in zip(actions_computed, actions): 61 | assert_allclose(action_computed, action) 62 | 63 | def test_no_error_skew_sym(self): 64 | # Test no LinAlgError is raised. 65 | n, m = 3, 2 66 | seed = 7028 67 | g = random_skew_sym(n, m, random_state=seed) 68 | support_enumeration(g) 69 | 70 | 71 | def test_support_enumeration_invalid_g(): 72 | bimatrix = [[(3, 3), (3, 2)], 73 | [(2, 2), (5, 6)], 74 | [(0, 3), (6, 1)]] 75 | assert_raises(TypeError, support_enumeration, bimatrix) 76 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # QuantEcon.py 2 | 3 | A high performance, open source Python code library for economics 4 | 5 | ```python 6 | from quantecon.markov import DiscreteDP 7 | aiyagari_ddp = DiscreteDP(R, Q, beta) 8 | results = aiyagari_ddp.solve(method='policy_iteration') 9 | ``` 10 | 11 | [![Build Status](https://github.com/QuantEcon/QuantEcon.py/actions/workflows/ci.yml/badge.svg)](https://github.com/QuantEcon/QuantEcon.py/actions?query=workflow%3Abuild) 12 | [![Coverage Status](https://coveralls.io/repos/QuantEcon/QuantEcon.py/badge.svg)](https://coveralls.io/r/QuantEcon/QuantEcon.py) 13 | [![Documentation Status](https://readthedocs.org/projects/quanteconpy/badge/?version=latest)](https://quanteconpy.readthedocs.io/en/latest/?badge=latest) 14 | 15 | ## Installation 16 | 17 | Before installing `quantecon` we recommend you install the [Anaconda](https://www.anaconda.com/download/) Python distribution, which includes a full suite of scientific python tools. **Note:** `quantecon` is now only supporting Python version 3.5+. This is mainly to allow code to be written taking full advantage of new features such as using the `@` symbol for matrix multiplication. Therefore please install the latest Python 3 Anaconda distribution. 18 | 19 | Next you can install quantecon by opening a terminal prompt and typing 20 | 21 | pip install quantecon 22 | 23 | ## Usage 24 | 25 | Once `quantecon` has been installed you should be able to import it as follows: 26 | 27 | ```python 28 | import quantecon as qe 29 | ``` 30 | 31 | You can check the version by running 32 | 33 | ```python 34 | print(qe.__version__) 35 | ``` 36 | 37 | If your version is below what’s available on [PyPI](https://pypi.python.org/pypi/quantecon/) then it is time to upgrade. This can be done by running 38 | 39 | pip install --upgrade quantecon 40 | 41 | ## Examples and Sample Code 42 | 43 | Many examples of QuantEcon.py in action can be found at [Quantitative Economics](https://lectures.quantecon.org/). See also the 44 | 45 | * [Documentation](https://quanteconpy.readthedocs.org/en/latest/) 46 | * [Notebook gallery](https://notes.quantecon.org) 47 | 48 | QuantEcon.py is supported financially by the [Alfred P. Sloan Foundation](http://www.sloan.org/) and is part of the [QuantEcon organization](https://quantecon.org). 49 | 50 | ## Downloading the `quantecon` Repository 51 | 52 | An alternative is to download the sourcecode of the `quantecon` package and install it manually from [the github repository](https://github.com/QuantEcon/QuantEcon.py/). For example, if you have git installed type 53 | 54 | git clone https://github.com/QuantEcon/QuantEcon.py 55 | 56 | Once you have downloaded the source files then the package can be installed by running 57 | 58 | pip install flit 59 | flit install 60 | 61 | (To learn the basics about setting up Git see [this link](https://help.github.com/articles/set-up-git/).) 62 | -------------------------------------------------------------------------------- /quantecon/random/tests/test_utilities.py: -------------------------------------------------------------------------------- 1 | """ 2 | Tests for util/random.py 3 | 4 | Functions 5 | --------- 6 | probvec 7 | sample_without_replacement 8 | 9 | """ 10 | import numbers 11 | import numpy as np 12 | from numpy.testing import (assert_array_equal, assert_allclose, assert_raises, 13 | assert_) 14 | from quantecon.random import probvec, sample_without_replacement, draw 15 | 16 | 17 | # probvec # 18 | 19 | class TestProbvec: 20 | def setup_method(self): 21 | self.m, self.k = 2, 3 # m vectors of dimension k 22 | seed = 1234 23 | 24 | self.out_parallel = probvec(self.m, self.k, random_state=seed) 25 | self.out_cpu = \ 26 | probvec(self.m, self.k, random_state=seed, parallel=False) 27 | 28 | def test_shape(self): 29 | for out in [self.out_parallel, self.out_cpu]: 30 | assert_(out.shape == (self.m, self.k)) 31 | 32 | def test_parallel_cpu(self): 33 | assert_array_equal(self.out_parallel, self.out_cpu) 34 | 35 | 36 | # sample_without_replacement # 37 | 38 | def test_sample_without_replacement_shape(): 39 | assert_array_equal(sample_without_replacement(2, 0).shape, (0,)) 40 | 41 | n, k, m = 5, 3, 4 42 | assert_array_equal( 43 | sample_without_replacement(n, k).shape, 44 | (k,) 45 | ) 46 | assert_array_equal( 47 | sample_without_replacement(n, k, num_trials=m).shape, 48 | (m, k) 49 | ) 50 | 51 | 52 | def test_sample_without_replacement_uniqueness(): 53 | n = 10 54 | a = sample_without_replacement(n, n) 55 | b = np.unique(a) 56 | assert_(len(b) == n) 57 | 58 | 59 | def test_sample_without_replacement_value_error(): 60 | # n <= 0 61 | assert_raises(ValueError, sample_without_replacement, 0, 2) 62 | assert_raises(ValueError, sample_without_replacement, -1, -1) 63 | 64 | # k > n 65 | assert_raises(ValueError, sample_without_replacement, 2, 3) 66 | 67 | 68 | # draw # 69 | 70 | class TestDraw: 71 | def setup_method(self): 72 | self.pmf = np.array([0.4, 0.1, 0.5]) 73 | self.cdf = np.cumsum(self.pmf) 74 | self.n = len(self.pmf) 75 | 76 | def test_return_types(self): 77 | out = draw(self.cdf) 78 | assert_(isinstance(out, numbers.Integral)) 79 | 80 | size = 10 81 | out = draw(self.cdf, size) 82 | assert_(out.shape == (size,)) 83 | 84 | def test_return_values(self): 85 | out = draw(self.cdf) 86 | assert_(out in range(self.n)) 87 | 88 | size = 10 89 | out = draw(self.cdf, size) 90 | assert_(np.isin(out, range(self.n)).all()) 91 | 92 | def test_lln(self): 93 | size = 1000000 94 | out = draw(self.cdf, size) 95 | hist, bin_edges = np.histogram(out, bins=self.n, density=True) 96 | pmf_computed = hist * np.diff(bin_edges) 97 | atol = 1e-2 98 | assert_allclose(pmf_computed, self.pmf, atol=atol) 99 | -------------------------------------------------------------------------------- /quantecon/tests/test_kalman.py: -------------------------------------------------------------------------------- 1 | """ 2 | Tests for the kalman.py 3 | 4 | """ 5 | import numpy as np 6 | from numpy.testing import assert_allclose 7 | from quantecon import LinearStateSpace 8 | from quantecon import Kalman 9 | 10 | 11 | class TestKalman: 12 | 13 | def setup_method(self): 14 | # Initial Values 15 | self.A = np.array([[.95, 0], [0., .95]]) 16 | self.C = np.eye(2) * np.sqrt(0.5) 17 | self.G = np.eye(2) * .5 18 | self.H = np.eye(2) * np.sqrt(0.2) 19 | 20 | self.Q = np.dot(self.C, self.C.T) 21 | self.R = np.dot(self.H, self.H.T) 22 | 23 | ss = LinearStateSpace(self.A, self.C, self.G, self.H) 24 | 25 | self.kf = Kalman(ss) 26 | 27 | self.methods = ['doubling', 'qz'] 28 | 29 | 30 | def teardown_method(self): 31 | del self.kf 32 | 33 | 34 | def test_stationarity(self): 35 | A, Q, G, R = self.A, self.Q, self.G, self.R 36 | kf = self.kf 37 | 38 | for method in self.methods: 39 | sig_inf, kal_gain = kf.stationary_values(method=method) 40 | 41 | mat_inv = np.linalg.inv(G.dot(sig_inf).dot(G.T) + R) 42 | 43 | # Compute the kalmain gain and sigma infinity according to the 44 | # recursive equations and compare 45 | kal_recursion = np.dot(A, sig_inf).dot(G.T).dot(mat_inv) 46 | sig_recursion = (A.dot(sig_inf).dot(A.T) - 47 | kal_recursion.dot(G).dot(sig_inf).dot(A.T) + Q) 48 | 49 | assert_allclose(kal_gain, kal_recursion, rtol=1e-4, atol=1e-2) 50 | assert_allclose(sig_inf, sig_recursion, rtol=1e-4, atol=1e-2) 51 | 52 | 53 | def test_update_using_stationary(self): 54 | kf = self.kf 55 | 56 | for method in self.methods: 57 | sig_inf, kal_gain = kf.stationary_values(method=method) 58 | 59 | kf.set_state(np.zeros((2, 1)), sig_inf) 60 | 61 | kf.update(np.zeros((2, 1))) 62 | 63 | assert_allclose(kf.Sigma, sig_inf, rtol=1e-4, atol=1e-2) 64 | assert_allclose(kf.x_hat.squeeze(), np.zeros(2), 65 | rtol=1e-4, atol=1e-2) 66 | 67 | 68 | def test_update_nonstationary(self): 69 | A, Q, G, R = self.A, self.Q, self.G, self.R 70 | kf = self.kf 71 | 72 | curr_x, curr_sigma = np.ones((2, 1)), np.eye(2) * .75 73 | y_observed = np.ones((2, 1)) * .75 74 | 75 | kf.set_state(curr_x, curr_sigma) 76 | kf.update(y_observed) 77 | 78 | mat_inv = np.linalg.inv(G.dot(curr_sigma).dot(G.T) + R) 79 | curr_k = np.dot(A, curr_sigma).dot(G.T).dot(mat_inv) 80 | new_sigma = (A.dot(curr_sigma).dot(A.T) - 81 | curr_k.dot(G).dot(curr_sigma).dot(A.T) + Q) 82 | 83 | new_xhat = A.dot(curr_x) + curr_k.dot(y_observed - G.dot(curr_x)) 84 | 85 | assert_allclose(kf.Sigma, new_sigma, rtol=1e-4, atol=1e-2) 86 | assert_allclose(kf.x_hat, new_xhat, rtol=1e-4, atol=1e-2) 87 | -------------------------------------------------------------------------------- /quantecon/optimize/minmax.py: -------------------------------------------------------------------------------- 1 | """ 2 | Contain a minmax problem solver routine. 3 | 4 | """ 5 | import numpy as np 6 | from numba import jit 7 | from .linprog_simplex import solve_tableau, PivOptions 8 | from .pivoting import _pivoting 9 | 10 | 11 | @jit(nopython=True, cache=True) 12 | def minmax(A, max_iter=10**6, piv_options=PivOptions()): 13 | r""" 14 | Given an m x n matrix `A`, return the value :math:`v^*` of the 15 | minmax problem: 16 | 17 | .. math:: 18 | 19 | v^* = \max_{x \in \Delta_m} \min_{y \in \Delta_n} x^T A y 20 | = \min_{y \in \Delta_n}\max_{x \in \Delta_m} x^T A y 21 | 22 | and the optimal solutions :math:`x^* \in \Delta_m` and 23 | :math:`y^* \in \Delta_n`: :math:`v^* = x^{*T} A y^*`, where 24 | :math:`\Delta_k = \{z \in \mathbb{R}^k_+ \mid z_1 + \cdots + z_k = 25 | 1\}`, :math:`k = m, n`. 26 | 27 | This routine is jit-compiled by Numba, using 28 | `optimize.linprog_simplex` routines. 29 | 30 | Parameters 31 | ---------- 32 | A : ndarray(float, ndim=2) 33 | ndarray of shape (m, n). 34 | 35 | max_iter : int, optional(default=10**6) 36 | Maximum number of iteration in the linear programming solver. 37 | 38 | piv_options : PivOptions, optional 39 | PivOptions namedtuple to set tolerance values used in the linear 40 | programming solver. 41 | 42 | Returns 43 | ------- 44 | v : float 45 | Value :math:`v^*` of the minmax problem. 46 | 47 | x : ndarray(float, ndim=1) 48 | Optimal solution :math:`x^*`, of shape (m,). 49 | 50 | y : ndarray(float, ndim=1) 51 | Optimal solution :math:`y^*`, of shape (n,). 52 | 53 | """ 54 | m, n = A.shape 55 | 56 | min_ = A.min() 57 | const = 0. 58 | if min_ <= 0: 59 | const = min_ * (-1) + 1 60 | 61 | tableau = np.zeros((m+2, n+1+m+1)) 62 | 63 | for i in range(m): 64 | for j in range(n): 65 | tableau[i, j] = A[i, j] + const 66 | tableau[i, n] = -1 67 | tableau[i, n+1+i] = 1 68 | 69 | tableau[-2, :n] = 1 70 | tableau[-2, -1] = 1 71 | tableau[-1, n] = -1 72 | 73 | # Phase 1 74 | pivcol = 0 75 | 76 | pivrow = 0 77 | max_ = tableau[0, pivcol] 78 | for i in range(1, m): 79 | if tableau[i, pivcol] > max_: 80 | pivrow = i 81 | max_ = tableau[i, pivcol] 82 | 83 | _pivoting(tableau, n, pivrow) 84 | _pivoting(tableau, pivcol, m) 85 | 86 | basis = np.arange(n+1, n+1+m+1) 87 | basis[pivrow] = n 88 | basis[-1] = 0 89 | 90 | # Phase 2 91 | solve_tableau(tableau, basis, max_iter-2, skip_aux=False, 92 | piv_options=piv_options) 93 | 94 | # Obtain solution 95 | x = np.empty(m) 96 | y = np.zeros(n) 97 | 98 | for i in range(m+1): 99 | if basis[i] < n: 100 | y[basis[i]] = tableau[i, -1] 101 | 102 | for j in range(m): 103 | x[j] = tableau[-1, n+1+j] 104 | if x[j] != 0: 105 | x[j] *= -1 106 | 107 | v = tableau[-1, -1] - const 108 | 109 | return v, x, y 110 | -------------------------------------------------------------------------------- /quantecon/_quadsums.py: -------------------------------------------------------------------------------- 1 | """ 2 | This module provides functions to compute quadratic sums of the form described 3 | in the docstrings. 4 | 5 | """ 6 | 7 | 8 | import numpy as np 9 | import scipy.linalg 10 | from ._matrix_eqn import solve_discrete_lyapunov 11 | 12 | 13 | def var_quadratic_sum(A, C, H, beta, x0): 14 | r""" 15 | Computes the expected discounted quadratic sum 16 | 17 | .. math:: 18 | 19 | q(x_0) = \mathbb{E} \Big[ \sum_{t=0}^{\infty} \beta^t x_t' H x_t \Big] 20 | 21 | Here :math:`{x_t}` is the VAR process :math:`x_{t+1} = A x_t + C w_t` 22 | with :math:`{x_t}` standard normal and :math:`x_0` the initial condition. 23 | 24 | Parameters 25 | ---------- 26 | A : array_like(float, ndim=2) 27 | The matrix described above in description. Should be n x n 28 | C : array_like(float, ndim=2) 29 | The matrix described above in description. Should be n x n 30 | H : array_like(float, ndim=2) 31 | The matrix described above in description. Should be n x n 32 | beta: scalar(float) 33 | Should take a value in (0, 1) 34 | x_0: array_like(float, ndim=1) 35 | The initial condtion. A conformable array (of length n, or with 36 | n rows) 37 | 38 | Returns 39 | ------- 40 | q0: scalar(float) 41 | Represents the value :math:`q(x_0)` 42 | 43 | Remarks: The formula for computing :math:`q(x_0)` is 44 | :math:`q(x_0) = x_0' Q x_0 + v` 45 | where 46 | 47 | * :math:`Q` is the solution to :math:`Q = H + \beta A' Q A`, and 48 | * :math:`v = \frac{trace(C' Q C) \beta}{(1 - \beta)}` 49 | 50 | """ 51 | # == Make sure that A, C, H and x0 are array_like == # 52 | 53 | A, C, H = list(map(np.atleast_2d, (A, C, H))) 54 | x0 = np.atleast_1d(x0) 55 | # == Start computations == # 56 | Q = scipy.linalg.solve_discrete_lyapunov(np.sqrt(beta) * A.T, H) 57 | cq = np.dot(np.dot(C.T, Q), C) 58 | v = np.trace(cq) * beta / (1 - beta) 59 | q0 = np.dot(np.dot(x0.T, Q), x0) + v 60 | 61 | return q0 62 | 63 | 64 | def m_quadratic_sum(A, B, max_it=50): 65 | r""" 66 | Computes the quadratic sum 67 | 68 | .. math:: 69 | 70 | V = \sum_{j=0}^{\infty} A^j B A^{j'} 71 | 72 | V is computed by solving the corresponding discrete lyapunov 73 | equation using the doubling algorithm. See the documentation of 74 | `util.solve_discrete_lyapunov` for more information. 75 | 76 | Parameters 77 | ---------- 78 | A : array_like(float, ndim=2) 79 | An n x n matrix as described above. We assume in order for 80 | convergence that the eigenvalues of :math:`A` have moduli bounded by 81 | unity 82 | B : array_like(float, ndim=2) 83 | An n x n matrix as described above. We assume in order for 84 | convergence that the eigenvalues of :math:`A` have moduli bounded by 85 | unity 86 | max_it : scalar(int), optional(default=50) 87 | The maximum number of iterations 88 | 89 | Returns 90 | ------- 91 | gamma1: array_like(float, ndim=2) 92 | Represents the value :math:`V` 93 | 94 | """ 95 | 96 | gamma1 = solve_discrete_lyapunov(A, B, max_it) 97 | 98 | return gamma1 99 | -------------------------------------------------------------------------------- /quantecon/tests/test_lss.py: -------------------------------------------------------------------------------- 1 | """ 2 | Tests for lss.py 3 | 4 | """ 5 | import numpy as np 6 | from numpy.testing import assert_allclose, assert_, assert_raises 7 | from quantecon import LinearStateSpace 8 | 9 | 10 | class TestLinearStateSpace: 11 | 12 | def setup_method(self): 13 | # Example 1 14 | A = .95 15 | C = .05 16 | G = 1. 17 | mu_0 = .75 18 | 19 | self.ss1 = LinearStateSpace(A, C, G, mu_0=mu_0) 20 | 21 | # Example 2 22 | ρ1 = 0.5 23 | ρ2 = 0.3 24 | α = 0.5 25 | 26 | A = np.array([[ρ1, ρ2, α], [1, 0, 0], [0, 0, 1]]) 27 | C = np.array([[1], [0], [0]]) 28 | G = np.array([[1, 0, 0]]) 29 | mu_0 = [0.5, 0.5, 1] 30 | 31 | self.ss2 = LinearStateSpace(A, C, G, mu_0=mu_0) 32 | 33 | def teardown_method(self): 34 | del self.ss1 35 | del self.ss2 36 | 37 | def test_stationarity(self): 38 | vals = self.ss1.stationary_distributions() 39 | ssmux, ssmuy, sssigx, sssigy, sssigyx = vals 40 | 41 | assert_(abs(ssmux - ssmuy) < 2e-8) 42 | assert_(abs(sssigx - sssigy) < 2e-8) 43 | assert_(abs(ssmux) < 2e-8) 44 | assert_(abs(sssigx - self.ss1.C**2/(1 - self.ss1.A**2)) < 2e-8) 45 | assert_(abs(sssigyx - self.ss1.G @ sssigx) < 2e-8) 46 | 47 | vals = self.ss2.stationary_distributions() 48 | ssmux, ssmuy, sssigx, sssigy, sssigyx = vals 49 | 50 | assert_allclose(ssmux.flatten(), np.array([2.5, 2.5, 1])) 51 | assert_allclose(ssmuy.flatten(), np.array([2.5])) 52 | assert_allclose( 53 | sssigx, 54 | self.ss2.A @ sssigx @ self.ss2.A.T + self.ss2.C @ self.ss2.C.T 55 | ) 56 | assert_allclose(sssigy, self.ss2.G @ sssigx @ self.ss2.G.T) 57 | assert_allclose(sssigyx, self.ss2.G @ sssigx) 58 | 59 | def test_simulate(self): 60 | ss = self.ss1 61 | 62 | sim = ss.simulate(ts_length=250) 63 | for arr in sim: 64 | assert_(len(arr[0]) == 250) 65 | 66 | def test_simulate_with_seed(self): 67 | ss = self.ss1 68 | 69 | xval, yval = ss.simulate(ts_length=5, random_state=5) 70 | expected_output = np.array([0.75, 0.69595649, 0.78269723, 0.73095776, 71 | 0.69989036]) 72 | 73 | assert_allclose(xval[0], expected_output) 74 | assert_allclose(yval[0], expected_output) 75 | 76 | def test_replicate(self): 77 | xval, yval = self.ss1.replicate(T=100, num_reps=5000) 78 | 79 | assert_allclose(xval, yval) 80 | assert_(xval.size == 5000) 81 | assert_(abs(np.mean(xval)) <= .05) 82 | 83 | def test_replicate_with_seed(self): 84 | xval, yval = self.ss1.replicate(T=100, num_reps=5, random_state=5) 85 | expected_output = np.array([0.10498898, 0.02892168, 0.04915998, 86 | 0.18568489, 0.04541764]) 87 | 88 | assert_allclose(xval[0], expected_output) 89 | assert_allclose(yval[0], expected_output) 90 | 91 | 92 | def test_non_square_A(): 93 | A = np.zeros((1, 2)) 94 | C = np.zeros((1, 1)) 95 | G = np.zeros((1, 1)) 96 | 97 | assert_raises(ValueError, LinearStateSpace, A, C, G) 98 | -------------------------------------------------------------------------------- /quantecon/game_theory/tests/test_random.py: -------------------------------------------------------------------------------- 1 | """ 2 | Tests for game_theory/random.py 3 | 4 | """ 5 | import numpy as np 6 | from numpy.testing import ( 7 | assert_allclose, assert_raises, assert_, assert_array_equal 8 | ) 9 | from quantecon.game_theory import ( 10 | random_game, covariance_game, random_pure_actions, random_mixed_actions, 11 | ) 12 | 13 | 14 | def test_random_game(): 15 | nums_actions = (2, 3, 4) 16 | g = random_game(nums_actions) 17 | assert_(g.nums_actions == nums_actions) 18 | 19 | # Generate seed by np.random.SeedSequence().entropy 20 | seed = 227108210370342174739429861866005407311 21 | gs = [ 22 | random_game(nums_actions, random_state=np.random.default_rng(seed)) 23 | for i in range(2) 24 | ] 25 | assert_array_equal(*[g.payoff_profile_array for g in gs]) 26 | 27 | 28 | def test_covariance_game(): 29 | nums_actions = (2, 3, 4) 30 | N = len(nums_actions) 31 | 32 | rho = 0.5 33 | g = covariance_game(nums_actions, rho=rho) 34 | assert_(g.nums_actions == nums_actions) 35 | 36 | seed = 289722416785475140936980467255496855908 37 | gs = [ 38 | covariance_game(nums_actions, rho=rho, 39 | random_state=np.random.default_rng(seed)) 40 | for i in range(2) 41 | ] 42 | assert_array_equal(*[g.payoff_profile_array for g in gs]) 43 | 44 | rho = 1 45 | g = covariance_game(nums_actions, rho=rho) 46 | for a in np.ndindex(*nums_actions): 47 | payoff_profile = g.payoff_profile_array[a] 48 | for i in range(N-1): 49 | assert_allclose(payoff_profile[i], payoff_profile[-1], atol=1e-7) 50 | 51 | rho = -1 / (N - 1) 52 | g = covariance_game(nums_actions, rho=rho) 53 | for a in np.ndindex(*nums_actions): 54 | assert_allclose(g.payoff_profile_array.sum(axis=-1), 55 | np.zeros(nums_actions), 56 | atol=1e-10) 57 | 58 | 59 | def test_random_game_value_error(): 60 | nums_actions = () # empty 61 | assert_raises(ValueError, random_game, nums_actions) 62 | 63 | 64 | def test_covariance_game_value_error(): 65 | nums_actions = () # empty 66 | assert_raises(ValueError, covariance_game, nums_actions, rho=0) 67 | 68 | nums_actions = (2,) # length one 69 | assert_raises(ValueError, covariance_game, nums_actions, rho=0) 70 | 71 | nums_actions = (2, 3, 4) 72 | 73 | rho = 1.1 # > 1 74 | assert_raises(ValueError, covariance_game, nums_actions, rho) 75 | 76 | rho = -1 # < -1/(N-1) 77 | assert_raises(ValueError, covariance_game, nums_actions, rho) 78 | 79 | 80 | def test_random_pure_actions(): 81 | nums_actions = (2, 3, 4) 82 | N = len(nums_actions) 83 | seed = 1234 84 | for gen in [lambda x: x, np.random.default_rng]: 85 | action_profiles = [ 86 | random_pure_actions(nums_actions, gen(seed)) for i in range(2) 87 | ] 88 | for i in range(N): 89 | assert_(action_profiles[0][i] < nums_actions[i]) 90 | assert_(action_profiles[0] == action_profiles[1]) 91 | 92 | 93 | def test_random_mixed_actions(): 94 | nums_actions = (2, 3, 4) 95 | seed = 1234 96 | action_profile = random_mixed_actions(nums_actions, seed) 97 | assert_(tuple([len(action) for action in action_profile]) == nums_actions) 98 | -------------------------------------------------------------------------------- /quantecon/_rank_nullspace.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from numpy.linalg import svd 3 | 4 | 5 | def rank_est(A, atol=1e-13, rtol=0): 6 | """ 7 | Estimate the rank (i.e. the dimension of the nullspace) of a matrix. 8 | 9 | The algorithm used by this function is based on the singular value 10 | decomposition of `A`. 11 | 12 | Parameters 13 | ---------- 14 | A : array_like(float, ndim=1 or 2) 15 | A should be at most 2-D. A 1-D array with length n will be 16 | treated as a 2-D with shape (1, n) 17 | atol : scalar(float), optional(default=1e-13) 18 | The absolute tolerance for a zero singular value. Singular 19 | values smaller than `atol` are considered to be zero. 20 | rtol : scalar(float), optional(default=0) 21 | The relative tolerance. Singular values less than rtol*smax are 22 | considered to be zero, where smax is the largest singular value. 23 | 24 | Returns 25 | ------- 26 | r : scalar(int) 27 | The estimated rank of the matrix. 28 | 29 | Note: If both `atol` and `rtol` are positive, the combined tolerance 30 | is the maximum of the two; that is: 31 | 32 | tol = max(atol, rtol * smax) 33 | 34 | Note: Singular values smaller than `tol` are considered to be zero. 35 | 36 | See also 37 | -------- 38 | numpy.linalg.matrix_rank 39 | matrix_rank is basically the same as this function, but it does 40 | not provide the option of the absolute tolerance. 41 | 42 | """ 43 | 44 | A = np.atleast_2d(A) 45 | s = svd(A, compute_uv=False) 46 | tol = max(atol, rtol * s[0]) 47 | rank = int((s >= tol).sum()) 48 | 49 | return rank 50 | 51 | 52 | def nullspace(A, atol=1e-13, rtol=0): 53 | """ 54 | Compute an approximate basis for the nullspace of A. 55 | 56 | The algorithm used by this function is based on the singular value 57 | decomposition of `A`. 58 | 59 | Parameters 60 | ---------- 61 | A : array_like(float, ndim=1 or 2) 62 | A should be at most 2-D. A 1-D array with length k will be 63 | treated as a 2-D with shape (1, k) 64 | atol : scalar(float), optional(default=1e-13) 65 | The absolute tolerance for a zero singular value. Singular 66 | values smaller than `atol` are considered to be zero. 67 | rtol : scalar(float), optional(default=0) 68 | The relative tolerance. Singular values less than rtol*smax are 69 | considered to be zero, where smax is the largest singular value. 70 | 71 | Returns 72 | ------- 73 | ns : array_like(float, ndim=2) 74 | If `A` is an array with shape (m, k), then `ns` will be an array 75 | with shape (k, n), where n is the estimated dimension of the 76 | nullspace of `A`. The columns of `ns` are a basis for the 77 | nullspace; each element in numpy.dot(A, ns) will be 78 | approximately zero. 79 | 80 | Note: If both `atol` and `rtol` are positive, the combined tolerance 81 | is the maximum of the two; that is: 82 | 83 | tol = max(atol, rtol * smax) 84 | 85 | Note: Singular values smaller than `tol` are considered to be zero. 86 | 87 | """ 88 | 89 | A = np.atleast_2d(A) 90 | u, s, vh = svd(A) 91 | tol = max(atol, rtol * s[0]) 92 | nnz = (s >= tol).sum() 93 | ns = vh[nnz:].conj().T 94 | 95 | return ns 96 | -------------------------------------------------------------------------------- /quantecon/util/combinatorics.py: -------------------------------------------------------------------------------- 1 | """ 2 | Useful routines for combinatorics 3 | 4 | """ 5 | from scipy.special import comb 6 | from numba import jit 7 | 8 | from .numba import comb_jit 9 | 10 | 11 | @jit(nopython=True, cache=True) 12 | def next_k_array(a): 13 | """ 14 | Given an array `a` of k distinct nonnegative integers, sorted in 15 | ascending order, return the next k-array in the lexicographic 16 | ordering of the descending sequences of the elements [1]_. `a` is 17 | modified in place. 18 | 19 | Parameters 20 | ---------- 21 | a : ndarray(int, ndim=1) 22 | Array of length k. 23 | 24 | Returns 25 | ------- 26 | a : ndarray(int, ndim=1) 27 | View of `a`. 28 | 29 | Examples 30 | -------- 31 | Enumerate all the subsets with k elements of the set {0, ..., n-1}. 32 | 33 | >>> n, k = 4, 2 34 | >>> a = np.arange(k) 35 | >>> while a[-1] < n: 36 | ... print(a) 37 | ... a = next_k_array(a) 38 | ... 39 | [0 1] 40 | [0 2] 41 | [1 2] 42 | [0 3] 43 | [1 3] 44 | [2 3] 45 | 46 | References 47 | ---------- 48 | .. [1] `Combinatorial number system 49 | `_, 50 | Wikipedia. 51 | 52 | """ 53 | # Logic taken from Algotirhm T in D. Knuth, The Art of Computer 54 | # Programming, Section 7.2.1.3 "Generating All Combinations". 55 | k = len(a) 56 | if k == 1 or a[0] + 1 < a[1]: 57 | a[0] += 1 58 | return a 59 | 60 | a[0] = 0 61 | i = 1 62 | x = a[i] + 1 63 | 64 | while i < k-1 and x == a[i+1]: 65 | i += 1 66 | a[i-1] = i - 1 67 | x = a[i] + 1 68 | a[i] = x 69 | 70 | return a 71 | 72 | 73 | def k_array_rank(a): 74 | """ 75 | Given an array `a` of k distinct nonnegative integers, sorted in 76 | ascending order, return its ranking in the lexicographic ordering of 77 | the descending sequences of the elements [1]_. 78 | 79 | Parameters 80 | ---------- 81 | a : ndarray(int, ndim=1) 82 | Array of length k. 83 | 84 | Returns 85 | ------- 86 | idx : scalar(int) 87 | Ranking of `a`. 88 | 89 | References 90 | ---------- 91 | .. [1] `Combinatorial number system 92 | `_, 93 | Wikipedia. 94 | 95 | """ 96 | k = len(a) 97 | idx = int(a[0]) # Convert to Python int 98 | for i in range(1, k): 99 | idx += comb(a[i], i+1, exact=True) 100 | return idx 101 | 102 | 103 | @jit(nopython=True, cache=True) 104 | def k_array_rank_jit(a): 105 | """ 106 | Numba jit version of `k_array_rank`. 107 | 108 | Notes 109 | ----- 110 | An incorrect value will be returned without warning or error if 111 | overflow occurs during the computation. It is the user's 112 | responsibility to ensure that the rank of the input array fits 113 | within the range of possible values of `np.intp`; a sufficient 114 | condition for it is `scipy.special.comb(a[-1]+1, len(a), exact=True) 115 | <= np.iinfo(np.intp).max`. 116 | 117 | """ 118 | k = len(a) 119 | idx = a[0] 120 | for i in range(1, k): 121 | idx += comb_jit(a[i], i+1) 122 | return idx 123 | -------------------------------------------------------------------------------- /docs/sphinxext/only_directives.py: -------------------------------------------------------------------------------- 1 | # 2 | # A pair of directives for inserting content that will only appear in 3 | # either html or latex. 4 | # 5 | 6 | from docutils.nodes import Body, Element 7 | from docutils.writers.html4css1 import HTMLTranslator 8 | try: 9 | from sphinx.latexwriter import LaTeXTranslator 10 | except ImportError: 11 | from sphinx.writers.latex import LaTeXTranslator 12 | 13 | import warnings 14 | warnings.warn("The numpydoc.only_directives module is deprecated;" 15 | "please use the only:: directive available in Sphinx >= 0.6", 16 | DeprecationWarning, stacklevel=2) 17 | 18 | from docutils.parsers.rst import directives 19 | 20 | class html_only(Body, Element): 21 | pass 22 | 23 | class latex_only(Body, Element): 24 | pass 25 | 26 | def run(content, node_class, state, content_offset): 27 | text = '\n'.join(content) 28 | node = node_class(text) 29 | state.nested_parse(content, content_offset, node) 30 | return [node] 31 | 32 | try: 33 | from docutils.parsers.rst import Directive 34 | except ImportError: 35 | from docutils.parsers.rst.directives import _directives 36 | 37 | def html_only_directive(name, arguments, options, content, lineno, 38 | content_offset, block_text, state, state_machine): 39 | return run(content, html_only, state, content_offset) 40 | 41 | def latex_only_directive(name, arguments, options, content, lineno, 42 | content_offset, block_text, state, state_machine): 43 | return run(content, latex_only, state, content_offset) 44 | 45 | for func in (html_only_directive, latex_only_directive): 46 | func.content = 1 47 | func.options = {} 48 | func.arguments = None 49 | 50 | _directives['htmlonly'] = html_only_directive 51 | _directives['latexonly'] = latex_only_directive 52 | else: 53 | class OnlyDirective(Directive): 54 | has_content = True 55 | required_arguments = 0 56 | optional_arguments = 0 57 | final_argument_whitespace = True 58 | option_spec = {} 59 | 60 | def run(self): 61 | self.assert_has_content() 62 | return run(self.content, self.node_class, 63 | self.state, self.content_offset) 64 | 65 | class HtmlOnlyDirective(OnlyDirective): 66 | node_class = html_only 67 | 68 | class LatexOnlyDirective(OnlyDirective): 69 | node_class = latex_only 70 | 71 | directives.register_directive('htmlonly', HtmlOnlyDirective) 72 | directives.register_directive('latexonly', LatexOnlyDirective) 73 | 74 | def setup(app): 75 | app.add_node(html_only) 76 | app.add_node(latex_only) 77 | 78 | # Add visit/depart methods to HTML-Translator: 79 | def visit_perform(self, node): 80 | pass 81 | def depart_perform(self, node): 82 | pass 83 | def visit_ignore(self, node): 84 | node.children = [] 85 | def depart_ignore(self, node): 86 | node.children = [] 87 | 88 | HTMLTranslator.visit_html_only = visit_perform 89 | HTMLTranslator.depart_html_only = depart_perform 90 | HTMLTranslator.visit_latex_only = visit_ignore 91 | HTMLTranslator.depart_latex_only = depart_ignore 92 | 93 | LaTeXTranslator.visit_html_only = visit_ignore 94 | LaTeXTranslator.depart_html_only = depart_ignore 95 | LaTeXTranslator.visit_latex_only = visit_perform 96 | LaTeXTranslator.depart_latex_only = depart_perform 97 | -------------------------------------------------------------------------------- /quantecon/util/numba.py: -------------------------------------------------------------------------------- 1 | """ 2 | Utilities to support Numba jitted functions 3 | 4 | """ 5 | import numpy as np 6 | from numba import jit, generated_jit, types 7 | try: 8 | from numba.np.linalg import _LAPACK # for Numba >= 0.49.0 9 | except ModuleNotFoundError: 10 | from numba.targets.linalg import _LAPACK # for Numba < 0.49.0 11 | 12 | 13 | # BLAS kinds as letters 14 | _blas_kinds = { 15 | types.float32: 's', 16 | types.float64: 'd', 17 | types.complex64: 'c', 18 | types.complex128: 'z', 19 | } 20 | 21 | 22 | @generated_jit(nopython=True, cache=True) 23 | def _numba_linalg_solve(a, b): 24 | """ 25 | Solve the linear equation ax = b directly calling a Numba internal 26 | function. The data in `a` and `b` are interpreted in Fortran order, 27 | and dtype of `a` and `b` must be the same, one of {float32, float64, 28 | complex64, complex128}. `a` and `b` are modified in place, and the 29 | solution is stored in `b`. *No error check is made for the inputs.* 30 | 31 | Parameters 32 | ---------- 33 | a : ndarray(ndim=2) 34 | 2-dimensional ndarray of shape (n, n). 35 | 36 | b : ndarray(ndim=1 or 2) 37 | 1-dimensional ndarray of shape (n,) or 2-dimensional ndarray of 38 | shape (n, nrhs). 39 | 40 | Returns 41 | ------- 42 | r : scalar(int) 43 | r = 0 if successful. 44 | 45 | Notes 46 | ----- 47 | From github.com/numba/numba/blob/main/numba/np/linalg.py 48 | 49 | """ 50 | numba_xgesv = _LAPACK().numba_xgesv(a.dtype) 51 | kind = ord(_blas_kinds[a.dtype]) 52 | 53 | def _numba_linalg_solve_impl(a, b): # pragma: no cover 54 | n = a.shape[-1] 55 | if b.ndim == 1: 56 | nrhs = 1 57 | else: # b.ndim == 2 58 | nrhs = b.shape[-1] 59 | F_INT_nptype = np.int32 60 | ipiv = np.empty(n, dtype=F_INT_nptype) 61 | 62 | r = numba_xgesv( 63 | kind, # kind 64 | n, # n 65 | nrhs, # nhrs 66 | a.ctypes, # a 67 | n, # lda 68 | ipiv.ctypes, # ipiv 69 | b.ctypes, # b 70 | n # ldb 71 | ) 72 | return r 73 | 74 | return _numba_linalg_solve_impl 75 | 76 | 77 | @jit(types.intp(types.intp, types.intp), nopython=True, cache=True) 78 | def comb_jit(N, k): 79 | """ 80 | Numba jitted function that computes N choose k. Return `0` if the 81 | outcome exceeds the maximum value of `np.intp` or if N < 0, k < 0, 82 | or k > N. 83 | 84 | Parameters 85 | ---------- 86 | N : scalar(int) 87 | 88 | k : scalar(int) 89 | 90 | Returns 91 | ------- 92 | val : scalar(int) 93 | 94 | """ 95 | # From scipy.special._comb_int_long 96 | # github.com/scipy/scipy/blob/v1.0.0/scipy/special/_comb.pyx 97 | INTP_MAX = np.iinfo(np.intp).max 98 | if N < 0 or k < 0 or k > N: 99 | return 0 100 | if k == 0: 101 | return 1 102 | if k == 1: 103 | return N 104 | if N == INTP_MAX: 105 | return 0 106 | 107 | M = N + 1 108 | nterms = min(k, N - k) 109 | 110 | val = 1 111 | 112 | for j in range(1, nterms + 1): 113 | # Overflow check 114 | if val > INTP_MAX // (M - j): 115 | return 0 116 | 117 | val *= M - j 118 | val //= j 119 | 120 | return val 121 | -------------------------------------------------------------------------------- /quantecon/optimize/tests/test_root_finding.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from numpy.testing import assert_almost_equal, assert_allclose 3 | from numba import njit 4 | 5 | from quantecon.optimize import ( 6 | newton, newton_halley, newton_secant, bisect, brentq 7 | ) 8 | 9 | 10 | @njit 11 | def func(x): 12 | """ 13 | Function for testing on. 14 | """ 15 | return (x**3 - 1) 16 | 17 | 18 | @njit 19 | def func_prime(x): 20 | """ 21 | Derivative for func. 22 | """ 23 | return (3*x**2) 24 | 25 | 26 | @njit 27 | def func_prime2(x): 28 | """ 29 | Second order derivative for func. 30 | """ 31 | return 6*x 32 | 33 | 34 | @njit 35 | def func_two(x): 36 | """ 37 | Harder function for testing on. 38 | """ 39 | return np.sin(4 * (x - 1/4)) + x + x**20 - 1 40 | 41 | 42 | @njit 43 | def func_two_prime(x): 44 | """ 45 | Derivative for func_two. 46 | """ 47 | return 4*np.cos(4*(x - 1/4)) + 20*x**19 + 1 48 | 49 | 50 | @njit 51 | def func_two_prime2(x): 52 | """ 53 | Second order derivative for func_two 54 | """ 55 | return 380*x**18 - 16*np.sin(4*(x - 1/4)) 56 | 57 | 58 | def test_newton_basic(): 59 | """ 60 | Uses the function f defined above to test the scalar maximization 61 | routine. 62 | """ 63 | true_fval = 1.0 64 | fval = newton(func, 5, func_prime) 65 | assert_almost_equal(true_fval, fval.root, decimal=4) 66 | 67 | 68 | def test_newton_basic_two(): 69 | """ 70 | Uses the function f defined above to test the scalar maximization 71 | routine. 72 | """ 73 | true_fval = 1.0 74 | fval = newton(func, 5, func_prime) 75 | assert_allclose(true_fval, fval.root, rtol=1e-5, atol=0) 76 | 77 | 78 | def test_newton_hard(): 79 | """ 80 | Harder test for convergence. 81 | """ 82 | true_fval = 0.408 83 | fval = newton(func_two, 0.4, func_two_prime) 84 | assert_allclose(true_fval, fval.root, rtol=1e-5, atol=0.01) 85 | 86 | 87 | def test_halley_basic(): 88 | """ 89 | Basic test for halley method 90 | """ 91 | true_fval = 1.0 92 | fval = newton_halley(func, 5, func_prime, func_prime2) 93 | assert_almost_equal(true_fval, fval.root, decimal=4) 94 | 95 | 96 | def test_halley_hard(): 97 | """ 98 | Harder test for halley method 99 | """ 100 | true_fval = 0.408 101 | fval = newton_halley(func_two, 0.4, func_two_prime, func_two_prime2) 102 | assert_allclose(true_fval, fval.root, rtol=1e-5, atol=0.01) 103 | 104 | 105 | def test_secant_basic(): 106 | """ 107 | Basic test for secant option. 108 | """ 109 | true_fval = 1.0 110 | fval = newton_secant(func, 5) 111 | assert_allclose(true_fval, fval.root, rtol=1e-5, atol=0.001) 112 | 113 | 114 | def test_secant_hard(): 115 | """ 116 | Harder test for convergence for secant function. 117 | """ 118 | true_fval = 0.408 119 | fval = newton_secant(func_two, 0.4) 120 | assert_allclose(true_fval, fval.root, rtol=1e-5, atol=0.01) 121 | 122 | 123 | def run_check(method, name): 124 | a = -1 125 | b = np.sqrt(3) 126 | true_fval = 0.408 127 | r = method(func_two, a, b) 128 | assert_allclose(true_fval, r.root, atol=0.01, rtol=1e-5, 129 | err_msg='method %s' % name) 130 | 131 | 132 | def test_bisect_basic(): 133 | run_check(bisect, 'bisect') 134 | 135 | 136 | def test_brentq_basic(): 137 | run_check(brentq, 'brentq') 138 | -------------------------------------------------------------------------------- /quantecon/markov/tests/test_estimate.py: -------------------------------------------------------------------------------- 1 | """ 2 | Tests for markov/estimate.py 3 | 4 | """ 5 | import numpy as np 6 | from numpy.testing import assert_array_equal, assert_allclose 7 | from quantecon import estimate_mc 8 | from quantecon.markov import fit_discrete_mc 9 | 10 | 11 | class TestEstimateMCDiscrete: 12 | def setup_method(self): 13 | self.test_index_series = np.array((0, 1, 1, 1, 1, 0, 2, 1)) 14 | self.initial_state_values = np.array(['a', 'b', 'c', 'd']) 15 | self.test_value_series = \ 16 | self.initial_state_values[self.test_index_series] 17 | 18 | self.P = np.array([[0., 0.5, 0.5 ], 19 | [0.25, 0.75, 0. ], 20 | [0., 1., 0. ]]) 21 | 22 | self.final_state_indices = np.array([0, 1, 2]) 23 | self.final_state_values = \ 24 | self.initial_state_values[self.final_state_indices] 25 | 26 | def test_integer_state(self): 27 | mc = estimate_mc(self.test_index_series) 28 | assert_allclose(mc.P, self.P) 29 | assert_array_equal(mc.state_values, self.final_state_indices) 30 | 31 | def test_non_integer_state(self): 32 | mc = estimate_mc(self.test_value_series) 33 | assert_allclose(mc.P, self.P) 34 | assert_array_equal(mc.state_values, self.final_state_values) 35 | 36 | mc = estimate_mc(self.test_index_series) 37 | mc.state_values = self.initial_state_values[mc.state_values] 38 | assert_allclose(mc.P, self.P) 39 | assert_array_equal(mc.state_values, self.final_state_values) 40 | 41 | def test_mult_dim_state(self): 42 | initial_state_values = np.array([[0.97097089, 0.76167618], 43 | [0.61878456, 0.41691572], 44 | [0.42137226, 0.09307409], 45 | [0.62696609, 0.40898893]]) 46 | X = initial_state_values[self.test_index_series] 47 | ind = np.lexsort( 48 | np.rot90(initial_state_values[self.final_state_indices]) 49 | ) 50 | final_state_values = initial_state_values[ind] 51 | 52 | mc = estimate_mc(X) 53 | assert_allclose(mc.P, self.P[np.ix_(ind, ind)]) 54 | assert_array_equal(mc.state_values, final_state_values) 55 | 56 | 57 | class TestFitDiscreteMC: 58 | def setup_method(self): 59 | self.grids = ((np.arange(4), np.arange(5))) 60 | self.X = [(-0.1, 1.2), (2, 0), (2, 3), 61 | (4.4, 4.0), (0.6, 0.4), (1.0, 0.1)] 62 | 63 | def test_order_f(self): 64 | mc = fit_discrete_mc(self.X, self.grids, order='F') 65 | S_expected = np.array([[1, 0], [2, 0], [0, 1], [2, 3], [3, 4]]) 66 | P_expected = np.array([[1., 0., 0., 0., 0.], 67 | [0., 0., 0., 1., 0.], 68 | [0., 1., 0., 0., 0.], 69 | [0., 0., 0., 0., 1.], 70 | [1., 0., 0., 0., 0.]]) 71 | 72 | assert_array_equal(mc.state_values, S_expected) 73 | assert_allclose(mc.P, P_expected) 74 | 75 | def test_order_c(self): 76 | mc = fit_discrete_mc(self.X, self.grids, order='C') 77 | S_expected = np.array([[0, 1], [1, 0], [2, 0], [2, 3], [3, 4]]) 78 | P_expected = np.array([[0., 0., 1., 0., 0.], 79 | [0., 1., 0., 0., 0.], 80 | [0., 0., 0., 1., 0.], 81 | [0., 0., 0., 0., 1.], 82 | [0., 1., 0., 0., 0.]]) 83 | 84 | assert_array_equal(mc.state_values, S_expected) 85 | assert_allclose(mc.P, P_expected) 86 | -------------------------------------------------------------------------------- /quantecon/tests/test_robustlq.py: -------------------------------------------------------------------------------- 1 | """ 2 | Tests for robustlq.py 3 | 4 | """ 5 | import numpy as np 6 | from numpy.testing import assert_allclose, assert_ 7 | from quantecon import LQ 8 | from quantecon import RBLQ 9 | 10 | 11 | class TestRBLQControl: 12 | 13 | def setup_method(self): 14 | # Initial Values 15 | a_0 = 100 16 | a_1 = 0.5 17 | rho = 0.9 18 | sigma_d = 0.05 19 | beta = 0.95 20 | c = 2 21 | gamma = 50.0 22 | theta = 0.002 23 | ac = (a_0 - c) / 2.0 24 | 25 | R = np.array([[0, ac, 0], 26 | [ac, -a_1, 0.5], 27 | [0., 0.5, 0]]) 28 | 29 | R = -R 30 | Q = gamma / 2 31 | Q_pf = 0. 32 | 33 | A = np.array([[1., 0., 0.], 34 | [0., 1., 0.], 35 | [0., 0., rho]]) 36 | B = np.array([[0.], 37 | [1.], 38 | [0.]]) 39 | B_pf = np.zeros((3, 1)) 40 | 41 | C = np.array([[0.], 42 | [0.], 43 | [sigma_d]]) 44 | 45 | # the *_pf endings refer to an example with pure forecasting 46 | # (see p171 in Robustness) 47 | self.rblq_test = RBLQ(Q, R, A, B, C, beta, theta) 48 | self.rblq_test_pf = RBLQ(Q_pf, R, A, B_pf, C, beta, theta) 49 | self.lq_test = LQ(Q, R, A, B, C, beta=beta) 50 | self.methods = ['doubling', 'qz'] 51 | 52 | def teardown_method(self): 53 | del self.rblq_test 54 | del self.rblq_test_pf 55 | 56 | def test_pure_forecasting(self): 57 | assert_(self.rblq_test_pf.pure_forecasting) 58 | 59 | def test_robust_rule_vs_simple(self): 60 | rblq = self.rblq_test 61 | rblq_pf = self.rblq_test_pf 62 | 63 | for method in self.methods: 64 | Fr, Kr, Pr = self.rblq_test.robust_rule(method=method) 65 | Fr_pf, Kr_pf, Pr_pf = self.rblq_test_pf.robust_rule(method=method) 66 | 67 | Fs, Ks, Ps = rblq.robust_rule_simple(P_init=Pr, tol=1e-12) 68 | Fs_pf, Ks_pf, Ps_pf = rblq_pf.robust_rule_simple( 69 | P_init=Pr_pf, tol=1e-12) 70 | 71 | assert_allclose(Fr, Fs, rtol=1e-4) 72 | assert_allclose(Kr, Ks, rtol=1e-4) 73 | assert_allclose(Pr, Ps, rtol=1e-4) 74 | 75 | atol = 1e-10 76 | assert_allclose(Fr_pf, Fs_pf, rtol=1e-4) 77 | assert_allclose(Kr_pf, Ks_pf, rtol=1e-4, atol=atol) 78 | assert_allclose(Pr_pf, Ps_pf, rtol=1e-4, atol=atol) 79 | 80 | 81 | def test_f2k_and_k2f(self): 82 | rblq = self.rblq_test 83 | 84 | for method in self.methods: 85 | Fr, Kr, Pr = self.rblq_test.robust_rule(method=method) 86 | K_f2k, P_f2k = rblq.F_to_K(Fr, method=method) 87 | F_k2f, P_k2f = rblq.K_to_F(Kr, method=method) 88 | assert_allclose(K_f2k, Kr, rtol=1e-4) 89 | assert_allclose(F_k2f, Fr, rtol=1e-4) 90 | assert_allclose(P_f2k, P_k2f, rtol=1e-4) 91 | 92 | def test_evaluate_F(self): 93 | rblq = self.rblq_test 94 | for method in self.methods: 95 | Fr, Kr, Pr = self.rblq_test.robust_rule(method=method) 96 | 97 | Kf, Pf, df, Of, of = rblq.evaluate_F(Fr) 98 | 99 | # In the future if we wanted, we could check more things, but I 100 | # think the other pieces are basically just plugging these into 101 | # equations so if these hold then the others should be correct 102 | # as well. 103 | assert_allclose(Pf, Pr) 104 | assert_allclose(Kf, Kr) 105 | -------------------------------------------------------------------------------- /quantecon/_ce_util.py: -------------------------------------------------------------------------------- 1 | """ 2 | Utility functions used in CompEcon 3 | 4 | Based routines found in the CompEcon toolbox by Miranda and Fackler. 5 | 6 | References 7 | ---------- 8 | Miranda, Mario J, and Paul L Fackler. Applied Computational Economics 9 | and Finance, MIT Press, 2002. 10 | 11 | """ 12 | from functools import reduce 13 | import numpy as np 14 | 15 | 16 | def ckron(*arrays): 17 | """ 18 | Repeatedly applies the np.kron function to an arbitrary number of 19 | input arrays 20 | 21 | Parameters 22 | ---------- 23 | *arrays : tuple/list of np.ndarray 24 | 25 | Returns 26 | ------- 27 | out : np.ndarray 28 | The result of repeated kronecker products. 29 | 30 | Notes 31 | ----- 32 | Based of original function `ckron` in CompEcon toolbox by Miranda 33 | and Fackler. 34 | 35 | References 36 | ---------- 37 | Miranda, Mario J, and Paul L Fackler. Applied Computational 38 | Economics and Finance, MIT Press, 2002. 39 | 40 | """ 41 | return reduce(np.kron, arrays) 42 | 43 | 44 | def gridmake(*arrays): 45 | """ 46 | Expands one or more vectors (or matrices) into a matrix where rows span the 47 | cartesian product of combinations of the input arrays. Each column of the 48 | input arrays will correspond to one column of the output matrix. 49 | 50 | Parameters 51 | ---------- 52 | *arrays : tuple/list of np.ndarray 53 | Tuple/list of vectors to be expanded. 54 | 55 | Returns 56 | ------- 57 | out : np.ndarray 58 | The cartesian product of combinations of the input arrays. 59 | 60 | Notes 61 | ----- 62 | Based of original function ``gridmake`` in CompEcon toolbox by 63 | Miranda and Fackler 64 | 65 | References 66 | ---------- 67 | Miranda, Mario J, and Paul L Fackler. Applied Computational Economics 68 | and Finance, MIT Press, 2002. 69 | 70 | """ 71 | if all([i.ndim == 1 for i in arrays]): 72 | d = len(arrays) 73 | if d == 2: 74 | out = _gridmake2(*arrays) 75 | else: 76 | out = _gridmake2(arrays[0], arrays[1]) 77 | for arr in arrays[2:]: 78 | out = _gridmake2(out, arr) 79 | 80 | return out 81 | else: 82 | raise NotImplementedError("Come back here") 83 | 84 | 85 | def _gridmake2(x1, x2): 86 | """ 87 | Expands two vectors (or matrices) into a matrix where rows span the 88 | cartesian product of combinations of the input arrays. Each column of the 89 | input arrays will correspond to one column of the output matrix. 90 | 91 | Parameters 92 | ---------- 93 | x1 : np.ndarray 94 | First vector to be expanded. 95 | 96 | x2 : np.ndarray 97 | Second vector to be expanded. 98 | 99 | Returns 100 | ------- 101 | out : np.ndarray 102 | The cartesian product of combinations of the input arrays. 103 | 104 | Notes 105 | ----- 106 | Based of original function ``gridmake2`` in CompEcon toolbox by 107 | Miranda and Fackler. 108 | 109 | References 110 | ---------- 111 | Miranda, Mario J, and Paul L Fackler. Applied Computational Economics 112 | and Finance, MIT Press, 2002. 113 | 114 | """ 115 | if x1.ndim == 1 and x2.ndim == 1: 116 | return np.column_stack([np.tile(x1, x2.shape[0]), 117 | np.repeat(x2, x1.shape[0])]) 118 | elif x1.ndim > 1 and x2.ndim == 1: 119 | first = np.tile(x1, (x2.shape[0], 1)) 120 | second = np.repeat(x2, x1.shape[0]) 121 | return np.column_stack([first, second]) 122 | else: 123 | raise NotImplementedError("Come back here") 124 | -------------------------------------------------------------------------------- /quantecon/distributions.py: -------------------------------------------------------------------------------- 1 | """ 2 | Probability distributions useful in economics. 3 | 4 | References 5 | ---------- 6 | 7 | http://en.wikipedia.org/wiki/Beta-binomial_distribution 8 | 9 | """ 10 | from math import sqrt 11 | import numpy as np 12 | from scipy.special import binom, beta 13 | 14 | 15 | class BetaBinomial: 16 | """ 17 | The Beta-Binomial distribution 18 | 19 | Parameters 20 | ---------- 21 | n : scalar(int) 22 | First parameter to the Beta-binomial distribution 23 | a : scalar(float) 24 | Second parameter to the Beta-binomial distribution 25 | b : scalar(float) 26 | Third parameter to the Beta-binomial distribution 27 | 28 | Attributes 29 | ---------- 30 | n, a, b : see Parameters 31 | 32 | """ 33 | 34 | def __init__(self, n, a, b): 35 | self.n, self.a, self.b = n, a, b 36 | 37 | @property 38 | def mean(self): 39 | "mean" 40 | n, a, b = self.n, self.a, self.b 41 | return n * a / (a + b) 42 | 43 | @property 44 | def std(self): 45 | "standard deviation" 46 | return sqrt(self.var) 47 | 48 | @property 49 | def var(self): 50 | "Variance" 51 | n, a, b = self.n, self.a, self.b 52 | top = n*a*b * (a + b + n) 53 | btm = (a+b)**2.0 * (a+b+1.0) 54 | return top / btm 55 | 56 | @property 57 | def skew(self): 58 | "skewness" 59 | n, a, b = self.n, self.a, self.b 60 | t1 = (a+b+2*n) * (b - a) / (a+b+2) 61 | t2 = sqrt((1+a+b) / (n*a*b * (n+a+b))) 62 | return t1 * t2 63 | 64 | def pdf(self): 65 | r""" 66 | Generate the vector of probabilities for the Beta-binomial 67 | (n, a, b) distribution. 68 | 69 | The Beta-binomial distribution takes the form 70 | 71 | .. math:: 72 | p(k \,|\, n, a, b) = 73 | {n \choose k} \frac{B(k + a, n - k + b)}{B(a, b)}, 74 | \qquad k = 0, \ldots, n, 75 | 76 | where :math:`B` is the beta function. 77 | 78 | Parameters 79 | ---------- 80 | n : scalar(int) 81 | First parameter to the Beta-binomial distribution 82 | a : scalar(float) 83 | Second parameter to the Beta-binomial distribution 84 | b : scalar(float) 85 | Third parameter to the Beta-binomial distribution 86 | 87 | Returns 88 | ------- 89 | probs: array_like(float) 90 | Vector of probabilities over k 91 | 92 | """ 93 | n, a, b = self.n, self.a, self.b 94 | k = np.arange(n + 1) 95 | probs = binom(n, k) * beta(k + a, n - k + b) / beta(a, b) 96 | return probs 97 | 98 | # def cdf(self): 99 | # r""" 100 | # Generate the vector of cumulative probabilities for the 101 | # Beta-binomial(n, a, b) distribution. 102 | 103 | # The cdf of the Beta-binomial distribution takes the form 104 | 105 | # .. math:: 106 | # P(k \,|\, n, a, b) = 1 - 107 | # \frac{B(b+n-k-1, a+k+1) {}_3F_2(a,b;k)}{B(a,b) B(n-k, k+2)}, 108 | # \qquad k = 0, \ldots, n 109 | 110 | # where :math:`B` is the beta function. 111 | 112 | # Parameters 113 | # ---------- 114 | # n : scalar(int) 115 | # First parameter to the Beta-binomial distribution 116 | # a : scalar(float) 117 | # Second parameter to the Beta-binomial distribution 118 | # b : scalar(float) 119 | # Third parameter to the Beta-binomial distribution 120 | 121 | # Returns 122 | # ------- 123 | # probs: array_like(float) 124 | # Vector of probabilities over k 125 | 126 | # """ 127 | -------------------------------------------------------------------------------- /quantecon/markov/estimate.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from numba import njit 3 | from .core import MarkovChain 4 | from .._gridtools import cartesian_nearest_index, cartesian 5 | 6 | 7 | def estimate_mc(X): 8 | r""" 9 | Estimate the Markov chain associated with a time series :math:`X = 10 | (X_0, \ldots, X_{T-1})` assuming that the state space is the finite 11 | set :math:`\{X_0, \ldots, X_{T-1}\}` (duplicates removed). The 12 | estimation is by maximum likelihood. The estimated transition 13 | probabilities are given by the matrix :math:`P` such that 14 | :math:`P[i, j] = N_{ij} / N_i`, where :math:`N_{ij} = 15 | \sum_{t=0}^{T-1} 1_{\{X_t=s_i, X_{t+1}=s_j\}}`, the number of 16 | transitions from state :math:`s_i` to state :math:`s_j`, while 17 | :math:`N_i` is the total number of visits to :math:`s_i`. The result 18 | is returned as a `MarkovChain` instance. 19 | 20 | Parameters 21 | ---------- 22 | X : array_like 23 | A time series of state values, from which the transition matrix 24 | will be estimated, where `X[t]` contains the t-th observation. 25 | 26 | Returns 27 | ------- 28 | mc : MarkovChain 29 | A MarkovChain instance where `mc.P` is a stochastic matrix 30 | estimated from the data `X` and `mc.state_values` is an array of 31 | values that appear in `X` (sorted in ascending order). 32 | 33 | """ 34 | X = np.asarray(X) 35 | axis = 0 if X.ndim > 1 else None 36 | state_values, indices = np.unique(X, return_inverse=True, axis=axis) 37 | 38 | n = len(state_values) 39 | P = np.zeros((n, n)) # dtype=float to modify in place upon normalization 40 | P = _count_transition_frequencies(indices, P) 41 | P /= P.sum(1)[:, np.newaxis] 42 | 43 | mc = MarkovChain(P, state_values=state_values) 44 | return mc 45 | 46 | 47 | @njit(cache=True) 48 | def _count_transition_frequencies(index_series, trans_counter): 49 | T = len(index_series) 50 | i = index_series[0] 51 | for t in range(1, T): 52 | j = index_series[t] 53 | trans_counter[i, j] += 1 54 | i = j 55 | return trans_counter 56 | 57 | 58 | def fit_discrete_mc(X, grids, order='C'): 59 | r""" 60 | Function that takes an arbitrary time series :math: `(X_t)_{t=0}^{T-1}` in 61 | :math: `\mathbb R^n` plus a set of grid points in each dimension and converts 62 | it to a MarkovChain by first applying discretization onto the grid 63 | and then estimation of the Markov chain. 64 | 65 | Parameters 66 | ---------- 67 | 68 | X: array_like(ndim=2) 69 | Time-series such that the t-th row is :math:`x_t`. 70 | It should be of the shape T x n, where n is the number of dimensions. 71 | 72 | grids: array_like(array_like(ndim=1)) 73 | Array of `n` sorted arrays. Set of grid points in each dimension 74 | 75 | Examples 76 | -------- 77 | 78 | >>> grids = (np.arange(3), np.arange(2)) 79 | >>> X = [(-0.1, 1.2), (2, 0), (0.6, 0.4), (1.0, 0.1)] 80 | >>> mc = fit_discrete_mc(X, grids) 81 | >>> mc.state_values 82 | array([[0, 1], 83 | [1, 0], 84 | [2, 0]]) 85 | >>> mc.P 86 | array([[0., 0., 1.], 87 | [0., 1., 0.], 88 | [0., 1., 0.]]) 89 | 90 | Returns 91 | ------- 92 | 93 | mc: MarkovChain 94 | An instance of the MarkovChain class constructed after discretization 95 | onto the grid. 96 | """ 97 | X_indices = cartesian_nearest_index(X, grids, order=order) 98 | mc = estimate_mc(X_indices) 99 | # Assign the visited states in the cartesian product as the state values 100 | prod = cartesian(grids, order=order) 101 | mc.state_values = prod[mc.state_values] 102 | return mc 103 | -------------------------------------------------------------------------------- /quantecon/markov/tests/test_approximation.py: -------------------------------------------------------------------------------- 1 | """ 2 | Tests for approximation.py file (i.e. tauchen) 3 | 4 | """ 5 | import numpy as np 6 | import pytest 7 | from quantecon.markov import tauchen, rouwenhorst 8 | from numpy.testing import assert_, assert_allclose, assert_raises 9 | 10 | #from quantecon.markov.approximation import rouwenhorst 11 | 12 | 13 | class TestTauchen: 14 | 15 | def setup_method(self): 16 | self.rho, self.sigma = np.random.rand(2) 17 | self.n = np.random.randint(3, 25) 18 | self.n_std = np.random.randint(5) 19 | self.tol = 1e-12 20 | self.mu = 0. 21 | 22 | mc = tauchen(self.n, self.rho, self.sigma, self.mu, self.n_std) 23 | self.x, self.P = mc.state_values, mc.P 24 | 25 | def teardown_method(self): 26 | del self.x 27 | del self.P 28 | 29 | def testStateCenter(self): 30 | for mu in [0., 1., -1.]: 31 | mu_expect = mu / (1 - self.rho) 32 | mc = tauchen(self.n, self.rho, self.sigma, mu, self.n_std) 33 | assert_allclose(mu_expect, np.mean(mc.state_values), atol=self.tol) 34 | 35 | def testShape(self): 36 | i, j = self.P.shape 37 | 38 | assert_(i == j) 39 | 40 | def testDim(self): 41 | dim_x = self.x.ndim 42 | dim_P = self.P.ndim 43 | 44 | assert_(dim_x == 1 and dim_P == 2) 45 | 46 | def test_transition_mat_row_sum_1(self): 47 | assert_allclose(np.sum(self.P, axis=1), 1, atol=self.tol) 48 | 49 | def test_positive_probs(self): 50 | assert_(np.all(self.P > -self.tol)) 51 | 52 | def test_states_sum_0(self): 53 | assert_(abs(np.sum(self.x)) < self.tol) 54 | 55 | def test_old_tauchen_api_warning(self): 56 | # Test the warning 57 | with pytest.warns(UserWarning): 58 | # This will raise an error because `n` must be an int 59 | assert_raises(TypeError, tauchen, 4.0, self.rho, self.sigma, 60 | self.mu, self.n_std) 61 | 62 | class TestRouwenhorst: 63 | 64 | def setup_method(self): 65 | self.rho, self.sigma = np.random.uniform(0, 1, size=2) 66 | self.n = np.random.randint(3, 26) 67 | self.mu = np.random.randint(0, 11) 68 | self.tol = 1e-10 69 | 70 | with pytest.warns(UserWarning): 71 | mc = rouwenhorst(self.n, self.rho, self.sigma, self.mu) 72 | self.x, self.P = mc.state_values, mc.P 73 | 74 | def teardown_method(self): 75 | del self.x 76 | del self.P 77 | 78 | def testShape(self): 79 | i, j = self.P.shape 80 | 81 | assert_(i == j) 82 | 83 | def testDim(self): 84 | dim_x = self.x.ndim 85 | dim_P = self.P.ndim 86 | assert_(dim_x == 1 and dim_P == 2) 87 | 88 | def test_transition_mat_row_sum_1(self): 89 | assert_allclose(np.sum(self.P, axis=1), 1, atol=self.tol) 90 | 91 | def test_positive_probs(self): 92 | assert_(np.all(self.P > -self.tol)) 93 | 94 | def test_states_sum_0(self): 95 | tol = self.tol + self.n*(self.mu/(1 - self.rho)) 96 | assert_(abs(np.sum(self.x)) < tol) 97 | 98 | def test_control_case(self): 99 | n = 3; mu = 1; sigma = 0.5; rho = 0.8; 100 | with pytest.warns(UserWarning): 101 | mc_rouwenhorst = rouwenhorst(n, rho, sigma, mu) 102 | mc_rouwenhorst.x, mc_rouwenhorst.P = mc_rouwenhorst.state_values, mc_rouwenhorst.P 103 | sigma_y = np.sqrt(sigma**2 / (1-rho**2)) 104 | psi = sigma_y * np.sqrt(n-1) 105 | known_x = np.array([-psi+5.0, 5., psi+5.0]) 106 | known_P = np.array( 107 | [[0.81, 0.18, 0.01], [0.09, 0.82, 0.09], [0.01, 0.18, 0.81]]) 108 | assert_(np.sum(mc_rouwenhorst.x - known_x) < self.tol and 109 | np.sum(mc_rouwenhorst.P - known_P) < self.tol) 110 | -------------------------------------------------------------------------------- /quantecon/tests/test_estspec.py: -------------------------------------------------------------------------------- 1 | """ 2 | Tests for estspec.py 3 | 4 | TODO: write tests that check accuracy of returns 5 | 6 | """ 7 | import pytest 8 | import numpy as np 9 | import re 10 | from numpy.testing import assert_, assert_raises 11 | from quantecon import smooth, periodogram, ar_periodogram 12 | from quantecon.tests.util import capture 13 | 14 | 15 | x_20 = np.random.rand(20) 16 | x_21 = np.random.rand(21) 17 | 18 | 19 | class PeriodogramBase: 20 | 21 | @classmethod 22 | def setup_method(cls): 23 | if cls is PeriodogramBase: 24 | raise pytest.skip("Skip PeriodogramBase tests" + 25 | " it's a base class") 26 | 27 | def test_func_w_shape_even_x(self): 28 | assert_(self.w_20.size == x_20.size // 2 + 1) 29 | 30 | def test_func_w_shape_odd_x(self): 31 | assert_(self.w_21.size == x_21.size // 2 + 1) 32 | 33 | def test_func_Iw_shape_even_x(self): 34 | assert_(self.Iw_20.size == x_20.size // 2 + 1) 35 | 36 | def test_func_Iw_shape_odd_x(self): 37 | assert_(self.Iw_21.size == x_21.size // 2 + 1) 38 | 39 | def test_func_w_Iw_same_shape(self): 40 | assert_(self.w_20.shape == self.Iw_20.shape) 41 | assert_(self.w_21.shape == self.Iw_21.shape) 42 | 43 | def test_func_I(self): 44 | pass 45 | 46 | 47 | class TestPeriodogram(PeriodogramBase): 48 | 49 | @classmethod 50 | def setup_method(cls): 51 | if cls is PeriodogramBase: 52 | raise pytest.skip("Skip BaseTest tests, it's a base class") 53 | super(TestPeriodogram, cls).setup_method() 54 | cls.window_length = 7 55 | cls.w_20, cls.Iw_20 = periodogram(x_20) 56 | cls.w_21, cls.Iw_21 = periodogram(x_21) 57 | cls.funcname = "periodogram" 58 | 59 | 60 | class TestArPeriodogram(PeriodogramBase): 61 | 62 | @classmethod 63 | def setup_method(cls): 64 | if cls is PeriodogramBase: 65 | raise pytest.skip("Skip BaseTest tests, it's a base class") 66 | super(TestArPeriodogram, cls).setup_method() 67 | cls.window_length = 7 68 | cls.w_20, cls.Iw_20 = ar_periodogram(x_20) 69 | cls.w_21, cls.Iw_21 = ar_periodogram(x_21) 70 | cls.funcname = "ar_periodogram" 71 | 72 | # I need to over-ride these b/c this function always has 73 | # w.size == x.size //2 74 | def test_func_w_shape_even_x(self): 75 | assert_(self.w_20.size == x_20.size // 2) 76 | 77 | def test_func_Iw_shape_even_x(self): 78 | assert_(self.Iw_20.size == x_20.size // 2) 79 | 80 | 81 | class TestSmooth: 82 | 83 | @classmethod 84 | def setup_method(cls): 85 | cls.x_20 = np.random.rand(20) 86 | cls.x_21 = np.random.rand(21) 87 | cls.window_length = 7 88 | 89 | def test_smooth(self): # does smoothing smooth? 90 | pass 91 | 92 | def test_smooth_raise_long_window(self): 93 | "estspec: raise error if smooth(*a, window_len) too large" 94 | assert_raises(ValueError, smooth, self.x_20, window_len=25) 95 | 96 | def test_smooth_short_window_err(self): 97 | "estspec: raise error in smooth(*a, window_len) if window_len too small" 98 | assert_raises(ValueError, smooth, self.x_20, window_len=2) 99 | 100 | def test_smooth_default_hanning(self): 101 | "estspec: smooth defaults to hanning on unrecognized window" 102 | with capture(smooth, x=self.x_20, window="foobar") as output: 103 | reg = re.compile("Defaulting") 104 | assert_(reg.search(output)) 105 | 106 | def test_smooth_window_len_must_be_odd(self): 107 | "estspec: smooth changes even window_len to odd" 108 | with capture(smooth, x=self.x_20, window_len=4) as output: 109 | reg = re.compile("reset") 110 | assert_(reg.search(output)) 111 | -------------------------------------------------------------------------------- /quantecon/tests/test_lqnash.py: -------------------------------------------------------------------------------- 1 | """ 2 | Tests for lqnash.py 3 | 4 | """ 5 | import numpy as np 6 | from numpy.testing import assert_allclose 7 | from quantecon import nnash 8 | from quantecon import LQ 9 | 10 | 11 | class TestLQNash: 12 | def test_noninteractive(self): 13 | "Test case for when agents don't interact with each other" 14 | # Copied these values from test_lqcontrol 15 | a = np.array([[.95, 0.], [0, .95]]) 16 | b1 = np.array([.95, 0.]) 17 | b2 = np.array([0., .95]) 18 | r1 = np.array([[-.25, 0.], [0., 0.]]) 19 | r2 = np.array([[0., 0.], [0., -.25]]) 20 | q1 = np.array([[-.15]]) 21 | q2 = np.array([[-.15]]) 22 | f1, f2, p1, p2 = nnash(a, b1, b2, r1, r2, q1, q2, 0, 0, 0, 0, 0, 0, 23 | tol=1e-8, max_iter=10000) 24 | 25 | alq = a[:1, :1] 26 | blq = b1[:1].reshape((1, 1)) 27 | rlq = r1[:1, :1] 28 | qlq = q1 29 | 30 | lq_obj = LQ(qlq, rlq, alq, blq, beta=1.) 31 | p, f, d = lq_obj.stationary_values() 32 | 33 | assert_allclose(f1, f2[:, ::-1]) 34 | assert_allclose(f1[0, 0], f[0]) 35 | assert_allclose(p1[0, 0], p2[1, 1]) 36 | assert_allclose(p1[0, 0], p[0, 0]) 37 | 38 | def test_nnash(self): 39 | "Use judd test case for nnash. Follows judd.m" 40 | # Define Parameters 41 | delta = 0.02 42 | d = np.array([[-1, 0.5], [0.5, -1]]) 43 | B = np.array([25, 25]) 44 | c1 = np.array([1, -2, 1]) 45 | c2 = np.array([1, -2, 1]) 46 | e1 = np.array([10, 10, 3]) 47 | e2 = np.array([10, 10, 3]) 48 | delta_1 = 1 - delta 49 | 50 | ## Define matrices 51 | a = np.array([[delta_1, 0, -delta_1*B[0]], 52 | [0, delta_1, -delta_1*B[1]], 53 | [0, 0, 1]]) 54 | 55 | b1 = delta_1 * np.array([[1, -d[0, 0]], 56 | [0, -d[1, 0]], 57 | [0, 0]]) 58 | b2 = delta_1 * np.array([[0, -d[0, 1]], 59 | [1, -d[1, 1]], 60 | [0, 0]]) 61 | 62 | r1 = -np.array([[0.5*c1[2], 0, 0.5*c1[1]], 63 | [0, 0, 0], 64 | [0.5*c1[1], 0, c1[0]]]) 65 | r2 = -np.array([[0, 0, 0], 66 | [0, 0.5*c2[2], 0.5*c2[1]], 67 | [0, 0.5*c2[1], c2[0]]]) 68 | 69 | q1 = np.array([[-0.5*e1[2], 0], [0, d[0, 0]]]) 70 | q2 = np.array([[-0.5*e2[2], 0], [0, d[1, 1]]]) 71 | 72 | s1 = np.zeros((2, 2)) 73 | s2 = np.copy(s1) 74 | 75 | w1 = np.array([[0, 0], 76 | [0, 0], 77 | [-0.5*e1[1], B[0]/2.]]) 78 | w2 = np.array([[0, 0], 79 | [0, 0], 80 | [-0.5*e2[1], B[1]/2.]]) 81 | 82 | m1 = np.array([[0, 0], [0, d[0, 1] / 2.]]) 83 | m2 = np.copy(m1) 84 | 85 | # build model and solve it 86 | f1, f2, p1, p2 = nnash(a, b1, b2, r1, r2, q1, q2, s1, s2, w1, w2, m1, 87 | m2) 88 | 89 | aaa = a - b1.dot(f1) - b2.dot(f2) 90 | aa = aaa[:2, :2] 91 | tf = np.eye(2)-aa 92 | tfi = np.linalg.inv(tf) 93 | xbar = tfi.dot(aaa[:2, 2]) 94 | 95 | # Define answers from matlab. TODO: this is ghetto 96 | f1_ml = np.array([ 97 | [0.243666582208565, 0.027236062661951, -6.827882928738190], 98 | [0.392370733875639, 0.139696450885998, -37.734107291009138] 99 | ]) 100 | 101 | f2_ml = np.array([ 102 | [0.027236062661951, 0.243666582208565, -6.827882928738186], 103 | [0.139696450885998, 0.392370733875639, -37.734107291009131] 104 | ]) 105 | 106 | xbar_ml = np.array([1.246871007582702, 1.246871007582685]) 107 | 108 | assert_allclose(f1, f1_ml) 109 | assert_allclose(f2, f2_ml) 110 | assert_allclose(xbar, xbar_ml) 111 | -------------------------------------------------------------------------------- /quantecon/markov/utilities.py: -------------------------------------------------------------------------------- 1 | """ 2 | Utility routines for the markov submodule 3 | 4 | """ 5 | import numpy as np 6 | from numba import jit 7 | 8 | 9 | @jit(nopython=True, cache=True) 10 | def sa_indices(num_states, num_actions): 11 | """ 12 | Generate `s_indices` and `a_indices` for `DiscreteDP`, for the case 13 | where all the actions are feasible at every state. 14 | 15 | Parameters 16 | ---------- 17 | num_states : scalar(int) 18 | Number of states. 19 | 20 | num_actions : scalar(int) 21 | Number of actions. 22 | 23 | Returns 24 | ------- 25 | s_indices : ndarray(int, ndim=1) 26 | Array containing the state indices. 27 | 28 | a_indices : ndarray(int, ndim=1) 29 | Array containing the action indices. 30 | 31 | Examples 32 | -------- 33 | >>> s_indices, a_indices = qe.markov.sa_indices(4, 3) 34 | >>> s_indices 35 | array([0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3]) 36 | >>> a_indices 37 | array([0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2]) 38 | 39 | """ 40 | L = num_states * num_actions 41 | dtype = np.int_ 42 | s_indices = np.empty(L, dtype=dtype) 43 | a_indices = np.empty(L, dtype=dtype) 44 | 45 | i = 0 46 | for s in range(num_states): 47 | for a in range(num_actions): 48 | s_indices[i] = s 49 | a_indices[i] = a 50 | i += 1 51 | 52 | return s_indices, a_indices 53 | 54 | 55 | @jit(nopython=True, cache=True) 56 | def _fill_dense_Q(s_indices, a_indices, Q_in, Q_out): 57 | L = Q_in.shape[0] 58 | for i in range(L): 59 | Q_out[s_indices[i], a_indices[i], :] = Q_in[i, :] 60 | 61 | return Q_out 62 | 63 | 64 | @jit(nopython=True, cache=True) 65 | def _s_wise_max_argmax(a_indices, a_indptr, vals, out_max, out_argmax): 66 | n = len(out_max) 67 | for i in range(n): 68 | if a_indptr[i] != a_indptr[i+1]: 69 | m = a_indptr[i] 70 | for j in range(a_indptr[i]+1, a_indptr[i+1]): 71 | if vals[j] > vals[m]: 72 | m = j 73 | out_max[i] = vals[m] 74 | out_argmax[i] = a_indices[m] 75 | 76 | 77 | @jit(nopython=True, cache=True) 78 | def _s_wise_max(a_indices, a_indptr, vals, out_max): 79 | n = len(out_max) 80 | for i in range(n): 81 | if a_indptr[i] != a_indptr[i+1]: 82 | m = a_indptr[i] 83 | for j in range(a_indptr[i]+1, a_indptr[i+1]): 84 | if vals[j] > vals[m]: 85 | m = j 86 | out_max[i] = vals[m] 87 | 88 | 89 | @jit(nopython=True, cache=True) 90 | def _find_indices(a_indices, a_indptr, sigma, out): 91 | n = len(sigma) 92 | for i in range(n): 93 | for j in range(a_indptr[i], a_indptr[i+1]): 94 | if sigma[i] == a_indices[j]: 95 | out[i] = j 96 | 97 | 98 | @jit(nopython=True, cache=True) 99 | def _has_sorted_sa_indices(s_indices, a_indices): 100 | """ 101 | Check whether `s_indices` and `a_indices` are sorted in 102 | lexicographic order. 103 | 104 | Parameters 105 | ---------- 106 | s_indices, a_indices : ndarray(ndim=1) 107 | 108 | Returns 109 | ------- 110 | bool 111 | Whether `s_indices` and `a_indices` are sorted. 112 | 113 | """ 114 | L = len(s_indices) 115 | for i in range(L-1): 116 | if s_indices[i] > s_indices[i+1]: 117 | return False 118 | if s_indices[i] == s_indices[i+1]: 119 | if a_indices[i] >= a_indices[i+1]: 120 | return False 121 | return True 122 | 123 | 124 | @jit(nopython=True, cache=True) 125 | def _generate_a_indptr(num_states, s_indices, out): 126 | """ 127 | Generate `a_indptr`; stored in `out`. `s_indices` is assumed to be 128 | in sorted order. 129 | 130 | Parameters 131 | ---------- 132 | num_states : scalar(int) 133 | 134 | s_indices : ndarray(int, ndim=1) 135 | 136 | out : ndarray(int, ndim=1) 137 | Length must be num_states+1. 138 | 139 | """ 140 | idx = 0 141 | out[0] = 0 142 | for s in range(num_states-1): 143 | while(s_indices[idx] == s): 144 | idx += 1 145 | out[s+1] = idx 146 | out[num_states] = len(s_indices) 147 | -------------------------------------------------------------------------------- /quantecon/tests/test_dle.py: -------------------------------------------------------------------------------- 1 | """ 2 | Tests for dle.py file 3 | """ 4 | 5 | import numpy as np 6 | from numpy.testing import assert_allclose 7 | from quantecon import DLE 8 | 9 | ATOL = 1e-10 10 | 11 | 12 | class TestDLE: 13 | 14 | def setup_method(self): 15 | """ 16 | Given LQ control is tested we will test the transformation 17 | to alter the problem into a form suitable to solve using LQ 18 | """ 19 | # Initial Values 20 | gam = 0 21 | gamma = np.array([[gam], [0]]) 22 | phic = np.array([[1], [0]]) 23 | phig = np.array([[0], [1]]) 24 | phi1 = 1e-4 25 | phii = np.array([[0], [-phi1]]) 26 | deltak = np.array([[.95]]) 27 | thetak = np.array([[1]]) 28 | beta = np.array([[1 / 1.05]]) 29 | ud = np.array([[5, 1, 0], [0, 0, 0]]) 30 | a22 = np.array([[1, 0, 0], [0, 0.8, 0], [0, 0, 0.5]]) 31 | c2 = np.array([[0, 1, 0], [0, 0, 1]]).T 32 | llambda = np.array([[0]]) 33 | pih = np.array([[1]]) 34 | deltah = np.array([[.9]]) 35 | thetah = np.array([[1]]) - deltah 36 | ub = np.array([[30, 0, 0]]) 37 | 38 | information = (a22, c2, ub, ud) 39 | technology = (phic, phig, phii, gamma, deltak, thetak) 40 | preferences = (beta, llambda, pih, deltah, thetah) 41 | 42 | self.dle = DLE(information, technology, preferences) 43 | 44 | def teardown_method(self): 45 | del self.dle 46 | 47 | def test_transformation_Q(self): 48 | Q_solution = np.array([[5.e-09]]) 49 | assert_allclose(Q_solution, self.dle.Q) 50 | 51 | def test_transformation_R(self): 52 | R_solution = np.array([[0., 0., 0., 0., 0.], 53 | [0., 0., 0., 0., 0.], 54 | [0., 0., 312.5, -12.5, 0.], 55 | [0., 0., -12.5, 0.5, 0.], 56 | [0., 0., 0., 0., 0.]]) 57 | assert_allclose(R_solution, self.dle.R) 58 | 59 | def test_transformation_A(self): 60 | A_solution = np.array([[0.9, 0., 0.5, 0.1, 0.], 61 | [0., 0.95, 0., 0., 0.], 62 | [0., 0., 1., 0., 0.], 63 | [0., 0., 0., 0.8, 0.], 64 | [0., 0., 0., 0., 0.5]]) 65 | assert_allclose(A_solution, self.dle.A) 66 | 67 | def test_transformation_B(self): 68 | B_solution = np.array([[-0.], 69 | [1.], 70 | [0.], 71 | [0.], 72 | [0.]]) 73 | assert_allclose(B_solution, self.dle.B) 74 | 75 | def test_transformation_C(self): 76 | C_solution = np.array([[0., 0.], 77 | [0., 0.], 78 | [0., 0.], 79 | [1., 0.], 80 | [0., 1.]]) 81 | assert_allclose(C_solution, self.dle.C) 82 | 83 | def test_transformation_W(self): 84 | W_solution = np.array([[0., 0., 0., 0., 0.]]) 85 | assert_allclose(W_solution, self.dle.W) 86 | 87 | def test_compute_steadystate(self): 88 | solutions = { 89 | 'css' : np.array([[5.]]), 90 | 'sss' : np.array([[5.]]), 91 | 'iss' : np.array([[0.]]), 92 | 'dss' : np.array([[5.], [0.]]), 93 | 'bss' : np.array([[30.]]), 94 | 'kss' : np.array([[0.]]), 95 | 'hss' : np.array([[5.]]), 96 | } 97 | self.dle.compute_steadystate() 98 | for item in solutions.keys(): 99 | assert_allclose(self.dle.__dict__[ 100 | item], solutions[item], atol=ATOL) 101 | 102 | def test_canonical(self): 103 | solutions = { 104 | 'pihat': np.array([[1.]]), 105 | 'llambdahat': np.array([[-1.48690584e-19]]), 106 | 'ubhat': np.array([[30., -0., -0.]]) 107 | } 108 | self.dle.canonical() 109 | for item in solutions.keys(): 110 | assert_allclose(self.dle.__dict__[ 111 | item], solutions[item], atol=ATOL) 112 | -------------------------------------------------------------------------------- /quantecon/game_theory/tests/test_fictplay.py: -------------------------------------------------------------------------------- 1 | """ 2 | Filename: test_fictplay.py 3 | 4 | Tests for fictplay.py 5 | 6 | """ 7 | import numpy as np 8 | from numpy.testing import assert_array_almost_equal 9 | from scipy.stats import norm 10 | 11 | from quantecon.game_theory import FictitiousPlay, StochasticFictitiousPlay 12 | 13 | 14 | class TestFictitiousPlayDecreaingGain: 15 | 16 | def setup_method(self): 17 | '''Setup a FictitiousPlay instance''' 18 | # symmetric 2x2 coordination game 19 | matching_pennies = [[(1, -1), (-1, 1)], 20 | [(-1, 1), (1, -1)]] 21 | self.fp = FictitiousPlay(matching_pennies) 22 | 23 | def test_play(self): 24 | x = (np.array([1, 0]), np.array([0.5, 0.5])) 25 | assert_array_almost_equal(self.fp.play(actions=(0, 0)), x) 26 | 27 | def test_time_series(self): 28 | x = self.fp.time_series(ts_length=3, init_actions=(0, 0)) 29 | assert_array_almost_equal(x[0], [[1, 0], 30 | [1, 0], 31 | [1, 0]]) 32 | assert_array_almost_equal(x[1], [[1, 0], 33 | [1/2, 1/2], 34 | [1/3, 2/3]]) 35 | 36 | 37 | class TestFictitiousPlayConstantGain: 38 | 39 | def setup_method(self): 40 | matching_pennies = [[(1, -1), (-1, 1)], 41 | [(-1, 1), (1, -1)]] 42 | self.fp = FictitiousPlay(matching_pennies, gain=0.1) 43 | 44 | def test_play(self): 45 | x = (np.array([1, 0]), np.array([0.9, 0.1])) 46 | assert_array_almost_equal(self.fp.play(actions=(0, 0)), x) 47 | 48 | def test_time_series(self): 49 | x = self.fp.time_series(ts_length=3, init_actions=(0, 0)) 50 | assert_array_almost_equal(x[0], [[1, 0], 51 | [1, 0], 52 | [1, 0]]) 53 | assert_array_almost_equal(x[1], [[1, 0], 54 | [0.9, 0.1], 55 | [0.81, 0.19]]) 56 | 57 | 58 | class TestStochasticFictitiosuPlayDecreaingGain: 59 | 60 | def setup_method(self): 61 | matching_pennies = [[(1, -1), (-1, 1)], 62 | [(-1, 1), (1, -1)]] 63 | distribution = norm() 64 | self.fp = StochasticFictitiousPlay(matching_pennies, 65 | distribution=distribution) 66 | 67 | def test_play(self): 68 | seed = 272733541340907175684079858751241831341 69 | x = [self.fp.play(actions=(0, 0), 70 | random_state=np.random.default_rng(seed)) 71 | for i in range(2)] 72 | assert_array_almost_equal(x[0], x[1]) 73 | 74 | def test_time_series(self): 75 | seed = 226177486389088886197048956835604946950 76 | x = [self.fp.time_series(ts_length=3, init_actions=(0, 0), 77 | random_state=np.random.default_rng(seed)) 78 | for i in range(2)] 79 | assert_array_almost_equal(x[0][0], x[1][0]) 80 | assert_array_almost_equal(x[0][1], x[1][1]) 81 | 82 | 83 | class TestStochasticFictitiosuPlayConstantGain: 84 | 85 | def setup_method(self): 86 | matching_pennies = [[(1, -1), (-1, 1)], 87 | [(-1, 1), (1, -1)]] 88 | distribution = norm() 89 | self.fp = StochasticFictitiousPlay(matching_pennies, gain=0.1, 90 | distribution=distribution) 91 | 92 | def test_play(self): 93 | seed = 271001177347704493622442691590340912076 94 | x = [self.fp.play(actions=(0, 0), 95 | random_state=np.random.default_rng(seed)) 96 | for i in range(2)] 97 | assert_array_almost_equal(x[0], x[1]) 98 | 99 | def test_time_series(self): 100 | seed = 143773081180220547556482766921740826832 101 | x = [self.fp.time_series(ts_length=3, init_actions=(0, 0), 102 | random_state=np.random.default_rng(seed)) 103 | for i in range(2)] 104 | assert_array_almost_equal(x[0][0], x[1][0]) 105 | assert_array_almost_equal(x[0][1], x[1][1]) 106 | -------------------------------------------------------------------------------- /quantecon/util/notebooks.py: -------------------------------------------------------------------------------- 1 | """ 2 | Support functions to Support QuantEcon.notebooks 3 | 4 | The purpose of these utilities is to implement simple support functions to allow for automatic downloading 5 | of any support files (python modules, or data) that may be required to run demonstration notebooks. 6 | 7 | Note 8 | ---- 9 | Files on the REMOTE Github Server can be organised into folders but they will end up at the root level of 10 | when downloaded as a support File 11 | 12 | "https://github.com/QuantEcon/QuantEcon.notebooks/raw/master/dependencies/mpi/something.py" --> ./something.py 13 | 14 | TODO 15 | ---- 16 | 1. Write Style guide for QuantEcon.notebook contributions 17 | 2. Write an interface for Dat Server 18 | 3. Platform Agnostic (replace wget usage) 19 | 20 | """ 21 | 22 | import os 23 | 24 | #-Remote Structure-# 25 | REPO = "https://github.com/QuantEcon/QuantEcon.notebooks" 26 | RAW = "raw" 27 | BRANCH = "master" 28 | #Hard Coded Dependencies Folder on QuantEcon.notebooks 29 | FOLDER = "dependencies" 30 | 31 | 32 | def fetch_nb_dependencies(files, repo=REPO, raw=RAW, branch=BRANCH, folder=FOLDER, overwrite=False, verbose=True): 33 | """ 34 | Retrieve raw files from QuantEcon.notebooks or other Github repo 35 | 36 | Parameters 37 | ---------- 38 | file_list list or dict 39 | A list of files to specify a collection of filenames 40 | A dict of dir : list(files) to specify a directory 41 | repo str, optional(default=REPO) 42 | raw str, optional(default=RAW) 43 | This is here in case github changes access to their raw files through web links 44 | branch str, optional(default=BRANCH) 45 | folder str, optional(default=FOLDER) 46 | overwrite bool, optional(default=False) 47 | verbose bool, optional(default=True) 48 | 49 | Examples 50 | -------- 51 | Consider a notebook that is dependant on a ``csv`` file to execute. If this file is 52 | located in a Github repository then it can be fetched using this utility 53 | 54 | Assuming the file is at the root level in the ``master`` branch then: 55 | 56 | >>> from quantecon.util import fetch_nb_dependencies 57 | >>> status = fetch_nb_dependencies(["test.csv"], repo="https://") 58 | 59 | More than one file may be requested in the list provided 60 | 61 | >>> status = fetch_nb_dependencies(["test.csv", "data.csv"], repo="https://") 62 | 63 | A folder location can be added using ``folder=`` 64 | 65 | >>> status = fetch_nb_dependencies("test.csv", report="https://", folder="data") 66 | 67 | You can also specify a specific branch using ``branch=`` keyword argument. 68 | 69 | This will download the requested file(s) to your local working directory. The default 70 | behaviour is **not** to overwrite a local file if it is present. This can be switched off 71 | by setting ``overwrite=True``. 72 | 73 | """ 74 | import requests 75 | 76 | #-Generate Common Data Structure-# 77 | if type(files) == list: 78 | files = {"" : files} 79 | 80 | status = [] 81 | 82 | #-Obtain each requested file-# 83 | for directory in files.keys(): 84 | if directory != "": 85 | if verbose: print("Parsing directory: %s"%directory) 86 | for fl in files[directory]: 87 | if directory != "": 88 | fl = directory+"/"+fl 89 | #-Check for Local Copy of File (Default Behaviour is to Skip)-# 90 | if not overwrite: 91 | if os.path.isfile(fl): 92 | if verbose: print( 93 | "A file named %s already exists in the specified directory ... skipping download." % fl) 94 | status.append(False) 95 | continue 96 | else: 97 | if verbose: print("Overwriting file %s ..."%fl) 98 | if verbose: print("Fetching file: %s"%fl) 99 | #-Get file in OS agnostic way using requests-# 100 | url = "/".join([repo, raw, branch, folder, fl]) 101 | r = requests.get(url) 102 | with open(fl, "wb") as fl: 103 | fl.write(r.content) 104 | status.append(True) 105 | 106 | return status 107 | -------------------------------------------------------------------------------- /quantecon/_inequality.py: -------------------------------------------------------------------------------- 1 | """ 2 | Implements inequality and segregation measures such as Gini, Lorenz Curve 3 | 4 | """ 5 | 6 | import numpy as np 7 | from numba import njit, prange 8 | 9 | 10 | @njit 11 | def lorenz_curve(y): 12 | """ 13 | Calculates the Lorenz Curve, a graphical representation of 14 | the distribution of income or wealth. 15 | 16 | It returns the cumulative share of people (x-axis) and 17 | the cumulative share of income earned. 18 | 19 | Parameters 20 | ---------- 21 | y : array_like(float or int, ndim=1) 22 | Array of income/wealth for each individual. 23 | Unordered or ordered is fine. 24 | 25 | Returns 26 | ------- 27 | cum_people : array_like(float, ndim=1) 28 | Cumulative share of people for each person index (i/n) 29 | cum_income : array_like(float, ndim=1) 30 | Cumulative share of income for each person index 31 | 32 | 33 | References 34 | ---------- 35 | .. [1] https://en.wikipedia.org/wiki/Lorenz_curve 36 | 37 | Examples 38 | -------- 39 | >>> a_val, n = 3, 10_000 40 | >>> y = np.random.pareto(a_val, size=n) 41 | >>> f_vals, l_vals = lorenz(y) 42 | 43 | """ 44 | 45 | n = len(y) 46 | y = np.sort(y) 47 | s = np.zeros(n + 1) 48 | s[1:] = np.cumsum(y) 49 | cum_people = np.zeros(n + 1) 50 | cum_income = np.zeros(n + 1) 51 | for i in range(1, n + 1): 52 | cum_people[i] = i / n 53 | cum_income[i] = s[i] / s[n] 54 | return cum_people, cum_income 55 | 56 | 57 | @njit(parallel=True) 58 | def gini_coefficient(y): 59 | r""" 60 | Implements the Gini inequality index 61 | 62 | Parameters 63 | ---------- 64 | y : array_like(float) 65 | Array of income/wealth for each individual. 66 | Ordered or unordered is fine 67 | 68 | Returns 69 | ------- 70 | Gini index: float 71 | The gini index describing the inequality of the array of income/wealth 72 | 73 | References 74 | ---------- 75 | 76 | https://en.wikipedia.org/wiki/Gini_coefficient 77 | """ 78 | n = len(y) 79 | i_sum = np.zeros(n) 80 | for i in prange(n): 81 | for j in range(n): 82 | i_sum[i] += abs(y[i] - y[j]) 83 | return np.sum(i_sum) / (2 * n * np.sum(y)) 84 | 85 | 86 | def shorrocks_index(A): 87 | r""" 88 | Implements Shorrocks mobility index 89 | 90 | Parameters 91 | ---------- 92 | A : array_like(float) 93 | Square matrix with transition probabilities (mobility matrix) of 94 | dimension m 95 | 96 | Returns 97 | ------- 98 | Shorrocks index: float 99 | The Shorrocks mobility index calculated as 100 | 101 | .. math:: 102 | 103 | s(A) = \frac{m - \sum_j a_{jj} }{m - 1} \in (0, 1) 104 | 105 | An index equal to 0 indicates complete immobility. 106 | 107 | References 108 | ---------- 109 | .. [1] Wealth distribution and social mobility in the US: 110 | A quantitative approach (Benhabib, Bisin, Luo, 2017). 111 | https://www.econ.nyu.edu/user/bisina/RevisionAugust.pdf 112 | """ 113 | 114 | A = np.asarray(A) # Convert to array if not already 115 | m, n = A.shape 116 | 117 | if m != n: 118 | raise ValueError('A must be a square matrix') 119 | 120 | diag_sum = np.diag(A).sum() 121 | 122 | return (m - diag_sum) / (m - 1) 123 | 124 | 125 | def rank_size(data, c=1.0): 126 | """ 127 | Generate rank-size data corresponding to distribution data. 128 | 129 | Examples 130 | -------- 131 | >>> y = np.exp(np.random.randn(1000)) # simulate data 132 | >>> rank_data, size_data = rank_size(y, c=0.85) 133 | 134 | Parameters 135 | ---------- 136 | data : array_like 137 | the set of observations 138 | c : int or float 139 | restrict plot to top (c x 100)% of the distribution 140 | 141 | Returns 142 | ------- 143 | rank_data : array_like(float, ndim=1) 144 | Location in the population when sorted from smallest to largest 145 | size_data : array_like(float, ndim=1) 146 | Size data for top (c x 100)% of the observations 147 | """ 148 | w = - np.sort(- data) # Reverse sort 149 | w = w[:int(len(w) * c)] # extract top (c * 100)% 150 | rank_data = np.arange(len(w)) + 1 151 | size_data = w 152 | return rank_data, size_data 153 | 154 | -------------------------------------------------------------------------------- /docs/sphinxext/ipython_console_highlighting.py: -------------------------------------------------------------------------------- 1 | """reST directive for syntax-highlighting ipython interactive sessions. 2 | 3 | XXX - See what improvements can be made based on the new (as of Sept 2009) 4 | 'pycon' lexer for the python console. At the very least it will give better 5 | highlighted tracebacks. 6 | """ 7 | 8 | #----------------------------------------------------------------------------- 9 | # Needed modules 10 | 11 | # Standard library 12 | import re 13 | 14 | # Third party 15 | from pygments.lexer import Lexer, do_insertions 16 | from pygments.lexers.agile import (PythonConsoleLexer, PythonLexer, 17 | PythonTracebackLexer) 18 | from pygments.token import Comment, Generic 19 | 20 | from sphinx import highlighting 21 | 22 | #----------------------------------------------------------------------------- 23 | # Global constants 24 | line_re = re.compile('.*?\n') 25 | 26 | #----------------------------------------------------------------------------- 27 | # Code begins - classes and functions 28 | 29 | class IPythonConsoleLexer(Lexer): 30 | """ 31 | For IPython console output or doctests, such as: 32 | 33 | .. sourcecode:: ipython 34 | 35 | In [1]: a = 'foo' 36 | 37 | In [2]: a 38 | Out[2]: 'foo' 39 | 40 | In [3]: print a 41 | foo 42 | 43 | In [4]: 1 / 0 44 | 45 | Notes: 46 | 47 | - Tracebacks are not currently supported. 48 | 49 | - It assumes the default IPython prompts, not customized ones. 50 | """ 51 | 52 | name = 'IPython console session' 53 | aliases = ['ipython'] 54 | mimetypes = ['text/x-ipython-console'] 55 | input_prompt = re.compile("(In \[[0-9]+\]: )|( \.\.\.+:)") 56 | output_prompt = re.compile("(Out\[[0-9]+\]: )|( \.\.\.+:)") 57 | continue_prompt = re.compile(" \.\.\.+:") 58 | tb_start = re.compile("\-+") 59 | 60 | def get_tokens_unprocessed(self, text): 61 | pylexer = PythonLexer(**self.options) 62 | tblexer = PythonTracebackLexer(**self.options) 63 | 64 | curcode = '' 65 | insertions = [] 66 | for match in line_re.finditer(text): 67 | line = match.group() 68 | input_prompt = self.input_prompt.match(line) 69 | continue_prompt = self.continue_prompt.match(line.rstrip()) 70 | output_prompt = self.output_prompt.match(line) 71 | if line.startswith("#"): 72 | insertions.append((len(curcode), 73 | [(0, Comment, line)])) 74 | elif input_prompt is not None: 75 | insertions.append((len(curcode), 76 | [(0, Generic.Prompt, input_prompt.group())])) 77 | curcode += line[input_prompt.end():] 78 | elif continue_prompt is not None: 79 | insertions.append((len(curcode), 80 | [(0, Generic.Prompt, continue_prompt.group())])) 81 | curcode += line[continue_prompt.end():] 82 | elif output_prompt is not None: 83 | # Use the 'error' token for output. We should probably make 84 | # our own token, but error is typically in a bright color like 85 | # red, so it works fine for our output prompts. 86 | insertions.append((len(curcode), 87 | [(0, Generic.Error, output_prompt.group())])) 88 | curcode += line[output_prompt.end():] 89 | else: 90 | if curcode: 91 | for item in do_insertions(insertions, 92 | pylexer.get_tokens_unprocessed(curcode)): 93 | yield item 94 | curcode = '' 95 | insertions = [] 96 | yield match.start(), Generic.Output, line 97 | if curcode: 98 | for item in do_insertions(insertions, 99 | pylexer.get_tokens_unprocessed(curcode)): 100 | yield item 101 | 102 | 103 | def setup(app): 104 | """Setup as a sphinx extension.""" 105 | 106 | # This is only a lexer, so adding it below to pygments appears sufficient. 107 | # But if somebody knows that the right API usage should be to do that via 108 | # sphinx, by all means fix it here. At least having this setup.py 109 | # suppresses the sphinx warning we'd get without it. 110 | pass 111 | 112 | #----------------------------------------------------------------------------- 113 | # Register the extension as a valid pygments lexer 114 | highlighting.lexers['ipython'] = IPythonConsoleLexer() 115 | --------------------------------------------------------------------------------