Intersection Interfaces

See also

More Optimization Interfaces

User guide page on the topic.

These protocols are intersections of various types of problems.

An intersection protocol indicates that a type implements all protocols that are part of the intersection. Taking for example OptEnv, an intersection of SingleOptimizable and Env, you can shorten your line of base classes:

>>> from gymnasium.spaces import Box
>>> from gymnasium import Env
>>> from cernml import coi
...
>>> class Both(coi.OptEnv):
...     def __init__(self, render_mode=None):
...         super().__init__(render_mode)
...         self.optimization_space = Box(-1, 1)
...         self.observation_space = Box(-1, 1)
...         self.action_space = Box(-1, 1)
...
...     def get_initial_params(self): ...
...
...     def compute_single_objective(self, params): ...
...
>>> env = Both()
>>> isinstance(env, coi.SingleOptimizable)
True
>>> isinstance(env, Env)
True
>>> isinstance(env, coi.OptEnv)
True

Vice versa, you can use intersection protocols to test if a class implements both protocols, even if it doesn’t directly subclass the intersection protocol:

>>> class Indirect(Env, coi.SingleOptimizable):
...     def __init__(self, render_mode=None):
...         super().__init__(render_mode)
...         self.optimization_space = Box(-1, 1)
...         self.observation_space = Box(-1, 1)
...         self.action_space = Box(-1, 1)
...
...     def get_initial_params(self): ...
...
...     def compute_single_objective(self, params): ...
...
>>> env = Indirect()
>>> isinstance(env, coi.SingleOptimizable)
True
>>> isinstance(env, Env)
True
>>> isinstance(env, coi.OptEnv)
True

The intersection protocols of this package come with a few limitations given by the implementation:

  • Implementors must be real subclasses of one of the environment base classes (Env or one of the Additional Interfaces), be it direct or indirect. Virtual subclasses are not recognized.

    This restriction is given by Env not actually being a Protocol; accordingly, it does not apply to SingleOptimizable.

  • Static type checkers like MyPy generally don’t recognize the intersections as true protocols, even though they behave as such at runtime. Again, this is a limitation of Env not actually being a Protocol.

  • Implementor classes that don’t subclass an intersection protocol must provide all three spaces [1] as attributes or properties at class scope to be recognized as subclasses. Type annotations are not sufficient.

    This restriction is given by the fact that for protocols, issubclass() only checks the type hints of a subclass if the subclass is itself a protocol. If the subclass is concrete (i.e. not a protocol) and also doesn’t have a protocol among its bases, there is no way for Python to verify the implementation.

  • Implementor instances, on the other hand, merely need to define the three spaces [1]. This may happen e.g. within __init__(). This may lead to the situation that a class MyEnv is not a subclass of OptEnv, but MyEnv() is an instance of it. This is simply given by the dynamic nature of Python.

class cernml.coi.OptEnv(render_mode: str | None = None)

Bases: Env[ObsType, ActType], SingleOptimizable[ParamType]

An intersection of Env and SingleOptimizable.

Any class that inherits from both also inherits from this class.

class cernml.coi.OptGoalEnv(render_mode: str | None = None)

Bases: GoalEnv, SingleOptimizable[ParamType], Generic[ObsType, GoalType, ActType, ParamType]

An intersection of GoalEnv and SingleOptimizable.

Any class that inherits from both also inherits from this class.

class cernml.coi.SeparableOptEnv(render_mode: str | None = None)

Bases: SeparableEnv[ObsType, ActType], SingleOptimizable[ParamType]

An intersection of SeparableEnv and SingleOptimizable.

Any class that inherits from both also inherits from this class.

class cernml.coi.SeparableOptGoalEnv(render_mode: str | None = None)

Bases: SeparableGoalEnv[ObsType, GoalType, ActType], SingleOptimizable[ParamType]

An intersection of SeparableGoalEnv and SingleOptimizable.

Any class that inherits from both also inherits from this class.