You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat!: Better reproducibility management of dynamic problems with SeededEnvironment (#73)
* refactor: replace MersenneTwister with Xoshiro
* refactor: replace MersenneTwister with Xoshiro
* feat: implement the SeededEnv wrapper, and adapt and cleanup policy evaluation and various dynamic environments
* feat: user now needs to implement build_environment insteadof generate_environment
* test: update tests + unit tests for SeededEnvironment
* doc: update documentation
* cleanup
You implement **`build_environment`**, which returns a *bare* environment. The package then automatically wraps it in a [`SeededEnvironment`](@ref) (attaching a seed and RNG) through two public entry points:
132
+
133
+
```julia
134
+
generate_environment(bench; seed=nothing, kwargs...) -> SeededEnvironment # one env
135
+
generate_environments(bench, n; seed=nothing, kwargs...) -> Vector{SeededEnvironment} # n envs
136
+
```
137
+
138
+
For environments that cannot be drawn independently (e.g. loaded from files), override `generate_environments` instead of implementing `build_environment`.
139
+
An override must return already-wrapped `SeededEnvironment`s (use `SeededEnvironment(env; seed=...)`).
140
+
Do not override `generate_environment`: it just delegates to `generate_environments`.
141
+
132
142
### `AbstractEnvironment` interface
133
143
134
-
Your environment type must implement:
144
+
Your environment must be **stateless about randomness**: it does *not* store its own RNG or seed.
145
+
All randomness is owned by the wrapping [`SeededEnvironment`](@ref), which passes its `rng` into every `reset!` and `step!`.
146
+
Draw all stochasticity from that `rng` so that re-seeding the wrapper (via [`reset_to_initial!`](@ref)) replays an episode exactly.
135
147
136
148
```julia
137
-
get_seed(env::MyEnv) # Return the RNG seed used at creation
138
-
reset!(env::MyEnv; reset_rng::Bool, seed=get_seed(env)) # Reset to initial state
139
-
observe(env::MyEnv) -> (observation, info) # Current observation
140
-
step!(env::MyEnv, action) -> reward # Apply action, advance state
141
-
is_terminated(env::MyEnv) -> Bool # True when episode has ended
149
+
reset!(env::MyEnv, rng::AbstractRNG) # Reset to a starting state, sampling from rng
150
+
observe(env::MyEnv) -> (observation, state) # Current observation and internal state
151
+
step!(env::MyEnv, action, rng::AbstractRNG) -> reward # Apply action, advance state (draw from rng)
152
+
is_terminated(env::MyEnv) -> Bool # True when the episode has ended
142
153
```
143
154
155
+
Environments may ignore the `rng` argument.
156
+
144
157
### Baseline policies (required for `generate_dataset`)
Copy file name to clipboardExpand all lines: docs/src/using_benchmarks.md
+13-3Lines changed: 13 additions & 3 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -132,15 +132,25 @@ rollout and returns the resulting trajectory.
132
132
133
133
## Seed / RNG control
134
134
135
-
All `generate_dataset` and `generate_environments` calls accept either `seed`
136
-
(creates an internal `MersenneTwister`) or `rng` for full control:
135
+
`generate_dataset` accepts either `seed` (creates an internal `Xoshiro`) or `rng` (any `AbstractRNG`)::
137
136
138
137
```julia
139
138
using Random
140
-
rng =MersenneTwister(42)
139
+
rng =Xoshiro(42)
141
140
dataset =generate_dataset(bench, 50; rng=rng)
142
141
```
143
142
143
+
`generate_environments` takes a `seed`:
144
+
145
+
```julia
146
+
envs =generate_environments(bench, 10; seed=0)
147
+
```
148
+
149
+
### Reproducibility in dynamic benchmarks
150
+
151
+
Each environment returned by `generate_environments` or `generate_environment` is a `SeededEnvironment`: a wrapper that owns the random number generator and a stored seed.
152
+
It is the single source of randomness for the episode, so re-running a policy on the same environment replays the exact same trajectory. `evaluate_policy!` resets to the stored seed before each run, which is what makes evaluation reproducible (pass `seed=...` to override the stored seed for a given run).
0 commit comments