Transition Functions#
In this section we describe the transition function protocol, the transition function registry, and how to write your own custom transition functions.
The TransitionFunction Protocol#
A transition function is a generative function which represents a (stochastic)
mapping from a state-action pair to a next states. Using the typing
standard library, the transition function type is defined as a callable
typing.Protocol
which receives a
State
, an
gym_gridverse.action.Action
, and an optional
numpy.random.Generator
, and edits the input
State
.
- class TransitionFunction(*args, **kwargs)[source]
Signature that all reset functions must follow
Note
A transition function may (and often does) accept additional arguments;
this is possible so long as the extra arguments either have default
values, or are binded to specific values later on, e.g., using
functools.partial()
.
The Transition Function Registry#
The transition_functions
module contains some
pre-defined transition functions and the
transition_function_registry
,
a dictionary-like object through which to register and retrieve transition
functions. Transition functions are registered using the
register()
method,
which can be used as a decorator (also see
Custom Transition Functions). As a dictionary,
transition_function_registry
has a keys()
method which returns the names of registered
functions.
Custom Transition Functions#
Custom transition functions can be defined so long as they satisfy some basic rules; A custom transition function:
MUST satisfy the
TransitionFunction
protocol.MUST edit the input state, rather than return a new state altogether.
SHOULD use the
rng
argument as the source for any stochasticity.MUST use
get_gv_rng_if_none()
(only if therng
is used at all).
Warning
The rng
argument is used to control the source of randomness and allow
for the environment to be seeded via
set_seed()
, which in turn
guarantees the reproducibility of traces, runs, and experiments; if you wish
to use external sources of randomness, you will have to manage them and their
seeding yourself.
Practical Example 1#
Note
The examples shown here can be found in the examples/
folder.
In this example, we are going to write a transition function in which at every time-step one of the Floor tiles turns into a Wall tile.
from typing import Optional
import numpy.random as rnd
from gym_gridverse.action import Action
from gym_gridverse.envs.transition_functions import transition_function_registry
from gym_gridverse.grid_object import Floor, Wall
from gym_gridverse.rng import get_gv_rng_if_none
from gym_gridverse.state import State
@transition_function_registry.register
def creeping_walls(
state: State,
action: Action,
*,
rng: Optional[rnd.Generator] = None,
) -> None:
"""randomly converts a Floor into a Wall"""
rng = get_gv_rng_if_none(rng) # necessary to use rng object!
# all Floor positions
floor_positions = [
position
for position in state.grid.area.positions()
if isinstance(state.grid[position], Floor)
]
try:
# choose a random Floor position ...
i = rng.choice(len(floor_positions))
except ValueError:
# (floor_positions was an empty list)
pass
else:
# ... and turn it into a Wall
position = floor_positions[i]
state.grid[position] = Wall()
Practical Example 2#
In this example, we are going to write a transition function in which the agent moves until it hits an obstacle.
from typing import Optional
import numpy.random as rnd
from gym_gridverse.action import Action
from gym_gridverse.envs.transition_functions import transition_function_registry
from gym_gridverse.geometry import Orientation, Position
from gym_gridverse.state import State
_action_orientations = {
Action.MOVE_FORWARD: Orientation.F,
Action.MOVE_LEFT: Orientation.L,
Action.MOVE_RIGHT: Orientation.R,
Action.MOVE_BACKWARD: Orientation.B,
}
@transition_function_registry.register
def chessrook_movement(
state: State,
action: Action,
*,
rng: Optional[rnd.Generator] = None,
) -> None:
"""moves the agent like a chess rook, until it hits an obstacle"""
# get agent's movement direction
try:
action_orientation = _action_orientations[action]
except KeyError:
# not a movement action
return
movement_orientation = state.agent.orientation * action_orientation
position_delta = Position.from_orientation(movement_orientation)
# check positions until a blocking cell is found
position = state.agent.position
while not state.grid[position + position_delta].blocks_movement:
position = position + position_delta
state.agent.position = position
Practical Example 3#
In this example, we are going to write a transition function which randomizes the execution of another transition function.
from typing import Optional
import numpy.random as rnd
from gym_gridverse.action import Action
from gym_gridverse.envs.transition_functions import (
TransitionFunction,
transition_function_registry,
)
from gym_gridverse.rng import get_gv_rng_if_none
from gym_gridverse.state import State
@transition_function_registry.register
def random_transition(
state: State,
action: Action,
*,
transition_function: TransitionFunction,
p_success: float,
rng: Optional[rnd.Generator] = None,
) -> None:
"""randomly determines whether to perform a transition"""
rng = get_gv_rng_if_none(rng) # necessary to use rng object!
# flip coin to run the transition_function
if rng.random() <= p_success:
transition_function(state, action, rng=rng)