rlshiftlab 0.2.0__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.
- rlshiftlab-0.2.0/.github/workflows/ci.yml +22 -0
- rlshiftlab-0.2.0/.gitignore +8 -0
- rlshiftlab-0.2.0/CHANGELOG.md +41 -0
- rlshiftlab-0.2.0/LICENSE +21 -0
- rlshiftlab-0.2.0/PKG-INFO +341 -0
- rlshiftlab-0.2.0/PLAN.md +254 -0
- rlshiftlab-0.2.0/PLAN_V0.2.md +313 -0
- rlshiftlab-0.2.0/README.md +324 -0
- rlshiftlab-0.2.0/docs/.gitkeep +0 -0
- rlshiftlab-0.2.0/docs/cliffwalking_degradation.png +0 -0
- rlshiftlab-0.2.0/examples/.gitkeep +0 -0
- rlshiftlab-0.2.0/examples/cartpole_heuristic.py +70 -0
- rlshiftlab-0.2.0/examples/cliffwalking_multiseed.py +236 -0
- rlshiftlab-0.2.0/examples/cliffwalking_q_vs_sarsa.py +132 -0
- rlshiftlab-0.2.0/examples/custom_metrics.py +90 -0
- rlshiftlab-0.2.0/pyproject.toml +40 -0
- rlshiftlab-0.2.0/src/rlshiftlab/__init__.py +23 -0
- rlshiftlab-0.2.0/src/rlshiftlab/comparisons.py +301 -0
- rlshiftlab-0.2.0/src/rlshiftlab/exports.py +98 -0
- rlshiftlab-0.2.0/src/rlshiftlab/groups.py +440 -0
- rlshiftlab-0.2.0/src/rlshiftlab/py.typed +0 -0
- rlshiftlab-0.2.0/src/rlshiftlab/records.py +50 -0
- rlshiftlab-0.2.0/src/rlshiftlab/report.py +273 -0
- rlshiftlab-0.2.0/src/rlshiftlab/results.py +200 -0
- rlshiftlab-0.2.0/src/rlshiftlab/runner.py +458 -0
- rlshiftlab-0.2.0/src/rlshiftlab/shifts.py +170 -0
- rlshiftlab-0.2.0/src/rlshiftlab/stats.py +208 -0
- rlshiftlab-0.2.0/tests/.gitkeep +0 -0
- rlshiftlab-0.2.0/tests/test_comparisons.py +214 -0
- rlshiftlab-0.2.0/tests/test_crossed_stats.py +122 -0
- rlshiftlab-0.2.0/tests/test_groups.py +175 -0
- rlshiftlab-0.2.0/tests/test_metrics.py +287 -0
- rlshiftlab-0.2.0/tests/test_results.py +178 -0
- rlshiftlab-0.2.0/tests/test_runner.py +276 -0
- rlshiftlab-0.2.0/tests/test_shifts.py +245 -0
- rlshiftlab-0.2.0/tests/test_stats.py +104 -0
- rlshiftlab-0.2.0/tests/test_v02_outputs.py +214 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on: [push, pull_request]
|
|
4
|
+
|
|
5
|
+
jobs:
|
|
6
|
+
checks:
|
|
7
|
+
runs-on: ubuntu-latest
|
|
8
|
+
strategy:
|
|
9
|
+
fail-fast: false
|
|
10
|
+
matrix:
|
|
11
|
+
python-version: ["3.9", "3.12"]
|
|
12
|
+
steps:
|
|
13
|
+
- uses: actions/checkout@v4
|
|
14
|
+
- uses: actions/setup-python@v5
|
|
15
|
+
with:
|
|
16
|
+
python-version: ${{ matrix.python-version }}
|
|
17
|
+
- name: Install development dependencies
|
|
18
|
+
run: python -m pip install -e ".[dev]"
|
|
19
|
+
- name: Run tests
|
|
20
|
+
run: python -m pytest -q
|
|
21
|
+
- name: Run Ruff
|
|
22
|
+
run: python -m ruff check .
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.2.0 (unreleased)
|
|
4
|
+
|
|
5
|
+
- Added evaluation of caller-supplied groups of frozen policies, with equally
|
|
6
|
+
weighted member means, descriptive observed member spread, and pointwise
|
|
7
|
+
crossed member/episode bootstrap confidence intervals.
|
|
8
|
+
- Added direct left-minus-right comparisons for single policies and groups
|
|
9
|
+
on complete, paired evaluation episode keys. Group comparisons resample
|
|
10
|
+
training members independently on each side.
|
|
11
|
+
- Added step-contribution and episode-summary callbacks for custom scalar
|
|
12
|
+
per-episode metrics, with raw episode records for audit.
|
|
13
|
+
- Added separate group, member, custom-metric, episode, and comparison CSVs;
|
|
14
|
+
custom metric and group plots; and self-contained HTML reports that can
|
|
15
|
+
include explicitly selected comparisons.
|
|
16
|
+
- Added a multi-seed CliffWalking example and a small custom-metrics API
|
|
17
|
+
example. The package continues to evaluate already frozen policies; the
|
|
18
|
+
examples keep any training code outside the package.
|
|
19
|
+
- Preserved the v0.1 single-policy API and its 14-column CSV, with a v0.1
|
|
20
|
+
baseline archive and reproducibility manifest in `artifacts/v0.1/`.
|
|
21
|
+
|
|
22
|
+
Intervals are pointwise and approximate, and observed member spread also
|
|
23
|
+
contains finite evaluation noise. Local packaging and install validation is
|
|
24
|
+
recorded in `WORK_LOG.md`; publication remains a separate human action.
|
|
25
|
+
|
|
26
|
+
## 0.1.0 (unreleased)
|
|
27
|
+
|
|
28
|
+
- Added an installable, typed Python package with three controlled shifts:
|
|
29
|
+
observation noise, action slip, and sticky actions.
|
|
30
|
+
- Added paired, seeded evaluation of frozen callable policies and confidence
|
|
31
|
+
intervals for mean return, nominal-minus-shifted drop, and optional success
|
|
32
|
+
rate.
|
|
33
|
+
- Added deterministic tidy CSV export, a text summary, headless degradation
|
|
34
|
+
plots, and a self-contained HTML report.
|
|
35
|
+
- Added runnable CliffWalking Q-learning/SARSA and CartPole heuristic examples,
|
|
36
|
+
including generated CSV, HTML, and PNG artifacts.
|
|
37
|
+
- Added a pytest suite and GitHub Actions pytest and Ruff checks on Python
|
|
38
|
+
3.9 and 3.12.
|
|
39
|
+
- Added the project specification, usage documentation, and MIT license.
|
|
40
|
+
|
|
41
|
+
Its packaging check was deferred to the v0.2 final validation gate.
|
rlshiftlab-0.2.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 RLShiftLab 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.
|
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: rlshiftlab
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Stress-test frozen RL policies under controlled shifts and get degradation curves with confidence intervals.
|
|
5
|
+
License: MIT
|
|
6
|
+
License-File: LICENSE
|
|
7
|
+
Requires-Python: >=3.9
|
|
8
|
+
Requires-Dist: gymnasium<2,>=0.29
|
|
9
|
+
Requires-Dist: matplotlib>=3.6
|
|
10
|
+
Requires-Dist: numpy>=1.23
|
|
11
|
+
Provides-Extra: dev
|
|
12
|
+
Requires-Dist: build; extra == 'dev'
|
|
13
|
+
Requires-Dist: pytest>=7; extra == 'dev'
|
|
14
|
+
Requires-Dist: ruff; extra == 'dev'
|
|
15
|
+
Requires-Dist: twine; extra == 'dev'
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
|
|
18
|
+
# RLShiftLab
|
|
19
|
+
|
|
20
|
+
RLShiftLab 0.2.0 evaluates frozen policies under controlled observation and
|
|
21
|
+
action shifts. A single-policy run produces a degradation curve: shift
|
|
22
|
+
strength on the x axis, mean episode return with a pointwise 95% confidence
|
|
23
|
+
interval on the y axis. Version 0.2 also evaluates groups of already trained
|
|
24
|
+
policies, reports the observed spread across their members, compares policies
|
|
25
|
+
directly on paired evaluation episodes, and records custom per-episode
|
|
26
|
+
metrics. Each shift is varied separately. Reports describe measured
|
|
27
|
+
differences and do not establish their causes.
|
|
28
|
+
|
|
29
|
+
## Install locally
|
|
30
|
+
|
|
31
|
+
Requires Python 3.9 or newer. From a checkout of this repository:
|
|
32
|
+
|
|
33
|
+
```sh
|
|
34
|
+
python -m pip install -e .
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
For tests and lint checks, use `python -m pip install -e ".[dev]"`, then
|
|
38
|
+
`python -m pytest -q` and `python -m ruff check .`.
|
|
39
|
+
|
|
40
|
+
## Quickstart
|
|
41
|
+
|
|
42
|
+
Save this as `quickstart.py` and run `python quickstart.py` from a writable
|
|
43
|
+
directory after installing the package:
|
|
44
|
+
|
|
45
|
+
```python
|
|
46
|
+
import gymnasium as gym
|
|
47
|
+
from rlshiftlab import ActionSlip, ObservationNoise, Suite
|
|
48
|
+
|
|
49
|
+
def policy(obs):
|
|
50
|
+
return int(obs[2] + 0.5 * obs[3] > 0)
|
|
51
|
+
|
|
52
|
+
suite = Suite(
|
|
53
|
+
env_fn=lambda: gym.make("CartPole-v1"),
|
|
54
|
+
shifts=[ObservationNoise(levels=[0.01, 0.05]),
|
|
55
|
+
ActionSlip(levels=[0.05, 0.1])],
|
|
56
|
+
n_episodes=20,
|
|
57
|
+
seed=0,
|
|
58
|
+
success_fn=lambda ep: ep.truncated,
|
|
59
|
+
)
|
|
60
|
+
results = suite.evaluate({"angle+velocity": policy})
|
|
61
|
+
|
|
62
|
+
print(results.summary())
|
|
63
|
+
results.to_csv("results.csv")
|
|
64
|
+
results.plot("degradation.png")
|
|
65
|
+
results.to_html("report.html")
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
`policy` is a callable from observation to action. RLShiftLab does not train
|
|
69
|
+
or alter it. Use a deterministic policy for paired comparisons; if a policy
|
|
70
|
+
has a `reset()` method, RLShiftLab calls it at the start of each episode.
|
|
71
|
+
The suite gives every policy and condition the same sequence of environment
|
|
72
|
+
reset seeds. Shift randomness is also seeded per episode, and reused across
|
|
73
|
+
levels and policies for the same shift. Repeated runs with the same inputs and
|
|
74
|
+
seed produce identical CSV rows.
|
|
75
|
+
|
|
76
|
+
The nominal row has no shift. For shifted rows, **drop** is the nominal episode
|
|
77
|
+
return minus the shifted return, paired by episode seed; a positive value means
|
|
78
|
+
lower return under the shift. Mean-return and drop intervals use percentile
|
|
79
|
+
bootstrap over episodes. Optional success-rate intervals use the Wilson
|
|
80
|
+
method. The CSV includes mean episode length and, when `success_fn` is given,
|
|
81
|
+
success rate. A policy's behavior can still be stateful or stochastic if its
|
|
82
|
+
callable is written that way; the suite cannot guarantee reproducibility for
|
|
83
|
+
randomness that the policy manages independently. This original
|
|
84
|
+
`Suite.evaluate()` interface and its 14-column `to_csv()` export remain
|
|
85
|
+
compatible with v0.1.
|
|
86
|
+
|
|
87
|
+
## Shifts and outputs
|
|
88
|
+
|
|
89
|
+
- `ObservationNoise`: Gaussian noise for Box observations; random replacement
|
|
90
|
+
for Discrete observations.
|
|
91
|
+
- `ActionSlip`: random replacement of an action before it reaches the environment.
|
|
92
|
+
- `StickyActions`: repeat the previously executed action with a given probability.
|
|
93
|
+
|
|
94
|
+
Discrete random replacement may select the original value. At replacement
|
|
95
|
+
probability `p` with `n` equally likely values, the observed mismatch rate is
|
|
96
|
+
`p * (1 - 1/n)`. The level is the replacement probability, not a guaranteed
|
|
97
|
+
mismatch rate.
|
|
98
|
+
|
|
99
|
+
`Results.to_csv()` writes tidy rows; `Results.plot()` returns a Matplotlib
|
|
100
|
+
figure and optionally writes a PNG; `Results.to_html()` writes a self-contained
|
|
101
|
+
report with the plot, setup, summary, and full results table. Plotting uses the
|
|
102
|
+
headless Agg canvas.
|
|
103
|
+
|
|
104
|
+
## v0.2: groups of frozen policies
|
|
105
|
+
|
|
106
|
+
Supply a mapping from group name to member ID to an **already trained**
|
|
107
|
+
callable policy. Each member ID should identify a separately trained frozen
|
|
108
|
+
policy, such as a run with its own training seed. Multiple checkpoints from
|
|
109
|
+
one training run are not independent training members. RLShiftLab evaluates
|
|
110
|
+
the supplied members; it does not train them or verify their independence.
|
|
111
|
+
The runnable multi-seed CliffWalking example in `examples/` shows how to
|
|
112
|
+
train the members outside the package and pass their frozen policies to
|
|
113
|
+
`evaluate_groups()`.
|
|
114
|
+
|
|
115
|
+
For example, after creating four frozen CliffWalking callables in your own
|
|
116
|
+
training code, configure `suite` with that CliffWalking environment and its
|
|
117
|
+
evaluation shifts. The earlier CartPole quickstart suite is for CartPole
|
|
118
|
+
policies and cannot evaluate these CliffWalking tables:
|
|
119
|
+
|
|
120
|
+
```python
|
|
121
|
+
groups = {
|
|
122
|
+
"Q-learning": {"train-0": q_policy_0, "train-1": q_policy_1},
|
|
123
|
+
"SARSA": {"train-0": sarsa_policy_0, "train-1": sarsa_policy_1},
|
|
124
|
+
}
|
|
125
|
+
group_results = suite.evaluate_groups(groups)
|
|
126
|
+
group_results.to_csv("groups.csv")
|
|
127
|
+
group_results.to_member_csv("members.csv")
|
|
128
|
+
group_results.to_episode_csv("episodes.csv")
|
|
129
|
+
group_results.plot("group_degradation.png", band="ci")
|
|
130
|
+
group_results.plot("member_spread.png", band="std")
|
|
131
|
+
|
|
132
|
+
contrast = group_results.compare("Q-learning", "SARSA", metric="return")
|
|
133
|
+
contrast.to_csv("comparison.csv")
|
|
134
|
+
contrast.plot("comparison.png")
|
|
135
|
+
group_results.to_html("group_report.html", comparisons=[contrast])
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
The same `Suite` configuration works for both single policies and groups.
|
|
139
|
+
At least two members are required per group, but groups may have different
|
|
140
|
+
member counts. Each member receives the same ordered evaluation reset seeds
|
|
141
|
+
at every condition. The group mean first averages episodes within a member,
|
|
142
|
+
then gives each member equal weight. The reported `member_std`, minimum, and
|
|
143
|
+
maximum describe the observed spread of member means. That spread includes
|
|
144
|
+
finite evaluation noise and is neither a confidence interval nor a pure
|
|
145
|
+
estimate of training variance.
|
|
146
|
+
|
|
147
|
+
Group confidence intervals use a crossed bootstrap that resamples members
|
|
148
|
+
and evaluation episode keys. They are approximate **pointwise** 95%
|
|
149
|
+
intervals, not simultaneous bands or multiple-comparison-adjusted claims.
|
|
150
|
+
Intervals from only a few members may be unstable. Single-policy success
|
|
151
|
+
rates retain Wilson intervals; group success rates use the crossed bootstrap.
|
|
152
|
+
The [pigeonhole bootstrap paper](https://arxiv.org/abs/0712.1111) informs the
|
|
153
|
+
two-axis resampling design, and [Agarwal et al.'s RL evaluation
|
|
154
|
+
paper](https://arxiv.org/abs/2108.13264) motivates careful reporting across
|
|
155
|
+
trained runs. Neither source guarantees exact coverage for a particular
|
|
156
|
+
RLShiftLab experiment.
|
|
157
|
+
|
|
158
|
+
## Direct comparisons and custom metrics
|
|
159
|
+
|
|
160
|
+
Call `Results.compare(left, right, metric="return")` for two individual
|
|
161
|
+
policies, or `GroupResults.compare(...)` for two groups. Every nominal and
|
|
162
|
+
shifted comparison uses the same evaluation episode keys on both sides;
|
|
163
|
+
missing or duplicate keys are errors. The estimate is **left minus right**.
|
|
164
|
+
A positive value means the left side has a larger measured metric, which is
|
|
165
|
+
not always better (for example, more falls or longer episodes). Single-policy
|
|
166
|
+
comparisons bootstrap the paired episode differences. Group comparisons
|
|
167
|
+
resample members independently on each side while sharing evaluation episode
|
|
168
|
+
keys; equal member ID strings do not assert matched training runs. Each
|
|
169
|
+
contrast has its own pointwise 95% interval and can be saved as CSV, a
|
|
170
|
+
zero-reference plot, or standalone HTML. Pass a list to `to_html(...,
|
|
171
|
+
comparisons=[contrast])` to include chosen comparisons in an evaluation
|
|
172
|
+
report.
|
|
173
|
+
|
|
174
|
+
Pass `step_metrics` and `episode_metrics` to `Suite` to collect scalar metrics
|
|
175
|
+
in both single and group runs. Each step callback contributes a finite
|
|
176
|
+
number; RLShiftLab sums it over the episode. For the configured CliffWalking
|
|
177
|
+
environment, a reward of -100 marks a fall, so this metric counts falls:
|
|
178
|
+
|
|
179
|
+
```python
|
|
180
|
+
suite = Suite(
|
|
181
|
+
env_fn=make_cliffwalking_env,
|
|
182
|
+
shifts=[ActionSlip([0.1, 0.2])],
|
|
183
|
+
n_episodes=100,
|
|
184
|
+
seed=1000,
|
|
185
|
+
step_metrics={"cliff_falls": lambda t: float(t.reward == -100.0)},
|
|
186
|
+
episode_metrics={"return_per_step": lambda ep: ep.episode_return / ep.length},
|
|
187
|
+
)
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
Here `make_cliffwalking_env` is your environment factory. The -100 rule is
|
|
191
|
+
specific to that environment, not a generic fall detector. Step callbacks
|
|
192
|
+
receive a `Transition` with policy-visible observations, requested action,
|
|
193
|
+
next observation, reward, termination/truncation flags, step index, episode
|
|
194
|
+
seed, and step `info`. Its requested action may differ from the action
|
|
195
|
+
actually executed after action slip or sticky actions. Episode callbacks
|
|
196
|
+
receive a copy of the final `EpisodeResult`, including step metric totals,
|
|
197
|
+
length, return, flags, and final step `info`. Each callback must return one
|
|
198
|
+
finite numeric scalar; no full trajectory is retained. The resulting
|
|
199
|
+
`metric_rows` and `to_metrics_csv()` summarize each metric, while
|
|
200
|
+
`to_episode_csv()` includes scalar values under `metric:<name>` columns.
|
|
201
|
+
|
|
202
|
+
The raw episode CSV records policy/group, member, shift, level, episode index,
|
|
203
|
+
reset seed, return, length, flags, optional success, and custom scalars. It
|
|
204
|
+
does not serialize arbitrary observations or `info` objects. These records
|
|
205
|
+
make pairing and reported aggregates auditable.
|
|
206
|
+
|
|
207
|
+
Repeated runs with the same inputs, software versions, suite seed, and pure
|
|
208
|
+
deterministic or consistently resettable policy and metric callbacks produce
|
|
209
|
+
the same CSV bytes. Suite seeds control Gymnasium resets and shift randomness;
|
|
210
|
+
they do not control randomness inside arbitrary user policies or callbacks.
|
|
211
|
+
|
|
212
|
+
| Result object | Main export | Additional exports |
|
|
213
|
+
| --- | --- | --- |
|
|
214
|
+
| `Results` | `to_csv(path)` keeps the v0.1 14 columns | `to_metrics_csv(path)`, `to_episode_csv(path)`, `plot(metric=...)`, `to_html(path, comparisons=...)` |
|
|
215
|
+
| `GroupResults` | `to_csv(path)` writes aggregate rows | `to_member_csv(path)`, `to_metrics_csv(path)`, `to_episode_csv(path)`, `plot(metric=..., band="ci" or "std")`, `to_html(path, comparisons=...)` |
|
|
216
|
+
| `ComparisonResults` | `to_csv(path)` writes left-minus-right rows | `plot(path)`, `to_html(path)`, `summary()` |
|
|
217
|
+
|
|
218
|
+
## v0.2 examples
|
|
219
|
+
|
|
220
|
+
Run either example from the repository root:
|
|
221
|
+
|
|
222
|
+
```sh
|
|
223
|
+
python examples/cliffwalking_multiseed.py
|
|
224
|
+
python examples/custom_metrics.py
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
The [multi-seed CliffWalking example](examples/cliffwalking_multiseed.py)
|
|
228
|
+
trains its Q-learning and SARSA tables in the example code, then evaluates
|
|
229
|
+
them as frozen members. It writes separate group, member, metric, episode,
|
|
230
|
+
and paired-comparison files, plus plots and HTML, under
|
|
231
|
+
[`artifacts/v0.2/cliffwalking/`](artifacts/v0.2/cliffwalking/).
|
|
232
|
+
The [small custom-metrics example](examples/custom_metrics.py) uses fixed toy
|
|
233
|
+
policies to demonstrate step and episode metric callbacks and comparison
|
|
234
|
+
exports under [`artifacts/v0.2/custom_metrics/`](artifacts/v0.2/custom_metrics/).
|
|
235
|
+
Its outputs illustrate the API, not a research benchmark. The package does
|
|
236
|
+
not perform the training in either case.
|
|
237
|
+
|
|
238
|
+
### Measured multi-seed CliffWalking run
|
|
239
|
+
|
|
240
|
+
The generated [v0.2 group results](artifacts/v0.2/cliffwalking/group.csv)
|
|
241
|
+
used deterministic CliffWalking-v1, ten Q-learning training seeds (0–9) and
|
|
242
|
+
ten SARSA training seeds (100–109), with 500 training episodes per member.
|
|
243
|
+
Each frozen member was evaluated on the same 100 reset seeds per condition
|
|
244
|
+
for one nominal condition and eight shift levels (18,000 raw episodes).
|
|
245
|
+
[run.json](artifacts/v0.2/cliffwalking/run.json) records the configuration,
|
|
246
|
+
software versions, and source/output hashes.
|
|
247
|
+
|
|
248
|
+
Nominal mean return across members was -13.000 for Q-learning (observed
|
|
249
|
+
member SD 0.000) and -35.300 for SARSA (SD 57.870). In the
|
|
250
|
+
[member results](artifacts/v0.2/cliffwalking/member.csv), SARSA training seed
|
|
251
|
+
100 had nominal return -200, length 200, and no success; each of the other
|
|
252
|
+
nine SARSA seeds had return -17, length 17, and success. These member scores
|
|
253
|
+
underlie the reported nominal spread; they do not identify why that one
|
|
254
|
+
training outcome occurred.
|
|
255
|
+
|
|
256
|
+
At action slip 0.3, group mean returns were -149.928 for Q-learning and
|
|
257
|
+
-41.324 for SARSA. The direct Q-learning-minus-SARSA return difference was
|
|
258
|
+
-108.604, with a pointwise 95% CI of [-138.084, -79.740]. The corresponding
|
|
259
|
+
[cliff-fall difference](artifacts/v0.2/cliffwalking/falls_comparison.csv) was
|
|
260
|
+
+1.066 falls per episode [0.808, 1.329], so Q-learning had more recorded
|
|
261
|
+
falls under that shift in this configured run. These are measured differences
|
|
262
|
+
for these trained tables, environment settings, and evaluation seeds; they
|
|
263
|
+
are not causal explanations or a general ranking of the algorithms.
|
|
264
|
+
|
|
265
|
+

|
|
266
|
+
|
|
267
|
+
The [return comparison CSV](artifacts/v0.2/cliffwalking/return_comparison.csv),
|
|
268
|
+
[episode audit CSV](artifacts/v0.2/cliffwalking/episodes.csv), and
|
|
269
|
+
[standalone report](artifacts/v0.2/cliffwalking/report.html) provide the
|
|
270
|
+
condition-level and per-episode evidence. A repeated execution with the
|
|
271
|
+
same configuration produced byte-identical hashes for all 13 generated
|
|
272
|
+
outputs. That repeatability applies to this deterministic example and
|
|
273
|
+
software setup, not to arbitrary user-supplied stochastic callbacks.
|
|
274
|
+
|
|
275
|
+
The [toy custom-metrics outputs](artifacts/v0.2/custom_metrics/) use two
|
|
276
|
+
fixed policies and 20 paired evaluation episodes per condition. At action
|
|
277
|
+
slip 0.5, the measured always-zero-minus-always-one return difference was
|
|
278
|
+
0.85 [0.5, 1.2], while its unsafe-event difference was -0.5 [-0.7, -0.3].
|
|
279
|
+
This demonstrates the metric and comparison APIs; it is not a research
|
|
280
|
+
benchmark.
|
|
281
|
+
|
|
282
|
+
## Historical v0.1 examples and measured results
|
|
283
|
+
|
|
284
|
+
The original v0.1 repository contains a CliffWalking Q-learning versus SARSA example and a
|
|
285
|
+
CartPole comparison of two hand-coded policies in `examples/`. Training code
|
|
286
|
+
for CliffWalking stays in the example; the RLShiftLab package evaluates only
|
|
287
|
+
the resulting frozen policies. The CliffWalking example uses the registered
|
|
288
|
+
`CliffWalking-v0` when available, or `CliffWalking-v1` with
|
|
289
|
+
`is_slippery=False` on newer Gymnasium versions. See the [Gymnasium
|
|
290
|
+
CliffWalking documentation](https://gymnasium.farama.org/v1.3.0/environments/toy_text/cliff_walking/)
|
|
291
|
+
for the v1 environment and its optional slippery mode.
|
|
292
|
+
|
|
293
|
+
Run the examples from the repository root:
|
|
294
|
+
|
|
295
|
+
```sh
|
|
296
|
+
python examples/cliffwalking_q_vs_sarsa.py
|
|
297
|
+
python examples/cartpole_heuristic.py
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
The CliffWalking run writes [results.csv](results.csv),
|
|
301
|
+
[report.html](report.html), and the figure below. The CartPole run writes
|
|
302
|
+
[results.csv](artifacts/cartpole/results.csv),
|
|
303
|
+
[report.html](artifacts/cartpole/report.html), and
|
|
304
|
+
[degradation.png](artifacts/cartpole/degradation.png) under
|
|
305
|
+
`artifacts/cartpole/`.
|
|
306
|
+
|
|
307
|
+

|
|
308
|
+
|
|
309
|
+
In the generated CliffWalking results (200 evaluation episodes per condition),
|
|
310
|
+
nominal mean return was -13.000 for Q-learning and -19.000 for SARSA. At action
|
|
311
|
+
slip 0.3, mean return was -198.250 for Q-learning and -48.815 for SARSA. The
|
|
312
|
+
paired drops from nominal were 185.250 (95% CI [154.945, 220.342]) and 29.815
|
|
313
|
+
(95% CI [23.120, 37.440]), respectively. This run measured a larger drop for
|
|
314
|
+
Q-learning at that shift level. The example trained one table for each
|
|
315
|
+
algorithm with 500 episodes before evaluation; it does not measure variation
|
|
316
|
+
across training seeds.
|
|
317
|
+
|
|
318
|
+
In the generated CartPole results (100 evaluation episodes per condition),
|
|
319
|
+
nominal mean return was 500.000 for the angle+velocity heuristic and 41.040
|
|
320
|
+
for the angle-only heuristic. At observation noise 0.2, their mean returns were
|
|
321
|
+
267.400 and 38.310, respectively. These values describe the two particular
|
|
322
|
+
frozen policies and the configured evaluation seeds.
|
|
323
|
+
|
|
324
|
+
## What v0.2 does not do
|
|
325
|
+
|
|
326
|
+
Version 0.2 does not train or adapt policies inside the package, measure
|
|
327
|
+
recovery, apply reward shifts or delays, change environment dynamics, support
|
|
328
|
+
vectorized environments or a CLI, or accept Dict, MultiDiscrete, or Tuple
|
|
329
|
+
observation spaces. A group member ID is caller-provided provenance, not
|
|
330
|
+
proof that training runs were independent. Reports do not provide causal
|
|
331
|
+
explanations or multiplicity-adjusted confidence intervals.
|
|
332
|
+
|
|
333
|
+
## Roadmap after v0.2
|
|
334
|
+
|
|
335
|
+
Future work may add learners and adaptation with recovery metrics, reward
|
|
336
|
+
shifts and delays, environment-specific dynamics hooks, and Dict and
|
|
337
|
+
MultiDiscrete spaces.
|
|
338
|
+
|
|
339
|
+
## License
|
|
340
|
+
|
|
341
|
+
MIT; see [LICENSE](LICENSE).
|