statrl 1.2609__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (123) hide show
  1. statrl-1.2609/LICENSE +21 -0
  2. statrl-1.2609/PKG-INFO +129 -0
  3. statrl-1.2609/README.md +104 -0
  4. statrl-1.2609/pyproject.toml +73 -0
  5. statrl-1.2609/setup.cfg +4 -0
  6. statrl-1.2609/src/statrl/__init__.py +0 -0
  7. statrl-1.2609/src/statrl/experiments/__init__.py +0 -0
  8. statrl-1.2609/src/statrl/experiments/analyzeruns.py +95 -0
  9. statrl-1.2609/src/statrl/experiments/massiveruns.py +124 -0
  10. statrl-1.2609/src/statrl/experiments/onerun.py +111 -0
  11. statrl-1.2609/src/statrl/experiments/parallelruns.py +60 -0
  12. statrl-1.2609/src/statrl/experiments/plotruns.py +144 -0
  13. statrl-1.2609/src/statrl/experiments/utils.py +140 -0
  14. statrl-1.2609/src/statrl/settings/__init__.py +0 -0
  15. statrl-1.2609/src/statrl/settings/bandits/__init__.py +0 -0
  16. statrl-1.2609/src/statrl/settings/bandits/adversarial/__init__.py +0 -0
  17. statrl-1.2609/src/statrl/settings/bandits/adversarial/lipschitz/__init__.py +0 -0
  18. statrl-1.2609/src/statrl/settings/bandits/adversarial/lipschitz/agent.py +53 -0
  19. statrl-1.2609/src/statrl/settings/bandits/adversarial/lipschitz/agents/ALF.py +202 -0
  20. statrl-1.2609/src/statrl/settings/bandits/adversarial/lipschitz/agents/__init__.py +0 -0
  21. statrl-1.2609/src/statrl/settings/bandits/adversarial/lipschitz/environment.py +117 -0
  22. statrl-1.2609/src/statrl/settings/bandits/adversarial/lipschitz/envs/__init__.py +0 -0
  23. statrl-1.2609/src/statrl/settings/bandits/adversarial/lipschitz/interaction.py +62 -0
  24. statrl-1.2609/src/statrl/settings/bandits/adversarial/lipschitz/wrappers/__init__.py +0 -0
  25. statrl-1.2609/src/statrl/settings/bandits/stochastic/__init__.py +0 -0
  26. statrl-1.2609/src/statrl/settings/bandits/stochastic/anytime/__init__.py +0 -0
  27. statrl-1.2609/src/statrl/settings/bandits/stochastic/anytime/_test.py +74 -0
  28. statrl-1.2609/src/statrl/settings/bandits/stochastic/anytime/agent.py +76 -0
  29. statrl-1.2609/src/statrl/settings/bandits/stochastic/anytime/agents/IMED.py +156 -0
  30. statrl-1.2609/src/statrl/settings/bandits/stochastic/anytime/agents/NPTS.py +136 -0
  31. statrl-1.2609/src/statrl/settings/bandits/stochastic/anytime/agents/TS.py +119 -0
  32. statrl-1.2609/src/statrl/settings/bandits/stochastic/anytime/agents/UCB.py +112 -0
  33. statrl-1.2609/src/statrl/settings/bandits/stochastic/anytime/agents/_Oracle.py +66 -0
  34. statrl-1.2609/src/statrl/settings/bandits/stochastic/anytime/agents/_Random.py +54 -0
  35. statrl-1.2609/src/statrl/settings/bandits/stochastic/anytime/agents/__init__.py +0 -0
  36. statrl-1.2609/src/statrl/settings/bandits/stochastic/anytime/environment.py +161 -0
  37. statrl-1.2609/src/statrl/settings/bandits/stochastic/anytime/envs/__init__.py +0 -0
  38. statrl-1.2609/src/statrl/settings/bandits/stochastic/anytime/envs/distributions.py +126 -0
  39. statrl-1.2609/src/statrl/settings/bandits/stochastic/anytime/envs/parametric.py +145 -0
  40. statrl-1.2609/src/statrl/settings/bandits/stochastic/anytime/interaction.py +110 -0
  41. statrl-1.2609/src/statrl/settings/bandits/stochastic/anytime/renderers/__init__.py +0 -0
  42. statrl-1.2609/src/statrl/settings/bandits/stochastic/anytime/renderers/textrenderer.py +69 -0
  43. statrl-1.2609/src/statrl/settings/bandits/stochastic/anytime/wrappers/__init__.py +0 -0
  44. statrl-1.2609/src/statrl/settings/bandits/stochastic/batch/__init__.py +0 -0
  45. statrl-1.2609/src/statrl/settings/bandits/stochastic/batch/_test.py +60 -0
  46. statrl-1.2609/src/statrl/settings/bandits/stochastic/batch/agent.py +110 -0
  47. statrl-1.2609/src/statrl/settings/bandits/stochastic/batch/agents/BABA.py +687 -0
  48. statrl-1.2609/src/statrl/settings/bandits/stochastic/batch/agents/BCB.py +255 -0
  49. statrl-1.2609/src/statrl/settings/bandits/stochastic/batch/agents/BIMED.py +235 -0
  50. statrl-1.2609/src/statrl/settings/bandits/stochastic/batch/agents/_Oracle.py +89 -0
  51. statrl-1.2609/src/statrl/settings/bandits/stochastic/batch/agents/_Random.py +80 -0
  52. statrl-1.2609/src/statrl/settings/bandits/stochastic/batch/agents/__init__.py +0 -0
  53. statrl-1.2609/src/statrl/settings/bandits/stochastic/batch/agents/baba_schedule.py +246 -0
  54. statrl-1.2609/src/statrl/settings/bandits/stochastic/batch/environment.py +121 -0
  55. statrl-1.2609/src/statrl/settings/bandits/stochastic/batch/envs/__init__.py +0 -0
  56. statrl-1.2609/src/statrl/settings/bandits/stochastic/batch/envs/parametric.py +206 -0
  57. statrl-1.2609/src/statrl/settings/bandits/stochastic/batch/envs/renderers/__init__.py +0 -0
  58. statrl-1.2609/src/statrl/settings/bandits/stochastic/batch/interaction.py +102 -0
  59. statrl-1.2609/src/statrl/settings/bandits/stochastic/kernel/__init__.py +0 -0
  60. statrl-1.2609/src/statrl/settings/bandits/stochastic/kernel/_test.py +27 -0
  61. statrl-1.2609/src/statrl/settings/bandits/stochastic/kernel/agent.py +83 -0
  62. statrl-1.2609/src/statrl/settings/bandits/stochastic/kernel/agents/_Oracle.py +62 -0
  63. statrl-1.2609/src/statrl/settings/bandits/stochastic/kernel/agents/_Random.py +39 -0
  64. statrl-1.2609/src/statrl/settings/bandits/stochastic/kernel/agents/__init__.py +0 -0
  65. statrl-1.2609/src/statrl/settings/bandits/stochastic/kernel/environment.py +237 -0
  66. statrl-1.2609/src/statrl/settings/bandits/stochastic/kernel/envs/__init__.py +0 -0
  67. statrl-1.2609/src/statrl/settings/bandits/stochastic/kernel/envs/kernels.py +175 -0
  68. statrl-1.2609/src/statrl/settings/bandits/stochastic/kernel/interaction.py +105 -0
  69. statrl-1.2609/src/statrl/settings/bandits/stochastic/kernel/renderers/__init__.py +0 -0
  70. statrl-1.2609/src/statrl/settings/bandits/stochastic/kernel/renderers/plotrenderer.py +256 -0
  71. statrl-1.2609/src/statrl/settings/bandits/stochastic/kernel/renderers/textrenderer.py +100 -0
  72. statrl-1.2609/src/statrl/settings/bandits/stochastic/knownhorizon/__init__.py +0 -0
  73. statrl-1.2609/src/statrl/settings/bandits/stochastic/knownhorizon/agent.py +71 -0
  74. statrl-1.2609/src/statrl/settings/bandits/stochastic/knownhorizon/agents/__init__.py +0 -0
  75. statrl-1.2609/src/statrl/settings/bandits/stochastic/knownhorizon/environment.py +13 -0
  76. statrl-1.2609/src/statrl/settings/bandits/stochastic/knownhorizon/envs/__init__.py +0 -0
  77. statrl-1.2609/src/statrl/settings/bandits/stochastic/knownhorizon/interaction.py +141 -0
  78. statrl-1.2609/src/statrl/settings/bandits/stochastic/knownhorizon/wrappers/__init__.py +0 -0
  79. statrl-1.2609/src/statrl/settings/bandits/stochastic/knownhorizon/wrappers/wrapper_anytime_knownhorizon.py +143 -0
  80. statrl-1.2609/src/statrl/settings/markovdecisionprocess/__init__.py +0 -0
  81. statrl-1.2609/src/statrl/settings/markovdecisionprocess/discrete_nostructure/__init__.py +0 -0
  82. statrl-1.2609/src/statrl/settings/markovdecisionprocess/discrete_nostructure/_test.py +68 -0
  83. statrl-1.2609/src/statrl/settings/markovdecisionprocess/discrete_nostructure/agent.py +82 -0
  84. statrl-1.2609/src/statrl/settings/markovdecisionprocess/discrete_nostructure/agents/Human.py +87 -0
  85. statrl-1.2609/src/statrl/settings/markovdecisionprocess/discrete_nostructure/agents/IMED_RL.py +411 -0
  86. statrl-1.2609/src/statrl/settings/markovdecisionprocess/discrete_nostructure/agents/PSRL.py +356 -0
  87. statrl-1.2609/src/statrl/settings/markovdecisionprocess/discrete_nostructure/agents/PSRL_original.py +306 -0
  88. statrl-1.2609/src/statrl/settings/markovdecisionprocess/discrete_nostructure/agents/_Oracle.py +260 -0
  89. statrl-1.2609/src/statrl/settings/markovdecisionprocess/discrete_nostructure/agents/_Random.py +56 -0
  90. statrl-1.2609/src/statrl/settings/markovdecisionprocess/discrete_nostructure/agents/__init__.py +0 -0
  91. statrl-1.2609/src/statrl/settings/markovdecisionprocess/discrete_nostructure/environment.py +250 -0
  92. statrl-1.2609/src/statrl/settings/markovdecisionprocess/discrete_nostructure/envs/__init__.py +0 -0
  93. statrl-1.2609/src/statrl/settings/markovdecisionprocess/discrete_nostructure/envs/randomMDP.py +174 -0
  94. statrl-1.2609/src/statrl/settings/markovdecisionprocess/discrete_nostructure/envs/riverswim.py +207 -0
  95. statrl-1.2609/src/statrl/settings/markovdecisionprocess/discrete_nostructure/interaction.py +101 -0
  96. statrl-1.2609/src/statrl/settings/markovdecisionprocess/discrete_nostructure/renderers/__init__.py +0 -0
  97. statrl-1.2609/src/statrl/settings/markovdecisionprocess/discrete_nostructure/renderers/htmlRenderer.py +1744 -0
  98. statrl-1.2609/src/statrl/settings/markovdecisionprocess/discrete_nostructure/renderers/textRenderer.py +89 -0
  99. statrl-1.2609/src/statrl/settings/markovdecisionprocess/discrete_nostructure/wrappers/__init__.py +0 -0
  100. statrl-1.2609/src/statrl/settings/markovdecisionprocess/gridworld/__init__.py +0 -0
  101. statrl-1.2609/src/statrl/settings/markovdecisionprocess/gridworld/_test.py +62 -0
  102. statrl-1.2609/src/statrl/settings/markovdecisionprocess/gridworld/agent.py +3 -0
  103. statrl-1.2609/src/statrl/settings/markovdecisionprocess/gridworld/agents/_Oracle.py +168 -0
  104. statrl-1.2609/src/statrl/settings/markovdecisionprocess/gridworld/agents/_Random.py +1 -0
  105. statrl-1.2609/src/statrl/settings/markovdecisionprocess/gridworld/agents/__init__.py +0 -0
  106. statrl-1.2609/src/statrl/settings/markovdecisionprocess/gridworld/environment.py +4 -0
  107. statrl-1.2609/src/statrl/settings/markovdecisionprocess/gridworld/envs/__init__.py +0 -0
  108. statrl-1.2609/src/statrl/settings/markovdecisionprocess/gridworld/envs/gridworlds.py +905 -0
  109. statrl-1.2609/src/statrl/settings/markovdecisionprocess/gridworld/interaction.py +102 -0
  110. statrl-1.2609/src/statrl/settings/markovdecisionprocess/gridworld/renderers/__init__.py +0 -0
  111. statrl-1.2609/src/statrl/settings/markovdecisionprocess/gridworld/renderers/htmlrenderer.py +114 -0
  112. statrl-1.2609/src/statrl/settings/markovdecisionprocess/gridworld/renderers/textRenderer.py +184 -0
  113. statrl-1.2609/src/statrl/settings/utils.py +366 -0
  114. statrl-1.2609/src/statrl/settings/validator.py +307 -0
  115. statrl-1.2609/src/statrl.egg-info/PKG-INFO +129 -0
  116. statrl-1.2609/src/statrl.egg-info/SOURCES.txt +121 -0
  117. statrl-1.2609/src/statrl.egg-info/dependency_links.txt +1 -0
  118. statrl-1.2609/src/statrl.egg-info/requires.txt +13 -0
  119. statrl-1.2609/src/statrl.egg-info/top_level.txt +1 -0
  120. statrl-1.2609/tests/test_batch.py +18 -0
  121. statrl-1.2609/tests/test_environment.py +20 -0
  122. statrl-1.2609/tests/test_experiments.py +93 -0
  123. statrl-1.2609/tests/test_utils.py +26 -0
statrl-1.2609/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 statrl contributors
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.
statrl-1.2609/PKG-INFO ADDED
@@ -0,0 +1,129 @@
1
+ Metadata-Version: 2.4
2
+ Name: statrl
3
+ Version: 1.2609
4
+ Summary: The Statistical Reinforcement Learning Toolkit Library
5
+ Author-email: Odalric-Ambrym Maillard <odalricambrym.maillard@inria.fr>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/StatisticalRL/statrl
8
+ Project-URL: Repository, https://github.com/StatisticalRL/statrl
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Requires-Python: >=3.9
11
+ Description-Content-Type: text/markdown
12
+ License-File: LICENSE
13
+ Requires-Dist: gymnasium>=1.0
14
+ Requires-Dist: numpy>=2.5
15
+ Requires-Dist: joblib>=1.5
16
+ Requires-Dist: matplotlib>=3.9
17
+ Requires-Dist: scipy>=1.17
18
+ Requires-Dist: pyyaml>=6.0
19
+ Provides-Extra: test
20
+ Requires-Dist: pytest; extra == "test"
21
+ Provides-Extra: lint
22
+ Requires-Dist: ruff; extra == "lint"
23
+ Requires-Dist: mypy; extra == "lint"
24
+ Dynamic: license-file
25
+
26
+ # statrl
27
+
28
+ [![Tests](https://github.com/StatisticalRL/statrl/actions/workflows/tests.yml/badge.svg)](https://github.com/StatisticalRL/statrl/actions/workflows/tests.yml)
29
+ [![Lint](https://github.com/StatisticalRL/statrl/actions/workflows/lint.yml/badge.svg)](https://github.com/StatisticalRL/statrl/actions/workflows/lint.yml)
30
+ [![Documentation](https://github.com/StatisticalRL/statrl/actions/workflows/docs.yml/badge.svg)](https://github.com/StatisticalRL/statrl/actions/workflows/docs.yml)
31
+ [![Python](https://img.shields.io/badge/python-3.9%2B-blue)](pyproject.toml)
32
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
33
+
34
+ The Statistical Reinforcement Learning Toolkit is a research library organised as a
35
+ taxonomy of *settings*, each with a matching environment, agent, and interaction loop.
36
+
37
+ 📖 **[Documentation](https://statisticalrl.github.io/statrl/)**
38
+
39
+ ## Install
40
+
41
+ Requires Python 3.9+. Not yet on PyPI (install from source):
42
+
43
+ ```bash
44
+ git clone https://github.com/StatisticalRL/statrl.git
45
+ cd statrl
46
+ pip install -e . # editable install for development
47
+ ```
48
+
49
+ ## Quickstart
50
+
51
+ ```python
52
+ from statrl.settings.bandits.stochastic.anytime.envs.parametric import BernoulliBandit
53
+ from statrl.settings.bandits.stochastic.anytime.agents.IMED import IMED
54
+ from statrl.settings.bandits.stochastic.anytime.agents._Oracle import Oracle
55
+ from statrl.settings.bandits.stochastic.anytime.interaction import BanditInteraction
56
+ from statrl.settings.utils import klBern
57
+
58
+ env = BernoulliBandit([0.2, 0.9, 0.5])
59
+ interaction = BanditInteraction()
60
+
61
+ scores = interaction.run(env, IMED(env.number_arms, klBern), horizon=2000)
62
+ oracle_scores = interaction.run(env, Oracle(env), horizon=2000)
63
+
64
+ print(f"regret after 2000 rounds: {oracle_scores[-1] - scores[-1]:.1f}")
65
+ ```
66
+
67
+ ## The protocol
68
+
69
+ Every setting shares the same protocol:
70
+
71
+ | Component | Responsibility |
72
+ | ----------- | ------------------------------------------------------------------------------------ |
73
+ | Environment | Holds the reward distributions; `step(arm)` samples a reward. |
74
+ | Agent | `reset()` starts a run; `select_arm()` chooses an arm; `update(arm, reward)` learns. |
75
+ | Interaction | `run(env, learner, horizon)` runs the loop and returns cumulative expected scores. |
76
+
77
+ ## Implemented settings
78
+
79
+ Under `statrl.settings`:
80
+
81
+ BANDITS:
82
+ - **`stochastic.anytime`**
83
+ - **`stochastic.knownhorizon`** : horizon-aware wrapper over the anytime setting.
84
+ - **`stochastic.batch`** : when considering batch schedule.
85
+ - **`stochastic.kernel`** : RKHS structure on arms.
86
+ - **`adversarial.lipschitz`** : an adversarial Lipschitz forecaster.
87
+
88
+ MARKOV DECISION PROCESSES:
89
+ - **`discrete_nostructure`**: Abstract discrete MDPs.
90
+ - **`gridworld`**: Gridworld MDPs.
91
+
92
+ ## Running experiments
93
+
94
+ `statrl.experiments` benchmarks agents: many replicates in parallel, regret against an
95
+ oracle, and plots.
96
+
97
+ ```python
98
+ from statrl.experiments.massiveruns import runLargeMulticoreExperiment
99
+
100
+ runLargeMulticoreExperiment(
101
+ env, agents, oracle, interact,
102
+ timeHorizon=1000, nbReplicates=100, root_folder="results/",
103
+ )
104
+ ```
105
+
106
+ Results (per-replicate dumps, a logfile, regret plots) are written under `root_folder`.
107
+ See [`examples/`](examples/) for complete runnable scripts.
108
+
109
+ ## Development
110
+
111
+ ```bash
112
+ pip install -e ".[test,lint]"
113
+ pytest # tests
114
+ pytest --doctest-modules src/statrl \
115
+ --ignore-glob='*_test.py' # docstring examples
116
+ ruff check src tests # lint
117
+ mypy # type-check
118
+ ```
119
+
120
+ ## Documentation
121
+
122
+ Full docs (quickstart, user guide, API reference) live under `docs/`:
123
+
124
+ ```bash
125
+ pip install -r docs/requirements.txt
126
+ sphinx-build -b html -W --keep-going docs/source docs/_build/html
127
+ sphinx-build -b doctest docs/source docs/_build/doctest
128
+ xdg-open docs/_build/html/index.html
129
+ ```
@@ -0,0 +1,104 @@
1
+ # statrl
2
+
3
+ [![Tests](https://github.com/StatisticalRL/statrl/actions/workflows/tests.yml/badge.svg)](https://github.com/StatisticalRL/statrl/actions/workflows/tests.yml)
4
+ [![Lint](https://github.com/StatisticalRL/statrl/actions/workflows/lint.yml/badge.svg)](https://github.com/StatisticalRL/statrl/actions/workflows/lint.yml)
5
+ [![Documentation](https://github.com/StatisticalRL/statrl/actions/workflows/docs.yml/badge.svg)](https://github.com/StatisticalRL/statrl/actions/workflows/docs.yml)
6
+ [![Python](https://img.shields.io/badge/python-3.9%2B-blue)](pyproject.toml)
7
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
8
+
9
+ The Statistical Reinforcement Learning Toolkit is a research library organised as a
10
+ taxonomy of *settings*, each with a matching environment, agent, and interaction loop.
11
+
12
+ 📖 **[Documentation](https://statisticalrl.github.io/statrl/)**
13
+
14
+ ## Install
15
+
16
+ Requires Python 3.9+. Not yet on PyPI (install from source):
17
+
18
+ ```bash
19
+ git clone https://github.com/StatisticalRL/statrl.git
20
+ cd statrl
21
+ pip install -e . # editable install for development
22
+ ```
23
+
24
+ ## Quickstart
25
+
26
+ ```python
27
+ from statrl.settings.bandits.stochastic.anytime.envs.parametric import BernoulliBandit
28
+ from statrl.settings.bandits.stochastic.anytime.agents.IMED import IMED
29
+ from statrl.settings.bandits.stochastic.anytime.agents._Oracle import Oracle
30
+ from statrl.settings.bandits.stochastic.anytime.interaction import BanditInteraction
31
+ from statrl.settings.utils import klBern
32
+
33
+ env = BernoulliBandit([0.2, 0.9, 0.5])
34
+ interaction = BanditInteraction()
35
+
36
+ scores = interaction.run(env, IMED(env.number_arms, klBern), horizon=2000)
37
+ oracle_scores = interaction.run(env, Oracle(env), horizon=2000)
38
+
39
+ print(f"regret after 2000 rounds: {oracle_scores[-1] - scores[-1]:.1f}")
40
+ ```
41
+
42
+ ## The protocol
43
+
44
+ Every setting shares the same protocol:
45
+
46
+ | Component | Responsibility |
47
+ | ----------- | ------------------------------------------------------------------------------------ |
48
+ | Environment | Holds the reward distributions; `step(arm)` samples a reward. |
49
+ | Agent | `reset()` starts a run; `select_arm()` chooses an arm; `update(arm, reward)` learns. |
50
+ | Interaction | `run(env, learner, horizon)` runs the loop and returns cumulative expected scores. |
51
+
52
+ ## Implemented settings
53
+
54
+ Under `statrl.settings`:
55
+
56
+ BANDITS:
57
+ - **`stochastic.anytime`**
58
+ - **`stochastic.knownhorizon`** : horizon-aware wrapper over the anytime setting.
59
+ - **`stochastic.batch`** : when considering batch schedule.
60
+ - **`stochastic.kernel`** : RKHS structure on arms.
61
+ - **`adversarial.lipschitz`** : an adversarial Lipschitz forecaster.
62
+
63
+ MARKOV DECISION PROCESSES:
64
+ - **`discrete_nostructure`**: Abstract discrete MDPs.
65
+ - **`gridworld`**: Gridworld MDPs.
66
+
67
+ ## Running experiments
68
+
69
+ `statrl.experiments` benchmarks agents: many replicates in parallel, regret against an
70
+ oracle, and plots.
71
+
72
+ ```python
73
+ from statrl.experiments.massiveruns import runLargeMulticoreExperiment
74
+
75
+ runLargeMulticoreExperiment(
76
+ env, agents, oracle, interact,
77
+ timeHorizon=1000, nbReplicates=100, root_folder="results/",
78
+ )
79
+ ```
80
+
81
+ Results (per-replicate dumps, a logfile, regret plots) are written under `root_folder`.
82
+ See [`examples/`](examples/) for complete runnable scripts.
83
+
84
+ ## Development
85
+
86
+ ```bash
87
+ pip install -e ".[test,lint]"
88
+ pytest # tests
89
+ pytest --doctest-modules src/statrl \
90
+ --ignore-glob='*_test.py' # docstring examples
91
+ ruff check src tests # lint
92
+ mypy # type-check
93
+ ```
94
+
95
+ ## Documentation
96
+
97
+ Full docs (quickstart, user guide, API reference) live under `docs/`:
98
+
99
+ ```bash
100
+ pip install -r docs/requirements.txt
101
+ sphinx-build -b html -W --keep-going docs/source docs/_build/html
102
+ sphinx-build -b doctest docs/source docs/_build/doctest
103
+ xdg-open docs/_build/html/index.html
104
+ ```
@@ -0,0 +1,73 @@
1
+ [build-system]
2
+ requires = ["setuptools>=64"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "statrl"
7
+ version = "1.2609"
8
+ description = "The Statistical Reinforcement Learning Toolkit Library"
9
+ authors = [{ name = "Odalric-Ambrym Maillard", email = "odalricambrym.maillard@inria.fr" }]
10
+ readme = "README.md"
11
+ license = { text = "MIT" }
12
+ classifiers = ["License :: OSI Approved :: MIT License"]
13
+ requires-python = ">=3.9"
14
+ dependencies = [
15
+ "gymnasium>=1.0",
16
+ "numpy>=2.5",
17
+ "joblib>=1.5",
18
+ "matplotlib>=3.9",
19
+ "scipy>=1.17",
20
+ "pyyaml>=6.0",
21
+ ]
22
+
23
+ [project.optional-dependencies]
24
+ test = ["pytest"]
25
+ lint = ["ruff", "mypy"]
26
+
27
+ [project.urls]
28
+ Homepage = "https://github.com/StatisticalRL/statrl"
29
+ Repository = "https://github.com/StatisticalRL/statrl"
30
+
31
+ [tool.setuptools]
32
+ package-dir = { "" = "src" }
33
+
34
+ [tool.setuptools.packages.find]
35
+ where = ["src"]
36
+
37
+ [tool.pytest.ini_options]
38
+ pythonpath = ["src"]
39
+ testpaths = ["tests"]
40
+
41
+ [tool.mypy]
42
+ files = ["src"]
43
+ # Newer numpy stubs use PEP 695 `type` aliases, which need python_version >=
44
+ # 3.12 to parse, even though the package itself still targets
45
+ # requires-python = ">=3.9".
46
+ python_version = "3.12"
47
+ # gymnasium/scipy/joblib lack stubs, so don't error on their missing imports.
48
+ ignore_missing_imports = true
49
+
50
+ [tool.ruff]
51
+ # src layout, so ruff resolves `settings`/`experiments` as first-party.
52
+ src = ["src"]
53
+ # target-version is inferred from project.requires-python.
54
+ #packages = [
55
+ # "statrl",
56
+ # "statrl.experiments",
57
+ # "statrl.settings",
58
+ # "statrl.settings.bandits",
59
+ # "statrl.settings.bandits.stochastic",
60
+ # "statrl.settings.bandits.stochastic.anytime",
61
+ # "statrl.settings.bandits.stochastic.anytime.agents",
62
+ # "statrl.settings.bandits.stochastic.knownhorizon",
63
+ # "statrl.settings.bandits.stochastic.knownhorizon.wrappers",
64
+ # "statrl.settings.bandits.adversarial",
65
+ # "statrl.settings.bandits.adversarial.lipschitz",
66
+ # "statrl.settings.bandits.adversarial.lipschitz.agents",
67
+ #]
68
+
69
+ [tool.ruff.lint]
70
+ # Pinned explicitly: ruff's own default rule set has grown well past this
71
+ # selection in recent releases, and this project only wants pyflakes (F) plus
72
+ # the pycodestyle error subset (E4/E7/E9).
73
+ select = ["E4", "E7", "E9", "F"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
File without changes
File without changes
@@ -0,0 +1,95 @@
1
+
2
+ import pickle
3
+ import time
4
+ import numpy as np
5
+
6
+ def computeScoreDiffs(names: list[str], dump_scores: list[list[str]], timeHorizon: int, envName: str, root_folder: str) -> tuple[list[np.ndarray], list[np.ndarray], list[np.ndarray],list[np.ndarray],list[np.ndarray], list[np.ndarray], list[int]]:
7
+ """Turn per-replicate score dumps into regret statistics over time.
8
+
9
+ Loads every dump, subtracts each agent's cumulative score from the
10
+ oracle's averaged one to obtain regret, and summarizes the replicates by
11
+ their mean, median, and four quantiles.
12
+
13
+ Parameters
14
+ ----------
15
+ names : list of str
16
+ Agent names, in the same order as ``dump_scores``. Used to name the
17
+ per-agent regret pickles.
18
+ dump_scores : list of list of str
19
+ One list of dump filenames per agent. **The last entry must be the
20
+ oracle's**, and it is what every other entry is compared against — the
21
+ function has no other way to tell which agent is the reference.
22
+ timeHorizon : int
23
+ Number of rounds each run played.
24
+ envName : str
25
+ Environment name, used in the output filenames.
26
+ root_folder : str
27
+ Directory the regret pickles are written to.
28
+
29
+ Returns
30
+ -------
31
+ mean, median : list of ndarray
32
+ Per-agent mean and median regret at each sampled time.
33
+ quantile1, quantile2, quantile3, quantile4 : list of ndarray
34
+ Per-agent regret quantiles at levels 0.1, 0.25, 0.75, and 0.9. The
35
+ plots shade 0.1-0.9 and 0.25-0.75 as nested bands.
36
+ times : list of int
37
+ Sampled time steps, shared by every returned series.
38
+
39
+ Notes
40
+ -----
41
+ Long runs are downsampled to at most ~1000 points
42
+ (``skip = timeHorizon // 1000``), which bounds both plot size and memory.
43
+ Each returned series therefore has ``len(times)`` entries, not
44
+ ``timeHorizon``.
45
+
46
+ The oracle's score is averaged across its replicates *before* the
47
+ subtraction, so the result is regret against mean oracle performance
48
+ rather than a paired difference per replicate.
49
+ """
50
+
51
+ median = []
52
+ mean = []
53
+ quantile1 = []
54
+ quantile2 = []
55
+ quantile3 = []
56
+ quantile4 = []
57
+ nbAlgs = len(dump_scores) - 1
58
+
59
+ #Downsample the times, especially in case timeHorizon is huge.
60
+ skip = max(1, (timeHorizon // 1000))
61
+ times = [t for t in range(0,timeHorizon,skip)]
62
+
63
+ #file_oracle = open(dump_scores[-1], 'rb')
64
+ #scores_oracle = pickle.load(file_oracle)
65
+ # Comment the following line for BatchMabs:
66
+ #scores_oracle = scores_oracle[0]
67
+ #file_oracle.close()
68
+
69
+ data_o = []
70
+ for oracle_file in dump_scores[-1]:
71
+ with open(oracle_file, 'rb') as file:
72
+ scores_oi = pickle.load(file)
73
+ data_o.append([scores_oi[t] for t in times])
74
+ scores_oracle = np.mean(data_o, axis=0)
75
+
76
+ for j in range(nbAlgs):
77
+ data_j = []
78
+ for alg_file in dump_scores[j]:
79
+ with open(alg_file, 'rb') as file:
80
+ scores_ij = pickle.load(file)
81
+ data_j.append([scores_oracle[k] - scores_ij[t] for k, t in enumerate(times)])
82
+
83
+ filename = f"{root_folder}regret_{envName}_{names[j]}_{timeHorizon}_{j}_{time.time()}"
84
+ with open(filename, 'wb') as out_file:
85
+ pickle.dump(data_j, out_file)
86
+
87
+ mean.append(np.mean(data_j, axis=0))
88
+ median.append(np.quantile(data_j, 0.5, axis=0))
89
+ quantile1.append(np.quantile(data_j, 0.1, axis=0))
90
+ quantile2.append(np.quantile(data_j, 0.25, axis=0))
91
+ quantile3.append(np.quantile(data_j, 0.75, axis=0))
92
+ quantile4.append(np.quantile(data_j, 0.9, axis=0))
93
+
94
+ return mean,median,quantile1,quantile2, quantile3, quantile4,times
95
+
@@ -0,0 +1,124 @@
1
+
2
+
3
+ import statrl.experiments.onerun as oR
4
+ import statrl.experiments.parallelruns as pR
5
+ import statrl.experiments.analyzeruns as aR
6
+ import statrl.experiments.plotruns as plR
7
+ from statrl.experiments.utils import clear_auxiliaryfiles
8
+
9
+ import time
10
+ import os
11
+ from typing import Any
12
+ ROOT="results/"
13
+
14
+
15
+ def runLargeMulticoreExperiment(env: Any, agents: list[Any], oracle: Any, interact: Any, timeHorizon: int=1000, nbReplicates: int=100, root_folder: str=ROOT) -> None:
16
+ """Benchmark several agents on one environment and plot their regret.
17
+
18
+ For each agent it runs ``nbReplicates`` independent interactions in parallel, runs the oracle for
19
+ the same number, computes regret as the oracle's cumulative score minus each agent's, and writes
20
+ a logfile and regret figures under ``root_folder``.
21
+
22
+ Parameters
23
+ ----------
24
+ env : object
25
+ Environment to benchmark on. Must expose ``name``; an optional
26
+ ``displayname`` is used as the figure title when present.
27
+ agents : list of object
28
+ Agents to compare. Their ``name`` attributes must be distinct — dump
29
+ filenames and plot legends are keyed on them, so duplicates silently
30
+ merge two agents' results.
31
+ oracle : object
32
+ Reference agent defining zero regret, and the only one required to
33
+ expose a ``policy`` (it is written to the logfile). Must belong to the
34
+ same setting as ``agents``.
35
+ interact : statrl.experiments.onerun.Interaction
36
+ Interaction loop of the setting, shared by every agent in the run.
37
+ timeHorizon : int, default=1000
38
+ Number of rounds per interaction.
39
+ nbReplicates : int, default=100
40
+ Number of independent runs per agent. Regret quantiles are taken
41
+ across these, so a handful of replicates gives a very rough band.
42
+ root_folder : str, default='results/'
43
+ Output directory, created if absent. Must end with a separator.
44
+
45
+ Returns
46
+ -------
47
+ None
48
+ Everything is written to disk. ``root_folder`` receives a
49
+ ``logfile_*.txt``, one ``regret_*`` pickle per agent, and the figures
50
+ ``Regrets_*.png`` / ``.pdf`` in linear and log-y scale. The
51
+ intermediate ``aux_*`` dumps are deleted on the way out.
52
+
53
+ See Also
54
+ --------
55
+ statrl.experiments.parallelruns.multicoreRuns : The parallel layer underneath.
56
+ statrl.experiments.analyzeruns.computeScoreDiffs : Turns the dumps into regret statistics.
57
+ statrl.experiments.plotruns.plotScoreDiffs : Draws the figures.
58
+
59
+ Notes
60
+ -----
61
+ Cost grows as ``(len(agents) + 1) * nbReplicates * timeHorizon``. Start
62
+ small — the defaults already amount to 100 000 rounds per agent.
63
+
64
+ Examples
65
+ --------
66
+ >>> from statrl.settings.bandits.stochastic.anytime.envs.parametric import BernoulliBandit
67
+ >>> from statrl.settings.bandits.stochastic.anytime.agents.IMED import IMED
68
+ >>> from statrl.settings.bandits.stochastic.anytime.agents._Oracle import Oracle
69
+ >>> from statrl.settings.bandits.stochastic.anytime.agents._Random import Random
70
+ >>> from statrl.settings.bandits.stochastic.anytime.interaction import BanditInteraction
71
+ >>> from statrl.settings.utils import klBern
72
+ >>> env = BernoulliBandit([0.2, 0.9, 0.5]) # doctest: +SKIP
73
+ >>> runLargeMulticoreExperiment( # doctest: +SKIP
74
+ ... env,
75
+ ... agents=[IMED(env.number_arms, klBern), Random(env)],
76
+ ... oracle=Oracle(env),
77
+ ... interact=BanditInteraction(),
78
+ ... timeHorizon=1000, nbReplicates=50,
79
+ ... )
80
+ """
81
+ os.makedirs(root_folder, exist_ok=True)
82
+
83
+ envName = env.name
84
+ learners = agents
85
+
86
+ print("-"*30+"Massive Multicore Experiment"+"-"*30)
87
+ print(f'Environment: {envName}')
88
+ print(f'Learners: {[learner.name for learner in learners]}')
89
+ print(f'[INFO] Run {nbReplicates} many interactions of length {timeHorizon} for each learner:')
90
+ dump_scores = []
91
+ names = []
92
+ meanelapsedtimes = []
93
+
94
+ for learner in learners:
95
+ names.append(learner.name)
96
+ dump_scores_learner, meanelapsedtime_learner = pR.multicoreRuns(env, learner, interact, nbReplicates, timeHorizon, oR.oneRunWithDump, root_folder=root_folder)
97
+ dump_scores.append(dump_scores_learner)
98
+ meanelapsedtimes.append(meanelapsedtime_learner)
99
+
100
+ dump_scoresopt, meanelapsedtime = pR.multicoreRuns(env, oracle, interact, nbReplicates, timeHorizon,
101
+ oR.oneRunWithDump, root_folder=root_folder)
102
+ dump_scores.append(dump_scoresopt)
103
+
104
+ ## Report statistics and compute regret:
105
+ timestamp = str(time.time())
106
+ logfilename = f"{root_folder}logfile_{envName}_{timestamp}.txt"
107
+ with open(logfilename, 'w') as logfile:
108
+ logfile.write("Environment " + envName + "\n")
109
+ logfile.write("Optimal policy is: " + str(oracle.policy) + "\n")
110
+ logfile.write("Learners " + str([learner.name for learner in learners]) + "\n")
111
+ logfile.write("Time horizon is " + str(timeHorizon) + ", nb of replicates is " + str(nbReplicates) + "\n")
112
+ for name, meanelapsedtime in zip(names, meanelapsedtimes):
113
+ logfile.write(f"{name} average runtime is {meanelapsedtime}\n")
114
+ print("[INFO] A log-file has been generated in ", logfilename)
115
+ print("[INFO] Compute Statistics...")
116
+ mean, median, quantile1, quantile2,quantile3,quantile4, times = aR.computeScoreDiffs(names, dump_scores, timeHorizon, envName, root_folder=root_folder)
117
+ print("[INFO] Plot results...")
118
+
119
+ title = env.displayname if hasattr(env, "displayname") else envName
120
+ labelx,labely = interact.plotlabels
121
+ plR.plotScoreDiffs(names, envName, (title,labelx,labely), mean, median, quantile1, quantile2,quantile3,quantile4, times, timeHorizon, logfile=logfile, timestamp=timestamp, root_folder=root_folder)
122
+ print("[INFO] Clean Auxiliary files...")
123
+ clear_auxiliaryfiles(env, root_folder)
124
+ print("[INFO] Massive multicore experiment successfully completed.")
@@ -0,0 +1,111 @@
1
+
2
+ from statrl.experiments.utils import dump
3
+
4
+ import time
5
+
6
+ from abc import ABC, abstractmethod
7
+ from typing import Any
8
+ import numpy as np
9
+
10
+ class Interaction(ABC):
11
+ """Base class for the interaction loop of a setting.
12
+ """
13
+
14
+ @abstractmethod
15
+ def run(self, env: Any, learner: Any, horizon: int)-> np.ndarray:
16
+ """Run one interaction and return its cumulative expected score.
17
+
18
+ Parameters
19
+ ----------
20
+ env : object
21
+ Environment of the setting. Reset by the implementation.
22
+ learner : object
23
+ Agent of the setting. Reset by the implementation, so one instance
24
+ can serve many replicates.
25
+ horizon : int
26
+ Number of rounds to play.
27
+
28
+ Returns
29
+ -------
30
+ ndarray of shape (horizon,)
31
+ Cumulative *expected* reward. Implementations must return exactly
32
+ ``horizon`` entries — :func:`oneRunWithDump` asserts it.
33
+ """
34
+ return np.array([])
35
+
36
+
37
+ @abstractmethod
38
+ def renderrun(self, env: Any, learner: Any, horizon: int) -> None:
39
+ """Run one interaction with rendering enabled, returning no score.
40
+
41
+ Parameters
42
+ ----------
43
+ env : object
44
+ Environment of the setting; the implementation attaches renderers.
45
+ learner : object
46
+ Agent of the setting.
47
+ horizon : int
48
+ Number of rounds to play.
49
+ """
50
+ ...
51
+
52
+ @property
53
+ @abstractmethod
54
+ def plotlabels(self) -> tuple[str, str]:
55
+ """tuple of (str, str): Axis labels ``(x, y)`` for the regret plots.
56
+
57
+ Read by
58
+ :func:`~statrl.experiments.massiveruns.runLargeMulticoreExperiment`
59
+ and forwarded to
60
+ :func:`~statrl.experiments.plotruns.plotScoreDiffs`. Settings whose
61
+ rounds are not time steps override it — the batch setting labels its
62
+ x-axis by episode.
63
+ """
64
+ raise NotImplementedError
65
+
66
+
67
+
68
+
69
+ def oneRunWithDump(env: Any, learner: Any, interact: Any, timeHorizon: int, root_folder: str) -> str:
70
+ """Run one replicate and pickle its score series to disk.
71
+
72
+ The unit of work handed to :mod:`joblib` by
73
+ :func:`~statrl.experiments.parallelruns.multicoreRuns`. Results travel
74
+ back through the filesystem rather than through the return value, because
75
+ returning full score series from every worker would serialize
76
+ ``nbReplicates x timeHorizon`` floats through the process pool.
77
+
78
+ Parameters
79
+ ----------
80
+ env : object
81
+ Environment for this replicate; already a private deep copy.
82
+ learner : object
83
+ Agent for this replicate; already a private deep copy.
84
+ interact : Interaction
85
+ The interaction loop of the setting.
86
+ timeHorizon : int
87
+ Number of rounds to play.
88
+ root_folder : str
89
+ Directory the dump is written to. Must end with a separator, and must
90
+ already exist.
91
+
92
+ Returns
93
+ -------
94
+ str
95
+ Path of the pickle written, of the form
96
+ ``{root_folder}aux_{env}_scores_{agent}_{horizon}_{timestamp}``. The
97
+ ``aux_`` prefix is what
98
+ :func:`~statrl.experiments.utils.clear_auxiliaryfiles` deletes once
99
+ the statistics have been computed.
100
+
101
+ Raises
102
+ ------
103
+ AssertionError
104
+ If the interaction returned fewer or more than ``timeHorizon`` scores.
105
+ """
106
+ scoretimeseries=interact.run(env,learner,timeHorizon)
107
+ assert len(scoretimeseries) == timeHorizon
108
+
109
+ tag = f"{env.name}_scores_{learner.name}_{timeHorizon}_{time.time()}"
110
+ filename = dump(scoretimeseries,"aux",tag,root_folder)
111
+ return filename