Observation Layout Adaptation

This module implements a block transformation for adapt the layout of the observation space of an environment that may be arbitrarily nested.

class gym_jiminy.common.wrappers.observation_layout.AdaptLayoutObservation(env, layout)[source]

Bases: BaseTransformObservation[OtherMaybeNestedObs, MaybeNestedObs, Act], Generic[OtherMaybeNestedObs, MaybeNestedObs, Act]

Adapt the data structure of the original nested observation space, by filtering out some leaves and/or re-ordering others.

This wrapper does nothing but exposing a subset of all the leaves of the original observation space with a completely independent data structure. For flattening the observation space after filtering, you should wrap the environment with FlattenObservation as yet another layer.

It is possible to operate on subtrees directly without going all the way down each leaf. Similarly, one can extract slices (possibly not contiguous) of gym.spaces.Box spaces. Extra leaves can be added to subtrees of original data that has already been re-mapped, knowing that the items of the layout are always processed in order. Moreover, the same output key can appear multiple times. In such case, all the associated values will be stacked as a tuple while preserving their order.

Let us consider the following nested observation space:

gym.spaces.Dict(
x1=gym.spaces.Tuple([

gym.spaces.Box(float(‘-inf’), float(‘inf’), (7, 6, 5, 4)),

]), x2=gym.spaces.Dict(

y1=gym.spaces.Dict(

z1=gym.spaces.Discrete(5)

), y2=gym.spaces.Tuple([

gym.spaces.Discrete(2), gym.spaces.Box(float(‘-inf’), float(‘inf’), (2, 3)), gym.spaces.Discrete(3),

])))

Here is an example that aggregates one leaf to a leaf of a subtree that has already been remapped:

[((), (“x2”, “y2”)), ((0,), (“x1”,))]

gym.spaces.Tuple([
gym.spaces.Tuple([

gym.spaces.Discrete(2), gym.spaces.Tuple([

gym.spaces.Box(float(‘-inf’), float(‘inf’), (7, 6, 5, 4)),

])

]), gym.spaces.Box(float(‘-inf’), float(‘inf’), (2, 3)), gym.spaces.Discrete(3),

]))

Here is an example that aggregate one leaf and one multi-dimensional slice of a Box space (array[:, 1:3]) under two separate keys of a nested dict:

[((“A”, “B1”), (“x2”, “y2”, 1)),

((“A”, “B2”), (“x1”, 0, [(), (1, 4), (1, 3)]))]

gym.spaces.Dict(
A=gym.spaces.Dict(

B1=gym.spaces.Box(float(‘-inf’), float(‘inf’), (2, 3)), B2=gym.spaces.Box(float(‘-inf’), float(‘inf’), (7, 3, 2, 4))

))

Here is an example that aggregate two subtree in the same nested structure:

[((), (“x2”,)), ((“y1”, “z2”), (“x1”,))]

gym.spaces.Dict(
y1=gym.spaces.Dict(

z1=gym.spaces.Discrete(5) z2=gym.spaces.Tuple([

gym.spaces.Box(float(‘-inf’), float(‘inf’), (7, 6, 5, 4)),

])

), y2=gym.spaces.Tuple([

gym.spaces.Discrete(2), gym.spaces.Box(float(‘-inf’), float(‘inf’), (2, 3)), gym.spaces.Discrete(3),

]))

Parameters:
  • env (InterfaceJiminyEnv[MaybeNestedObs, Act]) – Base or already wrapped jiminy environment.

  • layout (Sequence[Tuple[Tuple[str | int, ...] | str | int, Tuple[str | int, ...] | Tuple[Unpack[Tuple[str | int, ...]], Sequence[Tuple | int | Tuple[int | None, int | None]]]]]) – Sequence of tuples (nested_key_out, nested_key_in) mapping the desired path in the output data structure to the original path in input data structure. These tuples are guaranteed to be processed in order. The same path may appear multiple time in the output data structure. If so, the corresponding subtrees while be aggregated in sequence. If the parent node of the first subtree being considered is already a sequence, then all subsequent extracted subtrees will be appended to it directly. If not, then a dedicated top-level sequence container while be created first.

transform_observation()[source]

No-op transform since the transform observation is sharing memory with the wrapped one since it is just a partial view.

Return type:

None

class gym_jiminy.common.wrappers.observation_layout.FilterObservation(env, nested_filter_keys)[source]

Bases: BaseTransformObservation[NestedObs, NestedObs, Act], Generic[NestedObs, Act]

Filter nested observation space.

This wrapper does nothing but providing an observation only exposing a subset of all the leaves of the original observation space. This wrapper is a specialization of AdaptLayoutObservation, which is more generic. For flattening the observation space after filtering, you should wrap the environment with FlattenObservation as yet another layer.

Note that it is possible to operate on subtrees directly without going all the way down each leaf.

Beware that the original ordered of leaves within their parent container is maintain, whatever the order in which they appear in the specified list of filtered nested keys.

Let us consider the following nested observation space:

gym.spaces.Dict(
x1=gym.spaces.Tuple([

gym.spaces.Box(float(‘-inf’), float(‘inf’), (7, 6, 5, 4)),

]), x2=gym.spaces.Dict(

y1=gym.spaces.Dict(

z1=gym.spaces.Discrete(5)

), y2=gym.spaces.Tuple([

gym.spaces.Discrete(2), gym.spaces.Box(float(‘-inf’), float(‘inf’), (2, 3)), gym.spaces.Discrete(3),

])))

Here is an example that filter out part of a sequence container and a mapping container:

[(“x2”, “y2”, 2), (“x2”, “y2”, 0), (“x1”,)]

gym.spaces.Dict(
x1=gym.spaces.Tuple([

gym.spaces.Box(float(‘-inf’), float(‘inf’), (7, 6, 5, 4)),

]), x2=gym.spaces.Dict(

y2=gym.spaces.Tuple([

gym.spaces.Discrete(2), gym.spaces.Discrete(3),

])))

Parameters:
  • env (InterfaceJiminyEnv[NestedObs, Act]) – Base or already wrapped jiminy environment.

  • kwargs – Extra keyword arguments for multiple inheritance.

  • nested_filter_keys (Sequence[Tuple[str | int, ...] | str | int])

transform_observation()[source]

No-op transform since the transform observation is sharing memory with the wrapped one since it is just a partial view.

Return type:

None