Skip to content

Commit 8cbfe2f

Browse files
committed
Add data folder with demo_types and collect method + tests
1 parent 6773e3f commit 8cbfe2f

3 files changed

Lines changed: 148 additions & 0 deletions

File tree

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
"""Demo collection utilities."""
2+
3+
import logging
4+
from typing import Any, Callable
5+
6+
import numpy as np
7+
8+
from programmatic_policy_learning.data.demo_types import Demo, Trajectory
9+
10+
EnvFactory = Callable[[], Any]
11+
12+
13+
def collect_demo(
14+
env_factory: EnvFactory, expert: Any, max_demo_length: int | float = np.inf
15+
) -> Trajectory:
16+
"""Collect a demonstration trajectory from an environment using an expert
17+
policy."""
18+
19+
env = env_factory()
20+
reset_out = env.reset()
21+
if isinstance(reset_out, tuple) and len(reset_out) == 2:
22+
obs, info = reset_out
23+
else:
24+
obs = reset_out
25+
info = {}
26+
logging.warning("env.reset() returned a single value (old gym API)")
27+
28+
steps: list[Demo] = []
29+
t = 0
30+
expert.reset(obs, info)
31+
while True:
32+
action = expert.step()
33+
steps.append(Demo(obs=obs, act=action))
34+
step_out = env.step(action)
35+
# handle gym vs. gymnasium
36+
if len(step_out) == 4:
37+
obs, reward, done, _ = step_out
38+
terminated, truncated = done, False
39+
else:
40+
obs, reward, terminated, truncated, _ = step_out
41+
t += 1
42+
if terminated or truncated or (t >= max_demo_length):
43+
if not reward > 0:
44+
# keep behavior parity with original: warn if didn’t succeed
45+
print("WARNING: demo did not succeed!")
46+
break
47+
return Trajectory(steps=steps)
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
"""Types for demonstrations and trajectories."""
2+
3+
from dataclasses import dataclass
4+
from typing import Generic, Sequence, TypeVar
5+
6+
ObsT = TypeVar("ObsT")
7+
ActT = TypeVar("ActT")
8+
9+
10+
@dataclass(frozen=True)
11+
class Demo(Generic[ObsT, ActT]):
12+
"""A single demonstration step, containing observation and action."""
13+
14+
obs: ObsT
15+
act: ActT
16+
17+
18+
@dataclass(frozen=True)
19+
class Trajectory(Generic[ObsT, ActT]):
20+
"""A sequence of demonstration steps (Demo), called a trajectory."""
21+
22+
steps: Sequence[Demo[ObsT, ActT]]

‎tests/data/test_collect.py‎

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
"""Tests for data/collect.py."""
2+
3+
from typing import Any
4+
5+
import numpy as np
6+
from omegaconf import DictConfig, OmegaConf
7+
8+
from programmatic_policy_learning.approaches.random_actions import RandomActionsApproach
9+
from programmatic_policy_learning.data.collect import collect_demo
10+
from programmatic_policy_learning.data.demo_types import Demo, Trajectory
11+
from programmatic_policy_learning.envs.registry import EnvRegistry
12+
13+
14+
def test_collect_demo_returns_trajectory_DummyEnv():
15+
"""Test that collect_demo returns a Trajectory with Demo steps,
16+
DummyEnv."""
17+
18+
class DummySpace:
19+
"""DummySpace."""
20+
21+
def sample(self):
22+
"""Sample."""
23+
return 0
24+
25+
def seed(self, _):
26+
"""Seed."""
27+
pass # pylint: disable=unnecessary-pass
28+
29+
class DummyEnv:
30+
"""DummyEnv."""
31+
32+
def reset(self):
33+
"""Reset."""
34+
return np.zeros((2, 2)), {}
35+
36+
def step(self, _):
37+
"""Step."""
38+
return np.zeros((2, 2)), 1.0, True, False, {}
39+
40+
@property
41+
def observation_space(self):
42+
"""Observation space."""
43+
return DummySpace()
44+
45+
@property
46+
def action_space(self):
47+
"""Action space."""
48+
return DummySpace()
49+
50+
env = DummyEnv()
51+
env_factory = lambda: env # returns a new environment each time you call
52+
53+
expert = RandomActionsApproach(
54+
"TEST", env.observation_space, env.action_space, seed=1
55+
)
56+
traj = collect_demo(env_factory, expert, max_demo_length=5)
57+
assert isinstance(traj, Trajectory)
58+
assert all(isinstance(step, Demo) for step in traj.steps)
59+
assert len(traj.steps) > 0
60+
61+
62+
def test_collect_demo_with_real_env():
63+
"""Test collect_demo with a real environment using EnvRegistry."""
64+
cfg: DictConfig = OmegaConf.create(
65+
{
66+
"provider": "ggg",
67+
"make_kwargs": {"id": "TwoPileNim0-v0"},
68+
}
69+
)
70+
registry = EnvRegistry()
71+
env_factory = lambda: registry.load(cfg)
72+
env: Any = env_factory() # type: ignore
73+
expert = RandomActionsApproach(
74+
"TEST", env.observation_space, env.action_space, seed=1
75+
)
76+
traj = collect_demo(env_factory, expert, max_demo_length=5)
77+
assert isinstance(traj, Trajectory)
78+
assert all(isinstance(step, Demo) for step in traj.steps)
79+
assert len(traj.steps) > 0

0 commit comments

Comments
 (0)