├── .coveragerc ├── .github ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE.md ├── PULL_REQUEST_TEMPLATE.md └── workflows │ ├── publish.yml │ └── test.yml ├── .gitignore ├── AUTHORS.md ├── CHANGELOG.md ├── Document ├── Document.ipynb └── Examples.ipynb ├── LICENSE ├── Otherfiles ├── logo.png ├── notebook_check.py ├── requirements-splitter.py └── usage_example.webp ├── README.md ├── Test ├── error_test.py ├── function_test.py └── plot_test.py ├── autopep8.sh ├── codecov.yml ├── dev-requirements.txt ├── paper ├── paper.bib └── paper.md ├── pyrandwalk ├── __init__.py ├── __main__.py ├── pyrandwalk_error.py ├── pyrandwalk_obj.py ├── pyrandwalk_param.py ├── pyrandwalk_test.py └── pyrandwalk_util.py ├── pytest.ini ├── requirements.txt ├── setup.cfg └── setup.py /.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | branch = True 3 | omit = 4 | */pyrandwalk/__main__.py 5 | */pyrandwalk/__init__.py 6 | */pyrandwalk/pyrandwalk_test.py 7 | [report] 8 | exclude_lines = 9 | pragma: no cover 10 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contribution 2 | 3 | Changes and improvements are more than welcome! ❤️ Feel free to fork and open a pull request. 4 | 5 | 6 | Please consider the following : 7 | 8 | 9 | 1. Fork it! 10 | 2. Create your feature branch (under `dev` branch) 11 | 3. Add your new features or fix detected bugs 12 | 4. Add standard `docstring` to your functions/methods 13 | 5. Add tests for your functions/methods (`doctest`, `Test` folder) 14 | 6. Update `README.md` (if needed) 15 | 7. Update `Document.ipynb` (if needed) 16 | 8. Pass all CI tests 17 | 9. Update `CHANGELOG.md` 18 | - Describe changes under `[Unreleased]` section 19 | 10. Update `AUTHORS.md` 20 | - Add your name under `# Other Contributors #` section 21 | 11. Submit a pull request into `dev` (please complete the pull request template) 22 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | #### Description 2 | 3 | #### Steps/Code to Reproduce 4 | 5 | #### Expected Behavior 6 | 7 | #### Actual Behavior 8 | 9 | #### Operating System 10 | 11 | #### Python Version 12 | 13 | #### pyrandwalk Version (Use : `pyrandwalk.__version__`) 14 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | #### Reference Issues/PRs 2 | 3 | #### What does this implement/fix? Explain your changes. 4 | 5 | 6 | #### Any other comments? 7 | 8 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | # This workflow will upload a Python Package using Twine when a release is created 2 | # For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries 3 | 4 | name: Upload Python Package 5 | 6 | on: 7 | push: 8 | # Sequence of patterns matched against refs/tags 9 | tags: 10 | - '*' # Push events to matching v*, i.e. v1.0, v20.15.10 11 | 12 | jobs: 13 | deploy: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v2 19 | - name: Set up Python 20 | uses: actions/setup-python@v1 21 | with: 22 | python-version: '3.x' 23 | - name: Install dependencies 24 | run: | 25 | python -m pip install --upgrade pip 26 | pip install setuptools wheel twine 27 | - name: Build and publish 28 | env: 29 | TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }} 30 | TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }} 31 | run: | 32 | python setup.py sdist bdist_wheel 33 | twine upload dist/*.tar.gz 34 | twine upload dist/*.whl 35 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | # This workflow will install Python dependencies, run tests and lint with a variety of Python versions 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions 3 | 4 | name: CI 5 | 6 | on: [push, pull_request] 7 | 8 | jobs: 9 | build: 10 | 11 | runs-on: ${{ matrix.os }} 12 | strategy: 13 | matrix: 14 | os: [ubuntu-latest, windows-latest, macOS-latest] 15 | python-version: [3.5, 3.6, 3.7, 3.8, 3.9] 16 | steps: 17 | - uses: actions/checkout@v2 18 | - name: Set up Python ${{ matrix.python-version }} 19 | uses: actions/setup-python@v2 20 | with: 21 | python-version: ${{ matrix.python-version }} 22 | - name: Installation 23 | run: | 24 | python -m pip install --upgrade pip 25 | pip install -r requirements.txt 26 | - name: First test 27 | run: | 28 | python -m pyrandwalk test 29 | python -m pyrandwalk 30 | - name: Test requirements Installation 31 | run: | 32 | python Otherfiles/requirements-splitter.py 33 | pip install --upgrade --upgrade-strategy=only-if-needed -r test-requirements.txt 34 | - name: Test with pytest (Python > 3.5) 35 | run: | 36 | python -m pytest Test --cov=pyrandwalk --cov-report=term 37 | if: matrix.python-version != 3.5 38 | - name: Test with pytest (Python 3.5) 39 | run: | 40 | python -m pytest --ignore-glob="*plot_test.py" Test --cov=pyrandwalk --cov-report=term 41 | if: matrix.python-version == 3.5 42 | - name: Notebook check 43 | run: | 44 | python Otherfiles/notebook_check.py 45 | if: matrix.python-version == 3.7 && matrix.os == 'ubuntu-latest' 46 | - name: Other tests 47 | run: | 48 | python -m vulture pyrandwalk/ Otherfiles/ --min-confidence 65 --exclude=__init__.py --sort-by-size 49 | python -m bandit -r pyrandwalk -s B311 50 | python -m pydocstyle -v --match-dir=pyrandwalk 51 | if: matrix.python-version == 3.7 52 | - name: Codecov 53 | run: | 54 | codecov 55 | if: matrix.python-version == 3.7 && matrix.os == 'ubuntu-latest' 56 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # Pyre type checker 129 | .pyre/ 130 | 131 | # VSCode 132 | .vscode/ 133 | -------------------------------------------------------------------------------- /AUTHORS.md: -------------------------------------------------------------------------------- 1 | # Authors # 2 | 3 | ---------- 4 | - Sadra Sabouri - Sharif University Of Technology ([sabouri.sadra@gmail.com](mailto:sabouri.sadra@gmail.com)) 5 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) 5 | and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). 6 | 7 | ## [Unreleased] 8 | ## [1.1] - 2021-08-25 9 | ### Added 10 | - all input `state` type support 11 | - new examples 12 | - `paper.bib` 13 | - `paper.md` 14 | ### Changes 15 | - `make_prob_dist` modified 16 | ## [1.0] - 2021-06-09 17 | ### Added 18 | - codefactor tests 19 | - pypi installation guide 20 | - `Document.ipynb` 21 | - `Examples.ipynb` 22 | - `notebook_check.py` 23 | - `precision` parameter added to `is_prob_dist` function 24 | - `payoff` attribute 25 | - `cost` attribute 26 | - `discount` attribute 27 | - `best_policy` method 28 | ### Changes 29 | - `make_prob_dist` bug fixed 30 | - `final_dist` bug fixed 31 | ## [0.09] - 2021-05-16 32 | ### Added 33 | - `RandomWalk` class 34 | - `prob_sec` method 35 | - `run` method 36 | - `final_dist` method 37 | - `is_irreducible` method 38 | - `trans_power` method 39 | - `get_edges` method 40 | - `get_graph` method 41 | - `get_colormap` method 42 | - `get_typeof_classes` method 43 | - `README.md` 44 | 45 | [Unreleased]: https://github.com/sadrasabouri/pyrandwalk/compare/v1.1...dev 46 | [1.1]: https://github.com/sadrasabouri/pyrandwalk/compare/v1.0...v1.1 47 | [1.0]: https://github.com/sadrasabouri/pyrandwalk/compare/v0.09...v1.0 48 | [0.09]: https://github.com/sadrasabouri/pyrandwalk/compare/9113e4c...v0.09 49 | -------------------------------------------------------------------------------- /Document/Document.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "| Google Colab | GitHub |\n", 8 | "| :---: | :---: |\n", 9 | "|
Run in Google Colab
|
View Source on GitHub
|" 10 | ] 11 | }, 12 | { 13 | "cell_type": "markdown", 14 | "metadata": {}, 15 | "source": [ 16 | "# Pyrandwalk Document" 17 | ] 18 | }, 19 | { 20 | "cell_type": "markdown", 21 | "metadata": {}, 22 | "source": [ 23 | "### Version : 1.1\n", 24 | "-----" 25 | ] 26 | }, 27 | { 28 | "cell_type": "markdown", 29 | "metadata": {}, 30 | "source": [ 31 | "## Table of contents" 32 | ] 33 | }, 34 | { 35 | "cell_type": "markdown", 36 | "metadata": {}, 37 | "source": [ 38 | "" 62 | ] 63 | }, 64 | { 65 | "cell_type": "markdown", 66 | "metadata": {}, 67 | "source": [ 68 | "## Overview" 69 | ] 70 | }, 71 | { 72 | "cell_type": "markdown", 73 | "metadata": {}, 74 | "source": [ 75 | "Pyrandwalk is an educational tool for simulating random walks, calculating the probability of given state sequences, etc. Random walk is a representation of the discrete-time, discrete-value Markov chain model used in stochastic processes." 76 | ] 77 | }, 78 | { 79 | "cell_type": "markdown", 80 | "metadata": {}, 81 | "source": [ 82 | "## Installation" 83 | ] 84 | }, 85 | { 86 | "cell_type": "markdown", 87 | "metadata": {}, 88 | "source": [ 89 | "### Source code\n", 90 | "- Download [Version 1.1](https://github.com/sadrasabouri/pyrandwalk/archive/v1.1.zip) or [Latest Source ](https://github.com/sadrasabouri/pyrandwalk/archive/dev.zip)\n", 91 | "- Run `pip install -r requirements.txt` or `pip3 install -r requirements.txt` (Need root access)\n", 92 | "- Run `python3 setup.py install` or `python setup.py install` (Need root access)" 93 | ] 94 | }, 95 | { 96 | "cell_type": "markdown", 97 | "metadata": {}, 98 | "source": [ 99 | "### PyPI\n", 100 | "\n", 101 | "- Check [Python Packaging User Guide](https://packaging.python.org/installing/)\n", 102 | "- Run `pip install pyrandwalk` or `pip3 install pyrandwalk` (Need root access)\n" 103 | ] 104 | }, 105 | { 106 | "cell_type": "markdown", 107 | "metadata": {}, 108 | "source": [ 109 | "## Usage" 110 | ] 111 | }, 112 | { 113 | "cell_type": "code", 114 | "execution_count": 1, 115 | "metadata": { 116 | "tags": [] 117 | }, 118 | "outputs": [ 119 | { 120 | "name": "stdout", 121 | "output_type": "stream", 122 | "text": [ 123 | "/usr/lib/python3/dist-packages/secretstorage/dhcrypto.py:15: CryptographyDeprecationWarning: int_from_bytes is deprecated, use int.from_bytes instead\r\n", 124 | " from cryptography.utils import int_from_bytes\r\n", 125 | "/usr/lib/python3/dist-packages/secretstorage/util.py:19: CryptographyDeprecationWarning: int_from_bytes is deprecated, use int.from_bytes instead\r\n", 126 | " from cryptography.utils import int_from_bytes\r\n" 127 | ] 128 | } 129 | ], 130 | "source": [ 131 | "!pip -q -q install pyrandwalk\n", 132 | "from pyrandwalk import *\n", 133 | "import numpy as np" 134 | ] 135 | }, 136 | { 137 | "cell_type": "markdown", 138 | "metadata": {}, 139 | "source": [ 140 | "### RandomWalk" 141 | ] 142 | }, 143 | { 144 | "cell_type": "markdown", 145 | "metadata": {}, 146 | "source": [ 147 | "Imagine bellow random walk which in it you have five states ({0, 1, 2, 3, 4}) as bellow:\n", 148 | "
\n", 149 | "
\n", 150 | "\n", 151 | "
\n", 152 | "
\n", 153 | "In this random walk you can only loop back in state 0 (absorbing state). In other states you can move to near states by the probability given on each edge.\n", 154 | "
\n", 155 | "
\n", 156 | "So let's make a RandomWalk object from this random walk:" 157 | ] 158 | }, 159 | { 160 | "cell_type": "code", 161 | "execution_count": 2, 162 | "metadata": {}, 163 | "outputs": [], 164 | "source": [ 165 | "states = [0, 1, 2, 3, 4]\n", 166 | "trans = np.array([[1, 0, 0, 0, 0],\n", 167 | " [0.25, 0, 0.75, 0, 0],\n", 168 | " [0, 0.25, 0, 0.75, 0],\n", 169 | " [0, 0, 0.25, 0, 0.75],\n", 170 | " [0, 0, 0, 1, 0]])\n", 171 | "rw = RandomWalk(states, trans)" 172 | ] 173 | }, 174 | { 175 | "cell_type": "code", 176 | "execution_count": 3, 177 | "metadata": {}, 178 | "outputs": [ 179 | { 180 | "data": { 181 | "text/plain": [ 182 | "[0, 1, 2, 3, 4]" 183 | ] 184 | }, 185 | "execution_count": 3, 186 | "metadata": {}, 187 | "output_type": "execute_result" 188 | } 189 | ], 190 | "source": [ 191 | "rw.S" 192 | ] 193 | }, 194 | { 195 | "cell_type": "code", 196 | "execution_count": 4, 197 | "metadata": {}, 198 | "outputs": [ 199 | { 200 | "data": { 201 | "text/plain": [ 202 | "array([[1. , 0. , 0. , 0. , 0. ],\n", 203 | " [0.25, 0. , 0.75, 0. , 0. ],\n", 204 | " [0. , 0.25, 0. , 0.75, 0. ],\n", 205 | " [0. , 0. , 0.25, 0. , 0.75],\n", 206 | " [0. , 0. , 0. , 1. , 0. ]])" 207 | ] 208 | }, 209 | "execution_count": 4, 210 | "metadata": {}, 211 | "output_type": "execute_result" 212 | } 213 | ], 214 | "source": [ 215 | "rw.P" 216 | ] 217 | }, 218 | { 219 | "cell_type": "markdown", 220 | "metadata": {}, 221 | "source": [ 222 | "### prob_sec\n", 223 | "\n", 224 | "With this method you can calculate the probability of a given sequence:" 225 | ] 226 | }, 227 | { 228 | "cell_type": "code", 229 | "execution_count": 5, 230 | "metadata": {}, 231 | "outputs": [ 232 | { 233 | "data": { 234 | "text/plain": [ 235 | "0.0125" 236 | ] 237 | }, 238 | "execution_count": 5, 239 | "metadata": {}, 240 | "output_type": "execute_result" 241 | } 242 | ], 243 | "source": [ 244 | "rw.prob_sec([2, 1, 0])" 245 | ] 246 | }, 247 | { 248 | "cell_type": "code", 249 | "execution_count": 6, 250 | "metadata": {}, 251 | "outputs": [ 252 | { 253 | "data": { 254 | "text/plain": [ 255 | "0.0625" 256 | ] 257 | }, 258 | "execution_count": 6, 259 | "metadata": {}, 260 | "output_type": "execute_result" 261 | } 262 | ], 263 | "source": [ 264 | "rw.prob_sec([2, 1, 0], initial_dist=[0, 0, 1, 0, 0])" 265 | ] 266 | }, 267 | { 268 | "cell_type": "markdown", 269 | "metadata": {}, 270 | "source": [ 271 | "" 274 | ] 275 | }, 276 | { 277 | "cell_type": "markdown", 278 | "metadata": {}, 279 | "source": [ 280 | "### run\n", 281 | "\n", 282 | "Run a random walk:" 283 | ] 284 | }, 285 | { 286 | "cell_type": "code", 287 | "execution_count": 7, 288 | "metadata": {}, 289 | "outputs": [], 290 | "source": [ 291 | "states, probs = rw.run()" 292 | ] 293 | }, 294 | { 295 | "cell_type": "code", 296 | "execution_count": 8, 297 | "metadata": {}, 298 | "outputs": [ 299 | { 300 | "data": { 301 | "text/plain": [ 302 | "[3, 4, 3, 2, 1, 2, 3, 4, 3, 4, 3]" 303 | ] 304 | }, 305 | "execution_count": 8, 306 | "metadata": {}, 307 | "output_type": "execute_result" 308 | } 309 | ], 310 | "source": [ 311 | "states" 312 | ] 313 | }, 314 | { 315 | "cell_type": "code", 316 | "execution_count": 9, 317 | "metadata": {}, 318 | "outputs": [ 319 | { 320 | "data": { 321 | "text/plain": [ 322 | "[0.2, 0.75, 1.0, 0.25, 0.25, 0.75, 0.75, 0.75, 1.0, 0.75, 1.0]" 323 | ] 324 | }, 325 | "execution_count": 9, 326 | "metadata": {}, 327 | "output_type": "execute_result" 328 | } 329 | ], 330 | "source": [ 331 | "probs" 332 | ] 333 | }, 334 | { 335 | "cell_type": "code", 336 | "execution_count": 10, 337 | "metadata": {}, 338 | "outputs": [], 339 | "source": [ 340 | "states, probs = rw.run(ntimes=20)" 341 | ] 342 | }, 343 | { 344 | "cell_type": "code", 345 | "execution_count": 11, 346 | "metadata": {}, 347 | "outputs": [ 348 | { 349 | "data": { 350 | "text/plain": [ 351 | "[3, 4, 3, 4, 3, 2, 3, 4, 3, 4, 3, 4, 3, 2, 3, 4, 3, 4, 3, 4, 3]" 352 | ] 353 | }, 354 | "execution_count": 11, 355 | "metadata": {}, 356 | "output_type": "execute_result" 357 | } 358 | ], 359 | "source": [ 360 | "states" 361 | ] 362 | }, 363 | { 364 | "cell_type": "code", 365 | "execution_count": 12, 366 | "metadata": {}, 367 | "outputs": [ 368 | { 369 | "data": { 370 | "text/plain": [ 371 | "[0.2,\n", 372 | " 0.75,\n", 373 | " 1.0,\n", 374 | " 0.75,\n", 375 | " 1.0,\n", 376 | " 0.25,\n", 377 | " 0.75,\n", 378 | " 0.75,\n", 379 | " 1.0,\n", 380 | " 0.75,\n", 381 | " 1.0,\n", 382 | " 0.75,\n", 383 | " 1.0,\n", 384 | " 0.25,\n", 385 | " 0.75,\n", 386 | " 0.75,\n", 387 | " 1.0,\n", 388 | " 0.75,\n", 389 | " 1.0,\n", 390 | " 0.75,\n", 391 | " 1.0]" 392 | ] 393 | }, 394 | "execution_count": 12, 395 | "metadata": {}, 396 | "output_type": "execute_result" 397 | } 398 | ], 399 | "source": [ 400 | "probs" 401 | ] 402 | }, 403 | { 404 | "cell_type": "markdown", 405 | "metadata": {}, 406 | "source": [ 407 | "" 410 | ] 411 | }, 412 | { 413 | "cell_type": "code", 414 | "execution_count": 13, 415 | "metadata": { 416 | "tags": [] 417 | }, 418 | "outputs": [ 419 | { 420 | "name": "stdout", 421 | "output_type": "stream", 422 | "text": [ 423 | "2 --> 1 (p = 0.250)\n", 424 | "1 --> 2 (p = 0.750)\n", 425 | "2 --> 3 (p = 0.750)\n", 426 | "3 --> 4 (p = 0.750)\n", 427 | "4 --> 3 (p = 1.000)\n", 428 | "3 --> 2 (p = 0.250)\n", 429 | "2 --> 1 (p = 0.250)\n", 430 | "1 --> 0 (p = 0.250)\n", 431 | "0 --> 0 (p = 1.000)\n", 432 | "0 --> 0 (p = 1.000)\n" 433 | ] 434 | } 435 | ], 436 | "source": [ 437 | "states, probs = rw.run(show=True)" 438 | ] 439 | }, 440 | { 441 | "cell_type": "code", 442 | "execution_count": 14, 443 | "metadata": {}, 444 | "outputs": [ 445 | { 446 | "data": { 447 | "text/plain": [ 448 | "[2, 1, 2, 3, 4, 3, 2, 1, 0, 0, 0]" 449 | ] 450 | }, 451 | "execution_count": 14, 452 | "metadata": {}, 453 | "output_type": "execute_result" 454 | } 455 | ], 456 | "source": [ 457 | "states" 458 | ] 459 | }, 460 | { 461 | "cell_type": "code", 462 | "execution_count": 15, 463 | "metadata": {}, 464 | "outputs": [ 465 | { 466 | "data": { 467 | "text/plain": [ 468 | "[0.2, 0.25, 0.75, 0.75, 0.75, 1.0, 0.25, 0.25, 0.25, 1.0, 1.0]" 469 | ] 470 | }, 471 | "execution_count": 15, 472 | "metadata": {}, 473 | "output_type": "execute_result" 474 | } 475 | ], 476 | "source": [ 477 | "probs" 478 | ] 479 | }, 480 | { 481 | "cell_type": "markdown", 482 | "metadata": {}, 483 | "source": [ 484 | "" 487 | ] 488 | }, 489 | { 490 | "cell_type": "markdown", 491 | "metadata": {}, 492 | "source": [ 493 | "### final_dist\n", 494 | "\n", 495 | "Calculate final distribution of the random walk:" 496 | ] 497 | }, 498 | { 499 | "cell_type": "code", 500 | "execution_count": 16, 501 | "metadata": {}, 502 | "outputs": [ 503 | { 504 | "data": { 505 | "text/plain": [ 506 | "array([1., 0., 0., 0., 0.])" 507 | ] 508 | }, 509 | "execution_count": 16, 510 | "metadata": {}, 511 | "output_type": "execute_result" 512 | } 513 | ], 514 | "source": [ 515 | "rw.final_dist()" 516 | ] 517 | }, 518 | { 519 | "cell_type": "markdown", 520 | "metadata": {}, 521 | "source": [ 522 | "" 525 | ] 526 | }, 527 | { 528 | "cell_type": "code", 529 | "execution_count": 17, 530 | "metadata": {}, 531 | "outputs": [ 532 | { 533 | "data": { 534 | "text/plain": [ 535 | "array([1., 0., 0., 0., 0.])" 536 | ] 537 | }, 538 | "execution_count": 17, 539 | "metadata": {}, 540 | "output_type": "execute_result" 541 | } 542 | ], 543 | "source": [ 544 | "rw.final_dist(precision=10**(-2))" 545 | ] 546 | }, 547 | { 548 | "cell_type": "markdown", 549 | "metadata": {}, 550 | "source": [ 551 | "" 554 | ] 555 | }, 556 | { 557 | "cell_type": "markdown", 558 | "metadata": {}, 559 | "source": [ 560 | "### is_irreducible\n", 561 | "\n", 562 | "Check if a random walk is irreducible:" 563 | ] 564 | }, 565 | { 566 | "cell_type": "code", 567 | "execution_count": 18, 568 | "metadata": {}, 569 | "outputs": [ 570 | { 571 | "data": { 572 | "text/plain": [ 573 | "False" 574 | ] 575 | }, 576 | "execution_count": 18, 577 | "metadata": {}, 578 | "output_type": "execute_result" 579 | } 580 | ], 581 | "source": [ 582 | "rw.is_irreducible()" 583 | ] 584 | }, 585 | { 586 | "cell_type": "markdown", 587 | "metadata": {}, 588 | "source": [ 589 | "### trans_power\n", 590 | "\n", 591 | "Return nth power of random walk transition matrix:" 592 | ] 593 | }, 594 | { 595 | "cell_type": "code", 596 | "execution_count": 19, 597 | "metadata": {}, 598 | "outputs": [ 599 | { 600 | "data": { 601 | "text/plain": [ 602 | "array([[1. , 0. , 0. , 0. , 0. ],\n", 603 | " [0.25 , 0.1875, 0. , 0.5625, 0. ],\n", 604 | " [0.0625, 0. , 0.375 , 0. , 0.5625],\n", 605 | " [0. , 0.0625, 0. , 0.9375, 0. ],\n", 606 | " [0. , 0. , 0.25 , 0. , 0.75 ]])" 607 | ] 608 | }, 609 | "execution_count": 19, 610 | "metadata": {}, 611 | "output_type": "execute_result" 612 | } 613 | ], 614 | "source": [ 615 | "rw.trans_power(2)" 616 | ] 617 | }, 618 | { 619 | "cell_type": "markdown", 620 | "metadata": {}, 621 | "source": [ 622 | "### get_edges\n", 623 | "\n", 624 | "Return edges of random walk's graph:" 625 | ] 626 | }, 627 | { 628 | "cell_type": "code", 629 | "execution_count": 20, 630 | "metadata": {}, 631 | "outputs": [ 632 | { 633 | "data": { 634 | "text/plain": [ 635 | "[(0, 0, 1.0),\n", 636 | " (1, 0, 0.25),\n", 637 | " (1, 2, 0.75),\n", 638 | " (2, 1, 0.25),\n", 639 | " (2, 3, 0.75),\n", 640 | " (3, 2, 0.25),\n", 641 | " (3, 4, 0.75),\n", 642 | " (4, 3, 1.0)]" 643 | ] 644 | }, 645 | "execution_count": 20, 646 | "metadata": {}, 647 | "output_type": "execute_result" 648 | } 649 | ], 650 | "source": [ 651 | "rw.get_edges()" 652 | ] 653 | }, 654 | { 655 | "cell_type": "markdown", 656 | "metadata": {}, 657 | "source": [ 658 | "### get_graph\n", 659 | "\n", 660 | "Return generated graph using networkx:" 661 | ] 662 | }, 663 | { 664 | "cell_type": "code", 665 | "execution_count": 21, 666 | "metadata": {}, 667 | "outputs": [], 668 | "source": [ 669 | "rw_graph = rw.get_graph()" 670 | ] 671 | }, 672 | { 673 | "cell_type": "markdown", 674 | "metadata": {}, 675 | "source": [ 676 | "### plot_graph\n", 677 | "\n", 678 | "Plot the random walk graph:" 679 | ] 680 | }, 681 | { 682 | "cell_type": "code", 683 | "execution_count": 22, 684 | "metadata": {}, 685 | "outputs": [ 686 | { 687 | "data": { 688 | "text/plain": [ 689 | "" 690 | ] 691 | }, 692 | "execution_count": 22, 693 | "metadata": {}, 694 | "output_type": "execute_result" 695 | }, 696 | { 697 | "data": { 698 | "image/png": "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n31Gnz59bhtv0KAB4eHhJCcnM3/+fAYPHlyq/QYHBzN16lRGjhxJdnY2AAMGDKBHjx4cP36cS5cuMXz4cIxSNqK77777OH78uLl87Ngx8+egoCCOHz9OQUFBofEGDRqUat930rFjR3bv3s3KlSuJiooC4OGHH+b48eOsXLmSdu3a4e3tzdGjR/ntb3/LtGnTOH/+PBkZGTzyyCMlfsZPP/2UJUuWFDo2e6uuXbuybds2Tpw4ccdabw7Q0tRz8uTJQsvHjh0jKCjoju8jrkmhW0F8+OGHpKSk4OfnV+T4kCFDeOONN0hNTS0ymIsTHR1NUFAQ77//PgBXrlyhVq1aeHt7869//YsFCxaUel/9+vXjr3/9KxcvXuTEiRO8++675lhYWBi+vr688cYb5OXlsXHjRpYvX07//v1Lvf+SNG3alHr16jF16lQzdG02G2FhYUydOtU8npuVlYXNZqNu3boAzJ49m71795a476CgINavX8/UqVOZOXNmkdvExMTQuXNnevXqxdatW8nNzSUvL++Ol6iVpp709HTeeecd8vLy+PTTT9m/fz9xcXF3/qWIS1LoVhChoaG0bdu22PHevXtz9OhRevfuja+v713te+zYsbzxxhvk5OQwY8YM/vSnPxEQEMCkSZPo169fqfczYcIEc1YeExNTaMbt6enJ8uXLWbVqFXXq1GHEiBHMmzePZs2a3VWtJenYsSNnz54lIiLCXBcVFUV6eroZus2bN2fMmDGEh4dTr149UlNTC21fnODgYNavX89rr71W7M0IixcvJiEhgUGDBhEYGEjjxo35xz/+wZo1a4rdb2nqCQsL44cffqBOnTr88Y9/ZNGiRdSuXbs0vxJxQTajtH87issLDQ1l1qxZPP74484uRcrInDlz+OCDD/jyyy+dXYqUEc10K4mkpCRsNhtdunRxdikiUgJ3Zxcg965Tp0589913zJ8/v9DVASLienR4QUTEQpoWiYhYSKErImIhha6IiIUUuiIiFlLoiohYSKErImIhha6IiIUUuiIiFlLoiohYSKErImIhha6IiIUUuiKllJEBU6fCz38OtWpBQADUrw99+sBXX4G6mEhpqOGNyB1cugQjR8I//wnVqsHVq4XHbTbw9YV69eDNN6FXL6eUKRWEQlekBCdPQlSU43tu7p239/GBCRNg3Ljyr00qJoWuSDEuXYJW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\n", 699 | "text/plain": [ 700 | "
" 701 | ] 702 | }, 703 | "metadata": {}, 704 | "output_type": "display_data" 705 | } 706 | ], 707 | "source": [ 708 | "rw.plot_graph()" 709 | ] 710 | }, 711 | { 712 | "cell_type": "markdown", 713 | "metadata": {}, 714 | "source": [ 715 | "### get_typeof_classes\n", 716 | "\n", 717 | "Return type of classes of the random walk:" 718 | ] 719 | }, 720 | { 721 | "cell_type": "code", 722 | "execution_count": 23, 723 | "metadata": {}, 724 | "outputs": [], 725 | "source": [ 726 | "rw_class_types = rw.get_typeof_classes()" 727 | ] 728 | }, 729 | { 730 | "cell_type": "code", 731 | "execution_count": 24, 732 | "metadata": {}, 733 | "outputs": [ 734 | { 735 | "data": { 736 | "text/plain": [ 737 | "([0], array([[1.]]))" 738 | ] 739 | }, 740 | "execution_count": 24, 741 | "metadata": {}, 742 | "output_type": "execute_result" 743 | } 744 | ], 745 | "source": [ 746 | "rw_class_types['recurrent']" 747 | ] 748 | }, 749 | { 750 | "cell_type": "code", 751 | "execution_count": 25, 752 | "metadata": {}, 753 | "outputs": [ 754 | { 755 | "data": { 756 | "text/plain": [ 757 | "[1, 2, 3, 4]" 758 | ] 759 | }, 760 | "execution_count": 25, 761 | "metadata": {}, 762 | "output_type": "execute_result" 763 | } 764 | ], 765 | "source": [ 766 | "rw_class_types['transient'][0]" 767 | ] 768 | }, 769 | { 770 | "cell_type": "code", 771 | "execution_count": 26, 772 | "metadata": {}, 773 | "outputs": [ 774 | { 775 | "data": { 776 | "text/plain": [ 777 | "array([[0. , 0.75, 0. , 0. ],\n", 778 | " [0.25, 0. , 0.75, 0. ],\n", 779 | " [0. , 0.25, 0. , 0.75],\n", 780 | " [0. , 0. , 1. , 0. ]])" 781 | ] 782 | }, 783 | "execution_count": 26, 784 | "metadata": {}, 785 | "output_type": "execute_result" 786 | } 787 | ], 788 | "source": [ 789 | "rw_class_types['transient'][1]" 790 | ] 791 | }, 792 | { 793 | "cell_type": "markdown", 794 | "metadata": {}, 795 | "source": [ 796 | "### best_policy\n", 797 | "\n", 798 | "For making the best policy problems for your random walk you can easily:" 799 | ] 800 | }, 801 | { 802 | "cell_type": "code", 803 | "execution_count": 27, 804 | "metadata": {}, 805 | "outputs": [ 806 | { 807 | "data": { 808 | "text/plain": [ 809 | "{'continue': [], 'stop': [0, 1, 2]}" 810 | ] 811 | }, 812 | "execution_count": 27, 813 | "metadata": {}, 814 | "output_type": "execute_result" 815 | } 816 | ], 817 | "source": [ 818 | "states = [0, 1, 2]\n", 819 | "trans = np.array([[1, 0, 0], [1/2, 0, 1/2], [0, 1, 0]])\n", 820 | "rw = RandomWalk(states, trans, payoff=[0, 1, 4], cost=[1, 0, 2], discount=0.5)\n", 821 | "rw.best_policy()" 822 | ] 823 | }, 824 | { 825 | "cell_type": "markdown", 826 | "metadata": {}, 827 | "source": [ 828 | "## Examples" 829 | ] 830 | }, 831 | { 832 | "cell_type": "markdown", 833 | "metadata": {}, 834 | "source": [ 835 | "We have already seen a example of using pyrandwalk in Usage section. If you want to see more examples including some solved exercises from references:\n", 836 | "\n", 837 | "
\n", 838 | "\n", 839 | "| Google Colab | GitHub |\n", 840 | "| :---: | :---: |\n", 841 | "|
Examples on Google Colab
|
Examples on GitHub
|\n" 842 | ] 843 | }, 844 | { 845 | "cell_type": "markdown", 846 | "metadata": {}, 847 | "source": [ 848 | "## Reference" 849 | ] 850 | }, 851 | { 852 | "cell_type": "markdown", 853 | "metadata": {}, 854 | "source": [ 855 | "
1- Gregory F.Lawler, \"Introduction to Stochastic Processes\".
\n", 856 | "
2- [Markusfeng](https://markusfeng.com/projects/graph/), \"Graph / Finite State Machine Designer\".
\n", 857 | "
Icon made by Becris from www.flaticon.com
\n" 858 | ] 859 | } 860 | ], 861 | "metadata": { 862 | "kernelspec": { 863 | "display_name": "Python 3", 864 | "name": "python3" 865 | }, 866 | "language_info": { 867 | "codemirror_mode": { 868 | "name": "ipython", 869 | "version": 3 870 | }, 871 | "file_extension": ".py", 872 | "mimetype": "text/x-python", 873 | "name": "python", 874 | "nbconvert_exporter": "python", 875 | "pygments_lexer": "ipython3", 876 | "version": "3.6.8-final" 877 | } 878 | }, 879 | "nbformat": 4, 880 | "nbformat_minor": 2 881 | } -------------------------------------------------------------------------------- /Document/Examples.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "| Google Colab | GitHub |\n", 8 | "| :---: | :---: |\n", 9 | "|
Run in Google Colab
|
View Source on GitHub
|" 10 | ] 11 | }, 12 | { 13 | "cell_type": "markdown", 14 | "metadata": {}, 15 | "source": [ 16 | "# Pyrandwalk Examples" 17 | ] 18 | }, 19 | { 20 | "cell_type": "markdown", 21 | "metadata": {}, 22 | "source": [ 23 | "### Version : 1.1\n", 24 | "-----" 25 | ] 26 | }, 27 | { 28 | "cell_type": "markdown", 29 | "metadata": {}, 30 | "source": [ 31 | "This example set contains bellow examples from the first reference (Introduction to Stochastic Processes):\n", 32 | "" 50 | ] 51 | }, 52 | { 53 | "cell_type": "code", 54 | "execution_count": 1, 55 | "metadata": { 56 | "tags": [] 57 | }, 58 | "outputs": [ 59 | { 60 | "output_type": "stream", 61 | "name": "stdout", 62 | "text": "/usr/lib/python3/dist-packages/secretstorage/dhcrypto.py:15: CryptographyDeprecationWarning: int_from_bytes is deprecated, use int.from_bytes instead\n from cryptography.utils import int_from_bytes\n/usr/lib/python3/dist-packages/secretstorage/util.py:19: CryptographyDeprecationWarning: int_from_bytes is deprecated, use int.from_bytes instead\n from cryptography.utils import int_from_bytes\n" 63 | } 64 | ], 65 | "source": [ 66 | "!pip -q -q install pyrandwalk\n", 67 | "from pyrandwalk import *\n", 68 | "import numpy as np" 69 | ] 70 | }, 71 | { 72 | "cell_type": "markdown", 73 | "metadata": {}, 74 | "source": [ 75 | "## 1. Finite_Markov_Chains" 76 | ] 77 | }, 78 | { 79 | "cell_type": "markdown", 80 | "metadata": {}, 81 | "source": [ 82 | "### Exercise1_2\n", 83 | "\n", 84 | "Consider a Markov chain with state space \\{0, 1\\} and transition matrix\n", 85 | "\n", 86 | "$$\n", 87 | "P =\n", 88 | "\\left(\\begin{array}{cc} \n", 89 | "1/3 & 2/3\\\\\n", 90 | "3/4 & 1/4\n", 91 | "\\end{array}\\right)\n", 92 | "$$" 93 | ] 94 | }, 95 | { 96 | "cell_type": "code", 97 | "execution_count": 2, 98 | "metadata": {}, 99 | "outputs": [], 100 | "source": [ 101 | "states = [0, 1]\n", 102 | "trans = np.array([[1/3, 2/3],\n", 103 | " [3/4, 1/4]])\n", 104 | "rw = RandomWalk(states, trans)" 105 | ] 106 | }, 107 | { 108 | "cell_type": "markdown", 109 | "metadata": {}, 110 | "source": [ 111 | "Assuming that the chain starts in state 0 at time n = 0, what is the probability that it is in state 1 at time n = 3?" 112 | ] 113 | }, 114 | { 115 | "cell_type": "code", 116 | "execution_count": 3, 117 | "metadata": { 118 | "tags": [] 119 | }, 120 | "outputs": [ 121 | { 122 | "name": "stdout", 123 | "output_type": "stream", 124 | "text": [ 125 | "[[0.49537037 0.50462963]\n", 126 | " [0.56770833 0.43229167]]\n", 127 | "ANSWER: 0.5046296296296295\n" 128 | ] 129 | } 130 | ], 131 | "source": [ 132 | "third_step_trans = rw.trans_power(3)\n", 133 | "print(third_step_trans)\n", 134 | "print(\"ANSWER:\", third_step_trans[0, 1])" 135 | ] 136 | }, 137 | { 138 | "cell_type": "markdown", 139 | "metadata": {}, 140 | "source": [ 141 | "### Exercise1_3\n", 142 | "\n", 143 | "Consider a Markov chain with state space \\{1, 2, 3\\} and transition matrix\n", 144 | "\n", 145 | "$$\n", 146 | "P =\n", 147 | "\\left(\\begin{array}{cc} \n", 148 | "0.4 & 0.2 & 0.4\\\\\n", 149 | "0.6 & 0 & 0.4 \\\\\n", 150 | "0.2 & 0.5 & 0.3\n", 151 | "\\end{array}\\right)\n", 152 | "$$\n" 153 | ] 154 | }, 155 | { 156 | "cell_type": "code", 157 | "execution_count": 4, 158 | "metadata": {}, 159 | "outputs": [], 160 | "source": [ 161 | "states = [1, 2, 3]\n", 162 | "trans = np.array([[0.4, 0.2, 0.4],\n", 163 | " [0.6, 0 , 0.4],\n", 164 | " [0.2, 0.5, 0.3]])\n", 165 | "rw = RandomWalk(states, trans)" 166 | ] 167 | }, 168 | { 169 | "cell_type": "markdown", 170 | "metadata": {}, 171 | "source": [ 172 | "what is the probability in the long run that the chain is in state 1?\n", 173 | "Solve this problem two different ways:\n", 174 | "\n", 175 | "1) by raising the matrix to a high power:" 176 | ] 177 | }, 178 | { 179 | "cell_type": "code", 180 | "execution_count": 5, 181 | "metadata": {}, 182 | "outputs": [ 183 | { 184 | "data": { 185 | "text/plain": [ 186 | "array([[0.37878788, 0.25757576, 0.36363636],\n", 187 | " [0.37878788, 0.25757576, 0.36363636],\n", 188 | " [0.37878788, 0.25757576, 0.36363636]])" 189 | ] 190 | }, 191 | "execution_count": 5, 192 | "metadata": {}, 193 | "output_type": "execute_result" 194 | } 195 | ], 196 | "source": [ 197 | "rw.trans_power(1000)" 198 | ] 199 | }, 200 | { 201 | "cell_type": "markdown", 202 | "metadata": {}, 203 | "source": [ 204 | "2) by directly computing the invariant probability vector as a left eigenvector:" 205 | ] 206 | }, 207 | { 208 | "cell_type": "code", 209 | "execution_count": 6, 210 | "metadata": {}, 211 | "outputs": [ 212 | { 213 | "data": { 214 | "text/plain": [ 215 | "array([0.37878788, 0.25757576, 0.36363636])" 216 | ] 217 | }, 218 | "execution_count": 6, 219 | "metadata": {}, 220 | "output_type": "execute_result" 221 | } 222 | ], 223 | "source": [ 224 | "rw.final_dist()" 225 | ] 226 | }, 227 | { 228 | "cell_type": "markdown", 229 | "metadata": {}, 230 | "source": [ 231 | "### Exercise1_4\n", 232 | "\n", 233 | "Do the same with\n", 234 | "\n", 235 | "$$\n", 236 | "P =\n", 237 | "\\left(\\begin{array}{cc} \n", 238 | "0.2 & 0.4 & 0.4\\\\\n", 239 | "0.1 & 0.5 & 0.4 \\\\\n", 240 | "0.6 & 0.3 & 0.1\n", 241 | "\\end{array}\\right)\n", 242 | "$$\n" 243 | ] 244 | }, 245 | { 246 | "cell_type": "code", 247 | "execution_count": 7, 248 | "metadata": {}, 249 | "outputs": [], 250 | "source": [ 251 | "states = [1, 2, 3]\n", 252 | "trans = np.array([[0.2, 0.4, 0.4],\n", 253 | " [0.1, 0.5, 0.4],\n", 254 | " [0.6, 0.3, 0.1]])\n", 255 | "rw = RandomWalk(states, trans)" 256 | ] 257 | }, 258 | { 259 | "cell_type": "markdown", 260 | "metadata": {}, 261 | "source": [ 262 | "1) by raising the matrix to a high power:" 263 | ] 264 | }, 265 | { 266 | "cell_type": "code", 267 | "execution_count": 8, 268 | "metadata": {}, 269 | "outputs": [ 270 | { 271 | "data": { 272 | "text/plain": [ 273 | "array([[0.28205128, 0.41025641, 0.30769231],\n", 274 | " [0.28205128, 0.41025641, 0.30769231],\n", 275 | " [0.28205128, 0.41025641, 0.30769231]])" 276 | ] 277 | }, 278 | "execution_count": 8, 279 | "metadata": {}, 280 | "output_type": "execute_result" 281 | } 282 | ], 283 | "source": [ 284 | "rw.trans_power(1000)" 285 | ] 286 | }, 287 | { 288 | "cell_type": "markdown", 289 | "metadata": {}, 290 | "source": [ 291 | "2) by directly computing the invariant probability vector as a left eigenvector:" 292 | ] 293 | }, 294 | { 295 | "cell_type": "code", 296 | "execution_count": 9, 297 | "metadata": {}, 298 | "outputs": [ 299 | { 300 | "data": { 301 | "text/plain": [ 302 | "array([0.28205128, 0.41025641, 0.30769231])" 303 | ] 304 | }, 305 | "execution_count": 9, 306 | "metadata": {}, 307 | "output_type": "execute_result" 308 | } 309 | ], 310 | "source": [ 311 | "rw.final_dist()" 312 | ] 313 | }, 314 | { 315 | "cell_type": "markdown", 316 | "metadata": {}, 317 | "source": [ 318 | "### Exercise1_5\n", 319 | "\n", 320 | "Consider the Markov chain with state space $ S = \\{0, ..., 5\\} $ and transition matrix:\n", 321 | "\n", 322 | "$$\n", 323 | "P =\n", 324 | "\\left(\\begin{array}{cc} \n", 325 | "0.5 & 0.5 & 0 & 0 & 0 & 0\\\\\n", 326 | "0.3 & 0.7 & 0 & 0 & 0 & 0 \\\\\n", 327 | "0 & 0 & 0.1 & 0 & 0 & 0.9 \\\\\n", 328 | "0.25 & 0.25 & 0 & 0 & 0.25 & 0.25 \\\\\n", 329 | "0 & 0 & 0.7 & 0 & 0.3 & 0 \\\\\n", 330 | "0 & 0.2 & 0 & 0.2 & 0.2 & 0.4 \\\\\n", 331 | "\\end{array}\\right)\n", 332 | "$$\n" 333 | ] 334 | }, 335 | { 336 | "cell_type": "code", 337 | "execution_count": 2, 338 | "metadata": {}, 339 | "outputs": [], 340 | "source": [ 341 | "states = list(range(6))\n", 342 | "trans = np.array([[0.5, 0.5, 0 , 0 , 0 , 0 ],\n", 343 | " [0.3, 0.7, 0 , 0 , 0 , 0 ],\n", 344 | " [0 , 0 , 0.1, 0 , 0 , 0.9],\n", 345 | " [.25, .25, 0 , 0 , .25, .25],\n", 346 | " [0 , 0 , 0.7, 0 , 0.3, 0 ],\n", 347 | " [0 , 0.2, 0 , 0.2, 0.2, 0.4]])\n", 348 | "rw = RandomWalk(states, trans)" 349 | ] 350 | }, 351 | { 352 | "cell_type": "markdown", 353 | "metadata": {}, 354 | "source": [ 355 | "What are the communication classes? Which ones are recurrent and which are transient?" 356 | ] 357 | }, 358 | { 359 | "cell_type": "code", 360 | "execution_count": 3, 361 | "metadata": {}, 362 | "outputs": [ 363 | { 364 | "output_type": "execute_result", 365 | "data": { 366 | "text/plain": "{'recurrent': ([0, 1],\n array([[0.5, 0.5],\n [0.3, 0.7]])),\n 'transient': ([2, 3, 4, 5],\n array([[0.1 , 0. , 0. , 0.9 ],\n [0. , 0. , 0.25, 0.25],\n [0.7 , 0. , 0.3 , 0. ],\n [0. , 0.2 , 0.2 , 0.4 ]]))}" 367 | }, 368 | "metadata": {}, 369 | "execution_count": 3 370 | } 371 | ], 372 | "source": [ 373 | "rw.get_typeof_classes()" 374 | ] 375 | }, 376 | { 377 | "cell_type": "markdown", 378 | "metadata": {}, 379 | "source": [ 380 | "Suppose the system starts in state 0. What is the probability that it will be in state 0 at some large time? Answer the same question assuming the system starts in state 5." 381 | ] 382 | }, 383 | { 384 | "cell_type": "code", 385 | "execution_count": 4, 386 | "metadata": { 387 | "tags": [] 388 | }, 389 | "outputs": [ 390 | { 391 | "output_type": "stream", 392 | "name": "stdout", 393 | "text": "0.37499999999998634\n8.081964030507363e-71\n" 394 | } 395 | ], 396 | "source": [ 397 | "p_1000 = rw.trans_power(1000)\n", 398 | "print(p_1000[0, 0])\n", 399 | "print(p_1000[5, 5])" 400 | ] 401 | }, 402 | { 403 | "cell_type": "markdown", 404 | "metadata": {}, 405 | "source": [ 406 | "### Exercise1_8\n", 407 | "\n", 408 | "Consider simple random walk on the graph below. (Recall that simple random walk on a graph is the Markov chain which at each time moves to an adjacent vertex, each adjacent vertex having the same probability):\n", 409 | "\n", 410 | "$$\n", 411 | "P =\n", 412 | "\\left(\\begin{array}{cc} \n", 413 | "0 & 1/3 & 1/3 & 1/3 & 0 \\\\\n", 414 | "1/3 & 0 & 1/3 & 0 & 1/3 \\\\\n", 415 | "1/2 & 1/2 & 0 & 0 & 0 \\\\\n", 416 | "1/2 & 0 & 0 & 0 & 1/2 \\\\\n", 417 | "0 & 1/2 & 0 & 1/2 & 0 \\\\\n", 418 | "\\end{array}\\right)\n", 419 | "$$\n" 420 | ] 421 | }, 422 | { 423 | "cell_type": "code", 424 | "execution_count": 7, 425 | "metadata": { 426 | "tags": [] 427 | }, 428 | "outputs": [], 429 | "source": [ 430 | "states = list(range(5))\n", 431 | "trans = np.array([[0, 1/3, 1/3, 1/3, 0],\n", 432 | " [1/3, 0, 1/3, 0, 1/3],\n", 433 | " [1/2, 1/2, 0, 0, 0],\n", 434 | " [1/2, 0, 0, 0, 1/2],\n", 435 | " [0 , 1/2, 0, 1/2, 0]])\n", 436 | "rw = RandomWalk(states, trans)" 437 | ] 438 | }, 439 | { 440 | "cell_type": "markdown", 441 | "metadata": {}, 442 | "source": [ 443 | "a) In the long run, what function of time is spent in vertex A?" 444 | ] 445 | }, 446 | { 447 | "cell_type": "code", 448 | "execution_count": 11, 449 | "metadata": { 450 | "tags": [] 451 | }, 452 | "outputs": [ 453 | { 454 | "output_type": "stream", 455 | "name": "stdout", 456 | "text": "0.2500000000000003\n" 457 | } 458 | ], 459 | "source": [ 460 | "final_dist = rw.final_dist()\n", 461 | "print(final_dist[0])" 462 | ] 463 | }, 464 | { 465 | "cell_type": "markdown", 466 | "metadata": {}, 467 | "source": [ 468 | "## 4. Optimal_Stopping" 469 | ] 470 | }, 471 | { 472 | "cell_type": "markdown", 473 | "metadata": {}, 474 | "source": [ 475 | "### Exercise4_1\n", 476 | "Consider a simple random walk ($p = 1/2$) with absorbing boundaries on $\\{0,1,2,...,10\\}$. Suppose the fallowing payoff function is given:\n", 477 | "\n", 478 | "$$\n", 479 | "[0,2,4,3,10,0,6,4,3,3,0]\n", 480 | "$$\n", 481 | "Find the optimal stopping rule and give the expected payoff starting at each site.\n" 482 | ] 483 | }, 484 | { 485 | "cell_type": "code", 486 | "execution_count": 5, 487 | "metadata": { 488 | "tags": [] 489 | }, 490 | "outputs": [], 491 | "source": [ 492 | "states = list(range(11))\n", 493 | "trans = np.array([[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],\n", 494 | " [.5,0,.5, 0, 0, 0, 0, 0, 0, 0, 0],\n", 495 | " [0, .5,0,.5, 0, 0, 0, 0, 0, 0, 0],\n", 496 | " [0, 0, .5,0,.5, 0, 0, 0, 0, 0, 0],\n", 497 | " [0, 0, 0, .5,0,.5, 0, 0, 0, 0, 0],\n", 498 | " [0, 0, 0, 0, .5,0,.5, 0, 0, 0, 0],\n", 499 | " [0, 0, 0, 0, 0, .5,0,.5, 0, 0, 0],\n", 500 | " [0, 0, 0, 0, 0, 0, .5,0,.5, 0, 0],\n", 501 | " [0, 0, 0, 0, 0, 0, 0, .5,0,.5, 0],\n", 502 | " [0, 0, 0, 0, 0, 0, 0, 0, .5,0,.5],\n", 503 | " [0, 0, 0, 0, 0, 0, 0 ,0, 0, 0, 1]])\n", 504 | "rw = RandomWalk(states, trans, payoff=[0,2,4,3,10,0,6,4,3,3,0])" 505 | ] 506 | }, 507 | { 508 | "cell_type": "code", 509 | "execution_count": 7, 510 | "metadata": { 511 | "tags": [] 512 | }, 513 | "outputs": [ 514 | { 515 | "output_type": "stream", 516 | "name": "stdout", 517 | "text": "{'continue': [1, 2, 3, 5, 6, 7, 8], 'stop': [0, 4, 9, 10]}\n" 518 | } 519 | ], 520 | "source": [ 521 | "best_policy = rw.best_policy()\n", 522 | "print(best_policy)" 523 | ] 524 | }, 525 | { 526 | "cell_type": "markdown", 527 | "metadata": {}, 528 | "source": [ 529 | "Which implies that it's better to stop in $[0, 4, 9, 10]$ and continue otherwise." 530 | ] 531 | } 532 | ], 533 | "metadata": { 534 | "kernelspec": { 535 | "display_name": "Python 3", 536 | "name": "python3" 537 | }, 538 | "language_info": { 539 | "codemirror_mode": { 540 | "name": "ipython", 541 | "version": 3 542 | }, 543 | "file_extension": ".py", 544 | "mimetype": "text/x-python", 545 | "name": "python", 546 | "nbconvert_exporter": "python", 547 | "pygments_lexer": "ipython3", 548 | "version": "3.6.8-final" 549 | }, 550 | "metadata": { 551 | "interpreter": { 552 | "hash": "916dbcbb3f70747c44a77c7bcd40155683ae19c65e1c03b4aa3499c5328201f1" 553 | } 554 | } 555 | }, 556 | "nbformat": 4, 557 | "nbformat_minor": 2 558 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Sadra Sabouri 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Otherfiles/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sadrasabouri/pyrandwalk/eebfe53668b0942f8ee2d7d94467e8af0b110e96/Otherfiles/logo.png -------------------------------------------------------------------------------- /Otherfiles/notebook_check.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Notebook-check script.""" 3 | import os 4 | import nbformat 5 | from nbconvert.preprocessors import ExecutePreprocessor 6 | from art import tprint 7 | 8 | NOTEBOOKS_LIST = [ 9 | "Document", 10 | "Examples"] 11 | 12 | EXTENSION = ".ipynb" 13 | 14 | if __name__ == "__main__": 15 | tprint("PYRANDWALK", "bulbhead") 16 | tprint("Document", "bulbhead") 17 | print("Processing ...") 18 | for index, notebook in enumerate(NOTEBOOKS_LIST): 19 | ep = ExecutePreprocessor(timeout=6000, kernel_name='python3') 20 | path = os.path.join("Document", notebook) 21 | with open(path + EXTENSION, "r", encoding="utf-8") as f: 22 | nb = nbformat.read(f, as_version=4) 23 | ep.preprocess(nb, {'metadata': {'path': 'Document/'}}) 24 | with open(path + EXTENSION, 'w', encoding='utf-8') as f: 25 | nbformat.write(nb, f) 26 | print("{0}.{1} [OK]".format(str(index + 1), notebook)) 27 | -------------------------------------------------------------------------------- /Otherfiles/requirements-splitter.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Requirements splitter.""" 3 | 4 | test_req = "" 5 | 6 | with open('dev-requirements.txt', 'r') as f: 7 | for line in f: 8 | if '==' not in line: 9 | test_req += line 10 | 11 | with open('test-requirements.txt', 'w') as f: 12 | f.write(test_req) 13 | -------------------------------------------------------------------------------- /Otherfiles/usage_example.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sadrasabouri/pyrandwalk/eebfe53668b0942f8ee2d7d94467e8af0b110e96/Otherfiles/usage_example.webp -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 | pyrandwalk-logo
6 |
7 |

:walking: Python Library for Random Walks

8 | 9 | built with Python3 10 | CodeFactor 11 | 12 | 13 | 14 | 15 | Document 16 | 17 |
18 | 19 | ---------- 20 | ## Table of contents 21 | * [Overview](https://github.com/sadrasabouri/pyrandwalk#overview) 22 | * [Installation](https://github.com/sadrasabouri/pyrandwalk#installation) 23 | * [Usage](https://github.com/sadrasabouri/pyrandwalk#usage) 24 | * [Contribution](https://github.com/sadrasabouri/pyrandwalk/blob/master/.github/CONTRIBUTING.md) 25 | * [References](https://github.com/sadrasabouri/pyrandwalk#references) 26 | * [Authors](https://github.com/sadrasabouri/pyrandwalk/blob/master/AUTHORS.md) 27 | * [Changelog](https://github.com/sadrasabouri/pyrandwalk/blob/master/CHANGELOG.md) 28 | * [License](https://github.com/sadrasabouri/pyrandwalk/blob/master/LICENSE) 29 | 30 | ## Overview 31 | 32 |

33 | Pyrandwalk is an educational tool for simulating random walks, calculating the probability of given state sequences, etc. Random walk is a representation of the discrete-time, discrete-value Markov chain model used in stochastic processes. 34 |

35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 |
PyPI Counter
Github Stars
47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 |
Branchmasterdev
CI
62 | 63 | 64 | 65 | ## Installation 66 | 67 | ### Source code 68 | - Download [Version 1.1](https://github.com/sadrasabouri/pyrandwalk/archive/v1.1.zip) or [Latest Source ](https://github.com/sadrasabouri/pyrandwalk/archive/dev.zip) 69 | - Run `pip install -r requirements.txt` or `pip3 install -r requirements.txt` (Need root access) 70 | - Run `python3 setup.py install` or `python setup.py install` (Need root access) 71 | 72 | ### PyPI 73 | 74 | - Check [Python Packaging User Guide](https://packaging.python.org/installing/) 75 | - Run `pip install pyrandwalk` or `pip3 install pyrandwalk` (Need root access) 76 | 77 | 78 | ## Usage 79 | 80 | 81 | ```pycon 82 | >>> from pyrandwalk import * 83 | >>> import numpy as np 84 | >>> states = [0, 1, 2, 3, 4] 85 | >>> trans = np.array([[1, 0, 0, 0, 0], 86 | ... [0.25, 0, 0.75, 0, 0], 87 | ... [0, 0.25, 0, 0.75, 0], 88 | ... [0, 0, 0.25, 0, 0.75], 89 | ... [0, 0, 0, 1, 0]]) 90 | >>> rw = RandomWalk(states, trans) 91 | ``` 92 | We are simulating random walks on the above graph (weights are probabilities): 93 | 94 | 95 | 96 | ### Probability of A Sequence 97 | 98 | Imagine you want to calculate probability which you start from state 2, go to state 1 and stuck in state 0. 99 | What's the probability of these walk sequences? 100 | ```pycon 101 | >>> rw.prob_sec([2, 1, 0]) 102 | 0.0125 103 | ``` 104 | 105 | Initial probability distribution is assumed to be uniform by default but you can change it by passing optional argument `initial_dist`: 106 | ```pycon 107 | >>> rw.prob_sec([2, 1, 0], initial_dist=[0, 0, 1, 0, 0]) 108 | 0.0625 109 | ``` 110 | 111 | 112 | ### Run a random walk 113 | 114 | You can start a random walk on given markov chain and see the result: 115 | 116 | ```pycon 117 | >>> states, probs = rw.run() 118 | >>> states 119 | [4, 3, 4, 3, 4, 3, 4, 3, 2, 3, 4] 120 | >>> probs 121 | [0.2, 1.0, 0.75, 1.0, 0.75, 1.0, 0.75, 1.0, 0.25, 0.75, 0.75] 122 | ``` 123 | 124 | By default your random walk will contain 10 steps, but you can change it by passing optional argument `ntimes`: 125 | 126 | ```pycon 127 | >>> states, probs = rw.run(ntimes=20) 128 | >>> states 129 | [3, 4, 3, 4, 3, 2, 1, 2, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 2, 3] 130 | >>> probs 131 | [0.2, 0.75, 1.0, 0.75, 1.0, 0.25, 0.25, 0.75, 0.75, 0.75, 1.0, 0.75, 1.0, 0.75, 1.0, 0.75, 1.0, 0.75, 1.0, 0.25, 0.75] 132 | ``` 133 | 134 | And if you want to see what's going on down there during the simulation you can set the `show` flag: 135 | 136 | ```pycon 137 | >>> states, probs = rw.run(ntimes=30, show=True) 138 | 1 --> 2 (p = 0.750) 139 | 2 --> 3 (p = 0.750) 140 | 3 --> 4 (p = 0.750) 141 | 4 --> 3 (p = 1.000) 142 | 3 --> 4 (p = 0.750) 143 | 4 --> 3 (p = 1.000) 144 | 3 --> 4 (p = 0.750) 145 | 4 --> 3 (p = 1.000) 146 | 3 --> 4 (p = 0.750) 147 | 4 --> 3 (p = 1.000) 148 | 3 --> 4 (p = 0.750) 149 | 4 --> 3 (p = 1.000) 150 | 3 --> 4 (p = 0.750) 151 | 4 --> 3 (p = 1.000) 152 | 3 --> 2 (p = 0.250) 153 | 2 --> 1 (p = 0.250) 154 | 1 --> 2 (p = 0.750) 155 | 2 --> 3 (p = 0.750) 156 | 3 --> 4 (p = 0.750) 157 | 4 --> 3 (p = 1.000) 158 | 3 --> 4 (p = 0.750) 159 | 4 --> 3 (p = 1.000) 160 | 3 --> 4 (p = 0.750) 161 | 4 --> 3 (p = 1.000) 162 | 3 --> 4 (p = 0.750) 163 | 4 --> 3 (p = 1.000) 164 | 3 --> 4 (p = 0.750) 165 | 4 --> 3 (p = 1.000) 166 | 3 --> 2 (p = 0.250) 167 | 2 --> 3 (p = 0.750) 168 | >>> states 169 | [1, 2, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 2, 1, 2, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 2, 3] 170 | >>> probs 171 | [0.2, 0.75, 0.75, 0.75, 1.0, 0.75, 1.0, 0.75, 1.0, 0.75, 1.0, 0.75, 1.0, 0.75, 1.0, 0.25, 0.25, 0.75, 0.75, 0.75, 1.0, 0.75, 1.0, 0.75, 1.0, 0.75, 1.0, 0.75, 1.0, 0.25, 0.75] 172 | ``` 173 | 174 | 175 | ### Final Probability Distribution 176 | 177 | You can easily find out the final probability distribution of you random walk by: 178 | ```pycon 179 | >>> rw.final_dist() 180 | array([1., 0., 0., 0., 0.]) 181 | ``` 182 | Which implies that the walk will in state `0` for sure as time goes on. 183 | 184 | ### Is it irreducible? 185 | 186 | You can check if your Markov chain is irreducible to lower rank ones or not by: 187 | 188 | ```pycon 189 | >>> rw.is_irreducible() 190 | False 191 | ``` 192 | 193 | 194 | ### nth transition matrix 195 | 196 | If you want to see what's the probability of moving from state `i` to `j` with `n` steps, you can easily calculate the nth transition matrix by: 197 | ```pycon 198 | >>> rw.trans_power(2) 199 | array([[1. , 0. , 0. , 0. , 0. ], 200 | [0.25 , 0.1875, 0. , 0.5625, 0. ], 201 | [0.0625, 0. , 0.375 , 0. , 0.5625], 202 | [0. , 0.0625, 0. , 0.9375, 0. ], 203 | [0. , 0. , 0.25 , 0. , 0.75 ]]) 204 | ``` 205 | 206 | 207 | ### Graph edges 208 | 209 | You can have your final graph edges in a list containing tuples like `(from, to, probability)` for each edge by: 210 | 211 | ```pycon 212 | >>> rw.get_edges() 213 | [(0, 0, 1.0), (1, 0, 0.25), (1, 2, 0.75), (2, 1, 0.25), (2, 3, 0.75), (3, 2, 0.25), (3, 4, 0.75), (4, 3, 1.0)] 214 | ``` 215 | 216 | ### Graph 217 | 218 | Making a *networkx* graph object from your random walk process is also token care of by this library: 219 | 220 | ```pycon 221 | >>> rw_graph = rw.get_graph() 222 | ``` 223 | 224 | ### __Colors of Nodes__ [will be removed] 225 | 226 | Until now we could not show graphs with self-loops using networkx so as far as this feature being added to networkx, we're using `blue` color for ordinary states and `red` color for states with self-loop. 227 | 228 | ```pycon 229 | >>> rw.get_colormap() 230 | ['red', 'blue', 'blue', 'blue', 'blue'] 231 | ``` 232 | 233 | 234 | ### Type of Classes 235 | 236 | For knowing which class is recurrent or transient you can use above method, you can also have reduced transition matrix for each set. 237 | 238 | ```pycon 239 | >>> rw_class_types = rw.get_typeof_classes() 240 | >>> rw_class_types['recurrent'] 241 | ([0], array([[1.]])) 242 | >>> rw_class_types['transient'][0] 243 | [1, 2, 3, 4] 244 | >>> rw_class_types['transient'][1] 245 | array([[0. , 0.75, 0. , 0. ], 246 | [0.25, 0. , 0.75, 0. ], 247 | [0. , 0.25, 0. , 0.75], 248 | [0. , 0. , 1. , 0. ]]) 249 | 250 | ``` 251 | 252 | 253 | ### The Best Policy Problems 254 | 255 | For making the best policy problems for your random walk you can easily: 256 | 257 | ```pycon 258 | >>> states = [0, 1, 2] 259 | >>> trans = np.array([[1, 0, 0], [1/2, 0, 1/2], [0, 1, 0]]) 260 | >>> rw = RandomWalk(states, trans, payoff=[0, 1, 4], cost=[1, 0, 2], discount=0.5) 261 | >>> rw.best_policy() 262 | {'continue': [], 'stop': [0, 1, 2]} 263 | ``` 264 | 265 | 266 | ## References 267 | 268 |
1- Lawler, Gregory F. Introduction to stochastic processes. Chapman and Hall/CRC, 2018.
269 |
2- Markusfeng
270 |
Icon made by Becris from www.flaticon.com
271 | -------------------------------------------------------------------------------- /Test/error_test.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | >>> from pyrandwalk import * 4 | >>> import numpy as np 5 | >>> rw = RandomWalk('test', [1]) 6 | Traceback (most recent call last): 7 | ... 8 | pyrandwalk.pyrandwalk_error.pyrandwalkStateError: Invalid type for state parameter. It should be either a list or a numpy array. 9 | >>> rw = RandomWalk([1], 'test') 10 | Traceback (most recent call last): 11 | ... 12 | pyrandwalk.pyrandwalk_error.pyrandwalkTransitionsError: Invalid type for transitions parameter. It should be either a list or a numpy array. 13 | >>> trans = [[1,2,3], [1,2,3], [1,2,3]] 14 | >>> rw = RandomWalk(['State0', 'State1'], trans) 15 | Traceback (most recent call last): 16 | ... 17 | pyrandwalk.pyrandwalk_error.pyrandwalkTransitionsError: Transition matrix size should be (STATES_SIZE, STATES_SIZE). 18 | >>> rw = RandomWalk(['State0', 'State1', 'State2'], trans) 19 | Traceback (most recent call last): 20 | ... 21 | pyrandwalk.pyrandwalk_error.pyrandwalkTransitionsError: Row 0 of transition matrix ([1, 2, 3]) is not a probability distribution. 22 | >>> trans = [[1/3,1/3,1/3], [0,0.5,0.5], [0.7,0,0.3]] 23 | >>> rw = RandomWalk(['State0', 'State1', 'State2'], trans) 24 | >>> rw.prob_sec('test') 25 | Traceback (most recent call last): 26 | ... 27 | pyrandwalk.pyrandwalk_error.pyrandwalkVectorError: Invalid type for sequence parameter. It should be either a list or a numpy array. 28 | >>> rw.prob_sec(['State0', 'State1'], initial_dist="test") 29 | Traceback (most recent call last): 30 | ... 31 | pyrandwalk.pyrandwalk_error.pyrandwalkVectorError: Invalid type for init_dist parameter. It should be either a list or a numpy array. 32 | """ -------------------------------------------------------------------------------- /Test/function_test.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | >>> from pyrandwalk import * 4 | >>> import numpy as np 5 | >>> states = [0, 1, 2, 3, 4] 6 | >>> trans = np.array([[1, 0, 0, 0, 0], 7 | ... [0.25, 0, 0.75, 0, 0], 8 | ... [0, 0.25, 0, 0.75, 0], 9 | ... [0, 0, 0.25, 0, 0.75], 10 | ... [0, 0, 0, 1, 0]]) 11 | >>> rw = RandomWalk(states, trans) 12 | >>> rw.prob_sec([2, 1, 0]) 13 | 0.0125 14 | >>> rw.prob_sec([2, 1, 0], initial_dist=[0, 0, 1, 0, 0]) 15 | 0.0625 16 | >>> np.random.seed(0) 17 | >>> states, probs = rw.run() 18 | >>> states 19 | [4, 3, 4, 3, 4, 3, 4, 3, 2, 3, 4] 20 | >>> probs 21 | [0.2, 1.0, 0.75, 1.0, 0.75, 1.0, 0.75, 1.0, 0.25, 0.75, 0.75] 22 | >>> np.random.seed(1) 23 | >>> states, probs = rw.run(ntimes=20) 24 | >>> states 25 | [3, 4, 3, 4, 3, 2, 1, 2, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 2, 3] 26 | >>> probs 27 | [0.2, 0.75, 1.0, 0.75, 1.0, 0.25, 0.25, 0.75, 0.75, 0.75, 1.0, 0.75, 1.0, 0.75, 1.0, 0.75, 1.0, 0.75, 1.0, 0.25, 0.75] 28 | >>> np.random.seed(10) 29 | >>> states, probs = rw.run(ntimes=30, show=True) 30 | 1 --> 2 (p = 0.750) 31 | 2 --> 3 (p = 0.750) 32 | 3 --> 4 (p = 0.750) 33 | 4 --> 3 (p = 1.000) 34 | 3 --> 4 (p = 0.750) 35 | 4 --> 3 (p = 1.000) 36 | 3 --> 4 (p = 0.750) 37 | 4 --> 3 (p = 1.000) 38 | 3 --> 4 (p = 0.750) 39 | 4 --> 3 (p = 1.000) 40 | 3 --> 4 (p = 0.750) 41 | 4 --> 3 (p = 1.000) 42 | 3 --> 4 (p = 0.750) 43 | 4 --> 3 (p = 1.000) 44 | 3 --> 2 (p = 0.250) 45 | 2 --> 1 (p = 0.250) 46 | 1 --> 2 (p = 0.750) 47 | 2 --> 3 (p = 0.750) 48 | 3 --> 4 (p = 0.750) 49 | 4 --> 3 (p = 1.000) 50 | 3 --> 4 (p = 0.750) 51 | 4 --> 3 (p = 1.000) 52 | 3 --> 4 (p = 0.750) 53 | 4 --> 3 (p = 1.000) 54 | 3 --> 4 (p = 0.750) 55 | 4 --> 3 (p = 1.000) 56 | 3 --> 4 (p = 0.750) 57 | 4 --> 3 (p = 1.000) 58 | 3 --> 2 (p = 0.250) 59 | 2 --> 3 (p = 0.750) 60 | >>> states 61 | [1, 2, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 2, 1, 2, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 2, 3] 62 | >>> probs 63 | [0.2, 0.75, 0.75, 0.75, 1.0, 0.75, 1.0, 0.75, 1.0, 0.75, 1.0, 0.75, 1.0, 0.75, 1.0, 0.25, 0.25, 0.75, 0.75, 0.75, 1.0, 0.75, 1.0, 0.75, 1.0, 0.75, 1.0, 0.75, 1.0, 0.25, 0.75] 64 | >>> rw.final_dist() 65 | array([1., 0., 0., 0., 0.]) 66 | >>> rw.is_irreducible() 67 | False 68 | >>> rw.trans_power(2) 69 | array([[1. , 0. , 0. , 0. , 0. ], 70 | [0.25 , 0.1875, 0. , 0.5625, 0. ], 71 | [0.0625, 0. , 0.375 , 0. , 0.5625], 72 | [0. , 0.0625, 0. , 0.9375, 0. ], 73 | [0. , 0. , 0.25 , 0. , 0.75 ]]) 74 | >>> rw.get_edges() 75 | [(0, 0, 1.0), (1, 0, 0.25), (1, 2, 0.75), (2, 1, 0.25), (2, 3, 0.75), (3, 2, 0.25), (3, 4, 0.75), (4, 3, 1.0)] 76 | >>> rw_graph = rw.get_graph() 77 | >>> rw.get_colormap() 78 | ['red', 'blue', 'blue', 'blue', 'blue'] 79 | >>> rw_class_types = rw.get_typeof_classes() 80 | >>> rw_class_types['recurrent'] 81 | ([0], array([[1.]])) 82 | >>> rw_class_types['transient'][0] 83 | [1, 2, 3, 4] 84 | >>> rw_class_types['transient'][1] 85 | array([[0. , 0.75, 0. , 0. ], 86 | [0.25, 0. , 0.75, 0. ], 87 | [0. , 0.25, 0. , 0.75], 88 | [0. , 0. , 1. , 0. ]]) 89 | >>> policy = rw.best_policy() 90 | >>> policy['stop'] 91 | [0, 1, 2, 3, 4] 92 | >>> policy['continue'] 93 | [] 94 | >>> states = [1, 2, 3] 95 | >>> trans = np.array([[0.4, 0.2, 0.4], 96 | ... [0.6, 0 , 0.4], 97 | ... [0.2, 0.5, 0.3]]) 98 | >>> rw = RandomWalk(states, trans) 99 | >>> rw.final_dist() 100 | array([0.37878788, 0.25757576, 0.36363636]) 101 | >>> states = [1, 2, 3] 102 | >>> trans = np.array([[0.2, 0.4, 0.4], 103 | ... [0.1, 0.5, 0.4], 104 | ... [0.6, 0.3, 0.1]]) 105 | >>> rw = RandomWalk(states, trans) 106 | >>> rw.final_dist() 107 | array([0.28205128, 0.41025641, 0.30769231]) 108 | >>> states = [0, 1, 2] 109 | >>> trans = np.array([[1, 0, 0], [1/2, 0, 1/2], [0, 1, 0]]) 110 | >>> rw = RandomWalk(states, trans, payoff=[0, 1, 4]) 111 | >>> policy = rw.best_policy() 112 | >>> policy['stop'] 113 | [0, 2] 114 | >>> policy['continue'] 115 | [1] 116 | >>> rw = RandomWalk(states, trans, payoff=[0, 1, 4], cost=[1, 3, 4], discount=0.1) 117 | >>> policy = rw.best_policy() 118 | >>> policy['stop'] 119 | [0, 1, 2] 120 | >>> policy['continue'] 121 | [] 122 | >>> np.random.seed(0) 123 | >>> states = ['I', 'eat', 'food'] 124 | >>> trans = np.array([[0.1, 0.8, 0.1], [0.15, 0.05, 0.8], [0.8, 0.15, 0.05]]) 125 | >>> rw = RandomWalk(states, trans) 126 | >>> states, probs = rw.run(show=True) 127 | I --> eat (p = 0.800) 128 | eat --> food (p = 0.800) 129 | food --> eat (p = 0.150) 130 | eat --> food (p = 0.800) 131 | food --> I (p = 0.800) 132 | I --> eat (p = 0.800) 133 | eat --> food (p = 0.800) 134 | food --> I (p = 0.800) 135 | I --> eat (p = 0.800) 136 | eat --> food (p = 0.800) 137 | >>> states 138 | ['I', 'eat', 'food', 'eat', 'food', 'I', 'eat', 'food', 'I', 'eat', 'food'] 139 | >>> probs 140 | [0.3333333333333333, 0.8, 0.8, 0.15, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8] 141 | >>> rw.final_dist() 142 | array([0.34133043, 0.33805889, 0.32061069]) 143 | >>> policy = rw.best_policy() 144 | >>> policy['stop'] 145 | ['I', 'eat', 'food'] 146 | >>> policy['continue'] 147 | [] 148 | >>> from pyrandwalk.pyrandwalk_util import * 149 | >>> make_prob_dist([1, 2, 2]) 150 | array([0.2, 0.4, 0.4]) 151 | >>> make_prob_dist([1, 1000, 2]) 152 | array([9.97008973e-04, 9.97008973e-01, 1.99401795e-03]) 153 | >>> make_prob_dist([1, 1000, 2], precision=10**(-2)) 154 | array([0., 1., 0.]) 155 | >>> is_prob_dist([1, 2, 3]) 156 | False 157 | >>> is_prob_dist([0.5, 0.25, 0.25]) 158 | True 159 | >>> is_prob_dist([1/3, 1/3, 1/3]) 160 | True 161 | >>> is_prob_dist([1/3, -1/3, 1/3]) 162 | False 163 | >>> is_prob_dist([1/3, -1/3, -1/3]) 164 | False 165 | >>> is_valid_vector_type([1, 2, 3]) 166 | True 167 | >>> is_valid_vector_type(np.array([1, 2, 3])) 168 | True 169 | >>> is_valid_vector_type(1) 170 | False 171 | """ -------------------------------------------------------------------------------- /Test/plot_test.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | >>> from pyrandwalk import * 4 | >>> import numpy as np 5 | >>> import matplotlib.pyplot as plt 6 | >>> rw = RandomWalk([0, 1], np.array([[0.25, 0.75],[0.3, 0.7]])) 7 | >>> ax = rw.plot_graph() 8 | >>> ax.get_title() 9 | 'My Random Walk Graph' 10 | >>> ax = rw.plot_graph(title="test") 11 | >>> ax.get_title() 12 | 'test' 13 | """ -------------------------------------------------------------------------------- /autopep8.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | python -m autopep8 pyrandwalk --recursive --aggressive --aggressive --in-place --pep8-passes 2000 --verbose -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | codecov: 2 | require_ci_to_pass: yes 3 | 4 | coverage: 5 | precision: 2 6 | round: up 7 | range: "70...100" 8 | status: 9 | patch: 10 | default: 11 | enabled: no 12 | project: 13 | default: 14 | threshold: 1% 15 | -------------------------------------------------------------------------------- /dev-requirements.txt: -------------------------------------------------------------------------------- 1 | art==5.1 2 | numpy==1.20.2 3 | codecov>=2.0.15 4 | pytest>=4.3.1 5 | pytest-cov>=2.6.1 6 | setuptools>=40.8.0 7 | vulture>=1.0 8 | bandit>=1.5.1 9 | pydocstyle>=3.0.0 10 | notebook>=5.2.2 11 | matplotlib>=3.0.0 12 | seaborn>=0.9.1 13 | -------------------------------------------------------------------------------- /paper/paper.bib: -------------------------------------------------------------------------------- 1 | @book{lawler2018introduction, 2 | title={Introduction to stochastic processes}, 3 | author={Lawler, Gregory F}, 4 | year={2018}, 5 | publisher={Chapman and Hall/CRC} 6 | } 7 | @article{Haghighi2017, 8 | doi = {10.21105/joss.00331}, 9 | url = {https://doi.org/10.21105/joss.00331}, 10 | year = {2017}, 11 | month = {sep}, 12 | publisher = {The Open Journal}, 13 | volume = {2}, 14 | number = {17}, 15 | author = {Sepand Haghighi}, 16 | title = {Pyrgg: Python Random Graph Generator}, 17 | journal = {The Journal of Open Source Software} 18 | } 19 | -------------------------------------------------------------------------------- /paper/paper.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 'PyRandWalk: A Python Library for Random Walks Simulaton' 3 | tags: 4 | - Python 5 | - educational 6 | - simulation 7 | - probability 8 | - Markov chains 9 | - stochastic processes 10 | - reinforcement learning 11 | authors: 12 | - name: Sadra Sabouri 13 | orcid: 0000-0003-1047-2346 14 | affiliation: 1 15 | affiliations: 16 | - name: Sharif University of Technology 17 | index: 1 18 | date: 15 August 2021 19 | bibliography: paper.bib 20 | 21 | --- 22 | 23 | # Summary 24 | 25 | Pyrandwalk is an educational tool for simulating random walks, calculating the probability of given state sequences, etc. Random walk is a representation of the discrete-time, discrete-value Markov chain model used in stochastic processes. 26 | 27 | The first idea of this paper is inherited from Pyrgg.[@Haghighi2017] 28 | 29 | # Statement of need 30 | 31 | There are classes of problems in which occurrences did not deterministically happen. In these types of problems each of the probable next states will be assigned to a probability showing its chance of happening. In some cases we use a simplification called Markov Assumption which suggests that each state is just correlated to its last one so we don't take into consideration before states. 32 | 33 | These types of problems are not only used in pure mathematics but also in many nowadays hot topics like Reinforcement Learning, Bigram Language Models, etc. Because understanding the mathematics behind the random walks and Markov chains needs good knowledge in statistics and probability and most of the new learners can not enter into these fields we decided to solve this issue by developing a Python library which gives a simple intuition about these rather hard problems called PyRandWalk.[@lawler2018introduction] 34 | 35 | # References 36 | -------------------------------------------------------------------------------- /pyrandwalk/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Pyrandwalk module.""" 3 | from .pyrandwalk_param import PYRANDWALK_VERSION 4 | from .pyrandwalk_obj import RandomWalk 5 | __version__ = PYRANDWALK_VERSION 6 | -------------------------------------------------------------------------------- /pyrandwalk/__main__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """pyrandwalk main.""" 3 | 4 | import doctest 5 | import sys 6 | from .pyrandwalk_obj import * 7 | from .pyrandwalk_param import * 8 | from art import tprint 9 | 10 | 11 | if __name__ == "__main__": 12 | args = sys.argv 13 | if len(args) > 1: 14 | if args[1].upper() == "TEST": 15 | error_flag = doctest.testfile( 16 | "pyrandwalk_test.py", 17 | optionflags=doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS 18 | | doctest.IGNORE_EXCEPTION_DETAIL, 19 | verbose=False)[0] 20 | sys.exit(error_flag) 21 | else: 22 | tprint("pyrandwalk") 23 | tprint("V:" + PYRANDWALK_VERSION) 24 | else: 25 | tprint("pyrandwalk") 26 | tprint("V:" + PYRANDWALK_VERSION) 27 | -------------------------------------------------------------------------------- /pyrandwalk/pyrandwalk_error.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """pyrandwalk errors.""" 3 | 4 | 5 | class pyrandwalkStateError(Exception): 6 | """State error class.""" 7 | 8 | 9 | class pyrandwalkTransitionsError(Exception): 10 | """Transitions matrix error class.""" 11 | 12 | 13 | class pyrandwalkVectorError(Exception): 14 | """Vector error class.""" 15 | -------------------------------------------------------------------------------- /pyrandwalk/pyrandwalk_obj.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Random Walk module.""" 3 | from .pyrandwalk_param import * 4 | from .pyrandwalk_util import * 5 | from .pyrandwalk_error import pyrandwalkStateError, pyrandwalkTransitionsError, pyrandwalkVectorError 6 | import numpy as np 7 | from numpy import linalg as la 8 | import networkx as nx 9 | 10 | 11 | class RandomWalk(): 12 | """ 13 | Random Walk class. 14 | 15 | >>> states = [0, 1, 2, 3, 4] 16 | >>> trans = np.array([[1, 0, 0, 0, 0], 17 | [0.25, 0, 0.75, 0, 0], 18 | [0, 0.25, 0, 0.75, 0], 19 | [0, 0, 0.25, 0, 0.75], 20 | [0, 0, 0, 1, 0]]) 21 | >>> rw = RandomWalk(states, trans) 22 | """ 23 | 24 | def __init__( 25 | self, 26 | states, 27 | transitions, 28 | payoff=None, 29 | cost=None, 30 | discount=1): 31 | """ 32 | Init method. 33 | 34 | :param states: list of states of random walk 35 | :type states: list or np.array 36 | :param transitions: matrix of transitions 37 | :type transitions: np.array 38 | :param payoff: list of payoff values for each state 39 | :type payoff: list or np.array 40 | :param cost: cost of each state 41 | :type cost: list or np.array 42 | :param discount: discount which will be applied in each step 43 | :type discount: float 44 | """ 45 | if not is_valid_vector_type(states): 46 | raise pyrandwalkStateError(INVALID_STATE_TYPE_ERROR) 47 | self.S = states 48 | if not is_valid_vector_type(transitions): 49 | raise pyrandwalkTransitionsError(INVALID_TRANSITIONS_TYPE_ERROR) 50 | if np.shape(transitions) != (len(states), len(states)): 51 | raise pyrandwalkTransitionsError(TRANSITIONS_SIZE_ERROR) 52 | for i, row in enumerate(transitions): 53 | if not is_prob_dist(row): 54 | raise pyrandwalkTransitionsError( 55 | TRANSITIONS_ROW_PROBABILITY_ERROR.format(i, row)) 56 | self.P = np.array(transitions) 57 | if payoff is None: 58 | payoff = [0] * len(states) 59 | if cost is None: 60 | cost = [0] * len(states) 61 | self.f, self.g, self.gamma = np.array(payoff), np.array(cost), discount 62 | 63 | def prob_sec(self, sequence, initial_dist=None): 64 | """ 65 | Calculate probability of given sequence. 66 | 67 | :param sequence: given sequence of states 68 | :type sequence: list / np.array 69 | :param initial_dist: initial probability disturbition of states 70 | :type initial_dist: list 71 | :return: probability of given sequence of states happening 72 | """ 73 | if not is_valid_vector_type(sequence): 74 | raise pyrandwalkVectorError(INVALID_SEQUENCE_TYPE_ERROR) 75 | if initial_dist is None: 76 | initial_dist = [1 / len(self.S)] * len(self.S) 77 | if not is_valid_vector_type(initial_dist): 78 | raise pyrandwalkVectorError(INVALID_INITDIST_TYPE_ERROR) 79 | current_state = sequence[0] 80 | probability = initial_dist[self.S.index(current_state)] 81 | for next_state in sequence[1:]: 82 | probability *= self.P[self.S.index(current_state), 83 | self.S.index(next_state)] 84 | current_state = next_state 85 | return probability 86 | 87 | def run(self, ntimes=10, show=False): 88 | """ 89 | Run random walk for ntimes and print out result on each transition. 90 | 91 | :param ntimes: numbers of running 92 | :type ntimes: int 93 | :param show: flag which is set when showing run is desired 94 | :return: (generated_states, probability) 95 | """ 96 | state = np.random.choice(self.S) 97 | states = [state] 98 | probabilities = [1 / len(self.S)] 99 | for i in range(ntimes): 100 | state_idx = self.S.index(state) 101 | next_state = np.random.choice(self.S, p=self.P[state_idx]) 102 | next_state_idx = self.S.index(next_state) 103 | probability = self.P[state_idx, next_state_idx] 104 | states.append(next_state) 105 | probabilities.append(probability) 106 | if show is True: 107 | print(RUN_PRINT.format(state, next_state, probability)) 108 | state = next_state 109 | return states, probabilities 110 | 111 | def final_dist(self, precision=10**(-10)): 112 | """ 113 | Return final probability distribution of the random walk. 114 | 115 | :param precision: shows the min threshold of probabilies 116 | :type precision: float 117 | :return: final distribution as np.array 118 | """ 119 | v, Q = la.eig(self.P.T) 120 | final_probs = Q[:, np.argmin(np.abs(v - 1))] 121 | final_probs = np.squeeze(final_probs.T) 122 | return make_prob_dist(final_probs, precision=precision) 123 | 124 | def is_irreducible(self): 125 | """ 126 | Return true if stochastic process associated with random walk is irreducible. 127 | 128 | :return: a bool which is true when the process is irreducible 129 | """ 130 | return nx.is_strongly_connected(self.get_graph()) 131 | 132 | def trans_power(self, n): 133 | """ 134 | Return nth power of transition matrix. 135 | 136 | :param n: power of desired matrix 137 | :type n: int 138 | :return: nth power of transition matrix 139 | """ 140 | return la.matrix_power(self.P, n) 141 | 142 | def get_edges(self): 143 | """ 144 | Return none-zero weighted edges of random walk. 145 | 146 | :return: list of tuples consisting of from_node, to_node and weight 147 | """ 148 | edges = [] 149 | state_idx = range(len(self.S)) 150 | for i in state_idx: 151 | for j in state_idx: 152 | if self.P[i, j] > 0: 153 | edges.append((i, j, self.P[i, j])) 154 | return edges 155 | 156 | def get_graph(self): 157 | """ 158 | Return directional graph generated from random walk. 159 | 160 | :return: directional weighted graph as networkx.DiGraph 161 | """ 162 | graph = nx.DiGraph() 163 | graph.add_weighted_edges_from(self.get_edges()) 164 | return graph 165 | 166 | def get_colormap(self): 167 | """ 168 | Return graph node color map.A node is red iff it has a ring edge. 169 | 170 | :return: list of color strings. 171 | """ 172 | graph = self.get_graph() 173 | colormap = ['blue' for _ in graph] 174 | for i, node in enumerate(graph): 175 | if graph.has_edge(node, node): 176 | colormap[i] = 'red' 177 | return colormap 178 | 179 | def plot_graph(self, suptitle="Graph", title="My Random Walk Graph"): 180 | """ 181 | Plot graph associated with random walk. 182 | 183 | :param suptitle: figure suptitle 184 | :type suptitle: string 185 | :param title: figure title 186 | :type title: string 187 | :return: generated plot 188 | """ 189 | import matplotlib.pyplot as plt 190 | colormap = self.get_colormap() 191 | fig, ax = plt.subplots() 192 | fig.canvas.set_window_title(suptitle) 193 | ax.set_title(title) 194 | nx.draw_circular(self.get_graph(), 195 | node_color=colormap, 196 | with_labels=True, 197 | font_weight='bold') 198 | return ax 199 | 200 | def get_typeof_classes(self): 201 | """ 202 | Return classes separated according to their types. 203 | 204 | :return: dictionary consisting of classes and their types 205 | """ 206 | class_list = nx.strongly_connected_components(self.get_graph()) 207 | class_dict = {} 208 | for class_ in class_list: 209 | class_ = list(class_) 210 | idx = [self.S.index(c) for c in class_] 211 | sub_trans = np.take(np.take(self.P, idx, axis=0), idx, axis=1) 212 | is_recurrent = np.all(sub_trans.sum(axis=1) == 1) 213 | if is_recurrent: 214 | class_dict['recurrent'] = (class_, sub_trans) 215 | else: 216 | class_dict['transient'] = (class_, sub_trans) 217 | return class_dict 218 | 219 | def best_policy(self, MIN_DIFF=10**(-4)): 220 | """ 221 | Seperate states into 2 sections continue and stop. 222 | 223 | :param MIN_DIFF: minimum difference for updates 224 | :type MIN_DIFF: float 225 | :return: dictionary of lists showing each section's state(s) 226 | """ 227 | stop_states = [] 228 | for i, state in enumerate(self.S): 229 | if self.P[i,i] == 1: 230 | stop_states.append(state) 231 | max_f = np.max(self.f) 232 | v = [self.f[i] if self.S[i] 233 | in stop_states else max_f for i in range(len(self.S))] 234 | v, diff = np.array(v), np.inf 235 | while(diff > MIN_DIFF): 236 | u = self.gamma * np.matmul(self.P, v) - self.g 237 | new_v = np.maximum(u, self.f) 238 | diff = np.sum(np.abs(new_v - v)) 239 | v = new_v 240 | policy = {"continue": [], "stop": []} 241 | for i, state in enumerate(self.S): 242 | if state in stop_states: 243 | policy["stop"].append(state) 244 | elif v[i] > self.f[i]: 245 | policy["continue"].append(state) 246 | else: 247 | policy["stop"].append(state) 248 | return policy 249 | -------------------------------------------------------------------------------- /pyrandwalk/pyrandwalk_param.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Parameters and constants.""" 3 | PYRANDWALK_VERSION = "1.1" 4 | 5 | RUN_PRINT = "{0} --> {1} (p = {2:.3f})" 6 | 7 | INVALID_STATE_TYPE_ERROR = "Invalid type for state parameter. It should be either a list or a numpy array." 8 | INVALID_TRANSITIONS_TYPE_ERROR = "Invalid type for transitions parameter. It should be either a list or a numpy array." 9 | INVALID_SEQUENCE_TYPE_ERROR = "Invalid type for sequence parameter. It should be either a list or a numpy array." 10 | INVALID_INITDIST_TYPE_ERROR = "Invalid type for initial_dist parameter. It should be either a list or a numpy array." 11 | TRANSITIONS_SIZE_ERROR = "Transition matrix size should be (STATES_SIZE, STATES_SIZE)." 12 | TRANSITIONS_ROW_PROBABILITY_ERROR = "Row {0} of transition matrix ({1}) is not a probability distribution." 13 | -------------------------------------------------------------------------------- /pyrandwalk/pyrandwalk_test.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Basic test file.""" 3 | """ 4 | >>> from pyrandwalk import * 5 | >>> import numpy as np 6 | >>> states = [0, 1, 2, 3, 4] 7 | >>> trans = np.array([[1, 0, 0, 0, 0], 8 | ... [0.25, 0, 0.75, 0, 0], 9 | ... [0, 0.25, 0, 0.75, 0], 10 | ... [0, 0, 0.25, 0, 0.75], 11 | ... [0, 0, 0, 1, 0]]) 12 | >>> rw = RandomWalk(states, trans) 13 | 14 | """ 15 | -------------------------------------------------------------------------------- /pyrandwalk/pyrandwalk_util.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Utility module.""" 3 | import numpy as np 4 | 5 | 6 | def make_prob_dist(freq, precision=10**(-10)): 7 | """ 8 | Return a fine probability distribution based on given frequencies of states. 9 | 10 | :param freq: inputted array showing rate of occurrence 11 | :type freq: list or np.array 12 | :param precision: shows the min threshold of probabilies 13 | :type precision: float 14 | :return: a np.array giving the probability disterbution 15 | """ 16 | freq = np.array(freq) 17 | final_dist = freq / np.sum(freq) 18 | zero_probe_idx = final_dist < precision 19 | final_dist[zero_probe_idx] = 0 20 | final_dist = final_dist / np.sum(final_dist) 21 | return np.real(final_dist) 22 | 23 | 24 | def is_prob_dist(dist, precision=10**(-10)): 25 | """ 26 | Check if given array is a probability distribution. 27 | 28 | :param dist: given array 29 | :type dist: list or np.array 30 | :return: True if dist is a probability distribution 31 | """ 32 | dist = np.array(dist) 33 | if np.sum(dist < 0) != 0: 34 | return False 35 | if np.abs(np.sum(dist) - 1) > precision: 36 | return False 37 | return True 38 | 39 | 40 | def is_valid_vector_type(vector): 41 | """ 42 | Check if given vector is a valid one. 43 | 44 | :param vector: given vector 45 | :type vector: anything 46 | :return: True if vector is list or numpy array 47 | """ 48 | return isinstance(vector, (list, np.ndarray)) 49 | -------------------------------------------------------------------------------- /pytest.ini: -------------------------------------------------------------------------------- 1 | # content of pytest.ini 2 | [pytest] 3 | addopts = --doctest-modules 4 | doctest_optionflags= NORMALIZE_WHITESPACE IGNORE_EXCEPTION_DETAIL -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | numpy>=1.9.0 2 | networkx>=2 3 | art>=1.8 4 | matplotlib>=3.0.0 5 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [bdist_wheel] 2 | universal = 1 -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Setup module.""" 3 | try: 4 | from setuptools import setup 5 | except ImportError: 6 | from distutils.core import setup 7 | 8 | 9 | def get_requires(): 10 | """Read requirements.txt.""" 11 | requirements = open("requirements.txt", "r").read() 12 | return list(filter(lambda x: x != "", requirements.split())) 13 | 14 | 15 | def read_description(): 16 | """Read README.md and CHANGELOG.md.""" 17 | try: 18 | with open("README.md") as r: 19 | description = "\n" 20 | description += r.read() 21 | return description 22 | except Exception: 23 | return ''' 24 | Pyrandwalk is a tool for simulating random walks, 25 | calculate the probability of given state sequences and etc. 26 | Random walk is a representation of discrete-time, 27 | discrete-value Markov chain model using in stochastic processes..''' 28 | 29 | 30 | setup( 31 | name='pyrandwalk', 32 | packages=['pyrandwalk'], 33 | version='1.1', 34 | description='Python Library for Random Walks', 35 | long_description=read_description(), 36 | long_description_content_type='text/markdown', 37 | author='Sadra Sabouri', 38 | author_email='sabouri.sadra@gmail.com', 39 | url='https://github.com/sadrasabouri/pyrandwalk', 40 | download_url='https://github.com/sadrasabouri/pyrandwalk/tarball/v1.1', 41 | keywords="random-walk markov-chain stochastic-processes", 42 | project_urls={ 43 | 'Source': 'https://github.com/sadrasabouri/pyrandwalk', 44 | }, 45 | install_requires=get_requires(), 46 | python_requires='>=3.5', 47 | classifiers=[ 48 | 'Development Status :: 4 - Beta', 49 | 'Intended Audience :: Developers', 50 | 'Natural Language :: English', 51 | 'License :: OSI Approved :: MIT License', 52 | 'Operating System :: OS Independent', 53 | 'Programming Language :: Python :: 3.5', 54 | 'Programming Language :: Python :: 3.6', 55 | 'Programming Language :: Python :: 3.7', 56 | 'Programming Language :: Python :: 3.8', 57 | 'Programming Language :: Python :: 3.9', 58 | 'Intended Audience :: Developers', 59 | 'Intended Audience :: Education', 60 | 'Intended Audience :: End Users/Desktop', 61 | 'Intended Audience :: Manufacturing', 62 | 'Intended Audience :: Science/Research', 63 | 'Topic :: Education', 64 | 'Topic :: Scientific/Engineering', 65 | 'Topic :: Scientific/Engineering :: Mathematics', 66 | ], 67 | license='MIT', 68 | ) 69 | --------------------------------------------------------------------------------