├── .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 | "
\n",
39 | " Overview \n",
40 | " Installation \n",
41 | " \n",
42 | " Source Code \n",
43 | " PyPI \n",
44 | " \n",
45 | " Usage \n",
46 | " \n",
47 | " RandomWalk \n",
48 | " prob_sec \n",
49 | " run \n",
50 | " final_dist \n",
51 | " is_irreducible \n",
52 | " trans_power \n",
53 | " get_edges \n",
54 | " get_graph \n",
55 | " plot_graph \n",
56 | " get_typeof_classes \n",
57 | " best_policy \n",
58 | " \n",
59 | " Examples \n",
60 | " References \n",
61 | " "
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 | "\n",
272 | " Notice : `initial_dist` (the initial distribution of the sequence) is came by order respected to states and has a uniform distribution by default. \n",
273 | " "
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 | "\n",
408 | " Notice : `ntimes` (number of steps which the random walk is going to run) is a integer (default value 10). \n",
409 | " "
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 | "\n",
485 | " Notice : `show` (a flag which sets if we want to have a transition log) is a boolean (default value False). \n",
486 | " "
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 | "\n",
523 | " Notice : added in version 1. \n",
524 | " "
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 | "\n",
552 | " Notice : `precision` if float number indecating the precision of final distribution (default value 10**(-10)). \n",
553 | " "
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 | "\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 |
6 |
7 |
:walking: Python Library for Random Walks
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
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 | PyPI Counter
40 |
41 |
42 |
43 | Github Stars
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 | Branch
53 | master
54 | dev
55 |
56 |
57 | CI
58 |
59 |
60 |
61 |
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 |
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 |
--------------------------------------------------------------------------------