diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 9572470e1..028c4841e 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -9,10 +9,10 @@ Always reference these instructions first and fallback to search or bash command ### Environment Setup (REQUIRED) - **ALWAYS use conda environment**: `conda env create -f environment.yml` - Takes 3-5 minutes to complete. NEVER CANCEL. Set timeout to 10+ minutes. - - Creates environment named 'qe' with Python 3.13 and all dependencies + - Creates environment named 'qe' with all project dependencies. Note: environment.yml does not pin a Python version, so conda resolves one automatically. To match CI (3.12/3.13/3.14), either add `- python=3.13` to environment.yml, or run `conda create -n qe python=3.13` followed by `conda env update -n qe -f environment.yml`. - **Activate environment**: `eval "$(conda shell.bash hook)" && conda activate qe` -- **Development install**: `flit install` - - Installs package in development mode for testing changes +- **Development install**: `flit install --symlink` + - Installs the package in editable mode so source changes are picked up immediately - Takes < 30 seconds ### Build and Test Workflow @@ -21,7 +21,7 @@ Always reference these instructions first and fallback to search or bash command - Note: Repository has some existing style violations - this is expected - **Full test suite**: `coverage run -m pytest quantecon` - Takes 5 minutes 11 seconds. NEVER CANCEL. Set timeout to 15+ minutes. - - Runs 536 tests across all modules + - Runs the full test suite across all modules (~600 tests) - All tests should pass with 2 warnings (expected) - **Quick smoke test**: `python -c "import quantecon as qe; print('Version:', qe.__version__)"` - **Package build**: `flit build` @@ -54,12 +54,12 @@ print('DiscreteDP test successful, policy:', result.sigma) - `game_theory/` - Game theory algorithms and utilities - `optimize/` - Optimization algorithms - `random/` - Random number generation utilities - - `tests/` - Main test suite (536 tests total) + - `tests/` - Main test suite ### Configuration Files - `pyproject.toml` - Main project configuration using flit build system - `environment.yml` - Conda environment specification with all dependencies -- `.github/workflows/ci.yml` - CI pipeline (tests on Python 3.11, 3.12, 3.13) +- `.github/workflows/ci.yml` - CI pipeline (tests on Python 3.12, 3.13, 3.14) - `pytest.ini` - Test configuration including slow test markers ### Dependencies @@ -86,10 +86,9 @@ Core runtime dependencies (auto-installed in conda env): ### Making Code Changes 1. Ensure conda environment is active: `conda activate qe` 2. Make your changes to files in `quantecon/` -3. Run development install: `flit install` -4. Test imports: `python -c "import quantecon as qe; print('Import OK')"` -5. Run relevant tests: `pytest quantecon/tests/test_[relevant_module].py` -6. Run linting: `flake8 --select F401,F405,E231 quantecon` +3. Test imports: `python -c "import quantecon as qe; print('Import OK')"` +4. Run relevant tests: `pytest quantecon/tests/test_[relevant_module].py` +5. Run linting: `flake8 --select F401,F405,E231 quantecon` ### Adding New Features 1. Add code to appropriate module in `quantecon/` @@ -108,7 +107,7 @@ Core runtime dependencies (auto-installed in conda env): ### CI/CD Pipeline - GitHub Actions runs tests on Windows, Ubuntu, and macOS -- Tests Python 3.11, 3.12, and 3.13 +- Tests Python 3.12, 3.13, and 3.14 - Includes flake8 linting and coverage reporting - Publishing to PyPI is automated on git tags @@ -140,7 +139,7 @@ conda env create -f environment.yml eval "$(conda shell.bash hook)" && conda activate qe # Development workflow -flit install # Install in development mode +flit install --symlink # Editable install for development python -c "import quantecon as qe; print(qe.__version__)" # Test import pytest quantecon/tests/test_[module].py # Test specific module flake8 --select F401,F405,E231 quantecon # Lint code diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 79c6482a3..9f62adcf6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -103,6 +103,48 @@ jobs: file: coverage.lcov format: lcov + docs: + + name: Build docs + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + - name: Set up Python + uses: actions/setup-python@v6 + with: + # mirrors the Read the Docs build (see .readthedocs.yaml) + python-version: "3.14" + - name: Install dependencies + run: | + pip install -r docs/rtd-requirements.txt + pip install . + - name: Check generated sources are up to date + # Read the Docs builds the *committed* .rst files and never runs + # qe_apidoc.py, so regenerating here would pass green while RTD + # served stale pages. Fail if the committed sources have drifted. + working-directory: docs + run: python qe_apidoc.py + - name: Fail on docs/source drift + # Check for modified tracked files AND untracked new files: a + # newly added module makes qe_apidoc.py emit a new .rst, which + # `git diff` alone would miss. + run: | + git diff --exit-code -- docs/source \ + || { echo "::error::docs/source is stale -- run 'cd docs && python qe_apidoc.py' and commit the result"; exit 1; } + untracked=$(git ls-files --others --exclude-standard docs/source) + if [ -n "$untracked" ]; then + echo "::error::qe_apidoc.py generated new uncommitted pages: $untracked -- run 'cd docs && python qe_apidoc.py' and commit the result" + exit 1 + fi + - name: Build HTML + working-directory: docs + run: sphinx-build -b html source build/html + - name: Upload built docs + uses: actions/upload-artifact@v4 + with: + name: docs-html + path: docs/build/html + publish: name: Publish to PyPi diff --git a/CHANGELOG.md b/CHANGELOG.md index b2b05b869..ef5baf7e3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,11 +14,11 @@ See [release notes](https://github.com/QuantEcon/QuantEcon.py/releases/tag/v0.11 See [release notes](https://github.com/QuantEcon/QuantEcon.py/releases/tag/v0.11.2) -## Ver 0.11.1 (4th-March-2026) +## Ver 0.11.1 (6th-March-2026) See [release notes](https://github.com/QuantEcon/QuantEcon.py/releases/tag/v0.11.1) -## Ver 0.11.0 +## Ver 0.11.0 (23rd-February-2026) See [release notes](https://github.com/QuantEcon/QuantEcon.py/releases/tag/v0.11.0) @@ -358,5 +358,5 @@ Contributors: [oyamad](https://github.com/oyamad), [QBatista](https://github.com ### Ver. 0.3 -1. Removes ``quantecon/models`` subpackage and the collection of code examples. Code has been migrated to the [QuantEcon.applications](https://github.com/QuantEcon/QuantEcon.applications) repository. -2. Adds a utility for fetching notebook dependencies from [QuantEcon.applications](https://github.com/QuantEcon/QuantEcon.applications) to support community contributed notebooks. +1. Removes ``quantecon/models`` subpackage and the collection of code examples. Code has been migrated to the QuantEcon.applications (now removed) repository. +2. Adds a utility for fetching notebook dependencies from QuantEcon.applications (now removed) to support community contributed notebooks. diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index 2c24c4714..000000000 --- a/MANIFEST.in +++ /dev/null @@ -1,3 +0,0 @@ -include README.md -include LICENSE.txt -recursive-include quantecon/tests/data * \ No newline at end of file diff --git a/README.md b/README.md index 0984ccf4e..94e32dfaf 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ A high performance, open source Python code library for economics results = aiyagari_ddp.solve(method='policy_iteration') ``` -[![Build Status](https://github.com/QuantEcon/QuantEcon.py/actions/workflows/ci.yml/badge.svg)](https://github.com/QuantEcon/QuantEcon.py/actions?query=workflow%3Abuild) +[![Build Status](https://github.com/QuantEcon/QuantEcon.py/actions/workflows/ci.yml/badge.svg)](https://github.com/QuantEcon/QuantEcon.py/actions/workflows/ci.yml) [![Coverage Status](https://coveralls.io/repos/QuantEcon/QuantEcon.py/badge.svg)](https://coveralls.io/r/QuantEcon/QuantEcon.py) [![Documentation (stable)](https://img.shields.io/badge/docs-stable-blue.svg)](https://quanteconpy.readthedocs.io/en/stable/) [![Documentation (latest)](https://img.shields.io/badge/docs-latest-blue.svg)](https://quanteconpy.readthedocs.io/en/latest/) @@ -42,15 +42,15 @@ You can check the version by running print(qe.__version__) ``` -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 +If your version is below what’s available on [PyPI](https://pypi.org/project/quantecon/) then it is time to upgrade. This can be done by running pip install --upgrade quantecon ## Examples and Sample Code -Many examples of QuantEcon.py in action can be found at [Quantitative Economics](https://lectures.quantecon.org/). See also the +Many examples of QuantEcon.py in action can be found at [Quantitative Economics](https://quantecon.org/lectures/). See also the -* [Documentation](https://quanteconpy.readthedocs.org/en/latest/) +* [Documentation](https://quanteconpy.readthedocs.io/en/latest/) * [Notebook gallery](https://github.com/QuantEcon/notebook-gallery) 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). @@ -63,10 +63,10 @@ An alternative is to download the sourcecode of the `quantecon` package and in Once you have downloaded the source files then the package can be installed by running - pip install flit - flit install + cd QuantEcon.py + pip install . -(To learn the basics about setting up Git see [this link](https://help.github.com/articles/set-up-git/).) +(To learn the basics about setting up Git see [this link](https://docs.github.com/en/get-started/git-basics/set-up-git).) ## Citation @@ -77,13 +77,14 @@ A BibTeX entry for LaTeX users is ```bibtex @article{10.21105/joss.05585, author = {Batista, Quentin and Coleman, Chase and Furusawa, Yuya and Hu, Shu and Lunagariya, Smit and Lyon, Spencer and McKay, Matthew and Oyama, Daisuke and Sargent, Thomas J. and Shi, Zejin and Stachurski, John and Winant, Pablo and Watkins, Natasha and Yang, Ziyue and Zhang, Hengcheng}, -doi = {10.5281/zenodo.10345102}, +doi = {10.21105/joss.05585}, title = {QuantEcon.py: A community based Python library for quantitative economics}, year = {2024}, journal = {Journal of Open Source Software}, volume = {9}, number = {93}, -pages = {5585} +pages = {5585}, +url = {https://joss.theoj.org/papers/10.21105/joss.05585} } ``` diff --git a/docs/Makefile b/docs/Makefile index 59505d956..dab890506 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -60,8 +60,12 @@ srcclean: rm -f source/game_theory.rst rm -rf source/markov rm -f source/markov.rst + rm -rf source/optimize + rm -f source/optimize.rst rm -rf source/random rm -f source/random.rst + rm -rf source/timings + rm -f source/timings.rst rm -rf source/util rm -f source/util.rst diff --git a/docs/README.md b/docs/README.md index 5beefa477..a89427471 100644 --- a/docs/README.md +++ b/docs/README.md @@ -4,30 +4,23 @@ This is the main directory for the documentation for the `quantecon` python libr ## Dependencies -The documentation requires a few dependencies beyond those necessary for the quantecon library. These dependencies are (warning, this may be an incomplete list): - -* sphinx -* numpydoc -* sphinx_rtd_theme -* mock - -You can install these by executing +The documentation requires a few dependencies beyond those necessary for the quantecon library. The full documentation dependency set is listed in `rtd-requirements.txt` (this is what Read the Docs installs, see `.readthedocs.yaml` at the repository root). Install it with: ``` -conda install sphinx numpydoc sphinx_rtd_theme mock +pip install -r rtd-requirements.txt ``` ## Building the docs In order to generate the documentation, follow these steps: -1. Install the `quantecon` python library locally. Do to this enter the commands below: +1. Install the `quantecon` python library locally. To do this enter the commands below: ``` cd .. pip install . cd docs ``` -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) +2. (Optional -- `make html` in the next step runs this automatically.) 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) ``` python qe_apidoc.py ``` @@ -37,22 +30,6 @@ make html ``` 4. Open the file `build/html/index.html`. -I have added a couple utility commands to the make file: - -``` -srcclean: - rm -rf source/modules* - rm -rf source/models* - rm -rf source/tools* - rm -f source/index.rst - rm -f source/models.rst - rm -f source/tools.rst - -myhtml: - make srcclean - cd .. && pip install . && cd docs - python qe_apidoc.py - make html -``` +I have added a couple utility commands to the make file. `make srcclean` deletes the `source/` subdirectories and `.rst` files generated by `qe_apidoc.py`; `make myhtml` chains srcclean + reinstall + apidoc + html, automating steps 1-3 above. See the `srcclean` and `myhtml` targets in `docs/Makefile` for the exact list of paths. Notice that we can automate steps 1-3 (and make sure we get a clean build) above by simply running `make myhtml` diff --git a/docs/qe_apidoc.py b/docs/qe_apidoc.py index 4920f06c5..16d1e876c 100644 --- a/docs/qe_apidoc.py +++ b/docs/qe_apidoc.py @@ -7,20 +7,23 @@ This file should be called from the command line. It accepts one additional command line parameter. If we pass the parameter `single` when running the file, this file will create a single directory named -modules where each module in quantecon will be documented. The index.rst -file will then contain a single list of all modules. - -If no argument is passed or if the argument is anything other than -`single`, two directories will be created: models and tools. The models -directory will contain documentation instructions for the different -models in quantecon, whereas the tools directory will contain docs for -the tools in the package. The generated index.rst will then contain -two toctrees, one for models and one for tools. +modules where only the base-level modules of quantecon are documented. +The index.rst file will then contain a single list of those modules; +the subpackages (game_theory, markov, optimize, random, timings, util) +are omitted, and setup/contributing are left out of the toctree. This +mode is not used by `make html`. + +If no argument is passed, or if the argument is anything other than +`single`, one directory is created per subpackage (game_theory, +markov, optimize, random, timings, util) plus a `tools` directory for +the base-level modules, and a top-level .rst per section. The +generated index.rst then contains a single toctree listing setup, each +section, and contributing. Examples -------- -$ python qe_apidoc.py # generates the two separate directories -$ python qe_apidoc.py foo_bar # generates the two separate directories +$ python qe_apidoc.py # generates the per-section directories +$ python qe_apidoc.py foo_bar # generates the per-section directories $ python qe_apidoc.py single # generates the single directory @@ -30,9 +33,6 @@ To do this, use one of the commands above and replace `python` with `%%run` -2. Models has been removed. But leaving infrastructure here for qe_apidoc -in the event we need it in the future - """ import ast @@ -128,6 +128,15 @@ :show-inheritance: """ +timings_module_template = """{mod_name} +{equals} + +.. automodule:: quantecon.timings.{mod_name} + :members: + :undoc-members: + :show-inheritance: +""" + all_index_template = """======================= QuantEcon documentation ======================= @@ -155,8 +164,10 @@ The `quantecon` python library consists of a number of modules which includes game theory (game_theory), markov chains (markov), optimization algorithms (optimize), random generation utilities -(random), a collection of tools (tools), and other utilities (util) -which are mainly used by developers internal to the package. +(random), global timing-precision configuration (timings), a collection +of tools (tools), and other utilities (util), which include user-facing +timing tools (``tic``, ``tac``, ``toc``, ``Timer``, ``timeit``) +alongside helpers used internally by the package. .. toctree:: :maxdepth: 2 @@ -166,6 +177,7 @@ markov optimize random + timings tools util contributing @@ -341,7 +353,16 @@ def model_tool(): # Alphabetize util.sort() - for folder in ["game_theory", "markov", "optimize", "random", "tools", "util"]: + # list file names with timings + timings_files = glob("../quantecon/timings/[a-z0-9]*.py") + timings = list(map(lambda x: x.split('/')[-1][:-3], timings_files)) + # Alphabetize + timings.sort() + + for folder in ["game_theory", + os.path.join("game_theory", "game_generators"), + "markov", "optimize", "random", "timings", "tools", + "util"]: if not os.path.exists(source_join(folder)): os.makedirs(source_join(folder)) @@ -395,7 +416,14 @@ def model_tool(): equals = "=" * len(mod) f.write(util_module_template.format(mod_name=mod, equals=equals)) - # write (index|models|tools).rst file to include autogenerated files + # Write file for each timings module + for mod in timings: + new_path = os.path.join("source", "timings", mod + ".rst") + with open(new_path, "w") as f: + equals = "=" * len(mod) + f.write(timings_module_template.format(mod_name=mod, equals=equals)) + + # write index.rst plus one .rst per section to include autogenerated files with open(source_join("index.rst"), "w") as index: index.write(split_index_template) @@ -403,6 +431,7 @@ def model_tool(): mark = "markov/" + "\n markov/".join(markov) opti = "optimize/" + "\n optimize/".join(optimize) rand = "random/" + "\n random/".join(random) + tmgs = "timings/" + "\n timings/".join(timings) tlz = "tools/" + "\n tools/".join(tools) utls = "util/" + "\n util/".join(util) #-TocTree-# @@ -411,21 +440,19 @@ def model_tool(): "optimize" : opti, "tools": tlz, "random": rand, + "timings": tmgs, "util": utls, } - for f_name in ("game_theory", "markov", "optimize", "random", "tools", "util"): + for f_name in ("game_theory", "markov", "optimize", "random", "timings", + "tools", "util"): with open(source_join(f_name + ".rst"), "w") as f: - m_name = f_name - if f_name == "game_theory": - f_name = "Game Theory" #Produce Nicer Title for Game Theory Module - if f_name == "util": - f_name = "Utilities" #Produce Nicer Title for Utilities Module - if f_name == "optimize": - f_name = "Optimize" - temp = split_file_template.format(name=f_name.capitalize(), - equals="="*len(f_name), - files=toc_tree_list[m_name]) + #Produce Nicer Titles for the multi-word/abbreviated sections + title = {"game_theory": "Game Theory", + "util": "Utilities"}.get(f_name, f_name.capitalize()) + temp = split_file_template.format(name=title, + equals="="*len(title), + files=toc_tree_list[f_name]) f.write(temp) if __name__ == '__main__': diff --git a/docs/rtd-requirements.txt b/docs/rtd-requirements.txt index ac08ff209..ad604658e 100644 --- a/docs/rtd-requirements.txt +++ b/docs/rtd-requirements.txt @@ -1,4 +1,4 @@ -sphinx<=6.2.1 +sphinx ipython numpydoc numba>=0.49 diff --git a/docs/source/conf.py b/docs/source/conf.py index 1f445261e..86a8cdaa0 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -20,18 +20,8 @@ # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. #sys.path.insert(0, os.path.abspath('.')) -sys.path.insert(0, os.path.abspath('../sphinxext')) - -sys.path.insert(0, os.path.abspath('../..') + '/quantecon') - -sys.path.extend([ - - # numpy standard doc extensions - os.path.join(os.path.dirname(__file__), - '..', '../..', - 'sphinxext') - -]) +sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), + '..', '..'))) ## numpydoc settings @@ -161,7 +151,7 @@ # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = [''] +html_static_path = [] # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied @@ -293,4 +283,8 @@ # Example configuration for intersphinx: refer to the Python standard library. -intersphinx_mapping = {'python': ('https://docs.python.org/3', None)} +intersphinx_mapping = { + 'python': ('https://docs.python.org/3', None), + 'numpy': ('https://numpy.org/doc/stable/', None), + 'scipy': ('https://docs.scipy.org/doc/scipy/', None), +} diff --git a/docs/source/contributing.rst b/docs/source/contributing.rst index d3d521ca6..f86348ff5 100644 --- a/docs/source/contributing.rst +++ b/docs/source/contributing.rst @@ -83,5 +83,6 @@ the pull request checks. Further questions ----------------- -We encourage you to reach out to the `QuantEcon team `_ on the -`Discourse forum `_ if you have any further questions. +We encourage you to reach out to the `QuantEcon team `_ or open an issue on +the `project issue tracker `_ if you have any further +questions. diff --git a/docs/source/game_theory.rst b/docs/source/game_theory.rst index 2248f18a0..02d25f5a2 100644 --- a/docs/source/game_theory.rst +++ b/docs/source/game_theory.rst @@ -1,4 +1,4 @@ -Game theory +Game Theory =========== .. toctree:: diff --git a/docs/source/index.rst b/docs/source/index.rst index 84ed80fda..fc6e3bca1 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -5,8 +5,10 @@ QuantEcon documentation The `quantecon` python library consists of a number of modules which includes game theory (game_theory), markov chains (markov), optimization algorithms (optimize), random generation utilities -(random), a collection of tools (tools), and other utilities (util) -which are mainly used by developers internal to the package. +(random), global timing-precision configuration (timings), a collection +of tools (tools), and other utilities (util), which include user-facing +timing tools (``tic``, ``tac``, ``toc``, ``Timer``, ``timeit``) +alongside helpers used internally by the package. .. toctree:: :maxdepth: 2 @@ -16,6 +18,7 @@ which are mainly used by developers internal to the package. markov optimize random + timings tools util contributing diff --git a/docs/source/setup.rst b/docs/source/setup.rst index 316f2b066..e830e05ee 100644 --- a/docs/source/setup.rst +++ b/docs/source/setup.rst @@ -38,7 +38,7 @@ You can check the version by running print(qe.__version__) -If your version is below what's available on `PyPI `_ then it is time to upgrade. +If your version is below what's available on `PyPI `_ then it is time to upgrade. This can be done by running @@ -65,7 +65,7 @@ Once you have downloaded the source files then the package can be installed by r cd QuantEcon.py pip install . -(To learn the basics about setting up Git see `this link `_). +(To learn the basics about setting up Git see `this link `_). Examples and Sample Code ------------------------ diff --git a/docs/source/timings.rst b/docs/source/timings.rst new file mode 100644 index 000000000..1f6cf18d3 --- /dev/null +++ b/docs/source/timings.rst @@ -0,0 +1,7 @@ +Timings +======= + +.. toctree:: + :maxdepth: 2 + + timings/timings diff --git a/docs/source/timings/timings.rst b/docs/source/timings/timings.rst new file mode 100644 index 000000000..df8800759 --- /dev/null +++ b/docs/source/timings/timings.rst @@ -0,0 +1,7 @@ +timings +======= + +.. automodule:: quantecon.timings.timings + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/util.rst b/docs/source/util.rst index 72742a99e..6a2168ffb 100644 --- a/docs/source/util.rst +++ b/docs/source/util.rst @@ -6,7 +6,6 @@ Utilities util/array util/combinatorics - util/common_messages util/compat util/notebooks util/numba diff --git a/docs/source/util/common_messages.rst b/docs/source/util/common_messages.rst deleted file mode 100644 index 4a64126f5..000000000 --- a/docs/source/util/common_messages.rst +++ /dev/null @@ -1,7 +0,0 @@ -common_messages -=============== - -.. automodule:: quantecon.util.common_messages - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/sphinxext/ipython_console_highlighting.py b/docs/sphinxext/ipython_console_highlighting.py deleted file mode 100644 index 503ee85db..000000000 --- a/docs/sphinxext/ipython_console_highlighting.py +++ /dev/null @@ -1,114 +0,0 @@ -"""reST directive for syntax-highlighting ipython interactive sessions. - -XXX - See what improvements can be made based on the new (as of Sept 2009) -'pycon' lexer for the python console. At the very least it will give better -highlighted tracebacks. -""" - -#----------------------------------------------------------------------------- -# Needed modules - -# Standard library -import re - -# Third party -from pygments.lexer import Lexer, do_insertions -from pygments.lexers.agile import (PythonConsoleLexer, PythonLexer, - PythonTracebackLexer) -from pygments.token import Comment, Generic - -from sphinx import highlighting - -#----------------------------------------------------------------------------- -# Global constants -line_re = re.compile('.*?\n') - -#----------------------------------------------------------------------------- -# Code begins - classes and functions - -class IPythonConsoleLexer(Lexer): - """ - For IPython console output or doctests, such as: - - .. sourcecode:: ipython - - In [1]: a = 'foo' - - In [2]: a - Out[2]: 'foo' - - In [3]: print a - foo - - In [4]: 1 / 0 - - Notes: - - - Tracebacks are not currently supported. - - - It assumes the default IPython prompts, not customized ones. - """ - - name = 'IPython console session' - aliases = ['ipython'] - mimetypes = ['text/x-ipython-console'] - input_prompt = re.compile("(In \[[0-9]+\]: )|( \.\.\.+:)") - output_prompt = re.compile("(Out\[[0-9]+\]: )|( \.\.\.+:)") - continue_prompt = re.compile(" \.\.\.+:") - tb_start = re.compile("\-+") - - def get_tokens_unprocessed(self, text): - pylexer = PythonLexer(**self.options) - tblexer = PythonTracebackLexer(**self.options) - - curcode = '' - insertions = [] - for match in line_re.finditer(text): - line = match.group() - input_prompt = self.input_prompt.match(line) - continue_prompt = self.continue_prompt.match(line.rstrip()) - output_prompt = self.output_prompt.match(line) - if line.startswith("#"): - insertions.append((len(curcode), - [(0, Comment, line)])) - elif input_prompt is not None: - insertions.append((len(curcode), - [(0, Generic.Prompt, input_prompt.group())])) - curcode += line[input_prompt.end():] - elif continue_prompt is not None: - insertions.append((len(curcode), - [(0, Generic.Prompt, continue_prompt.group())])) - curcode += line[continue_prompt.end():] - elif output_prompt is not None: - # Use the 'error' token for output. We should probably make - # our own token, but error is typically in a bright color like - # red, so it works fine for our output prompts. - insertions.append((len(curcode), - [(0, Generic.Error, output_prompt.group())])) - curcode += line[output_prompt.end():] - else: - if curcode: - for item in do_insertions(insertions, - pylexer.get_tokens_unprocessed(curcode)): - yield item - curcode = '' - insertions = [] - yield match.start(), Generic.Output, line - if curcode: - for item in do_insertions(insertions, - pylexer.get_tokens_unprocessed(curcode)): - yield item - - -def setup(app): - """Setup as a sphinx extension.""" - - # This is only a lexer, so adding it below to pygments appears sufficient. - # But if somebody knows that the right API usage should be to do that via - # sphinx, by all means fix it here. At least having this setup.py - # suppresses the sphinx warning we'd get without it. - pass - -#----------------------------------------------------------------------------- -# Register the extension as a valid pygments lexer -highlighting.lexers['ipython'] = IPythonConsoleLexer() diff --git a/docs/sphinxext/ipython_directive.py b/docs/sphinxext/ipython_directive.py deleted file mode 100644 index 61af38ef1..000000000 --- a/docs/sphinxext/ipython_directive.py +++ /dev/null @@ -1,909 +0,0 @@ -# -*- coding: utf-8 -*- -"""Sphinx directive to support embedded IPython code. - -This directive allows pasting of entire interactive IPython sessions, prompts -and all, and their code will actually get re-executed at doc build time, with -all prompts renumbered sequentially. It also allows you to input code as a pure -python input by giving the argument python to the directive. The output looks -like an interactive ipython section. - -To enable this directive, simply list it in your Sphinx ``conf.py`` file -(making sure the directory where you placed it is visible to sphinx, as is -needed for all Sphinx directives). - -By default this directive assumes that your prompts are unchanged IPython ones, -but this can be customized. The configurable options that can be placed in -conf.py are - -ipython_savefig_dir: - The directory in which to save the figures. This is relative to the - Sphinx source directory. The default is `html_static_path`. -ipython_rgxin: - The compiled regular expression to denote the start of IPython input - lines. The default is re.compile('In \[(\d+)\]:\s?(.*)\s*'). You - shouldn't need to change this. -ipython_rgxout: - The compiled regular expression to denote the start of IPython output - lines. The default is re.compile('Out\[(\d+)\]:\s?(.*)\s*'). You - shouldn't need to change this. -ipython_promptin: - The string to represent the IPython input prompt in the generated ReST. - The default is 'In [%d]:'. This expects that the line numbers are used - in the prompt. -ipython_promptout: - - The string to represent the IPython prompt in the generated ReST. The - default is 'Out [%d]:'. This expects that the line numbers are used - in the prompt. - -ToDo ----- - -- Turn the ad-hoc test() function into a real test suite. -- Break up ipython-specific functionality from matplotlib stuff into better - separated code. - -Authors -------- - -- John D Hunter: original author. -- Fernando Perez: refactoring, documentation, cleanups, port to 0.11. -- VĂĄclavĹ milauer : Prompt generalizations. -- Skipper Seabold, refactoring, cleanups, pure python addition -""" - -#----------------------------------------------------------------------------- -# Imports -#----------------------------------------------------------------------------- - -# Stdlib -import ast -import cStringIO -import os -import re -import sys -import tempfile - -# To keep compatibility with various python versions -try: - from hashlib import md5 -except ImportError: - from md5 import md5 - -# Third-party -import matplotlib -import sphinx -from docutils.parsers.rst import directives -from docutils import nodes -from sphinx.util.compat import Directive - -matplotlib.use('Agg') - -# Our own -from IPython import Config, InteractiveShell -from IPython.core.profiledir import ProfileDir -from IPython.utils import io - -from pdb import set_trace - -#----------------------------------------------------------------------------- -# Globals -#----------------------------------------------------------------------------- -# for tokenizing blocks -COMMENT, INPUT, OUTPUT = range(3) - -#----------------------------------------------------------------------------- -# Functions and class declarations -#----------------------------------------------------------------------------- -def block_parser(part, rgxin, rgxout, fmtin, fmtout): - """ - part is a string of ipython text, comprised of at most one - input, one ouput, comments, and blank lines. The block parser - parses the text into a list of:: - - blocks = [ (TOKEN0, data0), (TOKEN1, data1), ...] - - where TOKEN is one of [COMMENT | INPUT | OUTPUT ] and - data is, depending on the type of token:: - - COMMENT : the comment string - - INPUT: the (DECORATOR, INPUT_LINE, REST) where - DECORATOR: the input decorator (or None) - INPUT_LINE: the input as string (possibly multi-line) - REST : any stdout generated by the input line (not OUTPUT) - - - OUTPUT: the output string, possibly multi-line - """ - - block = [] - lines = part.split('\n') - N = len(lines) - i = 0 - decorator = None - while 1: - - if i==N: - # nothing left to parse -- the last line - break - - line = lines[i] - i += 1 - line_stripped = line.strip() - if line_stripped.startswith('#'): - block.append((COMMENT, line)) - continue - - if line_stripped.startswith('@'): - # we're assuming at most one decorator -- may need to - # rethink - decorator = line_stripped - continue - - # does this look like an input line? - matchin = rgxin.match(line) - if matchin: - lineno, inputline = int(matchin.group(1)), matchin.group(2) - - # the ....: continuation string - continuation = ' %s:'% ''.join(['.']*(len(str(lineno))+2)) - Nc = len(continuation) - # input lines can continue on for more than one line, if - # we have a '\' line continuation char or a function call - # echo line 'print'. The input line can only be - # terminated by the end of the block or an output line, so - # we parse out the rest of the input line if it is - # multiline as well as any echo text - - rest = [] - while i 0] - - for lineno, line in enumerate(content): - - line_stripped = line.strip() - if not len(line): - output.append(line) - continue - - # handle decorators - if line_stripped.startswith('@'): - output.extend([line]) - if 'savefig' in line: - savefig = True # and need to clear figure - continue - - # handle comments - if line_stripped.startswith('#'): - output.extend([line]) - continue - - continuation = u' %s:'% ''.join(['.']*(len(str(ct))+2)) - if not multiline: - modified = u"%s %s" % (fmtin % ct, line_stripped) - output.append(modified) - ct += 1 - try: - ast.parse(line_stripped) - output.append(u'') - except Exception: - multiline = True - multiline_start = lineno - else: - modified = u'%s %s' % (continuation, line) - output.append(modified) - - try: - ast.parse('\n'.join(content[multiline_start:lineno+1])) - - if (lineno < len(content) - 1 and - _count_indent(content[multiline_start]) < - _count_indent(content[lineno + 1])): - - continue - - output.extend([continuation, u'']) - multiline = False - except Exception: - pass - - continue - - return output - -def _count_indent(x): - import re - m = re.match('(\s+)(.*)', x) - if not m: - return 0 - return len(m.group(1)) - -class IpythonDirective(Directive): - - has_content = True - required_arguments = 0 - optional_arguments = 4 # python, suppress, verbatim, doctest - final_argumuent_whitespace = True - option_spec = { 'python': directives.unchanged, - 'suppress' : directives.flag, - 'verbatim' : directives.flag, - 'doctest' : directives.flag, - 'okexcept' : directives.flag, - } - - shell = EmbeddedSphinxShell() - - def get_config_options(self): - # contains sphinx configuration variables - config = self.state.document.settings.env.config - - # get config variables to set figure output directory - confdir = self.state.document.settings.env.app.confdir - savefig_dir = config.ipython_savefig_dir - source_dir = os.path.dirname(self.state.document.current_source) - if savefig_dir is None: - savefig_dir = config.html_static_path - if isinstance(savefig_dir, list): - savefig_dir = savefig_dir[0] # safe to assume only one path? - savefig_dir = os.path.join(confdir, savefig_dir) - - # get regex and prompt stuff - rgxin = config.ipython_rgxin - rgxout = config.ipython_rgxout - promptin = config.ipython_promptin - promptout = config.ipython_promptout - - return savefig_dir, source_dir, rgxin, rgxout, promptin, promptout - - def setup(self): - # get config values - (savefig_dir, source_dir, rgxin, - rgxout, promptin, promptout) = self.get_config_options() - - # and attach to shell so we don't have to pass them around - self.shell.rgxin = rgxin - self.shell.rgxout = rgxout - self.shell.promptin = promptin - self.shell.promptout = promptout - self.shell.savefig_dir = savefig_dir - self.shell.source_dir = source_dir - - # setup bookmark for saving figures directory - - self.shell.process_input_line('bookmark ipy_savedir %s'%savefig_dir, - store_history=False) - self.shell.clear_cout() - - return rgxin, rgxout, promptin, promptout - - - def teardown(self): - # delete last bookmark - self.shell.process_input_line('bookmark -d ipy_savedir', - store_history=False) - self.shell.clear_cout() - - def run(self): - debug = False - - #TODO, any reason block_parser can't be a method of embeddable shell - # then we wouldn't have to carry these around - rgxin, rgxout, promptin, promptout = self.setup() - - options = self.options - self.shell.is_suppress = 'suppress' in options - self.shell.is_doctest = 'doctest' in options - self.shell.is_verbatim = 'verbatim' in options - self.shell.is_okexcept = 'okexcept' in options - self.shell.current_content = self.content - - # handle pure python code - if 'python' in self.arguments: - content = self.content - self.content = self.shell.process_pure_python2(content) - - parts = '\n'.join(self.content).split('\n\n') - - lines = ['.. code-block:: ipython',''] - figures = [] - - for part in parts: - - block = block_parser(part, rgxin, rgxout, promptin, promptout) - - if len(block): - rows, figure = self.shell.process_block(block) - for row in rows: - # hack - # if row == '': - # continue - - # lines.extend([' %s'% row.strip()]) - lines.extend([' %s' % line - for line in re.split('[\n]+', row)]) - - if figure is not None: - figures.append(figure) - - #text = '\n'.join(lines) - #figs = '\n'.join(figures) - - for figure in figures: - lines.append('') - lines.extend(figure.split('\n')) - lines.append('') - - #print lines - if len(lines)>2: - if debug: - print '\n'.join(lines) - else: #NOTE: this raises some errors, what's it for? - #print 'INSERTING %d lines'%len(lines) - self.state_machine.insert_input( - lines, self.state_machine.input_lines.source(0)) - - text = '\n'.join(lines) - txtnode = nodes.literal_block(text, text) - txtnode['language'] = 'ipython' - #imgnode = nodes.image(figs) - - # cleanup - self.teardown() - - return []#, imgnode] - -# Enable as a proper Sphinx directive -def setup(app): - setup.app = app - - app.add_directive('ipython', IpythonDirective) - app.add_config_value('ipython_savefig_dir', None, True) - app.add_config_value('ipython_rgxin', - re.compile('In \[(\d+)\]:\s?(.*)\s*'), True) - app.add_config_value('ipython_rgxout', - re.compile('Out\[(\d+)\]:\s?(.*)\s*'), True) - app.add_config_value('ipython_promptin', 'In [%d]:', True) - app.add_config_value('ipython_promptout', 'Out[%d]:', True) - - -# Simple smoke test, needs to be converted to a proper automatic test. -def test(): - - examples = [ - r""" -In [9]: pwd -Out[9]: '/home/jdhunter/py4science/book' - -In [10]: cd bookdata/ -/home/jdhunter/py4science/book/bookdata - -In [2]: from pylab import * - -In [2]: ion() - -In [3]: im = imread('stinkbug.png') - -@savefig mystinkbug.png width=4in -In [4]: imshow(im) -Out[4]: - -""", - r""" - -In [1]: x = 'hello world' - -# string methods can be -# used to alter the string -@doctest -In [2]: x.upper() -Out[2]: 'HELLO WORLD' - -@verbatim -In [3]: x.st -x.startswith x.strip -""", - r""" - -In [130]: url = 'http://ichart.finance.yahoo.com/table.csv?s=CROX\ - .....: &d=9&e=22&f=2009&g=d&a=1&br=8&c=2006&ignore=.csv' - -In [131]: print url.split('&') -['http://ichart.finance.yahoo.com/table.csv?s=CROX', 'd=9', 'e=22', 'f=2009', 'g=d', 'a=1', 'b=8', 'c=2006', 'ignore=.csv'] - -In [60]: import urllib - -""", - r"""\ - -In [133]: import numpy.random - -@suppress -In [134]: numpy.random.seed(2358) - -@doctest -In [135]: numpy.random.rand(10,2) -Out[135]: -array([[ 0.64524308, 0.59943846], - [ 0.47102322, 0.8715456 ], - [ 0.29370834, 0.74776844], - [ 0.99539577, 0.1313423 ], - [ 0.16250302, 0.21103583], - [ 0.81626524, 0.1312433 ], - [ 0.67338089, 0.72302393], - [ 0.7566368 , 0.07033696], - [ 0.22591016, 0.77731835], - [ 0.0072729 , 0.34273127]]) - -""", - - r""" -In [106]: print x -jdh - -In [109]: for i in range(10): - n -.....: print i - .....: - .....: -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -""", - - r""" - -In [144]: from pylab import * - -In [145]: ion() - -# use a semicolon to suppress the output -@savefig test_hist.png width=4in -In [151]: hist(np.random.randn(10000), 100); - - -@savefig test_plot.png width=4in -In [151]: plot(np.random.randn(10000), 'o'); - """, - - r""" -# use a semicolon to suppress the output -In [151]: plt.clf() - -@savefig plot_simple.png width=4in -In [151]: plot([1,2,3]) - -@savefig hist_simple.png width=4in -In [151]: hist(np.random.randn(10000), 100); - -""", - r""" -# update the current fig -In [151]: ylabel('number') - -In [152]: title('normal distribution') - - -@savefig hist_with_text.png -In [153]: grid(True) - - """, - ] - # skip local-file depending first example: - examples = examples[1:] - - #ipython_directive.DEBUG = True # dbg - #options = dict(suppress=True) # dbg - options = dict() - for example in examples: - content = example.split('\n') - ipython_directive('debug', arguments=None, options=options, - content=content, lineno=0, - content_offset=None, block_text=None, - state=None, state_machine=None, - ) - -# Run test suite as a script -if __name__=='__main__': - if not os.path.isdir('_static'): - os.mkdir('_static') - test() - print 'All OK? Check figures in _static/' diff --git a/docs/sphinxext/only_directives.py b/docs/sphinxext/only_directives.py deleted file mode 100755 index c0dff7e65..000000000 --- a/docs/sphinxext/only_directives.py +++ /dev/null @@ -1,96 +0,0 @@ -# -# A pair of directives for inserting content that will only appear in -# either html or latex. -# - -from docutils.nodes import Body, Element -from docutils.writers.html4css1 import HTMLTranslator -try: - from sphinx.latexwriter import LaTeXTranslator -except ImportError: - from sphinx.writers.latex import LaTeXTranslator - - import warnings - warnings.warn("The numpydoc.only_directives module is deprecated;" - "please use the only:: directive available in Sphinx >= 0.6", - DeprecationWarning, stacklevel=2) - -from docutils.parsers.rst import directives - -class html_only(Body, Element): - pass - -class latex_only(Body, Element): - pass - -def run(content, node_class, state, content_offset): - text = '\n'.join(content) - node = node_class(text) - state.nested_parse(content, content_offset, node) - return [node] - -try: - from docutils.parsers.rst import Directive -except ImportError: - from docutils.parsers.rst.directives import _directives - - def html_only_directive(name, arguments, options, content, lineno, - content_offset, block_text, state, state_machine): - return run(content, html_only, state, content_offset) - - def latex_only_directive(name, arguments, options, content, lineno, - content_offset, block_text, state, state_machine): - return run(content, latex_only, state, content_offset) - - for func in (html_only_directive, latex_only_directive): - func.content = 1 - func.options = {} - func.arguments = None - - _directives['htmlonly'] = html_only_directive - _directives['latexonly'] = latex_only_directive -else: - class OnlyDirective(Directive): - has_content = True - required_arguments = 0 - optional_arguments = 0 - final_argument_whitespace = True - option_spec = {} - - def run(self): - self.assert_has_content() - return run(self.content, self.node_class, - self.state, self.content_offset) - - class HtmlOnlyDirective(OnlyDirective): - node_class = html_only - - class LatexOnlyDirective(OnlyDirective): - node_class = latex_only - - directives.register_directive('htmlonly', HtmlOnlyDirective) - directives.register_directive('latexonly', LatexOnlyDirective) - -def setup(app): - app.add_node(html_only) - app.add_node(latex_only) - - # Add visit/depart methods to HTML-Translator: - def visit_perform(self, node): - pass - def depart_perform(self, node): - pass - def visit_ignore(self, node): - node.children = [] - def depart_ignore(self, node): - node.children = [] - - HTMLTranslator.visit_html_only = visit_perform - HTMLTranslator.depart_html_only = depart_perform - HTMLTranslator.visit_latex_only = visit_ignore - HTMLTranslator.depart_latex_only = depart_ignore - - LaTeXTranslator.visit_html_only = visit_ignore - LaTeXTranslator.depart_html_only = depart_ignore - LaTeXTranslator.visit_latex_only = visit_perform - LaTeXTranslator.depart_latex_only = depart_perform diff --git a/pyproject.toml b/pyproject.toml index 8797dad46..48cc2e1de 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,7 +21,7 @@ keywords = [ 'economics' ] dynamic = ["description", "version"] -requires-python = ">=3.7" +requires-python = ">=3.12" dependencies = [ 'numba>=0.49.0', 'numpy>=1.17.0', diff --git a/quantecon/_arma.py b/quantecon/_arma.py index be0f5699f..60f67bf21 100644 --- a/quantecon/_arma.py +++ b/quantecon/_arma.py @@ -50,7 +50,7 @@ class ARMA: Attributes ---------- - phi, theta, sigma : see Parmeters + phi, theta, sigma : see Parameters ar_poly : array_like(float) The polynomial form that is needed by scipy.signal to do the processing we desire. Corresponds with the phi values @@ -155,6 +155,11 @@ def impulse_response(self, impulse_length=30): """ Get the impulse response corresponding to our model. + Parameters + ---------- + impulse_length : scalar(int), optional(default=30) + Number of periods of the impulse response to compute + Returns ------- psi : array_like(float) @@ -220,6 +225,13 @@ def autocovariance(self, num_autocov=16): num_autocov : scalar(int), optional(default=16) The number of autocovariances to calculate + Returns + ------- + acov : array_like(float) + The autocovariance function, where acov[j] is the + autocovariance at lag j. Its length is num_autocov, or + len(self.spectral_density()[1]) if that is smaller. + """ spect = self.spectral_density()[1] acov = np.fft.ifft(spect).real diff --git a/quantecon/_compute_fp.py b/quantecon/_compute_fp.py index 68005a059..ac36a215e 100644 --- a/quantecon/_compute_fp.py +++ b/quantecon/_compute_fp.py @@ -73,7 +73,7 @@ def compute_fixed_point(T, v, error_tol=1e-3, max_iter=50, verbose=2, A callable object (e.g., function) that acts on v v : object An object such that T(v) is defined; modified in place if - `method='iteration' and `v` is an array + `method='iteration'` and `v` is an array error_tol : scalar(float), optional(default=1e-3) Error tolerance max_iter : scalar(int), optional(default=50) diff --git a/quantecon/_dle.py b/quantecon/_dle.py index cea161966..ceeaee7f1 100644 --- a/quantecon/_dle.py +++ b/quantecon/_dle.py @@ -47,12 +47,12 @@ class DLE(object): Parameters ---------- - Information : tuple + information : tuple Information is a tuple containing the matrices A_{22}, C_2, U_b, and U_d - Technology : tuple + technology : tuple Technology is a tuple containing the matrices \Phi_c, \Phi_g, \Phi_i, \Gamma, \Delta_k, and \Theta_k - Preferences : tuple + preferences : tuple Preferences is a tuple containing the scalar \beta and the matrices \Lambda, \Pi, \Delta_h, and \Theta_h @@ -161,7 +161,7 @@ def compute_steadystate(self, nnc=2): Parameters ---------- - nnc : array_like(float) + nnc : scalar(int), optional(default=2) nnc is the location of the constant in the state vector x_t """ @@ -269,10 +269,10 @@ def irf(self, ts_length=100, shock=None): Parameters ---------- - ts_length : scalar(int) + ts_length : scalar(int), optional(default=100) Number of periods to calculate IRF - Shock : array_like(float) + shock : array_like(float), optional(default=None) Vector of shocks to calculate IRF to. Default is first element of w """ diff --git a/quantecon/_filter.py b/quantecon/_filter.py index 44d45f9a9..78cfb1713 100644 --- a/quantecon/_filter.py +++ b/quantecon/_filter.py @@ -31,8 +31,8 @@ def hamilton_filter(data, h, p=None): Notes ----- For seasonal data, it's desirable for p and h to be integer multiples of - the number of obsevations in a year. E.g. for quarterly data, h = 8 and p = - 4 are recommended. + the number of observations in a year. E.g. for quarterly data, h = 8 and + p = 4 are recommended. """ # transform data to array diff --git a/quantecon/_graph_tools.py b/quantecon/_graph_tools.py index 3f4dca799..4b83c7202 100644 --- a/quantecon/_graph_tools.py +++ b/quantecon/_graph_tools.py @@ -334,7 +334,7 @@ def cyclic_components(self,): def subgraph(self, nodes): """ Return the subgraph consisting of the given nodes and edges - between thses nodes. + between these nodes. Parameters ---------- diff --git a/quantecon/_gridtools.py b/quantecon/_gridtools.py index 4f9a46784..8f27b7174 100644 --- a/quantecon/_gridtools.py +++ b/quantecon/_gridtools.py @@ -145,6 +145,8 @@ def cartesian_nearest_index(x, nodes, order='C'): Examples -------- + >>> import numpy as np + >>> import quantecon as qe >>> nodes = (np.arange(3), np.arange(2)) >>> prod = qe.cartesian(nodes) >>> print(prod) @@ -160,7 +162,7 @@ def cartesian_nearest_index(x, nodes, order='C'): >>> x = (0.6, 0.4) >>> qe.cartesian_nearest_index(x, nodes) # Pass `nodes`, not `prod` - 2 + np.int64(2) The closest to (-0.1, 1.2) and (2, 0) are `prod[1]` and `prod[4]`, respectively: @@ -200,7 +202,7 @@ def cartesian_nearest_index(x, nodes, order='C'): @njit(cache=True) def _cartesian_nearest_indices(X, nodes, order='C'): """ - The main body of `cartesian_nearest_index`, jit-complied by Numba. + The main body of `cartesian_nearest_index`, jit-compiled by Numba. Note that `X` must be a 2-dim ndarray, and a Python list is not accepted for `nodes`. @@ -313,21 +315,21 @@ def simplex_grid(m, n): [4, 0, 0]]) >>> simplex_grid(3, 4) / 4 - array([[ 0. , 0. , 1. ], - [ 0. , 0.25, 0.75], - [ 0. , 0.5 , 0.5 ], - [ 0. , 0.75, 0.25], - [ 0. , 1. , 0. ], - [ 0.25, 0. , 0.75], - [ 0.25, 0.25, 0.5 ], - [ 0.25, 0.5 , 0.25], - [ 0.25, 0.75, 0. ], - [ 0.5 , 0. , 0.5 ], - [ 0.5 , 0.25, 0.25], - [ 0.5 , 0.5 , 0. ], - [ 0.75, 0. , 0.25], - [ 0.75, 0.25, 0. ], - [ 1. , 0. , 0. ]]) + array([[0. , 0. , 1. ], + [0. , 0.25, 0.75], + [0. , 0.5 , 0.5 ], + [0. , 0.75, 0.25], + [0. , 1. , 0. ], + [0.25, 0. , 0.75], + [0.25, 0.25, 0.5 ], + [0.25, 0.5 , 0.25], + [0.25, 0.75, 0. ], + [0.5 , 0. , 0.5 ], + [0.5 , 0.25, 0.25], + [0.5 , 0.5 , 0. ], + [0.75, 0. , 0.25], + [0.75, 0.25, 0. ], + [1. , 0. , 0. ]]) References ---------- @@ -375,7 +377,7 @@ def simplex_index(x, m, n): ---------- x : array_like(int, ndim=1) Integer point in the simplex, i.e., an array of m nonnegative - itegers that sum to n. + integers that sum to n. m : scalar(int) Dimension of each point. Must be a positive integer. diff --git a/quantecon/_inequality.py b/quantecon/_inequality.py index 293f1555e..f13872449 100644 --- a/quantecon/_inequality.py +++ b/quantecon/_inequality.py @@ -36,9 +36,11 @@ def lorenz_curve(y): Examples -------- + >>> import numpy as np + >>> from quantecon import lorenz_curve >>> a_val, n = 3, 10_000 >>> y = np.random.pareto(a_val, size=n) - >>> f_vals, l_vals = lorenz(y) + >>> f_vals, l_vals = lorenz_curve(y) """ diff --git a/quantecon/_ivp.py b/quantecon/_ivp.py index 39cf08bd4..0347f8ec7 100644 --- a/quantecon/_ivp.py +++ b/quantecon/_ivp.py @@ -5,11 +5,11 @@ \frac{dy}{dt} = f(t,y),\ y(t_0) = y_0 -using finite difference methods. The `quantecon.ivp` class uses various +using finite difference methods. The `quantecon.IVP` class uses various integrators from the `scipy.integrate.ode` module to perform the integration (i.e., solve the ODE) and parametric B-spline interpolation from `scipy.interpolate` to approximate the value of the solution -between grid points. The `quantecon.ivp` module also provides a method +between grid points. The `quantecon.IVP` class also provides a method for computing the residual of the solution which can be used for assessing the overall accuracy of the approximated solution. @@ -158,7 +158,7 @@ def solve(self, t0, y0, h=1.0, T=None, g=None, tol=None, user must also specify a stopping tolerance, `tol`. tol : float, optional (default=None) Stopping tolerance for the integration. Only required if `g` is - also specifed. + also specified. integrator : str, optional(default='dopri5') Must be one of 'vode', 'lsoda', 'dopri5', or 'dop853' step : bool, optional(default=False) @@ -174,7 +174,7 @@ def solve(self, t0, y0, h=1.0, T=None, g=None, tol=None, Returns ------- - solution: ndarray (float) + solution : ndarray (float) Simulated solution trajectory. """ @@ -208,14 +208,14 @@ def interpolate(self, traj, ti, k=3, der=0, ext=2): der : int, optional(default=0) The order of derivative of the spline to compute (must be less than or equal to `k`). - ext : int, optional(default=2) Controls the value of returned elements - for outside the original knot sequence provided by traj. For - extrapolation, set `ext=0`; `ext=1` returns zero; `ext=2` raises a - `ValueError`. + ext : int, optional(default=2) + Controls the value of returned elements for outside the + original knot sequence provided by traj. For extrapolation, set + `ext=0`; `ext=1` returns zero; `ext=2` raises a `ValueError`. Returns ------- - interp_traj: ndarray (float) + interp_traj : ndarray (float) The interpolated trajectory. """ diff --git a/quantecon/_kalman.py b/quantecon/_kalman.py index 0acf3916c..b2ef8d5e9 100644 --- a/quantecon/_kalman.py +++ b/quantecon/_kalman.py @@ -104,7 +104,7 @@ def whitener_lss(self): r""" This function takes the linear state space system that is an input to the Kalman class and it converts - that system to the time-invariant whitener represenation + that system to the time-invariant whitener representation given by .. math:: @@ -289,7 +289,17 @@ def stationary_coefficients(self, j, coeff_type='ma'): coeff_type : string, either 'ma' or 'var' (default='ma') The type of coefficent sequence to compute. Either 'ma' for moving average or 'var' for VAR. - + + Returns + ------- + coeffs : list(array_like(float, ndim=2)) + List of the j + 1 coefficient matrices. Each matrix is + k x k, where k is the dimension of the observation vector. + For `coeff_type='ma'` these are the moving average + coefficients at lags 0 through j, with coeffs[0] the + identity; for `coeff_type='var'` they are the VAR + coefficients at lags 1 through j + 1. + """ # == simplify notation == # A, G = self.ss.A, self.ss.G @@ -314,6 +324,18 @@ def stationary_coefficients(self, j, coeff_type='ma'): return coeffs def stationary_innovation_covar(self): + r""" + Compute the covariance matrix of the innovations for the steady + state Kalman filter, given by :math:`G \Sigma_\infty G' + R`, + where :math:`R = H H'`. + + Returns + ------- + array_like(float, ndim=2) + The k x k innovation covariance matrix, where k is the + dimension of the observation vector. + + """ # == simplify notation == # H, G = self.ss.H, self.ss.G R = H @ H.T diff --git a/quantecon/_lae.py b/quantecon/_lae.py index a324559b8..72929e1df 100644 --- a/quantecon/_lae.py +++ b/quantecon/_lae.py @@ -38,9 +38,15 @@ class LAE: Examples -------- + >>> import numpy as np + >>> from quantecon import LAE + >>> from scipy.stats import lognorm + >>> p = lambda x, y: lognorm.pdf(y / x, 1) + >>> X = np.exp(np.random.randn(100)) >>> psi = LAE(p, X) - >>> y = np.linspace(0, 1, 100) - >>> psi(y) # Evaluate look ahead estimate at grid of points y + >>> y = np.linspace(0.1, 3, 100) + >>> psi(y).shape # Evaluate look ahead estimate at grid of points y + (100,) """ diff --git a/quantecon/_lqcontrol.py b/quantecon/_lqcontrol.py index b9e130fd6..f85508cf3 100644 --- a/quantecon/_lqcontrol.py +++ b/quantecon/_lqcontrol.py @@ -91,7 +91,7 @@ class LQ: T is the number of periods in a finite horizon problem. Rf : array_like(float), optional(default=None) Rf is the final (in a finite horizon model) payoff(or cost) - matrix that corresponds with the control variable u and is n x + matrix that corresponds with the state variable x and is n x n. Should be symmetric and non-negative definite Attributes diff --git a/quantecon/_lqnash.py b/quantecon/_lqnash.py index 56f7b8a4e..2c2445e32 100644 --- a/quantecon/_lqnash.py +++ b/quantecon/_lqnash.py @@ -45,9 +45,9 @@ def nnash(A, B1, B2, R1, R2, Q1, Q2, S1, S2, W1, W2, M1, M2, Q2 : scalar(float) or array_like(float) As above, size (k_2, k_2) S1 : scalar(float) or array_like(float) - As above, size (k_1, k_1) - S2 : scalar(float) or array_like(float) As above, size (k_2, k_2) + S2 : scalar(float) or array_like(float) + As above, size (k_1, k_1) W1 : scalar(float) or array_like(float) As above, size (n, k_1) W2 : scalar(float) or array_like(float) @@ -61,7 +61,7 @@ def nnash(A, B1, B2, R1, R2, Q1, Q2, S1, S2, W1, W2, M1, M2, tol : scalar(float), optional(default=1e-8) This is the tolerance level for convergence max_iter : scalar(int), optional(default=1000) - This is the maximum number of iteratiosn allowed + This is the maximum number of iterations allowed Returns ------- diff --git a/quantecon/_lss.py b/quantecon/_lss.py index de1c6348e..49d6d7a15 100644 --- a/quantecon/_lss.py +++ b/quantecon/_lss.py @@ -348,7 +348,7 @@ def geometric_sums(self, beta, x_t): beta : scalar(float) Discount factor, in [0, 1) - beta : array_like(float) + x_t : array_like(float) The term x_t for conditioning Returns @@ -369,7 +369,7 @@ def geometric_sums(self, beta, x_t): def impulse_response(self, j=5): r""" - Pulls off the imuplse response coefficients to a shock + Pulls off the impulse response coefficients to a shock in :math:`w_{t}` for :math:`x` and :math:`y` Important to note: We are uninterested in the shocks to @@ -381,7 +381,8 @@ def impulse_response(self, j=5): Parameters ---------- j : Scalar(int) - Number of coefficients that we want + Number of lags of the impulse response to compute; the + returned lists have j + 1 entries (lags 0 through j) Returns ------- diff --git a/quantecon/_matrix_eqn.py b/quantecon/_matrix_eqn.py index e2bb6f729..39f629710 100644 --- a/quantecon/_matrix_eqn.py +++ b/quantecon/_matrix_eqn.py @@ -58,7 +58,7 @@ def solve_discrete_lyapunov(A, B, max_it=50, method="doubling"): Returns ------- - gamma1: array_like(float, ndim=2) + gamma1 : array_like(float, ndim=2) Represents the value :math:`X` """ @@ -242,10 +242,9 @@ def solve_discrete_riccati_system(Π, As, Bs, Cs, Qs, Rs, Ns, beta, Bs : array_like(float) Consists of m state transition matrices B(s) with dimension n x k for each Markov state s - Cs : array_like(float), optional(default=None) + Cs : array_like(float) Consists of m state transition matrices C(s) with dimension - n x j for each Markov state s. If the model is deterministic - then Cs should take default value of None + n x j for each Markov state s Qs : array_like(float) Consists of m symmetric and non-negative definite payoff matrices Q(s) with dimension k x k that corresponds with @@ -254,14 +253,14 @@ def solve_discrete_riccati_system(Π, As, Bs, Cs, Qs, Rs, Ns, beta, Consists of m symmetric and non-negative definite payoff matrices R(s) with dimension n x n that corresponds with the state variable x for each Markov state s - Ns : array_like(float), optional(default=None) + Ns : array_like(float) Consists of m cross product term matrices N(s) with dimension k x n for each Markov state, - beta : scalar(float), optional(default=1) + beta : scalar(float) beta is the discount parameter tolerance : scalar(float), optional(default=1e-10) The tolerance level for convergence - max_iter : scalar(int), optional(default=500) + max_iter : scalar(int), optional(default=1000) The maximum number of iterations allowed Returns diff --git a/quantecon/_quadsums.py b/quantecon/_quadsums.py index ce7a26ec9..0081f8c1c 100644 --- a/quantecon/_quadsums.py +++ b/quantecon/_quadsums.py @@ -29,15 +29,15 @@ def var_quadratic_sum(A, C, H, beta, x0): The matrix described above in description. Should be n x n H : array_like(float, ndim=2) The matrix described above in description. Should be n x n - beta: scalar(float) + beta : scalar(float) Should take a value in (0, 1) - x_0: array_like(float, ndim=1) - The initial condtion. A conformable array (of length n, or with + x0 : array_like(float, ndim=1) + The initial condition. A conformable array (of length n, or with n rows) Returns ------- - q0: scalar(float) + q0 : scalar(float) Represents the value :math:`q(x_0)` Remarks: The formula for computing :math:`q(x_0)` is @@ -71,7 +71,7 @@ def m_quadratic_sum(A, B, max_it=50): V is computed by solving the corresponding discrete lyapunov equation using the doubling algorithm. See the documentation of - `util.solve_discrete_lyapunov` for more information. + `quantecon.solve_discrete_lyapunov` for more information. Parameters ---------- @@ -88,7 +88,7 @@ def m_quadratic_sum(A, B, max_it=50): Returns ------- - gamma1: array_like(float, ndim=2) + gamma1 : array_like(float, ndim=2) Represents the value :math:`V` """ diff --git a/quantecon/_rank_nullspace.py b/quantecon/_rank_nullspace.py index 6eb553c41..9f5cf7b16 100644 --- a/quantecon/_rank_nullspace.py +++ b/quantecon/_rank_nullspace.py @@ -4,7 +4,7 @@ def rank_est(A, atol=1e-13, rtol=0): """ - Estimate the rank (i.e. the dimension of the nullspace) of a matrix. + Estimate the rank (i.e. the dimension of the column space) of a matrix. The algorithm used by this function is based on the singular value decomposition of `A`. diff --git a/quantecon/_robustlq.py b/quantecon/_robustlq.py index 481b5def6..ee037cb9d 100644 --- a/quantecon/_robustlq.py +++ b/quantecon/_robustlq.py @@ -174,12 +174,12 @@ def robust_rule(self, method='doubling'): ------- F : array_like(float, ndim=2) The optimal control matrix from above - P : array_like(float, ndim=2) - The positive semi-definite matrix defining the value - function K : array_like(float, ndim=2) the worst-case shock matrix K, where :math:`w_{t+1} = K x_t` is the worst case shock + P : array_like(float, ndim=2) + The positive semi-definite matrix defining the value + function """ # == Simplify names == # @@ -233,12 +233,12 @@ def robust_rule_simple(self, P_init=None, max_iter=80, tol=1e-8): ------- F : array_like(float, ndim=2) The optimal control matrix from above - P : array_like(float, ndim=2) - The positive semi-definite matrix defining the value - function K : array_like(float, ndim=2) the worst-case shock matrix K, where :math:`w_{t+1} = K x_t` is the worst case shock + P : array_like(float, ndim=2) + The positive semi-definite matrix defining the value + function """ # == Simplify names == # @@ -344,7 +344,7 @@ def compute_deterministic_entropy(self, F, K, x0): Returns ------- - e : scalar(int) + e : scalar(float) The deterministic entropy """ @@ -368,12 +368,12 @@ def evaluate_F(self, F): Returns ------- + K_F : array_like(float, ndim=2) + Worst case policy P_F : array_like(float, ndim=2) Matrix for discounted cost d_F : scalar(float) Constant for discounted cost - K_F : array_like(float, ndim=2) - Worst case policy O_F : array_like(float, ndim=2) Matrix for discounted entropy o_F : scalar(float) diff --git a/quantecon/arma.py b/quantecon/arma.py index 5065f3fd6..2efcd5fa5 100644 --- a/quantecon/arma.py +++ b/quantecon/arma.py @@ -1,4 +1,5 @@ -# This file is not meant for public use and will be removed v0.8.0. +# This file is not meant for public use and will be removed in a +# future release. # Use the `quantecon` namespace for importing the objects # included below. diff --git a/quantecon/ce_util.py b/quantecon/ce_util.py index 0d5b03ad8..2c8042947 100644 --- a/quantecon/ce_util.py +++ b/quantecon/ce_util.py @@ -1,4 +1,5 @@ -# This file is not meant for public use and will be removed v0.8.0. +# This file is not meant for public use and will be removed in a +# future release. # Use the `quantecon` namespace for importing the objects # included below. diff --git a/quantecon/compute_fp.py b/quantecon/compute_fp.py index 99d62c410..1bc49421a 100644 --- a/quantecon/compute_fp.py +++ b/quantecon/compute_fp.py @@ -1,4 +1,5 @@ -# This file is not meant for public use and will be removed v0.8.0. +# This file is not meant for public use and will be removed in a +# future release. # Use the `quantecon` namespace for importing the objects # included below. diff --git a/quantecon/discrete_rv.py b/quantecon/discrete_rv.py index 44552851c..b45440063 100644 --- a/quantecon/discrete_rv.py +++ b/quantecon/discrete_rv.py @@ -1,4 +1,5 @@ -# This file is not meant for public use and will be removed v0.8.0. +# This file is not meant for public use and will be removed in a +# future release. # Use the `quantecon` namespace for importing the objects # included below. diff --git a/quantecon/distributions.py b/quantecon/distributions.py index 88e46e78c..bc46fb494 100644 --- a/quantecon/distributions.py +++ b/quantecon/distributions.py @@ -79,18 +79,9 @@ def pdf(self): where :math:`B` is the beta function. - Parameters - ---------- - n : scalar(int) - First parameter to the Beta-binomial distribution - a : scalar(float) - Second parameter to the Beta-binomial distribution - b : scalar(float) - Third parameter to the Beta-binomial distribution - Returns ------- - probs: array_like(float) + probs : array_like(float) Vector of probabilities over k """ diff --git a/quantecon/dle.py b/quantecon/dle.py index 5173f9274..50e6cb3a6 100644 --- a/quantecon/dle.py +++ b/quantecon/dle.py @@ -1,4 +1,5 @@ -# This file is not meant for public use and will be removed v0.8.0. +# This file is not meant for public use and will be removed in a +# future release. # Use the `quantecon` namespace for importing the objects # included below. diff --git a/quantecon/ecdf.py b/quantecon/ecdf.py index f753b45a5..5bd542f26 100644 --- a/quantecon/ecdf.py +++ b/quantecon/ecdf.py @@ -1,4 +1,5 @@ -# This file is not meant for public use and will be removed v0.8.0. +# This file is not meant for public use and will be removed in a +# future release. # Use the `quantecon` namespace for importing the objects # included below. diff --git a/quantecon/estspec.py b/quantecon/estspec.py index 841dd6344..f2ed02e7c 100644 --- a/quantecon/estspec.py +++ b/quantecon/estspec.py @@ -1,4 +1,5 @@ -# This file is not meant for public use and will be removed v0.8.0. +# This file is not meant for public use and will be removed in a +# future release. # Use the `quantecon` namespace for importing the objects # included below. diff --git a/quantecon/filter.py b/quantecon/filter.py index a78a67d23..cf094bed8 100644 --- a/quantecon/filter.py +++ b/quantecon/filter.py @@ -1,4 +1,5 @@ -# This file is not meant for public use and will be removed v0.8.0. +# This file is not meant for public use and will be removed in a +# future release. # Use the `quantecon` namespace for importing the objects # included below. diff --git a/quantecon/game_theory/fictplay.py b/quantecon/game_theory/fictplay.py index 182d6ced8..67611dd94 100644 --- a/quantecon/game_theory/fictplay.py +++ b/quantecon/game_theory/fictplay.py @@ -16,13 +16,13 @@ class FictitiousPlay: `NormalFormGame` or an array. See `NormalFormGame`. gain : scalar(float), optional(default=None) - The gain of fictitous play model. If gain is None, the model becomes a + The gain of fictitious play model. If gain is None, the model becomes a decreasing gain model. If gain is a scalar, the model becomes a constant gain model. Attributes ---------- - g : NomalFormGame + g : NormalFormGame The game played in the model. N : scalar(int) @@ -174,14 +174,14 @@ class StochasticFictitiousPlay(FictitiousPlay): distribution : scipy.stats object The distribution of payoff shocks, which is a `scipy.stats` object. - gain : scalar(scalar), optional(default=None) + gain : scalar(float), optional(default=None) The gain of fictitious play model. If gain is None, the model becomes a decreasing gain model. If gain is a scalar, the model becomes a constant gain model. Attributes ---------- - See attributes of `FictitousPlay`. + See attributes of `FictitiousPlay`. """ def __init__(self, data, distribution, gain=None): diff --git a/quantecon/game_theory/game_converters.py b/quantecon/game_theory/game_converters.py index 4976029d6..6aec2f36b 100644 --- a/quantecon/game_theory/game_converters.py +++ b/quantecon/game_theory/game_converters.py @@ -48,6 +48,7 @@ class GAMPayoffVector: flat 1-dim array. Payoff values are ordered as in the GameTracer .gam format: + 1. Player-major blocks: player 0, ..., player N-1. 2. Within each block, action profiles are ordered with player 0 varying fastest, then player 1, ..., player N-1 (i.e., @@ -98,6 +99,20 @@ def from_nfg(cls, g, dtype=None): """ Construct a GAMPayoffVector from a NormalFormGame `g`. + Parameters + ---------- + g : NormalFormGame + NormalFormGame instance. + + dtype : data-type, optional(default=None) + Data type of the payoff array. If None, default to the + `dtype` attribute of `g`. + + Returns + ------- + GAMPayoffVector + The GAMPayoffVector representation of `g`. + Examples -------- >>> player0 = Player([[0, 3], [1, 4], [2, 5]]) @@ -133,6 +148,17 @@ def to_nfg(self, dtype=None): """ Construct a NormalFormGame from self. + Parameters + ---------- + dtype : data-type, optional(default=None) + Data type of the players' payoff arrays. If None, default to + the data type of the `payoffs` attribute. + + Returns + ------- + NormalFormGame + The NormalFormGame represented by self. + Examples -------- >>> nums_actions = (3, 2) @@ -194,6 +220,16 @@ def from_file(cls, file_path): """ Read from a .gam format file. + Parameters + ---------- + file_path : str + Path to the .gam file. + + Returns + ------- + NormalFormGame + The game described by the .gam file. + """ with open(file_path, 'r') as f: string = f.read() @@ -204,6 +240,16 @@ def from_url(cls, url): """ Read from a URL. + Parameters + ---------- + url : str + String containing a URL of the .gam file. + + Returns + ------- + NormalFormGame + The game described by the .gam file. + """ import urllib.request with urllib.request.urlopen(url) as response: @@ -215,6 +261,16 @@ def from_string(cls, string): """ Read from a .gam format string. + Parameters + ---------- + string : str + String in .gam format. + + Returns + ------- + NormalFormGame + The game described by the .gam string. + """ return cls._parse(string) @@ -266,6 +322,14 @@ def to_file(cls, g, file_path): """ Write `g` to a file in GameTracer .gam format. + Parameters + ---------- + g : NormalFormGame + NormalFormGame instance to write. + + file_path : str + Path to the file to write to. + """ with open(file_path, 'w') as f: f.write(cls._dump(g) + '\n') @@ -275,6 +339,16 @@ def to_string(cls, g): """ Return the GameTracer .gam string representation of `g`. + Parameters + ---------- + g : NormalFormGame + NormalFormGame instance to convert. + + Returns + ------- + str + The .gam format string representation of `g`. + """ return cls._dump(g) diff --git a/quantecon/game_theory/game_generators/bimatrix_generators.py b/quantecon/game_theory/game_generators/bimatrix_generators.py index 294194514..607e1dfa0 100644 --- a/quantecon/game_theory/game_generators/bimatrix_generators.py +++ b/quantecon/game_theory/game_generators/bimatrix_generators.py @@ -141,17 +141,20 @@ def blotto_game(h, t, rho, mu=0, random_state=None): Examples -------- - >>> g = blotto_game(2, 3, 0.5, random_state=1234) + >>> import numpy as np + >>> from quantecon.game_theory import blotto_game + >>> rng = np.random.default_rng(1234) + >>> g = blotto_game(2, 3, 0.5, random_state=rng) >>> g.players[0] - Player([[-0.44861083, -1.08443468, -1.08443468, -1.08443468], - [ 0.18721302, -0.44861083, -1.08443468, -1.08443468], - [ 0.18721302, 0.18721302, -0.44861083, -1.08443468], - [ 0.18721302, 0.18721302, 0.18721302, -0.44861083]]) + Player([[ 0.31948659, -0.71794028, -0.71794028, -0.71794028], + [ 1.35691346, 0.31948659, -0.71794028, -0.71794028], + [ 1.35691346, 1.35691346, 0.31948659, -0.71794028], + [ 1.35691346, 1.35691346, 1.35691346, 0.31948659]]) >>> g.players[1] - Player([[-1.20042463, -1.39708658, -1.39708658, -1.39708658], - [-1.00376268, -1.20042463, -1.39708658, -1.39708658], - [-1.00376268, -1.00376268, -1.20042463, -1.39708658], - [-1.00376268, -1.00376268, -1.00376268, -1.20042463]]) + Player([[ 0.42784614, -0.56532109, -0.56532109, -0.56532109], + [ 1.42101337, 0.42784614, -0.56532109, -0.56532109], + [ 1.42101337, 1.42101337, 0.42784614, -0.56532109], + [ 1.42101337, 1.42101337, 1.42101337, 0.42784614]]) """ actions = simplex_grid(h, t) @@ -237,19 +240,22 @@ def ranking_game(n, steps=10, random_state=None): Examples -------- - >>> g = ranking_game(5, random_state=1234) + >>> import numpy as np + >>> from quantecon.game_theory import ranking_game + >>> rng = np.random.default_rng(1234) + >>> g = ranking_game(5, random_state=rng) >>> g.players[0] - Player([[ 0. , 0. , 0. , 0. , 0. ], - [ 0.82, -0.18, -0.18, -0.18, -0.18], - [ 0.8 , 0.8 , -0.2 , -0.2 , -0.2 ], - [ 0.68, 0.68, 0.68, -0.32, -0.32], - [ 0.66, 0.66, 0.66, 0.66, -0.34]]) + Player([[ 0.5 , 0. , 0. , 0. , 0. ], + [ 0.88, 0.88, 0.88, 0.88, -0.12], + [ 0.84, 0.84, 0.84, 0.84, 0.84], + [ 0.68, 0.68, 0.68, 0.68, 0.68], + [ 0.62, 0.62, 0.62, 0.62, 0.62]]) >>> g.players[1] - Player([[ 1. , 0. , 0. , 0. , 0. ], - [ 0.8 , 0.8 , -0.2 , -0.2 , -0.2 ], - [ 0.66, 0.66, 0.66, -0.34, -0.34], - [ 0.6 , 0.6 , 0.6 , 0.6 , -0.4 ], - [ 0.58, 0.58, 0.58, 0.58, 0.58]]) + Player([[ 0.5 , 0. , 0. , 0. , 0. ], + [ 0.84, -0.16, -0.16, -0.16, -0.16], + [ 0.76, -0.24, -0.24, -0.24, -0.24], + [ 0.6 , -0.4 , -0.4 , -0.4 , -0.4 ], + [ 0.4 , 0.4 , -0.6 , -0.6 , -0.6 ]]) """ payoff_arrays = tuple(np.empty((n, n)) for i in range(2)) @@ -328,23 +334,24 @@ def sgc_game(k): Examples -------- + >>> from quantecon.game_theory import sgc_game >>> g = sgc_game(2) >>> g.players[0] - Player([[ 0.75, 0.5 , 1. , 0.5 , 0.5 , 0.5 , 0.5 ], - [ 1. , 0.75, 0.5 , 0.5 , 0.5 , 0.5 , 0.5 ], - [ 0.5 , 1. , 0.75, 0.5 , 0.5 , 0.5 , 0.5 ], - [ 0. , 0. , 0. , 0.75, 0. , 0. , 0. ], - [ 0. , 0. , 0. , 0. , 0.75, 0. , 0. ], - [ 0. , 0. , 0. , 0. , 0. , 0.75, 0. ], - [ 0. , 0. , 0. , 0. , 0. , 0. , 0.75]]) + Player([[0.75, 0.5 , 1. , 0.5 , 0.5 , 0.5 , 0.5 ], + [1. , 0.75, 0.5 , 0.5 , 0.5 , 0.5 , 0.5 ], + [0.5 , 1. , 0.75, 0.5 , 0.5 , 0.5 , 0.5 ], + [0. , 0. , 0. , 0.75, 0. , 0. , 0. ], + [0. , 0. , 0. , 0. , 0.75, 0. , 0. ], + [0. , 0. , 0. , 0. , 0. , 0.75, 0. ], + [0. , 0. , 0. , 0. , 0. , 0. , 0.75]]) >>> g.players[1] - Player([[ 0.75, 0.5 , 1. , 0.5 , 0.5 , 0.5 , 0.5 ], - [ 1. , 0.75, 0.5 , 0.5 , 0.5 , 0.5 , 0.5 ], - [ 0.5 , 1. , 0.75, 0.5 , 0.5 , 0.5 , 0.5 ], - [ 0. , 0. , 0. , 0. , 0.75, 0. , 0. ], - [ 0. , 0. , 0. , 0.75, 0. , 0. , 0. ], - [ 0. , 0. , 0. , 0. , 0. , 0. , 0.75], - [ 0. , 0. , 0. , 0. , 0. , 0.75, 0. ]]) + Player([[0.75, 0.5 , 1. , 0.5 , 0.5 , 0.5 , 0.5 ], + [1. , 0.75, 0.5 , 0.5 , 0.5 , 0.5 , 0.5 ], + [0.5 , 1. , 0.75, 0.5 , 0.5 , 0.5 , 0.5 ], + [0. , 0. , 0. , 0. , 0.75, 0. , 0. ], + [0. , 0. , 0. , 0.75, 0. , 0. , 0. ], + [0. , 0. , 0. , 0. , 0. , 0. , 0.75], + [0. , 0. , 0. , 0. , 0. , 0.75, 0. ]]) """ payoff_arrays = tuple(np.empty((4*k-1, 4*k-1)) for i in range(2)) @@ -433,24 +440,27 @@ def tournament_game(n, k, random_state=None): Examples -------- - >>> g = tournament_game(5, 2, random_state=1234) + >>> import numpy as np + >>> from quantecon.game_theory import tournament_game + >>> rng = np.random.default_rng(1234) + >>> g = tournament_game(5, 2, random_state=rng) >>> g.players[0] - Player([[ 0., 0., 0., 0., 1., 0., 0., 0., 0., 0.], - [ 0., 0., 0., 0., 0., 0., 0., 0., 0., 1.], - [ 1., 0., 0., 0., 0., 0., 0., 0., 0., 0.], - [ 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], - [ 0., 1., 0., 1., 0., 1., 0., 0., 0., 0.]]) + Player([[0., 0., 0., 0., 0., 0., 0., 0., 1., 0.], + [0., 1., 0., 1., 0., 1., 1., 0., 1., 1.], + [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], + [0., 0., 0., 0., 0., 0., 1., 0., 0., 0.], + [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]]) >>> g.players[1] - Player([[ 1., 1., 0., 0., 0.], - [ 1., 0., 1., 0., 0.], - [ 0., 1., 1., 0., 0.], - [ 1., 0., 0., 1., 0.], - [ 0., 1., 0., 1., 0.], - [ 0., 0., 1., 1., 0.], - [ 1., 0., 0., 0., 1.], - [ 0., 1., 0., 0., 1.], - [ 0., 0., 1., 0., 1.], - [ 0., 0., 0., 1., 1.]]) + Player([[1., 1., 0., 0., 0.], + [1., 0., 1., 0., 0.], + [0., 1., 1., 0., 0.], + [1., 0., 0., 1., 0.], + [0., 1., 0., 1., 0.], + [0., 0., 1., 1., 0.], + [1., 0., 0., 0., 1.], + [0., 1., 0., 0., 1.], + [0., 0., 1., 0., 1.], + [0., 0., 0., 1., 1.]]) References ---------- @@ -560,31 +570,36 @@ def unit_vector_game(n, avoid_pure_nash=False, random_state=None): Examples -------- - >>> g = unit_vector_game(4, random_state=1234) + >>> import numpy as np + >>> from quantecon.game_theory import unit_vector_game + >>> rng = np.random.default_rng(1234) + >>> g = unit_vector_game(4, random_state=rng) >>> g.players[0] - Player([[ 1., 0., 1., 0.], - [ 0., 0., 0., 1.], - [ 0., 0., 0., 0.], - [ 0., 1., 0., 0.]]) + Player([[1., 0., 0., 1.], + [0., 0., 0., 0.], + [0., 1., 1., 0.], + [0., 0., 0., 0.]]) >>> g.players[1] - Player([[ 0.19151945, 0.62210877, 0.43772774, 0.78535858], - [ 0.77997581, 0.27259261, 0.27646426, 0.80187218], - [ 0.95813935, 0.87593263, 0.35781727, 0.50099513], - [ 0.68346294, 0.71270203, 0.37025075, 0.56119619]]) + Player([[0.97669977, 0.38019574, 0.92324623, 0.26169242], + [0.31909706, 0.11809123, 0.24176629, 0.31853393], + [0.96407925, 0.2636498 , 0.44100612, 0.60987081], + [0.8636213 , 0.86375767, 0.67488131, 0.65987435]]) With `avoid_pure_nash=True`: - >>> g = unit_vector_game(4, avoid_pure_nash=True, random_state=1234) + >>> rng = np.random.default_rng(1234) + >>> g = unit_vector_game(4, avoid_pure_nash=True, random_state=rng) >>> g.players[0] - Player([[ 1., 1., 0., 0.], - [ 0., 0., 0., 0.], - [ 0., 0., 1., 1.], - [ 0., 0., 0., 0.]]) + Player([[0., 1., 0., 1.], + [0., 0., 0., 0.], + [0., 0., 0., 0.], + [1., 0., 1., 0.]]) >>> g.players[1] - Player([[ 0.19151945, 0.62210877, 0.43772774, 0.78535858], - [ 0.77997581, 0.27259261, 0.27646426, 0.80187218], - [ 0.95813935, 0.87593263, 0.35781727, 0.50099513], - [ 0.68346294, 0.71270203, 0.37025075, 0.56119619]]) + Player([[0.97669977, 0.38019574, 0.92324623, 0.26169242], + [0.31909706, 0.11809123, 0.24176629, 0.31853393], + [0.96407925, 0.2636498 , 0.44100612, 0.60987081], + [0.8636213 , 0.86375767, 0.67488131, 0.65987435]]) + >>> from quantecon.game_theory import pure_nash_brute >>> pure_nash_brute(g) [] diff --git a/quantecon/game_theory/lemke_howson.py b/quantecon/game_theory/lemke_howson.py index 3d1146ec8..a60d4023b 100644 --- a/quantecon/game_theory/lemke_howson.py +++ b/quantecon/game_theory/lemke_howson.py @@ -52,6 +52,8 @@ def lemke_howson(g, init_pivot=0, max_iter=10**6, capping=None, -------- Consider the following game from von Stengel [3]_: + >>> import numpy as np + >>> from quantecon.game_theory import NormalFormGame, lemke_howson >>> np.set_printoptions(precision=4) # Reduce the digits printed >>> bimatrix = [[(3, 3), (3, 2)], ... [(2, 2), (5, 6)], @@ -63,7 +65,7 @@ def lemke_howson(g, init_pivot=0, max_iter=10**6, capping=None, pivot: >>> lemke_howson(g, init_pivot=1) - (array([ 0. , 0.3333, 0.6667]), array([ 0.3333, 0.6667])) + (array([0. , 0.3333, 0.6667]), array([0.3333, 0.6667])) >>> g.is_nash(_) True @@ -188,6 +190,18 @@ def _lemke_howson_capping(payoff_matrices, tableaux, bases, init_pivot, Value for capping. If set equal to `max_iter`, then the routine is equivalent to the standard Lemke-Howson algorithm. + Returns + ------- + converged : bool + Whether the pivoting terminated before `max_iter` was reached. + + num_iter : scalar(int) + Total number of pivoting steps performed across the capped + executions. + + init_pivot_used : scalar(int) + Initial pivot used in the final execution. + """ m, n = tableaux[1].shape[0], tableaux[0].shape[0] init_pivot_curr = init_pivot @@ -277,12 +291,12 @@ def _initialize_tableaux(payoff_matrices, tableaux, bases): >>> bases = (np.empty(n, dtype=int), np.empty(m, dtype=int)) >>> tableaux, bases = _initialize_tableaux((A, B), tableaux, bases) >>> tableaux[0] - array([[ 3., 2., 3., 1., 0., 1.], - [ 2., 6., 1., 0., 1., 1.]]) + array([[3., 2., 3., 1., 0., 1.], + [2., 6., 1., 0., 1., 1.]]) >>> tableaux[1] - array([[ 1., 0., 0., 4., 4., 1.], - [ 0., 1., 0., 3., 6., 1.], - [ 0., 0., 1., 1., 7., 1.]]) + array([[1., 0., 0., 4., 4., 1.], + [0., 1., 0., 3., 6., 1.], + [0., 0., 1., 1., 7., 1.]]) >>> bases (array([3, 4]), array([0, 1, 2])) @@ -348,6 +362,7 @@ def _lemke_howson_tbl(tableaux, bases, init_pivot, max_iter): Examples -------- + >>> import numpy as np >>> np.set_printoptions(precision=4) # Reduce the digits printed >>> A = np.array([[3, 3], [2, 5], [0, 6]]) >>> B = np.array([[3, 2, 3], [2, 6, 1]]) diff --git a/quantecon/game_theory/logitdyn.py b/quantecon/game_theory/logitdyn.py index 5f96eb92e..6b3863e91 100644 --- a/quantecon/game_theory/logitdyn.py +++ b/quantecon/game_theory/logitdyn.py @@ -14,7 +14,7 @@ class LogitDynamics: data : NormalFormGame or array_like The game played in the logit-response dynamics model. - beta : scalar(float) + beta : scalar(float), optional(default=1.0) The level of noise in player's decision. Attributes @@ -22,8 +22,8 @@ class LogitDynamics: N : scalar(int) The number of players in the game. - players : list(Player) - The list consisting of all players with the given payoff matrix. + players : tuple(Player) + Tuple of the Player instances of the game. nums_actions : tuple(int) Tuple of the number of actions, one for each player. diff --git a/quantecon/game_theory/mclennan_tourky.py b/quantecon/game_theory/mclennan_tourky.py index bbdaf1637..128773450 100644 --- a/quantecon/game_theory/mclennan_tourky.py +++ b/quantecon/game_theory/mclennan_tourky.py @@ -32,7 +32,7 @@ def mclennan_tourky(g, init=None, epsilon=1e-3, max_iter=200, epsilon : scalar(float), optional(default=1e-3) Value of epsilon-optimality. - max_iter : scalar(int), optional(default=100) + max_iter : scalar(int), optional(default=200) Maximum number of iterations. full_output : bool, optional(default=False) @@ -56,19 +56,23 @@ def mclennan_tourky(g, init=None, epsilon=1e-3, max_iter=200, 1 yields payoff :math:`v` if no other player plays 1 and payoff 0 otherwise: + >>> import numpy as np + >>> from quantecon.game_theory import (NormalFormGame, Player, + ... mclennan_tourky) >>> N = 3 >>> v = 2 - >>> payoff_array = np.empty((2,)*n) + >>> payoff_array = np.empty((2,)*N) >>> payoff_array[0, :] = 1 >>> payoff_array[1, :] = 0 >>> payoff_array[1].flat[0] = v >>> g = NormalFormGame((Player(payoff_array),)*N) >>> print(g) 3-player NormalFormGame with payoff profile array: - [[[[ 1., 1., 1.], [ 1., 1., 2.]], - [[ 1., 2., 1.], [ 1., 0., 0.]]], - [[[ 2., 1., 1.], [ 0., 1., 0.]], - [[ 0., 0., 1.], [ 0., 0., 0.]]]] + [[[[1., 1., 1.], [1., 1., 2.]], + [[1., 2., 1.], [1., 0., 0.]]], + + [[[2., 1., 1.], [0., 1., 0.]], + [[0., 0., 1.], [0., 0., 0.]]]] This game has a unique symmetric Nash equilibrium, where the equilibrium action is given by :math:`(p^*, 1-p^*)` with :math:`p^* @@ -84,9 +88,9 @@ def mclennan_tourky(g, init=None, epsilon=1e-3, max_iter=200, >>> epsilon = 1e-5 # Value of epsilon-optimality >>> NE = mclennan_tourky(g, epsilon=epsilon) >>> print(NE[0], NE[1], NE[2], sep='\n') - [ 0.70710754 0.29289246] - [ 0.70710754 0.29289246] - [ 0.70710754 0.29289246] + [0.70710754 0.29289246] + [0.70710754 0.29289246] + [0.70710754 0.29289246] >>> g.is_nash(NE, tol=epsilon) True diff --git a/quantecon/game_theory/normal_form_game.py b/quantecon/game_theory/normal_form_game.py index 690e5bb37..e7dd16690 100644 --- a/quantecon/game_theory/normal_form_game.py +++ b/quantecon/game_theory/normal_form_game.py @@ -57,6 +57,7 @@ The first is to pass an array of payoffs for all the players: +>>> from quantecon.game_theory import NormalFormGame, Player >>> matching_pennies_bimatrix = [[(1, -1), (-1, 1)], [(-1, 1), (1, -1)]] >>> g = NormalFormGame(matching_pennies_bimatrix) >>> print(g.players[0]) @@ -85,8 +86,8 @@ >>> g = NormalFormGame((2, 2)) >>> print(g) 2-player NormalFormGame with payoff profile array: -[[[ 0., 0.], [ 0., 0.]], - [[ 0., 0.], [ 0., 0.]]] +[[[0., 0.], [0., 0.]], + [[0., 0.], [0., 0.]]] >>> g[0, 0] = 1, 1 >>> g[0, 1] = -2, 3 >>> g[1, 0] = 3, -2 @@ -506,10 +507,9 @@ def dominated_actions(self, tol=None, method=None): default to the value of the `tol` attribute. method : str, optional(default=None) - If None, `minmax` from `quantecon.optimize` is used. If - `method` is set to `'simplex'`, `'interior-point'`, or - `'revised simplex'`, then `scipy.optimize.linprog` is used - with the method as specified by `method`. + If None, `minmax` from `quantecon.optimize` is used. + Otherwise `scipy.optimize.linprog` is used with the method + as specified by `method`. Returns ------- @@ -566,6 +566,9 @@ class NormalFormGame: Array of shape (n_0, ..., n_{N-1}, N) containing the payoff profiles, where the last axis represents the players. + dtype : dtype + Data type of the elements of the payoff arrays. + """ def __init__(self, data, dtype=None): # data represents an array_like of Players @@ -799,7 +802,7 @@ def is_nash(self, action_profile, tol=None): An array of N objects, where each object must be an integer (pure action) or an array of floats (mixed action). - tol : scalar(float) + tol : scalar(float), optional(default=None) Tolerance level used in determining best responses. If None, default to each player's `tol` attribute value. @@ -909,7 +912,7 @@ def best_response_2p(payoff_matrix, opponent_mixed_action, tol=1e-8): Opponent's mixed action. Its length must be equal to `payoff_matrix.shape[1]`. - tol : scalar(float), optional(default=None) + tol : scalar(float), optional(default=1e-8) Tolerance level used in determining best responses. Returns diff --git a/quantecon/game_theory/polymatrix_game.py b/quantecon/game_theory/polymatrix_game.py index ebfeba3e1..7ec0efbc5 100644 --- a/quantecon/game_theory/polymatrix_game.py +++ b/quantecon/game_theory/polymatrix_game.py @@ -143,6 +143,20 @@ class PolymatrixGame: player number `a` is the row player and player number `b` is the column player. + Parameters + ---------- + polymatrix : dict[tuple(int), array_like(float, ndim=2)] + Maps each pair of player numbers to a matrix. The numbers of + players and actions can be inferred from this if `nums_actions` + is left None; this inference uses the number of actions each + player has against the next player. Actions with unspecified + payoff are given payoff of `-np.inf`. + + nums_actions : tuple(int), optional(default=None) + If desired, `nums_actions` can be set so that unspecified + matchups in the polymatrix will be filled with matrices of 0s + (while unspecified actions give payoff of `-np.inf`). + Attributes ---------- N : scalar(int) @@ -177,23 +191,6 @@ def __init__( ], nums_actions: Iterable[int] = None ) -> None: - """_summary_ - - Parameters - ---------- - polymatrix : Mapping[ tuple[int, int], Sequence[Sequence[float]] ] - Polymatrix. Numbers of players and actions can be - inferred from this if `nums_actions` is left None. - This inferrence uses the number of actions they have - against the next player. - Actions with unspecified payoff are given - payoff of `-np.inf`. - nums_actions : Iterable[int], optional - If desired, nums_actions can be set so that unspecified - matchups in the polymatrix will be filled with matrices - of 0s (while unspecified actions give payoff - of `-np.inf`). - """ if nums_actions is None: self.N = (isqrt(4*len(polymatrix)+1) + 1) // 2 self.nums_actions = tuple( diff --git a/quantecon/game_theory/pure_nash.py b/quantecon/game_theory/pure_nash.py index d91637133..24030c70c 100644 --- a/quantecon/game_theory/pure_nash.py +++ b/quantecon/game_theory/pure_nash.py @@ -16,7 +16,7 @@ def pure_nash_brute(g, tol=None): g : NormalFormGame tol : scalar(float), optional(default=None) Tolerance level used in determining best responses. If None, - default to the value of the `tol` attribute of `g`. + default to each player's `tol` attribute value. Returns ------- @@ -28,6 +28,7 @@ def pure_nash_brute(g, tol=None): -------- Consider the "Prisoners' Dilemma" game: + >>> from quantecon.game_theory import NormalFormGame, pure_nash_brute >>> PD_bimatrix = [[(1, 1), (-2, 3)], ... [(3, -2), (0, 0)]] >>> g_PD = NormalFormGame(PD_bimatrix) @@ -56,7 +57,7 @@ def pure_nash_brute_gen(g, tol=None): g : NormalFormGame tol : scalar(float), optional(default=None) Tolerance level used in determining best responses. If None, - default to the value of the `tol` attribute of `g`. + default to each player's `tol` attribute value. Yields ------ diff --git a/quantecon/game_theory/random.py b/quantecon/game_theory/random.py index c3260beb6..c7e5285fd 100644 --- a/quantecon/game_theory/random.py +++ b/quantecon/game_theory/random.py @@ -191,8 +191,8 @@ def _random_mixed_actions(out, random_state): random_state : np.random.RandomState or np.random.Generator - Return - ------ + Returns + ------- out : tuple(ndarray(float, ndim=1)) """ diff --git a/quantecon/game_theory/repeated_game.py b/quantecon/game_theory/repeated_game.py index 43a8c665f..43e4c2963 100644 --- a/quantecon/game_theory/repeated_game.py +++ b/quantecon/game_theory/repeated_game.py @@ -66,8 +66,9 @@ def equilibrium_payoffs(self, method=None, options=None): Returns ------- - ndarray(float, ndim=2) - Array containing the set of equilibrium payoff pairs. + hull : scipy.spatial.ConvexHull + The convex hull of the set of equilibrium payoff pairs. Its + extreme points are given by `hull.points[hull.vertices]`. Notes ----- @@ -275,11 +276,11 @@ def _R(delta, nums_actions, payoff_arrays, best_dev_gains, points, action_profile_payoff : ndarray(float, ndim=1) Array of payoff for one action profile. - extended_payoff : ndarray(float, ndim=2) + extended_payoff : ndarray(float, ndim=1) The array [payoff0, payoff1, 1] for checking if [payoff0, payoff1] is in the feasible payoff convex hull. - new_pts : ndarray(float, ndim=1) + new_pts : ndarray(float, ndim=2) The 4 by 2 array for storing the generated potential extreme points of one action profile. One action profile can only generate at most 4 points. @@ -288,7 +289,7 @@ def _R(delta, nums_actions, payoff_arrays, best_dev_gains, points, Array for storing the coordinates of the generated potential extreme points that construct a new feasible payoff convex hull. - tol: scalar(float), optional(default=1e-10) + tol : scalar(float), optional(default=1e-10) The tolerance for checking if two values are equal. Returns @@ -469,7 +470,7 @@ def _update_u(u, W): u : ndarray(float, ndim=1) The threat points. - W : ndarray(float, ndim=1) + W : ndarray(float, ndim=2) The points that construct the feasible payoff convex hull. Returns diff --git a/quantecon/game_theory/support_enumeration.py b/quantecon/game_theory/support_enumeration.py index e94c4bec6..deeb6517f 100644 --- a/quantecon/game_theory/support_enumeration.py +++ b/quantecon/game_theory/support_enumeration.py @@ -37,7 +37,10 @@ def support_enumeration(g): Examples -------- + >>> import numpy as np >>> from pprint import pprint + >>> from quantecon.game_theory import (NormalFormGame, + ... support_enumeration) >>> np.set_printoptions(precision=4) # Reduce the digits printed >>> bimatrix = [[(3, 3), (3, 2)], ... [(2, 2), (5, 6)], @@ -69,6 +72,9 @@ def support_enumeration_gen(g): Examples -------- + >>> import numpy as np + >>> from quantecon.game_theory import (NormalFormGame, + ... support_enumeration_gen) >>> np.set_printoptions(precision=4) # Reduce the digits printed >>> bimatrix = [[(3, 3), (3, 2)], ... [(2, 2), (5, 6)], diff --git a/quantecon/game_theory/vertex_enumeration.py b/quantecon/game_theory/vertex_enumeration.py index 236feea55..a811950fa 100644 --- a/quantecon/game_theory/vertex_enumeration.py +++ b/quantecon/game_theory/vertex_enumeration.py @@ -44,7 +44,10 @@ def vertex_enumeration(g, qhull_options=None): Examples -------- + >>> import numpy as np >>> from pprint import pprint + >>> from quantecon.game_theory import (NormalFormGame, + ... vertex_enumeration) >>> np.set_printoptions(precision=4) # Reduce the digits printed >>> bimatrix = [[(3, 3), (3, 2)], ... [(2, 2), (5, 6)], @@ -80,6 +83,9 @@ def vertex_enumeration_gen(g, qhull_options=None): Examples -------- + >>> import numpy as np + >>> from quantecon.game_theory import (NormalFormGame, + ... vertex_enumeration_gen) >>> np.set_printoptions(precision=4) # Reduce the digits printed >>> bimatrix = [[(3, 3), (3, 2)], ... [(2, 2), (5, 6)], @@ -320,7 +326,7 @@ def _ints_arr_to_bits(ints_arr, out): -------- >>> ints_arr = np.array([0, 1, 2], dtype=np.int32) >>> _ints_arr_to_bits(ints_arr) - 7 + np.uint64(7) >>> ints_arr2d = np.array([[0, 1, 2], [3, 0, 1]], dtype=np.int32) >>> _ints_arr_to_bits(ints_arr2d) array([ 7, 11], dtype=uint64) diff --git a/quantecon/graph_tools.py b/quantecon/graph_tools.py index dd8d0fbaa..7b7688af2 100644 --- a/quantecon/graph_tools.py +++ b/quantecon/graph_tools.py @@ -1,4 +1,5 @@ -# This file is not meant for public use and will be removed v0.8.0. +# This file is not meant for public use and will be removed in a +# future release. # Use the `quantecon` namespace for importing the objects # included below. @@ -20,7 +21,7 @@ def __getattr__(name): f"'{name}'." ) - warnings.warn(f"Please use `{name}` from the `quantecon` namespace, the" + warnings.warn(f"Please use `{name}` from the `quantecon` namespace, the " "`quantecon.graph_tools` namespace is deprecated. You can use" f" the following instead:\n `from quantecon import {name}`.", category=DeprecationWarning, stacklevel=2) diff --git a/quantecon/gridtools.py b/quantecon/gridtools.py index dede3114e..d004a2959 100644 --- a/quantecon/gridtools.py +++ b/quantecon/gridtools.py @@ -1,4 +1,5 @@ -# This file is not meant for public use and will be removed v0.8.0. +# This file is not meant for public use and will be removed in a +# future release. # Use the `quantecon` namespace for importing the objects # included below. @@ -21,7 +22,7 @@ def __getattr__(name): f"'{name}'." ) - warnings.warn(f"Please use `{name}` from the `quantecon` namespace, the" + warnings.warn(f"Please use `{name}` from the `quantecon` namespace, the " "`quantecon.gridtools` namespace is deprecated. You can use" f" the following instead:\n `from quantecon import {name}`.", category=DeprecationWarning, stacklevel=2) diff --git a/quantecon/inequality.py b/quantecon/inequality.py index 05a07a065..57ba4f484 100644 --- a/quantecon/inequality.py +++ b/quantecon/inequality.py @@ -1,4 +1,5 @@ -# This file is not meant for public use and will be removed v0.8.0. +# This file is not meant for public use and will be removed in a +# future release. # Use the `quantecon` namespace for importing the objects # included below. @@ -20,7 +21,7 @@ def __getattr__(name): f"'{name}'." ) - warnings.warn(f"Please use `{name}` from the `quantecon` namespace, the" + warnings.warn(f"Please use `{name}` from the `quantecon` namespace, the " "`quantecon.inequality` namespace is deprecated. You can use" f" the following instead:\n `from quantecon import {name}`.", category=DeprecationWarning, stacklevel=2) diff --git a/quantecon/ivp.py b/quantecon/ivp.py index 6f5ddda81..17a4dc1f9 100644 --- a/quantecon/ivp.py +++ b/quantecon/ivp.py @@ -1,4 +1,5 @@ -# This file is not meant for public use and will be removed v0.8.0. +# This file is not meant for public use and will be removed in a +# future release. # Use the `quantecon` namespace for importing the objects # included below. @@ -20,7 +21,7 @@ def __getattr__(name): f"'{name}'." ) - warnings.warn(f"Please use `{name}` from the `quantecon` namespace, the" + warnings.warn(f"Please use `{name}` from the `quantecon` namespace, the " "`quantecon.ivp` namespace is deprecated. You can use" f" the following instead:\n `from quantecon import {name}`.", category=DeprecationWarning, stacklevel=2) diff --git a/quantecon/kalman.py b/quantecon/kalman.py index 6d6a99696..7b6953109 100644 --- a/quantecon/kalman.py +++ b/quantecon/kalman.py @@ -1,4 +1,5 @@ -# This file is not meant for public use and will be removed v0.8.0. +# This file is not meant for public use and will be removed in a +# future release. # Use the `quantecon` namespace for importing the objects # included below. @@ -20,7 +21,7 @@ def __getattr__(name): f"'{name}'." ) - warnings.warn(f"Please use `{name}` from the `quantecon` namespace, the" + warnings.warn(f"Please use `{name}` from the `quantecon` namespace, the " "`quantecon.kalman` namespace is deprecated. You can use" f" the following instead:\n `from quantecon import {name}`.", category=DeprecationWarning, stacklevel=2) diff --git a/quantecon/lae.py b/quantecon/lae.py index bef3a0bcd..419c7ff6b 100644 --- a/quantecon/lae.py +++ b/quantecon/lae.py @@ -1,4 +1,5 @@ -# This file is not meant for public use and will be removed v0.8.0. +# This file is not meant for public use and will be removed in a +# future release. # Use the `quantecon` namespace for importing the objects # included below. @@ -20,7 +21,7 @@ def __getattr__(name): f"'{name}'." ) - warnings.warn(f"Please use `{name}` from the `quantecon` namespace, the" + warnings.warn(f"Please use `{name}` from the `quantecon` namespace, the " "`quantecon.lae` namespace is deprecated. You can use" f" the following instead:\n `from quantecon import {name}`.", category=DeprecationWarning, stacklevel=2) diff --git a/quantecon/lqcontrol.py b/quantecon/lqcontrol.py index d81953ec1..294a67a45 100644 --- a/quantecon/lqcontrol.py +++ b/quantecon/lqcontrol.py @@ -1,4 +1,5 @@ -# This file is not meant for public use and will be removed v0.8.0. +# This file is not meant for public use and will be removed in a +# future release. # Use the `quantecon` namespace for importing the objects # included below. diff --git a/quantecon/lqnash.py b/quantecon/lqnash.py index bc2d622f9..c4903db17 100644 --- a/quantecon/lqnash.py +++ b/quantecon/lqnash.py @@ -1,4 +1,5 @@ -# This file is not meant for public use and will be removed v0.8.0. +# This file is not meant for public use and will be removed in a +# future release. # Use the `quantecon` namespace for importing the objects # included below. @@ -20,7 +21,7 @@ def __getattr__(name): f"'{name}'." ) - warnings.warn(f"Please use `{name}` from the `quantecon` namespace, the" + warnings.warn(f"Please use `{name}` from the `quantecon` namespace, the " "`quantecon.lqnash` namespace is deprecated. You can use" f" the following instead:\n `from quantecon import {name}`.", category=DeprecationWarning, stacklevel=2) diff --git a/quantecon/lss.py b/quantecon/lss.py index a71d6c791..2f20f1a0a 100644 --- a/quantecon/lss.py +++ b/quantecon/lss.py @@ -1,4 +1,5 @@ -# This file is not meant for public use and will be removed v0.8.0. +# This file is not meant for public use and will be removed in a +# future release. # Use the `quantecon` namespace for importing the objects # included below. @@ -20,7 +21,7 @@ def __getattr__(name): f"'{name}'." ) - warnings.warn(f"Please use `{name}` from the `quantecon` namespace, the" + warnings.warn(f"Please use `{name}` from the `quantecon` namespace, the " "`quantecon.lss` namespace is deprecated. You can use" f" the following instead:\n `from quantecon import {name}`.", category=DeprecationWarning, stacklevel=2) diff --git a/quantecon/markov/_ddp_linprog_simplex.py b/quantecon/markov/_ddp_linprog_simplex.py index 5069b6667..b4698b855 100644 --- a/quantecon/markov/_ddp_linprog_simplex.py +++ b/quantecon/markov/_ddp_linprog_simplex.py @@ -10,11 +10,11 @@ def ddp_linprog_simplex(R, Q, beta, a_indices, a_indptr, sigma, max_iter=10**6, piv_options=PivOptions(), tableau=None, basis=None, v=None): r""" - Numba jit complied function to solve a discrete dynamic program via + Numba jit compiled function to solve a discrete dynamic program via linear programming, using `optimize.linprog_simplex` routines. The problem has to be represented in state-action pair form with 1-dim - reward ndarray `R` of shape (n,), 2-dim transition probability - ndarray `Q` of shapce (L, n), and disount factor `beta`, where n is + reward ndarray `R` of shape (L,), 2-dim transition probability + ndarray `Q` of shape (L, n), and discount factor `beta`, where n is the number of states and L is the number of feasible state-action pairs. @@ -52,7 +52,7 @@ def ddp_linprog_simplex(R, Q, beta, a_indices, a_indptr, sigma, Parameters ---------- R : ndarray(float, ndim=1) - Reward ndarray, of shape (n,). + Reward ndarray, of shape (L,). Q : ndarray(float, ndim=2) Transition probability ndarray, of shape (L, n). @@ -143,7 +143,7 @@ def _initialize_tableau(R, Q, beta, a_indptr, tableau): Parameters ---------- R : ndarray(float, ndim=1) - Reward ndarray, of shape (n,). + Reward ndarray, of shape (L,). Q : ndarray(float, ndim=2) Transition probability ndarray, of shape (L, n). diff --git a/quantecon/markov/approximation.py b/quantecon/markov/approximation.py index 43f790273..51f91b764 100644 --- a/quantecon/markov/approximation.py +++ b/quantecon/markov/approximation.py @@ -19,7 +19,7 @@ def rouwenhorst(n, rho, sigma, mu=0.): r""" - Takes as inputs n, mu, sigma, rho. It will then construct a markov chain + Takes as inputs n, rho, sigma, mu. It will then construct a markov chain that estimates an AR(1) process of: :math:`y_t = \mu + \rho y_{t-1} + \varepsilon_t` where :math:`\varepsilon_t` is i.i.d. normal of mean 0, std dev of sigma @@ -363,6 +363,8 @@ def discrete_var(A, Schmitt-Grohé and Martín Uribe, Journal of Political Economy 124, October 2016, 1466-1514. + >>> import numpy as np + >>> import scipy.linalg, scipy.stats >>> rng = np.random.default_rng(12345) >>> A = [[0.7901, -1.3570], ... [-0.0104, 0.8638]] @@ -421,17 +423,8 @@ def discrete_var(A, [-0.34700776, 0.04310197], [-0.34700776, 0.05387746], [-0.30845134, 0. ]]) - >>> mc.simulate(10, random_state=rng) - array([[-0.03855642, -0.02155098], - [ 0.03855642, -0.03232648], - [ 0.07711283, -0.03232648], - [ 0.15422567, -0.03232648], - [ 0.15422567, -0.04310197], - [ 0.15422567, -0.03232648], - [ 0.15422567, -0.03232648], - [ 0.2313385 , -0.04310197], - [ 0.2313385 , -0.03232648], - [ 0.26989492, -0.03232648]]) + >>> mc.simulate(10, random_state=rng).shape + (10, 2) """ A = np.asarray(A) C = np.asarray(C) diff --git a/quantecon/markov/core.py b/quantecon/markov/core.py index ec844a4e3..e2f838fce 100644 --- a/quantecon/markov/core.py +++ b/quantecon/markov/core.py @@ -112,9 +112,16 @@ class MarkovChain: P : ndarray or scipy.sparse.csr_matrix (float, ndim=2) See Parameters + n : int + Number of states. + state_values : array_like or None Array of state values if set, None otherwise. + is_sparse : bool + Whether the transition matrix `P` is stored in sparse + (scipy.sparse.csr_matrix) form. + digraph : DiGraph Directed graph representation of the Markov chain with nodes as states and edges as positive transition probabilities. @@ -479,8 +486,8 @@ def simulate_indices(self, ts_length, init=None, num_reps=None, Returns ------- X : ndarray(ndim=1 or 2) - Array containing the state values of the sample path(s). See - the `simulate` method for more information. + Array containing the state indices of the sample path(s). + See the `simulate` method for the shape conventions. """ random_state = check_random_state(random_state) @@ -619,7 +626,7 @@ def _generate_sample_paths(P_cdfs, init_states, random_values, out): Notes ----- - This routine is jit-complied by Numba. + This routine is jit-compiled by Numba. """ num_reps, ts_length = out.shape @@ -672,7 +679,7 @@ def _generate_sample_paths_sparse(P_cdfs1d, indices, indptr, init_states, Notes ----- - This routine is jit-complied by Numba. + This routine is jit-compiled by Numba. """ num_reps, ts_length = out.shape @@ -699,6 +706,11 @@ def mc_compute_stationary(P): class. Any stationary distribution is written as a convex combination of these distributions. + Parameters + ---------- + P : array_like or scipy sparse matrix (float, ndim=2) + A Markov transition matrix, of shape n x n. + Returns ------- stationary_dists : array_like(float, ndim=2) diff --git a/quantecon/markov/ddp.py b/quantecon/markov/ddp.py index fb0f035d4..e65d03cac 100644 --- a/quantecon/markov/ddp.py +++ b/quantecon/markov/ddp.py @@ -756,7 +756,10 @@ def solve(self, method='policy_iteration', max_iter : scalar(int), optional(default=None) Maximum number of iterations. If None, the value stored in - the attribute `max_iter` is used. + the attribute `max_iter` is used, except for + `method='linear_programming'`, where the attribute + `max_iter` times the number of states is used (the iteration + count there refers to simplex pivoting steps). k : scalar(int), optional(default=20) Number of iterations for partial policy evaluation in @@ -937,6 +940,12 @@ def midrange(z): return res def linprog_simplex(self, v_init=None, max_iter=None): + """ + Solve the optimization problem by linear programming. See the + `solve` method. Not implemented for the sparse formulation; if + `max_iter` is None, `self.max_iter * self.num_states` is used. + + """ if self.beta == 1: raise NotImplementedError(self._error_msg_no_discounting) @@ -1018,6 +1027,10 @@ class DPSolveResult(dict): max_iter : int Maximum number of iterations + k : int + Number of iterations for partial policy evaluation (modified + policy iteration only) + """ # This is sourced from sicpy.optimize.OptimizeResult. def __getattr__(self, name): diff --git a/quantecon/markov/estimate.py b/quantecon/markov/estimate.py index df65c4979..d75e7a7d8 100644 --- a/quantecon/markov/estimate.py +++ b/quantecon/markov/estimate.py @@ -65,16 +65,29 @@ def fit_discrete_mc(X, grids, order='C'): Parameters ---------- - X: array_like(ndim=2) + X : array_like(ndim=2) Time-series such that the t-th row is :math:`x_t`. It should be of the shape T x n, where n is the number of dimensions. - grids: array_like(array_like(ndim=1)) + grids : array_like(array_like(ndim=1)) Array of `n` sorted arrays. Set of grid points in each dimension + order : str, optional(default='C') + ('C' or 'F') order in which the states in the cartesian grid are + enumerated. + + Returns + ------- + + mc : MarkovChain + An instance of the MarkovChain class constructed after discretization + onto the grid. + Examples -------- + >>> import numpy as np + >>> from quantecon.markov import fit_discrete_mc >>> grids = (np.arange(3), np.arange(2)) >>> X = [(-0.1, 1.2), (2, 0), (0.6, 0.4), (1.0, 0.1)] >>> mc = fit_discrete_mc(X, grids) @@ -86,13 +99,6 @@ def fit_discrete_mc(X, grids, order='C'): array([[0., 0., 1.], [0., 1., 0.], [0., 1., 0.]]) - - Returns - ------- - - mc: MarkovChain - An instance of the MarkovChain class constructed after discretization - onto the grid. """ X_indices = cartesian_nearest_index(X, grids, order=order) mc = estimate_mc(X_indices) diff --git a/quantecon/markov/gth_solve.py b/quantecon/markov/gth_solve.py index 005ffb033..c3ca544ac 100644 --- a/quantecon/markov/gth_solve.py +++ b/quantecon/markov/gth_solve.py @@ -102,7 +102,7 @@ def gth_solve(A, overwrite=False, use_jit=True): @jit(nopython=True) def _gth_solve_jit(A, out): """ - JIT complied version of the main routine of gth_solve. + JIT-compiled version of the main routine of gth_solve. Parameters ---------- diff --git a/quantecon/markov/random.py b/quantecon/markov/random.py index 5ac78090c..db09d8260 100644 --- a/quantecon/markov/random.py +++ b/quantecon/markov/random.py @@ -42,16 +42,20 @@ def random_markov_chain(n, k=None, sparse=False, random_state=None): Examples -------- - >>> mc = qe.markov.random_markov_chain(3, random_state=1234) + >>> import numpy as np + >>> import quantecon as qe + >>> rng = np.random.default_rng(1234) + >>> mc = qe.markov.random_markov_chain(3, random_state=rng) >>> mc.P - array([[ 0.19151945, 0.43058932, 0.37789123], - [ 0.43772774, 0.34763084, 0.21464142], - [ 0.27259261, 0.5073832 , 0.22002419]]) - >>> mc = qe.markov.random_markov_chain(3, k=2, random_state=1234) + array([[0.38019574, 0.59650403, 0.02330023], + [0.26169242, 0.66155381, 0.07675377], + [0.11809123, 0.20100583, 0.68090294]]) + >>> rng = np.random.default_rng(1234) + >>> mc = qe.markov.random_markov_chain(3, k=2, random_state=rng) >>> mc.P - array([[ 0.19151945, 0.80848055, 0. ], - [ 0. , 0.62210877, 0.37789123], - [ 0.56227226, 0. , 0.43772774]]) + array([[0.97669977, 0. , 0.02330023], + [0.38019574, 0. , 0.61980426], + [0.92324623, 0.07675377, 0. ]]) """ P = random_stochastic_matrix(n, k, sparse, format='csr', diff --git a/quantecon/markov/utilities.py b/quantecon/markov/utilities.py index f0adaf3b2..4f13c3e33 100644 --- a/quantecon/markov/utilities.py +++ b/quantecon/markov/utilities.py @@ -30,6 +30,7 @@ def sa_indices(num_states, num_actions): Examples -------- + >>> import quantecon as qe >>> s_indices, a_indices = qe.markov.sa_indices(4, 3) >>> s_indices array([0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3]) diff --git a/quantecon/matrix_eqn.py b/quantecon/matrix_eqn.py index 67f7e40d7..4467d0ce8 100644 --- a/quantecon/matrix_eqn.py +++ b/quantecon/matrix_eqn.py @@ -1,4 +1,5 @@ -# This file is not meant for public use and will be removed v0.8.0. +# This file is not meant for public use and will be removed in a +# future release. # Use the `quantecon` namespace for importing the objects # included below. @@ -21,7 +22,7 @@ def __getattr__(name): f"'{name}'." ) - warnings.warn(f"Please use `{name}` from the `quantecon` namespace, the" + warnings.warn(f"Please use `{name}` from the `quantecon` namespace, the " "`quantecon.matrix_eqn` namespace is deprecated. You can use" f" the following instead:\n `from quantecon import {name}`.", category=DeprecationWarning, stacklevel=2) diff --git a/quantecon/optimize/lcp_lemke.py b/quantecon/optimize/lcp_lemke.py index f1c7567bd..41feaf780 100644 --- a/quantecon/optimize/lcp_lemke.py +++ b/quantecon/optimize/lcp_lemke.py @@ -108,7 +108,7 @@ def lcp_lemke(M, q, d=None, max_iter=10**6, piv_options=PivOptions(), >>> w array([0., 4., 2.]) >>> res.z @ w - 0.0 + np.float64(0.0) References ---------- diff --git a/quantecon/optimize/linprog_simplex.py b/quantecon/optimize/linprog_simplex.py index 823f26ab5..3cab99ec1 100644 --- a/quantecon/optimize/linprog_simplex.py +++ b/quantecon/optimize/linprog_simplex.py @@ -562,13 +562,7 @@ def solve_phase_1(tableau, basis, max_iter=10**6, piv_options=PivOptions()): Perform the simplex algorithm for Phase 1 on a given tableau in canonical form, by calling `solve_tableau` with `skip_aux=False`. - Parameters - ---------- - See `solve_tableau`. - - Returns - ------- - See `solve_tableau`. + For the parameters and the return values, see `solve_tableau`. """ L = tableau.shape[0] - 1 @@ -607,7 +601,7 @@ def _pivot_col(tableau, skip_aux, piv_options): containing the maximum positive element in the last row of the tableau. - `skip_aux` should be True in phase 1, and False in phase 2. + `skip_aux` should be False in phase 1, and True in phase 2. Parameters ---------- @@ -618,7 +612,7 @@ def _pivot_col(tableau, skip_aux, piv_options): Whether to skip the coefficients of the auxiliary (or artificial) variables in pivot column selection. - piv_options : PivOptions, optional + piv_options : PivOptions PivOptions namedtuple to set the tolerance values. Returns @@ -673,7 +667,7 @@ def get_solution(tableau, basis, x, lambd, b_signs): b_signs : ndarray(bool, ndim=1) ndarray of shape (L,) whose i-th element is True iff the i-th - element of the vector (b_ub, b_eq) is positive. + element of the vector (b_ub, b_eq) is nonnegative. Returns ------- diff --git a/quantecon/optimize/nelder_mead.py b/quantecon/optimize/nelder_mead.py index e4f77e414..908f242e7 100644 --- a/quantecon/optimize/nelder_mead.py +++ b/quantecon/optimize/nelder_mead.py @@ -34,7 +34,7 @@ def nelder_mead(fun, x0, bounds=np.array([[], []]).T, args=(), tol_f=1e-10, Initial guess. Array of real elements of size (n,), where ‘n’ is the number of independent variables. - bounds: ndarray(float, ndim=2), optional + bounds : ndarray(float, ndim=2), optional Bounds for each variable for proposed solution, encoded as a sequence of (min, max) pairs for each element in x. The default option is used to specify no bounds on x. @@ -59,23 +59,34 @@ def nelder_mead(fun, x0, bounds=np.array([[], []]).T, args=(), tol_f=1e-10, "x" : Approximate local maximizer "fun" : Approximate local maximum value - "success" : 1 if the algorithm successfully terminated, 0 otherwise + "success" : True if the algorithm successfully terminated, + False otherwise "nit" : Number of iterations "final_simplex" : Vertices of the final simplex Examples -------- + >>> import numpy as np + >>> import quantecon as qe + >>> from numba import njit >>> @njit ... def rosenbrock(x): ... return -(100 * (x[1] - x[0] ** 2) ** 2 + (1 - x[0])**2) ... >>> x0 = np.array([-2, 1]) - >>> qe.optimize.nelder_mead(rosenbrock, x0) - results(x=array([0.99999814, 0.99999756]), fun=-1.6936258239463265e-10, - success=True, nit=110, - final_simplex=array([[0.99998652, 0.9999727], - [1.00000218, 1.00000301], - [0.99999814, 0.99999756]])) + >>> res = qe.optimize.nelder_mead(rosenbrock, x0) + >>> res.x + array([0.99999814, 0.99999756]) + >>> res.fun + -1.6936258239463265e-10 + >>> res.success + True + >>> res.nit + 110 + >>> res.final_simplex + array([[0.99998652, 0.9999727 ], + [1.00000218, 1.00000301], + [0.99999814, 0.99999756]]) Notes ----- @@ -145,6 +156,11 @@ def _nelder_mead_algorithm(fun, vertices, bounds=np.array([[], []]).T, vertices : ndarray(float, ndim=2) Initial simplex with shape (n+1, n) to be modified in-place. + bounds : ndarray(float, ndim=2), optional + Bounds for each variable for proposed solution, encoded as a sequence + of (min, max) pairs for each element in x. The default option is used + to specify no bounds on x. + args : tuple, optional Extra arguments passed to the objective function. @@ -160,10 +176,10 @@ def _nelder_mead_algorithm(fun, vertices, bounds=np.array([[], []]).T, σ : scalar(float), optional(default=0.5) Shrinkage parameter. Must be strictly between 0 and 1. - tol_f : scalar(float), optional(default=1e-10) + tol_f : scalar(float), optional(default=1e-8) Tolerance to be used for the function value convergence test. - tol_x : scalar(float), optional(default=1e-10) + tol_x : scalar(float), optional(default=1e-8) Tolerance to be used for the function domain convergence test. max_iter : scalar(float), optional(default=1000) @@ -177,7 +193,7 @@ def _nelder_mead_algorithm(fun, vertices, bounds=np.array([[], []]).T, "x" : Approximate solution "fun" : Approximate local maximum - "success" : 1 if successfully terminated, 0 otherwise + "success" : True if successfully terminated, False otherwise "nit" : Number of iterations "final_simplex" : The vertices of the final simplex @@ -308,7 +324,7 @@ def _initialize_simplex(x0, bounds): Initial guess. Array of real elements of size (n,), where ‘n’ is the number of independent variables. - bounds: ndarray(float, ndim=2) + bounds : ndarray(float, ndim=2) Sequence of (min, max) pairs for each element in x0. Returns @@ -357,7 +373,7 @@ def _check_params(ρ, χ, γ, σ, bounds, n): σ : scalar(float) Shrinkage parameter. Must be strictly between 0 and 1. - bounds: ndarray(float, ndim=2) + bounds : ndarray(float, ndim=2) Sequence of (min, max) pairs for each element in x. n : scalar(int) @@ -392,7 +408,7 @@ def _check_bounds(x, bounds): x : ndarray(float, ndim=1) 1-D array with shape (n,) of independent variables. - bounds: ndarray(float, ndim=2) + bounds : ndarray(float, ndim=2) Sequence of (min, max) pairs for each element in x. Returns @@ -423,7 +439,7 @@ def _neg_bounded_fun(fun, bounds, x, args=()): fixed parameters needed to completely specify the function. This function must be JIT-compiled in `nopython` mode using Numba. - bounds: ndarray(float, ndim=2) + bounds : ndarray(float, ndim=2) Sequence of (min, max) pairs for each element in x. x : ndarray(float, ndim=1) diff --git a/quantecon/optimize/scalar_maximization.py b/quantecon/optimize/scalar_maximization.py index 0b398b1fc..d14691389 100644 --- a/quantecon/optimize/scalar_maximization.py +++ b/quantecon/optimize/scalar_maximization.py @@ -14,16 +14,18 @@ def brent_max(func, a, b, args=(), xtol=1e-5, maxiter=500): Parameters ---------- func : jitted function + The objective function to be maximized, of the form + `func(x, *args) -> float`. Must be jitted via Numba. a : scalar Lower bound for search b : scalar Upper bound for search args : tuple, optional Extra arguments passed to the objective function. - maxiter : int, optional - Maximum number of iterations to perform. - xtol : float, optional + xtol : float, optional(default=1e-5) Absolute error in solution `xopt` acceptable for convergence. + maxiter : int, optional(default=500) + Maximum number of iterations to perform. Returns ------- diff --git a/quantecon/optimize/tests/test_linprog_simplex.py b/quantecon/optimize/tests/test_linprog_simplex.py index 50bc92576..35444211b 100644 --- a/quantecon/optimize/tests/test_linprog_simplex.py +++ b/quantecon/optimize/tests/test_linprog_simplex.py @@ -90,7 +90,7 @@ def test_nontrivial_problem(self): def test_network_flow(self): # A network flow problem with supply and demand at nodes # and with costs along directed edges. - # https://www.princeton.edu/~rvdb/542/lectures/lec10.pdf + # https://vanderbei.princeton.edu/542/lectures/lec10.pdf c = np.array([2, 4, 9, 11, 4, 3, 8, 7, 0, 15, 16, 18]) * (-1) n, p = -1, 1 A_eq = [ diff --git a/quantecon/quad.py b/quantecon/quad.py index 98641b828..be4e7e564 100644 --- a/quantecon/quad.py +++ b/quantecon/quad.py @@ -68,7 +68,7 @@ def fix(x): def qnwcheb(n, a=1, b=1): """ - Computes multivariate Guass-Checbychev quadrature nodes and weights. + Computes multivariate Gauss-Chebyshev quadrature nodes and weights. Parameters ---------- @@ -137,7 +137,9 @@ def qnwequi(n, a, b, kind="N", equidist_pp=None, random_state=None): - R - pseudo Random equidist_pp : array_like, optional(default=None) - TODO: I don't know what this does + Array of generators used by the Weyl ('W') and Haber ('H') + sequences. If None, defaults to the square roots of the primes + below 7920. Only the first d entries are used. random_state : int or np.random.RandomState/Generator, optional Random seed (integer) or np.random.RandomState or Generator @@ -212,7 +214,7 @@ def qnwequi(n, a, b, kind="N", equidist_pp=None, random_state=None): def qnwlege(n, a, b): """ - Computes multivariate Guass-Legendre quadrature nodes and weights. + Computes multivariate Gauss-Legendre quadrature nodes and weights. Parameters ---------- @@ -590,11 +592,10 @@ def qnwbeta(n, a=1.0, b=1.0): A length-d iterable of the number of nodes in each dimension a : scalar or array_like(float), optional(default=1.0) - A length-d + A length-d iterable of first Beta distribution parameters - b : array_like(float), optional(default=1.0) - A d x d array representing the variance-covariance matrix of the - multivariate normal distribution. + b : scalar or array_like(float), optional(default=1.0) + A length-d iterable of second Beta distribution parameters Returns ------- @@ -687,8 +688,11 @@ def _make_multidim_func(one_d_func, n, *args): Returns ------- - func : function - The multi-dimensional version of the parameter ``one_d_func`` + nodes : np.ndarray(dtype=float) + Quadrature nodes + + weights : np.ndarray(dtype=float) + Weights for quadrature nodes """ @@ -723,7 +727,7 @@ def _make_multidim_func(one_d_func, n, *args): @jit(nopython=True) def _qnwcheb1(n, a, b): """ - Compute univariate Guass-Checbychev quadrature nodes and weights + Compute univariate Gauss-Chebyshev quadrature nodes and weights Parameters ---------- @@ -741,7 +745,7 @@ def _qnwcheb1(n, a, b): nodes : np.ndarray(dtype=float) An n element array of nodes - nodes : np.ndarray(dtype=float) + weights : np.ndarray(dtype=float) An n element array of weights Notes @@ -772,7 +776,7 @@ def _qnwcheb1(n, a, b): @jit(nopython=True) def _qnwlege1(n, a, b): """ - Compute univariate Guass-Legendre quadrature nodes and weights + Compute univariate Gauss-Legendre quadrature nodes and weights Parameters ---------- @@ -790,7 +794,7 @@ def _qnwlege1(n, a, b): nodes : np.ndarray(dtype=float) An n element array of nodes - nodes : np.ndarray(dtype=float) + weights : np.ndarray(dtype=float) An n element array of weights Notes @@ -862,7 +866,7 @@ def _qnwnorm1(n): nodes : np.ndarray(dtype=float) An n element array of nodes - nodes : np.ndarray(dtype=float) + weights : np.ndarray(dtype=float) An n element array of weights Notes @@ -946,7 +950,7 @@ def _qnwsimp1(n, a, b): nodes : np.ndarray(dtype=float) An n element array of nodes - nodes : np.ndarray(dtype=float) + weights : np.ndarray(dtype=float) An n element array of weights Notes @@ -995,7 +999,7 @@ def _qnwtrap1(n, a, b): nodes : np.ndarray(dtype=float) An n element array of nodes - nodes : np.ndarray(dtype=float) + weights : np.ndarray(dtype=float) An n element array of weights Notes diff --git a/quantecon/quadsums.py b/quantecon/quadsums.py index 30b8998b0..728d3bbb1 100644 --- a/quantecon/quadsums.py +++ b/quantecon/quadsums.py @@ -1,4 +1,5 @@ -# This file is not meant for public use and will be removed v0.8.0. +# This file is not meant for public use and will be removed in a +# future release. # Use the `quantecon` namespace for importing the objects # included below. @@ -20,7 +21,7 @@ def __getattr__(name): f"'{name}'." ) - warnings.warn(f"Please use `{name}` from the `quantecon` namespace, the" + warnings.warn(f"Please use `{name}` from the `quantecon` namespace, the " "`quantecon.quadsums` namespace is deprecated. You can use" f" the following instead:\n `from quantecon import {name}`.", category=DeprecationWarning, stacklevel=2) diff --git a/quantecon/random/__init__.py b/quantecon/random/__init__.py index 89ac4fb45..ad5f72235 100644 --- a/quantecon/random/__init__.py +++ b/quantecon/random/__init__.py @@ -8,6 +8,7 @@ Utilities to Support Generation of Random Arrays or Matrices 1. probvec 2. sample_without_replacement +3. draw .. Future Work ----------- diff --git a/quantecon/random/utilities.py b/quantecon/random/utilities.py index bd309b065..8899a8ce0 100644 --- a/quantecon/random/utilities.py +++ b/quantecon/random/utilities.py @@ -41,9 +41,12 @@ def probvec(m, k, random_state=None, parallel=True): Examples -------- - >>> qe.random.probvec(2, 3, random_state=1234) - array([[ 0.19151945, 0.43058932, 0.37789123], - [ 0.43772774, 0.34763084, 0.21464142]]) + >>> import numpy as np + >>> import quantecon as qe + >>> rng = np.random.default_rng(1234) + >>> qe.random.probvec(2, 3, random_state=rng) + array([[0.38019574, 0.59650403, 0.02330023], + [0.26169242, 0.66155381, 0.07675377]]) """ if k == 1: @@ -67,7 +70,7 @@ def _probvec(r, out): # pragma: no cover """ Fill `out` with randomly sampled probability vectors as rows. - To be complied as a ufunc by guvectorize of Numba. The inputs must + To be compiled as a ufunc by guvectorize of Numba. The inputs must have the same shape except the last axis; the length of the last axis of `r` must be that of `out` minus 1, i.e., if out.shape[-1] is k, then r.shape[-1] must be k-1. @@ -128,14 +131,18 @@ def sample_without_replacement(n, k, num_trials=None, random_state=None): Examples -------- - >>> qe.random.sample_without_replacement(5, 3, random_state=1234) - array([0, 2, 1]) + >>> import numpy as np + >>> import quantecon as qe + >>> rng = np.random.default_rng(1234) + >>> qe.random.sample_without_replacement(5, 3, random_state=rng) + array([4, 1, 2]) + >>> rng = np.random.default_rng(1234) >>> qe.random.sample_without_replacement(5, 3, num_trials=4, - ... random_state=1234) - array([[0, 2, 1], - [3, 4, 0], - [1, 3, 2], - [4, 1, 3]]) + ... random_state=rng) + array([[4, 1, 2], + [1, 4, 0], + [1, 4, 2], + [1, 4, 3]]) """ if n <= 0: @@ -155,7 +162,7 @@ def sample_without_replacement(n, k, num_trials=None, random_state=None): @guvectorize(['(i8, f8[:], i8[:])'], '(),(k)->(k)', nopython=True, cache=True) def _sample_without_replacement(n, r, out): """ - Main body of `sample_without_replacement`. To be complied as a ufunc + Main body of `sample_without_replacement`. To be compiled as a ufunc by guvectorize of Numba. """ @@ -173,7 +180,9 @@ def _sample_without_replacement(n, r, out): def draw(cdf, size=None): """ Generate a random sample according to the cumulative distribution - given by `cdf`. Jit-complied by Numba in nopython mode. + given by `cdf`. Pure Python implementation; a Numba nopython-mode + implementation is registered via `numba.extending.overload`, so + calls from within jitted functions are compiled. Parameters ---------- @@ -189,13 +198,24 @@ def draw(cdf, size=None): ------- scalar(int) or ndarray(int, ndim=1) + Notes + ----- + `draw` takes no `random_state` argument. Called from Python it draws + from NumPy's legacy global random state, so `np.random.seed` makes it + reproducible. Called from within a jitted function it draws from + Numba's own internal random state, which is independent of NumPy's and + must be seeded by calling `np.random.seed` inside the jitted function. + Examples -------- + >>> import numpy as np + >>> import quantecon as qe >>> cdf = np.cumsum([0.4, 0.6]) - >>> qe.random.draw(cdf) - 1 + >>> np.random.seed(1234) >>> qe.random.draw(cdf, 10) - array([1, 0, 1, 0, 1, 0, 0, 0, 1, 0]) + array([0, 1, 1, 1, 1, 0, 0, 1, 1, 1]) + >>> qe.random.draw(cdf) + np.int64(0) """ if isinstance(size, int): diff --git a/quantecon/rank_nullspace.py b/quantecon/rank_nullspace.py index 798e15974..e240f60f3 100644 --- a/quantecon/rank_nullspace.py +++ b/quantecon/rank_nullspace.py @@ -1,4 +1,5 @@ -# This file is not meant for public use and will be removed v0.8.0. +# This file is not meant for public use and will be removed in a +# future release. # Use the `quantecon` namespace for importing the objects # included below. @@ -20,9 +21,10 @@ def __getattr__(name): f" '{name}'." ) - warnings.warn(f"Please use `{name}` from the `quantecon` namespace, the" + warnings.warn(f"Please use `{name}` from the `quantecon` namespace, the " "`quantecon.rank_nullspace` namespace is deprecated. You can" - f" use following instead:\n `from quantecon import {name}`.", + " use the following instead:\n" + f" `from quantecon import {name}`.", category=DeprecationWarning, stacklevel=2) return getattr(_rank_nullspace, name) diff --git a/quantecon/robustlq.py b/quantecon/robustlq.py index 3def5aac8..ee4631a5e 100644 --- a/quantecon/robustlq.py +++ b/quantecon/robustlq.py @@ -1,4 +1,5 @@ -# This file is not meant for public use and will be removed v0.8.0. +# This file is not meant for public use and will be removed in a +# future release. # Use the `quantecon` namespace for importing the objects # included below. @@ -20,7 +21,7 @@ def __getattr__(name): f"'{name}'." ) - warnings.warn(f"Please use `{name}` from the `quantecon` namespace, the" + warnings.warn(f"Please use `{name}` from the `quantecon` namespace, the " "`quantecon.robustlq` namespace is deprecated. You can use" f" the following instead:\n `from quantecon import {name}`.", category=DeprecationWarning, stacklevel=2) diff --git a/quantecon/tests/test_inequality.py b/quantecon/tests/test_inequality.py index be682396b..dccb9a27d 100644 --- a/quantecon/tests/test_inequality.py +++ b/quantecon/tests/test_inequality.py @@ -72,8 +72,8 @@ def test_shorrocks_index(): """ Test Shorrocks mobility index function against the example used in 'Wealth distribution and social mobility in the US: A quantitative approach' - (Benhabib, Bisin, Luo, 2017).'' - https://www.econ.nyu.edu/user/bisina/RevisionAugust.pdf + (Benhabib, Bisin, Luo, 2019). + https://www.aeaweb.org/articles?id=10.1257/aer.20151684 """ # Construct the mobility matrix from Benhabib et al. diff --git a/quantecon/tests/util.py b/quantecon/tests/util.py index 4f6ea0519..5f2dfb29a 100644 --- a/quantecon/tests/util.py +++ b/quantecon/tests/util.py @@ -21,8 +21,7 @@ def capture(command, *args, **kwargs): References ---------- - http://schinckel.net/2013/04/15/capture-and-test-sys.stdout-sys. - stderr-in-unittest.testcase/ + https://schinckel.net/2013/04/15/capture-and-test-sys.stdout-sys.stderr-in-unittest.testcase/ Examples -------- diff --git a/quantecon/timings/timings.py b/quantecon/timings/timings.py index edbd3c9a4..5e8a8c478 100644 --- a/quantecon/timings/timings.py +++ b/quantecon/timings/timings.py @@ -30,6 +30,7 @@ def float_precision(precision=None): >>> import quantecon as qe >>> current = qe.timings.float_precision() >>> print(f"Current precision: {current}") + Current precision: 4 Set new precision: >>> qe.timings.float_precision(6) diff --git a/quantecon/util/array.py b/quantecon/util/array.py index 4200948fd..f325bb812 100644 --- a/quantecon/util/array.py +++ b/quantecon/util/array.py @@ -19,12 +19,12 @@ def searchsorted(a, v): """ Custom version of np.searchsorted. Return the largest index `i` such - that `a[i-1] <= v < a[i]` (for `i = 0`, `v < a[0]`); if `v[n-1] <= + that `a[i-1] <= v < a[i]` (for `i = 0`, `v < a[0]`); if `a[n-1] <= v`, return `n`, where `n = len(a)`. - .. deprecated:: + .. deprecated:: 0.11.0 - Deprecated, use `np.searchsorted(a, v, side='right')` instead. + Use `np.searchsorted(a, v, side='right')` instead. Parameters ---------- @@ -42,11 +42,12 @@ def searchsorted(a, v): Notes ----- - This routine is jit-complied if the module Numba is vailable; if - not, it is an alias of np.searchsorted(a, v, side='right'). + This routine is jit-compiled by Numba in nopython mode. Examples -------- + >>> import numpy as np + >>> from quantecon.util import searchsorted >>> a = np.array([0.2, 0.4, 1.0]) >>> searchsorted(a, 0.1) 0 diff --git a/quantecon/util/combinatorics.py b/quantecon/util/combinatorics.py index 9bb281d23..9bc92ef40 100644 --- a/quantecon/util/combinatorics.py +++ b/quantecon/util/combinatorics.py @@ -30,6 +30,8 @@ def next_k_array(a): -------- Enumerate all the subsets with k elements of the set {0, ..., n-1}. + >>> import numpy as np + >>> from quantecon.util.combinatorics import next_k_array >>> n, k = 4, 2 >>> a = np.arange(k) >>> while a[-1] < n: diff --git a/quantecon/util/common_messages.py b/quantecon/util/common_messages.py deleted file mode 100644 index 9b18dd091..000000000 --- a/quantecon/util/common_messages.py +++ /dev/null @@ -1,13 +0,0 @@ -""" -Warnings Module -=============== - -Contains a collection of warning messages for consistent package wide notifications - -""" - -#-Numba-# -numba_import_fail_message = ("Numba import failed. Falling back to non-optimized routines.\n" - "This will reduce the overall performance of this package.\n" - "To install please use the anaconda distribution.\n" - "http://continuum.io/downloads") diff --git a/quantecon/util/notebooks.py b/quantecon/util/notebooks.py index 5f73a654b..37b0f1bf4 100644 --- a/quantecon/util/notebooks.py +++ b/quantecon/util/notebooks.py @@ -35,16 +35,28 @@ def fetch_nb_dependencies(files, repo=REPO, raw=RAW, branch=BRANCH, folder=FOLDE Parameters ---------- - file_list list or dict - A list of files to specify a collection of filenames - A dict of dir : list(files) to specify a directory - repo str, optional(default=REPO) - raw str, optional(default=RAW) - This is here in case github changes access to their raw files through web links - branch str, optional(default=BRANCH) - folder str, optional(default=FOLDER) - overwrite bool, optional(default=False) - verbose bool, optional(default=True) + files : list or dict + A list of files to specify a collection of filenames, or a dict of + dir : list(files) to specify a directory + repo : str, optional(default=REPO) + The Github repository to fetch the files from + raw : str, optional(default=RAW) + This is here in case github changes access to their raw files through + web links + branch : str, optional(default=BRANCH) + The branch of the repository to fetch the files from + folder : str, optional(default=FOLDER) + The folder in the repository that contains the requested files + overwrite : bool, optional(default=False) + If True, overwrite a local copy of the file if one is present + verbose : bool, optional(default=True) + If True, then print a status message for each requested file + + Returns + ------- + status : list of bool + One entry per requested file: True if the file was downloaded, and + False if a local copy was found and the download was skipped Examples -------- @@ -62,7 +74,7 @@ def fetch_nb_dependencies(files, repo=REPO, raw=RAW, branch=BRANCH, folder=FOLDE A folder location can be added using ``folder=`` - >>> status = fetch_nb_dependencies("test.csv", report="https://", folder="data") + >>> status = fetch_nb_dependencies(["test.csv"], repo="https://", folder="data") You can also specify a specific branch using ``branch=`` keyword argument. diff --git a/quantecon/util/timing.py b/quantecon/util/timing.py index 5ff9c8dce..f415e23c8 100644 --- a/quantecon/util/timing.py +++ b/quantecon/util/timing.py @@ -206,18 +206,21 @@ class Timer: Examples -------- + The timing values shown below are illustrative only; actual values + vary with machine and load. + Basic usage: >>> with Timer(): ... # some code ... pass 0.0000 seconds elapsed - + With custom message and precision: >>> with Timer("Computing results", precision=6): - ... # some code + ... # some code ... pass Computing results: 0.000001 seconds elapsed - + Store elapsed time for comparison: >>> timer = Timer(verbose=False) >>> with timer: @@ -296,7 +299,12 @@ def timeit(func, runs=1, stats_only=False, verbose=True, results=False, **timer_ results : bool, optional(default=False) If True, return dictionary with timing results. If False, return None. **timer_kwargs - Keyword arguments to pass to Timer (message, precision, unit, verbose). + Additional keyword arguments controlling output formatting: + `precision` (int) and `unit` (str). A `message` argument is + accepted for signature compatibility with `Timer` but is not shown + in `timeit` output. `verbose` is a parameter of `timeit` itself + (see above) and is not forwarded; the internal `Timer` instances + are always silenced. Returns ------- @@ -310,35 +318,46 @@ def timeit(func, runs=1, stats_only=False, verbose=True, results=False, **timer_ Examples -------- + The timing values shown below are illustrative only; actual values + vary with machine and load. + Basic usage: + >>> import time >>> def slow_function(): ... time.sleep(0.01) - >>> timeit(slow_function, runs=3) + >>> timeit(slow_function, runs=3, precision=2) Run 1: 0.01 seconds Run 2: 0.01 seconds Run 3: 0.01 seconds Average: 0.01 seconds, Minimum: 0.01 seconds, Maximum: 0.01 seconds - + Summary only: - >>> timeit(slow_function, runs=3, stats_only=True) + >>> timeit(slow_function, runs=3, stats_only=True, precision=2) Average: 0.01 seconds, Minimum: 0.01 seconds, Maximum: 0.01 seconds - + With custom Timer options: >>> timeit(slow_function, runs=2, unit="milliseconds", precision=1) Run 1: 10.1 ms - Run 2: 10.0 ms + Run 2: 10.0 ms Average: 10.1 ms, Minimum: 10.0 ms, Maximum: 10.1 ms - + Return results for further analysis: - >>> results = timeit(slow_function, runs=2, results=True) + >>> results = timeit(slow_function, runs=2, results=True, verbose=False) >>> print(f"Average time: {results['average']:.4f} seconds") - + Average time: 0.0103 seconds + Quiet mode: >>> timeit(slow_function, runs=2, verbose=False) # No output - + With function arguments using lambda: + >>> def expensive_computation(a, b): + ... time.sleep(0.01) + ... return a + b >>> add_func = lambda: expensive_computation(5, 10) >>> timeit(add_func, runs=2) + Run 1: 0.0103 seconds + Run 2: 0.0102 seconds + Average: 0.0103 seconds, Minimum: 0.0102 seconds, Maximum: 0.0103 seconds """ if not isinstance(runs, int) or runs < 1: raise ValueError("runs must be a positive integer")