Skip to content

unienv_interface.space.space_utils.flatten_utils

Space

Space(backend: ComputeBackend[ArrayAPIArray, _SpaceBDeviceT, _SpaceBDTypeT, _SpaceBDRNGT], shape: Optional[Sequence[int]] = None, device: Optional[_SpaceBDeviceT] = None, dtype: Optional[_SpaceBDTypeT] = None)

Bases: ABC, Generic[SpaceDataT, _SpaceBDeviceT, _SpaceBDTypeT, _SpaceBDRNGT]

Abstract description of a valid data domain.

Spaces carry backend, device, shape, and dtype metadata and define the operations needed by the rest of UniEnv: validation, sampling, empty value creation, serialization-friendly representation, and backend/device conversion for both the space definition and its data.

backend instance-attribute

backend = backend

dtype instance-attribute

dtype = dtype

device property

device: Optional[_SpaceBDeviceT]

shape property

shape: tuple[int, ...] | None

Return the shape of the space as an immutable property.

to abstractmethod

to(backend: Optional[ComputeBackend] = None, device: Optional[Union[_SpaceBDeviceT, Any]] = None) -> Union[Space[SpaceDataT, _SpaceBDeviceT, _SpaceBDTypeT, _SpaceBDRNGT], Space]

Return an equivalent space on another backend and/or device.

sample abstractmethod

sample(rng: _SpaceBDRNGT, **kwargs) -> Tuple[_SpaceBDRNGT, SpaceDataT]

Draw one valid value from the space and return the advanced RNG.

create_empty abstractmethod

create_empty() -> SpaceDataT

Create an empty data structure for this space.

is_bounded abstractmethod

is_bounded(manner: Literal['both', 'below', 'above'] = 'both') -> bool

Return boolean specifying if this space is bounded in the specified manner.

contains abstractmethod

contains(x: Any) -> bool

Return boolean specifying if x is a valid member of this space.

is_subspaceeq

is_subspaceeq(other: Space) -> bool

Return whether this space is a non-strict subspace of other (self ⊆ other).

A space a is a non-strict subspace of b (a ⊆ b) when every valid sample of a is also a valid member of b; equality is allowed, i.e. a == b implies a.is_subspaceeq(b).

The primary use case is a controller that declares its REQUIRED observation space and checks required.is_subspaceeq(env_observation_space): for DictSpace this must hold even when the environment space exposes EXTRA keys beyond the required ones (unlike DictSpace.contains, which demands exact key equality). Controller-required-space checks should typically use is_subspaceeq rather than the strict is_subspace because a controller's required space may exactly equal the env space.

Comparison policy (structural only):

  • The two spaces must share the same backend type.
  • Dtypes must be strictly equal; no implicit cast widening is performed.
  • device is intentionally ignored — two spaces on different devices may still be in a subspace relation.

Cross-type comparisons (e.g. BoxSpace vs DictSpace) return False rather than raising. Subclasses override this method to provide concrete structural containment checks; the base implementation raises NotImplementedError to mirror the abstract-method style of this class.

is_subspace

is_subspace(other: Space) -> bool

Return whether this space is a STRICT subspace of other (self ⊂ other).

Defined uniformly for all spaces as::

self.is_subspace(other)  ⟺  self.is_subspaceeq(other) and not other.is_subspaceeq(self)

I.e. self ⊆ other holds but other ⊆ self does not, so self is a PROPER (strict) subspace of other. This is the relation versus the non-strict provided by :meth:is_subspaceeq.

This definition is used instead of relying on __eq__ because some space classes only have identity __eq__; defining strict containment via the symmetric non-strict check works uniformly for all classes regardless of their __eq__ implementation.

For structurally-distinct-but-mutually-containing spaces (which should not occur under the strict dtype/shape policies enforced by the per-class is_subspaceeq implementations) this degrades gracefully to False: if both self.is_subspaceeq(other) and other.is_subspaceeq(self) hold, the two spaces are considered equivalent and neither is a STRICT subspace of the other.

If either side's is_subspaceeq is not implemented (the base :meth:is_subspaceeq raises NotImplementedError), the exception propagates to the caller — it is NOT swallowed into False so that callers can tell that the comparison is unsupported.

Note: controller-required-space checks should typically use :meth:is_subspaceeq (a controller's required space may exactly equal the env space, in which case the strict is_subspace would return False).

get_repr abstractmethod

get_repr(abbreviate: bool = False, include_backend: bool = True, include_device: bool = True, include_dtype: bool = True) -> str

Return a string representation of the space.

data_to abstractmethod

data_to(data: SpaceDataT, backend: Optional[ComputeBackend] = None, device: Optional[Union[_SpaceBDeviceT, Any]] = None) -> Union[SpaceDataT, Any]

Convert space-compatible data to another backend and/or device.

abbr_device staticmethod

abbr_device(spaces: Iterable[Space[Any, _SpaceBDeviceT, _SpaceBDTypeT, _SpaceBDRNGT]]) -> Optional[_SpaceBDeviceT]

Return the shared device across spaces, or None if mixed/empty.

BinarySpace

BinarySpace(backend: ComputeBackend[BArrayType, BDeviceType, BDtypeType, BRNGType], shape: Sequence[int], dtype: Optional[BDtypeType] = None, device: Optional[BDeviceType] = None)

Bases: Space[BArrayType, BDeviceType, BDtypeType, BRNGType]

backend instance-attribute

backend = backend

dtype instance-attribute

dtype = dtype

device property

device: Optional[_SpaceBDeviceT]

shape property

shape: tuple[int, ...] | None

Return the shape of the space as an immutable property.

is_subspace

is_subspace(other: Space) -> bool

Return whether this space is a STRICT subspace of other (self ⊂ other).

Defined uniformly for all spaces as::

self.is_subspace(other)  ⟺  self.is_subspaceeq(other) and not other.is_subspaceeq(self)

I.e. self ⊆ other holds but other ⊆ self does not, so self is a PROPER (strict) subspace of other. This is the relation versus the non-strict provided by :meth:is_subspaceeq.

This definition is used instead of relying on __eq__ because some space classes only have identity __eq__; defining strict containment via the symmetric non-strict check works uniformly for all classes regardless of their __eq__ implementation.

For structurally-distinct-but-mutually-containing spaces (which should not occur under the strict dtype/shape policies enforced by the per-class is_subspaceeq implementations) this degrades gracefully to False: if both self.is_subspaceeq(other) and other.is_subspaceeq(self) hold, the two spaces are considered equivalent and neither is a STRICT subspace of the other.

If either side's is_subspaceeq is not implemented (the base :meth:is_subspaceeq raises NotImplementedError), the exception propagates to the caller — it is NOT swallowed into False so that callers can tell that the comparison is unsupported.

Note: controller-required-space checks should typically use :meth:is_subspaceeq (a controller's required space may exactly equal the env space, in which case the strict is_subspace would return False).

abbr_device staticmethod

abbr_device(spaces: Iterable[Space[Any, _SpaceBDeviceT, _SpaceBDTypeT, _SpaceBDRNGT]]) -> Optional[_SpaceBDeviceT]

Return the shared device across spaces, or None if mixed/empty.

to

to(backend=None, device=None)

sample

sample(rng: BRNGType) -> Tuple[BRNGType, BArrayType]

create_empty

create_empty()

is_bounded

is_bounded(manner='both')

contains

contains(x: BArrayType) -> bool

get_repr

get_repr(abbreviate=False, include_backend=True, include_device=True, include_dtype=True)

is_subspaceeq

is_subspaceeq(other: Any) -> bool

Return whether this binary space is a non-strict subspace of other (⊆).

True iff other is a BinarySpace on the same backend with equal shape and dtype. Since a binary space contains exactly the boolean tensors of its shape, two such spaces are in a subspace relation iff they are structurally identical (modulo device).

data_to

data_to(data, backend=None, device=None)

BoxSpace

BoxSpace(backend: ComputeBackend[BArrayType, BDeviceType, BDtypeType, BRNGType], low: SupportsFloat | BArrayType, high: SupportsFloat | BArrayType, dtype: BDtypeType, device: Optional[BDeviceType] = None, shape: Optional[Sequence[int]] = None)

Bases: Space[BArrayType, BDeviceType, BDtypeType, BRNGType]

Continuous or integer hyper-rectangle defined by elementwise bounds.

Create a box with broadcastable low and high bounds.

backend instance-attribute

backend = backend

dtype instance-attribute

dtype = dtype

device property

device: Optional[_SpaceBDeviceT]

shape property

shape: tuple[int, ...] | None

Return the shape of the space as an immutable property.

low property

low: BArrayType

high property

high: BArrayType

is_subspace

is_subspace(other: Space) -> bool

Return whether this space is a STRICT subspace of other (self ⊂ other).

Defined uniformly for all spaces as::

self.is_subspace(other)  ⟺  self.is_subspaceeq(other) and not other.is_subspaceeq(self)

I.e. self ⊆ other holds but other ⊆ self does not, so self is a PROPER (strict) subspace of other. This is the relation versus the non-strict provided by :meth:is_subspaceeq.

This definition is used instead of relying on __eq__ because some space classes only have identity __eq__; defining strict containment via the symmetric non-strict check works uniformly for all classes regardless of their __eq__ implementation.

For structurally-distinct-but-mutually-containing spaces (which should not occur under the strict dtype/shape policies enforced by the per-class is_subspaceeq implementations) this degrades gracefully to False: if both self.is_subspaceeq(other) and other.is_subspaceeq(self) hold, the two spaces are considered equivalent and neither is a STRICT subspace of the other.

If either side's is_subspaceeq is not implemented (the base :meth:is_subspaceeq raises NotImplementedError), the exception propagates to the caller — it is NOT swallowed into False so that callers can tell that the comparison is unsupported.

Note: controller-required-space checks should typically use :meth:is_subspaceeq (a controller's required space may exactly equal the env space, in which case the strict is_subspace would return False).

abbr_device staticmethod

abbr_device(spaces: Iterable[Space[Any, _SpaceBDeviceT, _SpaceBDTypeT, _SpaceBDRNGT]]) -> Optional[_SpaceBDeviceT]

Return the shared device across spaces, or None if mixed/empty.

to

to(backend: Optional[ComputeBackend] = None, device: Optional[Union[BDeviceType, Any]] = None) -> Union[BoxSpace[BArrayType, BDeviceType, BDtypeType, BRNGType], BoxSpace]

Return an equivalent box on another backend and/or device.

is_bounded

is_bounded(manner='both')

sample

sample(rng: BRNGType) -> Tuple[BRNGType, BArrayType]

Generates a single random sample inside the Box.

In creating a sample of the box, each coordinate is sampled (independently) from a distribution that is chosen according to the form of the interval:

  • :math:[a, b] : uniform distribution
  • :math:[a, \infty) : shifted exponential distribution
  • :math:(-\infty, b] : shifted negative exponential distribution
  • :math:(-\infty, \infty) : normal distribution

Returns:

Type Description
Tuple[BRNGType, BArrayType]

A sampled value from the Box

create_empty

create_empty() -> BArrayType

Allocate an uninitialized array with the box shape and dtype.

contains

contains(x: Any) -> bool

Return boolean specifying if x is a valid member of this space.

clip

clip(x: BArrayType) -> BArrayType

Clip the values of x to be within the bounds of this space.

get_repr

get_repr(abbreviate: bool = False, include_backend: bool = True, include_device: bool = True, include_dtype: bool = True) -> str

is_subspaceeq

is_subspaceeq(other: Any) -> bool

Return whether this box is a non-strict subspace of other (⊆).

True iff other is a BoxSpace on the same backend, with equal shape and dtype, and other's bounds contain self's bounds elementwise (other.low <= self.low and other.high >= self.high). Infinite bounds are handled correctly via direct comparison since ±inf compares as expected against finite values and itself. device is ignored.

data_to

data_to(data: BArrayType, backend: Optional[ComputeBackend] = None, device: Optional[Union[BDeviceType, Any]] = None) -> Union[BArrayType, Any]

Convert data to another backend.

DynamicBoxSpace

DynamicBoxSpace(backend: ComputeBackend[BArrayType, BDeviceType, BDtypeType, BRNGType], low: Union[int, float, BArrayType], high: Union[int, float, BArrayType], shape_low: Sequence[int], shape_high: Sequence[int], dtype: BDtypeType, device: Optional[BDeviceType] = None, fill_value: Union[int, float] = 0)

Bases: Space[BArrayType, BDeviceType, BDtypeType, BRNGType]

backend instance-attribute

backend = backend

dtype instance-attribute

dtype = dtype

device property

device: Optional[_SpaceBDeviceT]

shape property

shape: tuple[int, ...] | None

Return the shape of the space as an immutable property.

fill_value instance-attribute

fill_value = fill_value

low property

low: BArrayType

Broadcasted lower bounds at the space's broadcast shape.

For a dynamic box there is no single fixed shape — only a range [shape_low, shape_high] per axis. The stored _low/_high arrays are broadcastable to self._broadcast_shape (shape_low with size 1 on every dynamic axis, i.e. axes where shape_low != shape_high). This property returns the bounds broadcast to that _broadcast_shape, mirroring BoxSpace.low (which broadcasts to self.shape). Comparisons against data of any in-range shape broadcast correctly because dynamic axes carry size 1 here. Use get_low(shape) / get_high(shape) to materialize the bounds at a specific concrete shape.

high property

high: BArrayType

Broadcasted upper bounds at the space's broadcast shape.

See low for the rationale behind the chosen shape.

shape_low property

shape_low: Tuple[int, ...]

Return the lower shape of the BoxSpace.

shape_high property

shape_high: Tuple[int, ...]

Return the upper shape of the BoxSpace.

is_subspace

is_subspace(other: Space) -> bool

Return whether this space is a STRICT subspace of other (self ⊂ other).

Defined uniformly for all spaces as::

self.is_subspace(other)  ⟺  self.is_subspaceeq(other) and not other.is_subspaceeq(self)

I.e. self ⊆ other holds but other ⊆ self does not, so self is a PROPER (strict) subspace of other. This is the relation versus the non-strict provided by :meth:is_subspaceeq.

This definition is used instead of relying on __eq__ because some space classes only have identity __eq__; defining strict containment via the symmetric non-strict check works uniformly for all classes regardless of their __eq__ implementation.

For structurally-distinct-but-mutually-containing spaces (which should not occur under the strict dtype/shape policies enforced by the per-class is_subspaceeq implementations) this degrades gracefully to False: if both self.is_subspaceeq(other) and other.is_subspaceeq(self) hold, the two spaces are considered equivalent and neither is a STRICT subspace of the other.

If either side's is_subspaceeq is not implemented (the base :meth:is_subspaceeq raises NotImplementedError), the exception propagates to the caller — it is NOT swallowed into False so that callers can tell that the comparison is unsupported.

Note: controller-required-space checks should typically use :meth:is_subspaceeq (a controller's required space may exactly equal the env space, in which case the strict is_subspace would return False).

abbr_device staticmethod

abbr_device(spaces: Iterable[Space[Any, _SpaceBDeviceT, _SpaceBDTypeT, _SpaceBDRNGT]]) -> Optional[_SpaceBDeviceT]

Return the shared device across spaces, or None if mixed/empty.

pad_array_on_axis staticmethod

pad_array_on_axis(backend: ComputeBackend[BArrayType, BDeviceType, BDtypeType, BRNGType], data: BArrayType, axis: int, target_size: int, fill_value: Union[int, float] = 0) -> BArrayType

get_array_axis_length staticmethod

get_array_axis_length(backend: ComputeBackend[BArrayType, BDeviceType, BDtypeType, BRNGType], data: BArrayType, axis: int, fill_value: Union[int, float] = 0) -> int

unpad_array_on_axis staticmethod

unpad_array_on_axis(backend: ComputeBackend[BArrayType, BDeviceType, BDtypeType, BRNGType], data: BArrayType, axis: int, fill_value: Union[int, float] = 0) -> BArrayType

get_low

get_low(shape: Sequence[int]) -> BArrayType

get_high

get_high(shape: Sequence[int]) -> BArrayType

to

to(backend: Optional[ComputeBackend] = None, device: Optional[Union[BDeviceType, Any]] = None) -> Union[DynamicBoxSpace[BArrayType, BDeviceType, BDtypeType, BRNGType], DynamicBoxSpace]

is_bounded

is_bounded(manner='both')

sample

sample(rng: BRNGType) -> Tuple[BRNGType, BArrayType]

Generates a single random sample inside the Box.

In creating a sample of the box, each coordinate is sampled (independently) from a distribution that is chosen according to the form of the interval:

  • :math:[a, b] : uniform distribution
  • :math:[a, \infty) : shifted exponential distribution
  • :math:(-\infty, b] : shifted negative exponential distribution
  • :math:(-\infty, \infty) : normal distribution

Returns:

Type Description
Tuple[BRNGType, BArrayType]

A sampled value from the Box

pad_data

pad_data(data: BArrayType, start_axis: Optional[int] = None, end_axis: Optional[int] = None) -> BArrayType

Pad the data to the maximum shape of this space.

unpad_data

unpad_data(data: BArrayType, start_axis: Optional[int] = None, end_axis: Optional[int] = None) -> BArrayType

Unpad the data to the minimum shape of this space.

create_empty

create_empty()

contains

contains(x: Any) -> bool

Return boolean specifying if x is a valid member of this space.

shape_contains

shape_contains(shape: Sequence[int]) -> bool

Check if the shape is within the bounds of this space.

clip

clip(x: BArrayType) -> BArrayType

Clip the values of x to be within the bounds of this space.

get_repr

get_repr(abbreviate: bool = False, include_backend: bool = True, include_device: bool = True, include_dtype: bool = True) -> str

is_subspaceeq

is_subspaceeq(other: Any) -> bool

Return whether this dynamic box is a non-strict subspace of other (⊆).

True iff other is a DynamicBoxSpace on the same backend with equal dtype, self's shape range is contained within other's (other.shape_low <= self.shape_low and self.shape_high <= other.shape_high per dimension, inf-aware via plain integer comparison), and the value bounds of self are contained within other's. The value-bounds check is performed by broadcasting both spaces' bounds to a common shape (the elementwise maximum of the two shape_high vectors along each axis, restricted to axes where the two ranges overlap) via get_low/get_high.

.. note:: This class exposes get_low/get_high but no low/high properties, even though __eq__ and contains reference self.low/self.high. This implementation deliberately avoids self.low/self.high and uses get_low/get_high instead; the pre-existing bug in __eq__/contains is left untouched per scope.

data_to

data_to(data: BArrayType, backend: Optional[ComputeBackend] = None, device: Optional[Union[BDeviceType, Any]] = None) -> Union[BArrayType, Any]

Convert data to another backend.

DictSpace

DictSpace(backend: ComputeBackend[Any, BDeviceType, BDtypeType, BRNGType], spaces: Optional[Union[Dict[str, Space[Any, BDeviceType, BDtypeType, BRNGType]], Sequence[Tuple[str, Space[Any, BDeviceType, BDtypeType, BRNGType]]]]] = None, device: Optional[BDeviceType] = None)

Bases: Space[Dict[str, Any], BDeviceType, BDtypeType, BRNGType]

Cartesian product of named subspaces represented as a mapping.

Create a dictionary-valued space from named child spaces.

backend instance-attribute

backend = backend

dtype instance-attribute

dtype = dtype

device property

device: Optional[_SpaceBDeviceT]

shape property

shape: tuple[int, ...] | None

Return the shape of the space as an immutable property.

spaces instance-attribute

spaces = new_spaces

is_subspace

is_subspace(other: Space) -> bool

Return whether this space is a STRICT subspace of other (self ⊂ other).

Defined uniformly for all spaces as::

self.is_subspace(other)  ⟺  self.is_subspaceeq(other) and not other.is_subspaceeq(self)

I.e. self ⊆ other holds but other ⊆ self does not, so self is a PROPER (strict) subspace of other. This is the relation versus the non-strict provided by :meth:is_subspaceeq.

This definition is used instead of relying on __eq__ because some space classes only have identity __eq__; defining strict containment via the symmetric non-strict check works uniformly for all classes regardless of their __eq__ implementation.

For structurally-distinct-but-mutually-containing spaces (which should not occur under the strict dtype/shape policies enforced by the per-class is_subspaceeq implementations) this degrades gracefully to False: if both self.is_subspaceeq(other) and other.is_subspaceeq(self) hold, the two spaces are considered equivalent and neither is a STRICT subspace of the other.

If either side's is_subspaceeq is not implemented (the base :meth:is_subspaceeq raises NotImplementedError), the exception propagates to the caller — it is NOT swallowed into False so that callers can tell that the comparison is unsupported.

Note: controller-required-space checks should typically use :meth:is_subspaceeq (a controller's required space may exactly equal the env space, in which case the strict is_subspace would return False).

abbr_device staticmethod

abbr_device(spaces: Iterable[Space[Any, _SpaceBDeviceT, _SpaceBDTypeT, _SpaceBDRNGT]]) -> Optional[_SpaceBDeviceT]

Return the shared device across spaces, or None if mixed/empty.

to

to(backend: Optional[ComputeBackend] = None, device: Optional[Union[BDeviceType, Any]] = None) -> Union[DictSpace[BDeviceType, BDtypeType, BRNGType], DictSpace]

sample

sample(rng: BDeviceType) -> Tuple[BDeviceType, Dict[str, Any]]

create_empty

create_empty() -> Dict[str, Any]

Create an empty data structure for this space.

is_bounded

is_bounded(manner='both')

contains

contains(x: Any) -> bool

Return boolean specifying if x is a valid member of this space.

is_subspaceeq

is_subspaceeq(other: Any) -> bool

Return whether this dict space is a non-strict subspace of other (⊆).

True iff other is a DictSpace on the same backend, every key of self is present in other (self.keys() ⊆ other.keys()), and for every shared key self.spaces[k].is_subspaceeq(other.spaces[k]) holds recursively. Unlike contains, other is permitted to expose EXTRA keys beyond those of self — this is the controller required-observation-space use case where the environment may provide additional observation entries. device is ignored.

get_repr

get_repr(abbreviate: bool = False, include_backend: bool = True, include_device: bool = True, include_dtype: bool = True) -> str

keys

keys() -> KeysView

Returns the keys of the Dict.

data_to

data_to(data, backend=None, device=None)

GraphSpace

GraphSpace(backend: ComputeBackend[BArrayType, BDeviceType, BDtypeType, BRNGType], node_feature_space: Optional[BoxSpace[BArrayType, BDeviceType, BDtypeType, BRNGType]], edge_feature_space: Optional[BoxSpace[BArrayType, BDeviceType, BDtypeType, BRNGType]] = None, is_edge: bool = False, min_nodes: int = 1, max_nodes: Optional[int] = None, min_edges: int = 1, max_edges: Optional[int] = None, batch_shape: Sequence[int] = (), device: Optional[BDeviceType] = None)

Bases: Space[GraphInstance[BArrayType], BDeviceType, BDtypeType, BRNGType], Generic[BArrayType, BDeviceType, BDtypeType, BRNGType]

backend instance-attribute

backend = backend

dtype instance-attribute

dtype = dtype

device property

device: Optional[_SpaceBDeviceT]

shape property

shape: tuple[int, ...] | None

Return the shape of the space as an immutable property.

batch_shape instance-attribute

batch_shape = tuple((int(dim)) for dim in batch_shape)

is_edge instance-attribute

is_edge = is_edge

node_feature_space instance-attribute

node_feature_space = node_feature_space if (device is None or node_feature_space is None) else node_feature_space.to(device=device)

edge_feature_space instance-attribute

edge_feature_space = edge_feature_space if (device is None or edge_feature_space is None) else edge_feature_space.to(device=device)

min_nodes instance-attribute

min_nodes = min_nodes

max_nodes instance-attribute

max_nodes = max_nodes

min_edges instance-attribute

min_edges = min_edges

max_edges instance-attribute

max_edges = max_edges

is_subspace

is_subspace(other: Space) -> bool

Return whether this space is a STRICT subspace of other (self ⊂ other).

Defined uniformly for all spaces as::

self.is_subspace(other)  ⟺  self.is_subspaceeq(other) and not other.is_subspaceeq(self)

I.e. self ⊆ other holds but other ⊆ self does not, so self is a PROPER (strict) subspace of other. This is the relation versus the non-strict provided by :meth:is_subspaceeq.

This definition is used instead of relying on __eq__ because some space classes only have identity __eq__; defining strict containment via the symmetric non-strict check works uniformly for all classes regardless of their __eq__ implementation.

For structurally-distinct-but-mutually-containing spaces (which should not occur under the strict dtype/shape policies enforced by the per-class is_subspaceeq implementations) this degrades gracefully to False: if both self.is_subspaceeq(other) and other.is_subspaceeq(self) hold, the two spaces are considered equivalent and neither is a STRICT subspace of the other.

If either side's is_subspaceeq is not implemented (the base :meth:is_subspaceeq raises NotImplementedError), the exception propagates to the caller — it is NOT swallowed into False so that callers can tell that the comparison is unsupported.

Note: controller-required-space checks should typically use :meth:is_subspaceeq (a controller's required space may exactly equal the env space, in which case the strict is_subspace would return False).

abbr_device staticmethod

abbr_device(spaces: Iterable[Space[Any, _SpaceBDeviceT, _SpaceBDTypeT, _SpaceBDRNGT]]) -> Optional[_SpaceBDeviceT]

Return the shared device across spaces, or None if mixed/empty.

to

to(backend: Optional[ComputeBackend] = None, device: Optional[Union[BDeviceType, Any]] = None) -> Union[GraphSpace[BArrayType, BDeviceType, BDtypeType, BRNGType], GraphSpace]

sample

sample(rng: BRNGType) -> Tuple[BRNGType, GraphInstance]

create_empty

create_empty() -> GraphInstance[BArrayType]

is_bounded

is_bounded(manner='both')

contains

contains(x: GraphInstance[BArrayType]) -> bool

Return boolean specifying if x is a valid member of this space.

get_repr

get_repr(abbreviate=False, include_backend=True, include_device=True, include_dtype=True)

is_subspaceeq

is_subspaceeq(other: Any) -> bool

Return whether this graph space is a non-strict subspace of other (⊆).

True iff other is a GraphSpace on the same backend, the node and edge feature spaces are subspaces recursively, and self's node/edge count ranges are contained within other's:

  • other.min_nodes <= self.min_nodes and self.max_nodes <= other.max_nodes (treating None as +∞);
  • likewise for edges, with the additional requirement that self.is_edge and other.is_edge agree (an edge-less space cannot be a subspace of an edge-bearing one and vice versa).

device is ignored.

data_to

data_to(data, backend=None, device=None)

GraphInstance dataclass

GraphInstance(n_nodes: BArrayType, n_edges: Optional[BArrayType] = None, nodes_features: Optional[BArrayType] = None, edges_features: Optional[BArrayType] = None, edges: Optional[BArrayType] = None)

Bases: Generic[BArrayType]

n_nodes instance-attribute

n_nodes: BArrayType

Number of nodes in the graph, shape (*batch_shape)

n_edges class-attribute instance-attribute

n_edges: Optional[BArrayType] = None

Number of edges in the graph, shape (*batch_shape) or None if no edges are present.

nodes_features class-attribute instance-attribute

nodes_features: Optional[BArrayType] = None

Node features, shape (batch_shape, max(n_nodes), node_feature_space.shape) if node_feature_space is not None, otherwise None.

edges_features class-attribute instance-attribute

edges_features: Optional[BArrayType] = None

Edge features, shape (batch_shape, max(n_edges), edge_feature_space.shape) if edge_feature_space is not None, otherwise None.

edges class-attribute instance-attribute

edges: Optional[BArrayType] = None

Edges in the graph, shape (*batch_shape, max(n_edges), 2) where each edge is represented by a pair of node indices, or None if no edges are present.

TextSpace

TextSpace(backend: ComputeBackend[Any, BDeviceType, BDtypeType, BRNGType], max_length: int, *, min_length: int = 0, charset: Optional[FrozenSet[str] | str] = None, device: Optional[BDeviceType] = None)

Bases: Space[str, BDeviceType, BDtypeType, BRNGType]

backend instance-attribute

backend = backend

dtype instance-attribute

dtype = dtype

device property

device: Optional[_SpaceBDeviceT]

shape property

shape: tuple[int, ...] | None

Return the shape of the space as an immutable property.

min_length instance-attribute

min_length: int = min_length

max_length instance-attribute

max_length: int = max_length

charset property

charset: Optional[FrozenSet[str]]

charset_index property

charset_index: Optional[Mapping[str, int]]

charset_list property

charset_list: Optional[Tuple[str, ...]]

is_subspace

is_subspace(other: Space) -> bool

Return whether this space is a STRICT subspace of other (self ⊂ other).

Defined uniformly for all spaces as::

self.is_subspace(other)  ⟺  self.is_subspaceeq(other) and not other.is_subspaceeq(self)

I.e. self ⊆ other holds but other ⊆ self does not, so self is a PROPER (strict) subspace of other. This is the relation versus the non-strict provided by :meth:is_subspaceeq.

This definition is used instead of relying on __eq__ because some space classes only have identity __eq__; defining strict containment via the symmetric non-strict check works uniformly for all classes regardless of their __eq__ implementation.

For structurally-distinct-but-mutually-containing spaces (which should not occur under the strict dtype/shape policies enforced by the per-class is_subspaceeq implementations) this degrades gracefully to False: if both self.is_subspaceeq(other) and other.is_subspaceeq(self) hold, the two spaces are considered equivalent and neither is a STRICT subspace of the other.

If either side's is_subspaceeq is not implemented (the base :meth:is_subspaceeq raises NotImplementedError), the exception propagates to the caller — it is NOT swallowed into False so that callers can tell that the comparison is unsupported.

Note: controller-required-space checks should typically use :meth:is_subspaceeq (a controller's required space may exactly equal the env space, in which case the strict is_subspace would return False).

abbr_device staticmethod

abbr_device(spaces: Iterable[Space[Any, _SpaceBDeviceT, _SpaceBDTypeT, _SpaceBDRNGT]]) -> Optional[_SpaceBDeviceT]

Return the shared device across spaces, or None if mixed/empty.

to

to(backend=None, device=None)

character_index

character_index(char: str) -> Optional[int]

sample

sample(rng: BRNGType) -> Tuple[BRNGType, str]

create_empty

create_empty() -> str

is_bounded

is_bounded(manner='both')

contains

contains(x: Any) -> bool

get_repr

get_repr(abbreviate=False, include_backend=True, include_device=True, include_dtype=True)

is_subspaceeq

is_subspaceeq(other: Any) -> bool

Return whether this text space is a non-strict subspace of other (⊆).

True iff other is a TextSpace on the same backend, self's charset is a subset of other's charset (a None charset on self is a subset of any charset; a None charset on other only contains a None charset on self), self.min_length >= other.min_length and self.max_length <= other.max_length. device is ignored.

data_to

data_to(data, backend=None, device=None)

TupleSpace

TupleSpace(backend: ComputeBackend[Any, BDeviceType, BDtypeType, BRNGType], spaces: Iterable[Space[Any, BDeviceType, BDtypeType, BRNGType]], device: Optional[BDeviceType] = None)

Bases: Space[Tuple[Any, ...], BDeviceType, BDtypeType, BRNGType]

backend instance-attribute

backend = backend

dtype instance-attribute

dtype = dtype

device property

device: Optional[_SpaceBDeviceT]

shape property

shape: tuple[int, ...] | None

Return the shape of the space as an immutable property.

spaces instance-attribute

spaces: Tuple[Space[Any, BDeviceType, BDtypeType, BRNGType], ...] = tuple(new_spaces)

is_subspace

is_subspace(other: Space) -> bool

Return whether this space is a STRICT subspace of other (self ⊂ other).

Defined uniformly for all spaces as::

self.is_subspace(other)  ⟺  self.is_subspaceeq(other) and not other.is_subspaceeq(self)

I.e. self ⊆ other holds but other ⊆ self does not, so self is a PROPER (strict) subspace of other. This is the relation versus the non-strict provided by :meth:is_subspaceeq.

This definition is used instead of relying on __eq__ because some space classes only have identity __eq__; defining strict containment via the symmetric non-strict check works uniformly for all classes regardless of their __eq__ implementation.

For structurally-distinct-but-mutually-containing spaces (which should not occur under the strict dtype/shape policies enforced by the per-class is_subspaceeq implementations) this degrades gracefully to False: if both self.is_subspaceeq(other) and other.is_subspaceeq(self) hold, the two spaces are considered equivalent and neither is a STRICT subspace of the other.

If either side's is_subspaceeq is not implemented (the base :meth:is_subspaceeq raises NotImplementedError), the exception propagates to the caller — it is NOT swallowed into False so that callers can tell that the comparison is unsupported.

Note: controller-required-space checks should typically use :meth:is_subspaceeq (a controller's required space may exactly equal the env space, in which case the strict is_subspace would return False).

abbr_device staticmethod

abbr_device(spaces: Iterable[Space[Any, _SpaceBDeviceT, _SpaceBDTypeT, _SpaceBDRNGT]]) -> Optional[_SpaceBDeviceT]

Return the shared device across spaces, or None if mixed/empty.

to

to(backend=None, device=None)

sample

sample(rng: BRNGType) -> Tuple[BRNGType, Tuple[Any, ...]]

create_empty

create_empty() -> Tuple[Any, ...]

Create an empty data structure for this space.

is_bounded

is_bounded(manner='both')

contains

contains(x: Tuple[Any, ...]) -> bool

get_repr

get_repr(abbreviate=False, include_backend=True, include_device=True, include_dtype=True)

is_subspaceeq

is_subspaceeq(other: Any) -> bool

Return whether this tuple space is a non-strict subspace of other (⊆).

True iff other is a TupleSpace on the same backend, the two have the same arity, and for every index i the child self.spaces[i].is_subspaceeq(other.spaces[i]) holds recursively. device is ignored.

data_to

data_to(data, backend=None, device=None)

UnionSpace

UnionSpace(backend: ComputeBackend[Any, BDeviceType, BDtypeType, BRNGType], spaces: Iterable[Space[Any, BDeviceType, BDtypeType, BRNGType]], device: Optional[BDeviceType] = None)

Bases: Space[Tuple[int, Any], BDeviceType, BDtypeType, BRNGType]

backend instance-attribute

backend = backend

dtype instance-attribute

dtype = dtype

device property

device: Optional[_SpaceBDeviceT]

shape property

shape: tuple[int, ...] | None

Return the shape of the space as an immutable property.

spaces instance-attribute

spaces = tuple(spaces if device is None else [(space.to(device=device)) for space in spaces])

is_subspace

is_subspace(other: Space) -> bool

Return whether this space is a STRICT subspace of other (self ⊂ other).

Defined uniformly for all spaces as::

self.is_subspace(other)  ⟺  self.is_subspaceeq(other) and not other.is_subspaceeq(self)

I.e. self ⊆ other holds but other ⊆ self does not, so self is a PROPER (strict) subspace of other. This is the relation versus the non-strict provided by :meth:is_subspaceeq.

This definition is used instead of relying on __eq__ because some space classes only have identity __eq__; defining strict containment via the symmetric non-strict check works uniformly for all classes regardless of their __eq__ implementation.

For structurally-distinct-but-mutually-containing spaces (which should not occur under the strict dtype/shape policies enforced by the per-class is_subspaceeq implementations) this degrades gracefully to False: if both self.is_subspaceeq(other) and other.is_subspaceeq(self) hold, the two spaces are considered equivalent and neither is a STRICT subspace of the other.

If either side's is_subspaceeq is not implemented (the base :meth:is_subspaceeq raises NotImplementedError), the exception propagates to the caller — it is NOT swallowed into False so that callers can tell that the comparison is unsupported.

Note: controller-required-space checks should typically use :meth:is_subspaceeq (a controller's required space may exactly equal the env space, in which case the strict is_subspace would return False).

abbr_device staticmethod

abbr_device(spaces: Iterable[Space[Any, _SpaceBDeviceT, _SpaceBDTypeT, _SpaceBDRNGT]]) -> Optional[_SpaceBDeviceT]

Return the shared device across spaces, or None if mixed/empty.

to

to(backend=None, device=None)

sample

sample(rng: BRNGType) -> Tuple[BRNGType, Tuple[int, Any]]

create_empty

create_empty() -> Tuple[int, Any]

Create an empty data structure for this space.

is_bounded

is_bounded(manner='both')

contains

contains(x: Tuple[int, Any]) -> bool

Return boolean specifying if x is a valid member of this space.

get_repr

get_repr(abbreviate: bool = False, include_backend=True, include_device=True, include_dtype=True)

is_subspaceeq

is_subspaceeq(other: Any) -> bool

Return whether this union space is a non-strict subspace of other (⊆).

Kept simple: True iff other is a UnionSpace on the same backend with the same number of alternatives and, for every index i, self.spaces[i].is_subspaceeq(other.spaces[i]) holds (pairwise, same-index recursion). This is a conservative structural check — it does not attempt to reason about alternative reordering or set-union semantics. device is ignored.

data_to

data_to(data, backend=None, device=None)

BatchedSpace

BatchedSpace(single_space: Space[SpaceDataT, BDeviceType, BDtypeType, BRNGType], batch_shape: Sequence[int])

Bases: Space[ndarray, BDeviceType, BDtypeType, BRNGType]

This space represents a batch of

backend instance-attribute

backend = backend

dtype instance-attribute

dtype = dtype

device property

device: Optional[_SpaceBDeviceT]

shape property

shape: tuple[int, ...] | None

Return the shape of the space as an immutable property.

batch_shape instance-attribute

batch_shape = batch_shape

single_space instance-attribute

single_space = single_space

is_subspace

is_subspace(other: Space) -> bool

Return whether this space is a STRICT subspace of other (self ⊂ other).

Defined uniformly for all spaces as::

self.is_subspace(other)  ⟺  self.is_subspaceeq(other) and not other.is_subspaceeq(self)

I.e. self ⊆ other holds but other ⊆ self does not, so self is a PROPER (strict) subspace of other. This is the relation versus the non-strict provided by :meth:is_subspaceeq.

This definition is used instead of relying on __eq__ because some space classes only have identity __eq__; defining strict containment via the symmetric non-strict check works uniformly for all classes regardless of their __eq__ implementation.

For structurally-distinct-but-mutually-containing spaces (which should not occur under the strict dtype/shape policies enforced by the per-class is_subspaceeq implementations) this degrades gracefully to False: if both self.is_subspaceeq(other) and other.is_subspaceeq(self) hold, the two spaces are considered equivalent and neither is a STRICT subspace of the other.

If either side's is_subspaceeq is not implemented (the base :meth:is_subspaceeq raises NotImplementedError), the exception propagates to the caller — it is NOT swallowed into False so that callers can tell that the comparison is unsupported.

Note: controller-required-space checks should typically use :meth:is_subspaceeq (a controller's required space may exactly equal the env space, in which case the strict is_subspace would return False).

abbr_device staticmethod

abbr_device(spaces: Iterable[Space[Any, _SpaceBDeviceT, _SpaceBDTypeT, _SpaceBDRNGT]]) -> Optional[_SpaceBDeviceT]

Return the shared device across spaces, or None if mixed/empty.

to

to(backend=None, device=None)

sample

sample(rng: BRNGType) -> Tuple[BRNGType, BArrayType]

create_empty

create_empty()

is_bounded

is_bounded(manner='both')

contains

contains(x: BArrayType) -> bool

get_repr

get_repr(abbreviate=False, include_backend=True, include_device=True, include_dtype=True)

is_subspaceeq

is_subspaceeq(other: Any) -> bool

Return whether this batched space is a subspace of other.

True iff other is a BatchedSpace on the same backend with equal batch_shape and self.single_space.is_subspaceeq(other.single_space) holds recursively (non-strict ⊆). device is ignored.

data_to

data_to(data, backend=None, device=None)

is_flattenable

is_flattenable(space: Space, start_dim: int = 0) -> bool

flat_dim

flat_dim(space: Space, start_dim: int = 0) -> Optional[int]

flatten_space

flatten_space(space: Space, start_dim: int = 0) -> BoxSpace

flatten_data

flatten_data(space: Space, data: Any, start_dim: int = 0) -> BArrayType

unflatten_data

unflatten_data(space: Space, data: BArrayType, start_dim: int = 0) -> Any