Skip to content

Commit bff3e75

Browse files
Merge pull request #4 from ooctipus/flat_multi_task_2
Clean up mdp
2 parents 4939c1b + 5ca7ef8 commit bff3e75

25 files changed

Lines changed: 1974 additions & 3275 deletions

source/isaaclab/isaaclab/envs/manager_based_rl_env.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -339,7 +339,7 @@ def _configure_gym_env_spaces(self):
339339
term_dict[term_name] = gym.spaces.Box(low=low, high=high, shape=term_dim)
340340
self.single_observation_space[group_name] = gym.spaces.Dict(term_dict)
341341
# action space (unbounded since we don't impose any limits)
342-
action_dim = self.action_manager.total_action_dim
342+
action_dim = sum(self.action_manager.action_term_dim)
343343
self.single_action_space = gym.spaces.Box(low=-np.inf, high=np.inf, shape=(action_dim,))
344344

345345
# batch the spaces for vectorized environments

source/isaaclab/isaaclab/managers/scene_entity_cfg.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,12 @@
77

88
from __future__ import annotations
99

10+
import re
1011
from dataclasses import MISSING
1112
from typing import TYPE_CHECKING
1213

14+
import torch
15+
1316
from isaaclab.utils import configclass
1417

1518
if TYPE_CHECKING:
@@ -115,6 +118,29 @@ class for more details.
115118
116119
"""
117120

121+
groups: str | list[str] | None = None
122+
"""Regex pattern(s) matching task-group names from the :class:`EnvLayout`.
123+
124+
When set, :meth:`resolve` populates :attr:`env_ids` and :attr:`group_ids`
125+
with indices covering only the matched groups' environments.
126+
When ``None`` (default), both default to ``slice(None)`` (all envs).
127+
128+
Patterns are matched against :attr:`EnvLayout.group_names` using
129+
:func:`re.fullmatch`.
130+
"""
131+
132+
env_ids: slice | torch.Tensor = slice(None)
133+
"""Global env indices for indexing into ``(num_envs, ...)`` output buffers.
134+
135+
Populated by :meth:`resolve`. Defaults to ``slice(None)`` (all envs).
136+
"""
137+
138+
group_ids: slice | torch.Tensor = slice(None)
139+
"""Indices into the asset's data buffer (the view).
140+
141+
Populated by :meth:`resolve`. Defaults to ``slice(None)`` (all envs).
142+
"""
143+
118144
_resolved: bool = False
119145
"""Internal flag to prevent double resolution."""
120146

@@ -157,6 +183,9 @@ def resolve(self, scene: InteractiveScene):
157183
# convert object collection names to indices based on regex
158184
self._resolve_object_collection_names(scene)
159185

186+
# resolve group patterns into env_ids and group_ids
187+
self._resolve_groups(scene)
188+
160189
def _resolve_joint_names(self, scene: InteractiveScene):
161190
# convert joint names to indices based on regex
162191
if self.joint_names is not None or self.joint_ids != slice(None):
@@ -301,3 +330,28 @@ def _resolve_object_collection_names(self, scene: InteractiveScene):
301330
if isinstance(self.object_collection_ids, int):
302331
self.object_collection_ids = [self.object_collection_ids]
303332
self.object_collection_names = [entity.object_names[i] for i in self.object_collection_ids]
333+
334+
def _resolve_groups(self, scene: InteractiveScene):
335+
"""Resolve group patterns into env_ids and group_ids via the scene layout.
336+
337+
Args:
338+
scene: The interactive scene instance.
339+
340+
Raises:
341+
ValueError: If no groups match the specified patterns.
342+
"""
343+
layout = scene.layout
344+
if self.groups is None:
345+
return
346+
if isinstance(self.groups, str):
347+
self.groups = [self.groups]
348+
matched = []
349+
for pattern in self.groups:
350+
for name in layout.group_names:
351+
if re.fullmatch(pattern, name) and name not in matched:
352+
matched.append(name)
353+
if not matched:
354+
raise ValueError(f"No groups matched patterns {self.groups}. Available: {list(layout.group_names)}")
355+
group_view = layout.get(matched, asset=self.name)
356+
self.env_ids = group_view.env_ids
357+
self.group_ids = group_view.group_ids

source/isaaclab/isaaclab/scene/__init__.pyi

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,30 +7,16 @@ __all__ = [
77
"CloneCfg",
88
"CloneGroup",
99
"EnvLayout",
10-
"ExclusionSet",
1110
"GroupView",
1211
"InclusionSet",
13-
"IntersectionGroup",
1412
"InteractiveScene",
1513
"InteractiveSceneCfg",
16-
"PatternGroup",
17-
"PredicateGroup",
18-
"PrefixGroup",
19-
"SuffixGroup",
20-
"UnionGroup",
2114
]
2215

2316
from .clone_cfg import (
2417
CloneCfg,
2518
CloneGroup,
26-
ExclusionSet,
2719
InclusionSet,
28-
IntersectionGroup,
29-
PatternGroup,
30-
PredicateGroup,
31-
PrefixGroup,
32-
SuffixGroup,
33-
UnionGroup,
3420
)
3521
from .env_layout import EnvLayout, GroupView
3622
from .interactive_scene import InteractiveScene

source/isaaclab/isaaclab/scene/clone_cfg.py

Lines changed: 1 addition & 178 deletions
Original file line numberDiff line numberDiff line change
@@ -3,29 +3,10 @@
33
#
44
# SPDX-License-Identifier: BSD-3-Clause
55

6-
"""Configuration classes for environment cloning partitioning.
7-
8-
Built-in :class:`CloneGroup` descriptors
9-
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
10-
11-
========================== =============================================
12-
Descriptor Selection logic
13-
========================== =============================================
14-
:class:`InclusionSet` Explicit list of asset names
15-
:class:`ExclusionSet` Everything *except* listed asset names
16-
:class:`PrefixGroup` Assets whose name starts with a prefix
17-
:class:`SuffixGroup` Assets whose name ends with a suffix
18-
:class:`PatternGroup` Regex full-match on asset names
19-
:class:`PredicateGroup` Arbitrary ``Callable[[str], bool]``
20-
:class:`UnionGroup` Logical OR of child descriptors
21-
:class:`IntersectionGroup` Logical AND of child descriptors
22-
========================== =============================================
23-
"""
6+
"""Configuration classes for environment cloning partitioning."""
247

258
from __future__ import annotations
269

27-
import re
28-
from collections.abc import Callable
2910
from dataclasses import MISSING
3011

3112
from isaaclab.cloner.cloner_strategies import random as random_strategy
@@ -96,164 +77,6 @@ def resolve_assets(self, all_asset_names: list[str]) -> list[str]:
9677
return [a for a in self.assets if a in known]
9778

9879

99-
@configclass
100-
class ExclusionSet(CloneGroup):
101-
"""Clone group that includes everything *except* the listed assets.
102-
103-
Useful when a group should contain most scene assets and only a
104-
few should be excluded.
105-
106-
Example::
107-
108-
ExclusionSet(exclude=["ground_plane", "light"], weight=1)
109-
"""
110-
111-
exclude: list[str] = MISSING
112-
"""Asset names to *exclude* from this group."""
113-
114-
def resolve_assets(self, all_asset_names: list[str]) -> list[str]:
115-
excluded = set(self.exclude)
116-
return [a for a in all_asset_names if a not in excluded]
117-
118-
119-
@configclass
120-
class PrefixGroup(CloneGroup):
121-
"""Clone group that selects assets whose name starts with a prefix.
122-
123-
Example::
124-
125-
PrefixGroup(prefix="lift_", weight=1)
126-
# matches "lift_table", "lift_object", ...
127-
"""
128-
129-
prefix: str = MISSING
130-
"""Assets whose name starts with this string are included."""
131-
132-
def resolve_assets(self, all_asset_names: list[str]) -> list[str]:
133-
return [a for a in all_asset_names if a.startswith(self.prefix)]
134-
135-
136-
@configclass
137-
class SuffixGroup(CloneGroup):
138-
"""Clone group that selects assets whose name ends with a suffix.
139-
140-
Example::
141-
142-
SuffixGroup(suffix="_frame", weight=1)
143-
# matches "ee_frame", "cabinet_frame", ...
144-
"""
145-
146-
suffix: str = MISSING
147-
"""Assets whose name ends with this string are included."""
148-
149-
def resolve_assets(self, all_asset_names: list[str]) -> list[str]:
150-
return [a for a in all_asset_names if a.endswith(self.suffix)]
151-
152-
153-
@configclass
154-
class PatternGroup(CloneGroup):
155-
"""Clone group that selects assets matching any of the given regex patterns.
156-
157-
Each pattern is tested as a **full match** against the asset name
158-
(equivalent to ``re.fullmatch``). Standard ``re`` syntax is supported.
159-
160-
Example::
161-
162-
PatternGroup(patterns=["lift_.*", "cabinet"], weight=1)
163-
# matches "lift_table", "lift_object", "cabinet"
164-
"""
165-
166-
patterns: list[str] = MISSING
167-
"""Regex patterns (full-match) to test against asset names."""
168-
169-
def resolve_assets(self, all_asset_names: list[str]) -> list[str]:
170-
compiled = [re.compile(p) for p in self.patterns]
171-
return [a for a in all_asset_names if any(r.fullmatch(a) for r in compiled)]
172-
173-
174-
@configclass
175-
class PredicateGroup(CloneGroup):
176-
"""Clone group defined by an arbitrary callable predicate.
177-
178-
The :attr:`predicate` receives each asset name and returns ``True``
179-
to include it. This is the most flexible built-in descriptor.
180-
181-
Example::
182-
183-
PredicateGroup(
184-
predicate=lambda name: "sensor" not in name,
185-
weight=2,
186-
)
187-
"""
188-
189-
predicate: Callable[[str], bool] = MISSING
190-
"""Callable that returns ``True`` for asset names to include."""
191-
192-
def resolve_assets(self, all_asset_names: list[str]) -> list[str]:
193-
return [a for a in all_asset_names if self.predicate(a)]
194-
195-
196-
@configclass
197-
class UnionGroup(CloneGroup):
198-
"""Clone group that takes the union of multiple child descriptors.
199-
200-
An asset is included if **any** child descriptor claims it.
201-
202-
Example::
203-
204-
UnionGroup(
205-
groups=[
206-
PrefixGroup(prefix="lift_"),
207-
InclusionSet(assets=["shared_sensor"]),
208-
],
209-
weight=1,
210-
)
211-
"""
212-
213-
groups: list[CloneGroup] = MISSING
214-
"""Child descriptors whose results are merged (union)."""
215-
216-
def resolve_assets(self, all_asset_names: list[str]) -> list[str]:
217-
seen: set[str] = set()
218-
result: list[str] = []
219-
for g in self.groups:
220-
for a in g.resolve_assets(all_asset_names):
221-
if a not in seen:
222-
seen.add(a)
223-
result.append(a)
224-
return result
225-
226-
227-
@configclass
228-
class IntersectionGroup(CloneGroup):
229-
"""Clone group that takes the intersection of multiple child descriptors.
230-
231-
An asset is included only if **all** child descriptors claim it.
232-
233-
Example::
234-
235-
IntersectionGroup(
236-
groups=[
237-
PrefixGroup(prefix="lift_"),
238-
ExclusionSet(exclude=["lift_debug_viz"]),
239-
],
240-
weight=1,
241-
)
242-
"""
243-
244-
groups: list[CloneGroup] = MISSING
245-
"""Child descriptors whose results are intersected."""
246-
247-
def resolve_assets(self, all_asset_names: list[str]) -> list[str]:
248-
if not self.groups:
249-
return []
250-
sets = [set(g.resolve_assets(all_asset_names)) for g in self.groups]
251-
common = sets[0]
252-
for s in sets[1:]:
253-
common &= s
254-
return [a for a in all_asset_names if a in common]
255-
256-
25780
# ── top-level config ──────────────────────────────────────────────────────────
25881

25982

0 commit comments

Comments
 (0)