factoriax.analysis.state#

Read and draw how the world changed over an episode.

The module covers four subjects. Each has a plot, and two also have a finder that returns the numbers. The subjects are the inventory of a player, where a player walked, the resource left on the map, and the reward at each step.

Every function reads an optional field of Trajectory and raises ValueError when that field was not recorded.

Two coordinate orders meet in this module. A position is ordered (x, y), following the engine. A map array is indexed [y, x], following row-major order. The plots invert the y axis so that a picture matches the map, with y growing downward.

Module Attributes

DEFAULT_ITEM_LABELS

The name of each item, in ItemType value order.

DEFAULT_ITEM_COLORS

Fallback line colors, one for each entry of DEFAULT_ITEM_LABELS and in the same order.

factoriax.analysis.state.DEFAULT_ITEM_COLORS: list[str] = ['#c61010', '#4d72cb', '#b9f368', '#a63097', '#2beecd', '#dab181', '#3d10c6', '#53cb4d', '#f36896', '#307aa6', '#deee2b', '#c781da', '#10c66b', '#cb674d', '#6874f3', '#5da630', '#ee2bad', '#81d7da', '#c69810', '#874dcb', '#68f37f', '#a6303f', '#2b7dee', '#c0da81', '#c610c6', '#4dcba6', '#f3a168', '#3f30a6', '#4cee2b', '#da81aa', '#1099c6', '#cbc64d', '#c468f3', '#30a65c']#

Fallback line colors, one for each entry of DEFAULT_ITEM_LABELS and in the same order. Generated, so the list cannot fall behind the enum. The colors carry no meaning.

factoriax.analysis.state.DEFAULT_ITEM_LABELS: list[str] = ['EMPTY', 'COAL', 'IRON_ORE', 'COPPER_ORE', 'TIN_ORE', 'SILICON', 'LIMESTONE', 'IRON_PLATE', 'COPPER_PLATE', 'TIN_PLATE', 'WAFER', 'FRAME', 'CIRCUIT', 'WIRE', 'MOTOR', 'SENSOR', 'TIER1_SCIENCE_PACK', 'TIER2_SCIENCE_PACK', 'REFRACTORY', 'HULL', 'ENGINE_UNIT', 'AVIONICS', 'ROCKET_CORE', 'TIER3_SCIENCE_PACK', 'MINER', 'PALLET', 'CONVEYOR_BELT', 'ASSEMBLER', 'ARM', 'ROCKET', 'FURNACE', 'SCIENCE_LAB', 'SPLITTER', 'CROSSING']#

The name of each item, in ItemType value order. Built from the enum, so it cannot drift from it and always covers every item.

factoriax.analysis.state.inventory_over_time(traj: Trajectory, player: int = 0, num_item_types: int = 34) → ndarray#

Return the mean item count of each type at each timestep.

Parameters:
  • traj – A trajectory whose player_inventory field is set. That field already holds a count for each item, so nothing has to map slots to items. A rollout gives it four axes, (B, T, P, N), for one player as much as for several. Three axes are accepted too, for a recording built by hand.

  • player – Which player to read. It is ignored when the inventory has no player axis.

  • num_item_types – How many leading item ids to keep. The default covers every item the engine defines.

Returns:

Shape (T, num_item_types), the mean over episodes at each timestep. Column i is the item with ItemType value i, and column 0 is EMPTY, which stays at zero.

Return type:

numpy.ndarray

Raises:

ValueError – When the trajectory carries no player_inventory.

factoriax.analysis.state.plot_episode_rewards(traj: Trajectory, episode: int = 0, figsize: tuple[float, float] = (10, 6), title: str | None = None) → tuple[Figure, ndarray]#

Draw the step reward and the running total of one episode.

The figure holds two stacked panels that share an x axis. The top panel is a bar for each step. The bottom is the cumulative sum.

Parameters:
  • traj – A trajectory whose rewards field is set.

  • episode – Which episode to draw.

  • figsize – The size of the figure in inches. This function always makes its own figure, because it needs two panels.

  • title – The title over the top panel, or None for a default.

Returns:

(figure, axes), where axes holds the step-reward panel and the cumulative panel, in that order.

Return type:

tuple

Raises:

ValueError – When the trajectory carries no rewards.

Notes

A trajectory padded to a common episode length carries zeros in the pad steps. The cumulative line therefore runs flat at the end rather than stopping.

factoriax.analysis.state.plot_inventory(traj: Trajectory, player: int = 0, num_item_types: int = 34, item_labels: list[str] | None = None, item_colors: Sequence[str] | None = None, exclude_empty: bool = True, ax: Axes | None = None, figsize: tuple[float, float] = (12, 5), title: str | None = None) → tuple[Figure, Axes]#

Draw one line for each item type over the episode.

Parameters:
  • traj – A trajectory whose player_inventory field is set.

  • player – Which player to read.

  • num_item_types – How many leading item ids to draw. It also indexes item_labels and item_colors, so all three must agree.

  • item_labels – The name of each item, or None for DEFAULT_ITEM_LABELS, which is built from the ItemType enum and cannot drift from it.

  • item_colors – The line color of each item, or None for DEFAULT_ITEM_COLORS.

  • exclude_empty – True to skip item 0, EMPTY, which is always zero.

  • ax – The axes to draw on, or None for a new figure.

  • figsize – The size of that new figure in inches. It is ignored when ax is given.

  • title – The figure title, or None for a default.

Returns:

(figure, axes). The figure is not written and not closed, so the caller owns it.

Return type:

tuple

Raises:

IndexError – When num_item_types is greater than the length of item_labels or item_colors. Both defaults cover every item, so this needs a short list from the caller.

factoriax.analysis.state.plot_position_heatmap(traj: Trajectory, player: int = 0, map_width: int = 32, map_height: int = 32, time_range: tuple[int, int] | None = None, ax: Axes | None = None, figsize: tuple[float, float] = (7, 7), cmap: str = 'hot', title: str | None = None) → tuple[Figure, Axes]#

Draw the visit-frequency heatmap of one player.

Parameters:
Returns:

(figure, axes). The image uses origin="upper", so y grows downward and the picture matches the map.

Return type:

tuple

factoriax.analysis.state.plot_resource_depletion(traj: Trajectory, ax: Axes | None = None, figsize: tuple[float, float] = (12, 5), title: str | None = None) → tuple[Figure, Axes]#

Draw the total resource left on the map over time.

The line is the mean over episodes, and the band around it is one standard deviation. The total sums every map tile, so it mixes every ore into one number and does not separate them.

Parameters:
  • traj – A trajectory whose block_resources field is set, shaped (B, T, H, W). Recordings do not carry this field by default. A rollout has to snapshot env_state.block_resources at each step.

  • ax – The axes to draw on, or None for a new figure.

  • figsize – The size of that new figure in inches. It is ignored when ax is given.

  • title – The figure title, or None for a default.

Returns:

(figure, axes). The figure is not written and not closed, so the caller owns it.

Return type:

tuple

Raises:

ValueError – When the trajectory carries no block_resources.

factoriax.analysis.state.plot_trajectory_trace(traj: Trajectory, episode: int = 0, player: int = 0, map_width: int = 32, map_height: int = 32, ax: Axes | None = None, figsize: tuple[float, float] = (7, 7), title: str | None = None, cmap: str = 'viridis') → tuple[Figure, Axes]#

Draw the path one player walked in one episode.

The color of the path runs with time, dark at the start and bright at the end. A green circle marks the first position and a red square the last.

Parameters:
  • traj – A trajectory whose positions field is set.

  • episode – Which episode to draw.

  • player – Which player to draw.

  • map_width – The map size in tiles, which sets the axis limits.

  • map_height – The map size in tiles, which sets the axis limits.

  • ax – The axes to draw on, or None for a new figure.

  • figsize – The size of that new figure in inches. It is ignored when ax is given.

  • title – The figure title, or None for a default.

  • cmap – The colormap used for the time gradient.

Returns:

(figure, axes). The y axis is inverted, so the picture matches the map rather than standard plot orientation.

Return type:

tuple

Raises:

ValueError – When the trajectory carries no positions.

factoriax.analysis.state.position_heatmap(traj: Trajectory, player: int = 0, map_width: int = 32, map_height: int = 32, time_range: tuple[int, int] | None = None) → ndarray#

Count how often a player stood on each map tile.

Parameters:
  • traj – A trajectory whose positions field is set. Positions are ordered (x, y), which is the opposite of the [y, x] order used to index the returned grid.

  • player – Which player to read. It is ignored when the trajectory is not multi-player.

  • map_width – The map size in tiles. These must match the map the episode ran on. A value too small folds the outer tiles onto the border, because positions are clipped and not dropped.

  • map_height – The map size in tiles. These must match the map the episode ran on. A value too small folds the outer tiles onto the border, because positions are clipped and not dropped.

  • time_range – (start, end) to count only that window of timesteps, or None for the whole episode.

Returns:

Shape (map_height, map_width), indexed [y, x]. The values sum to 1.0, so each is the share of visits to that tile. A trajectory with no steps gives all zeros instead, because the division is skipped.

Return type:

numpy.ndarray

Raises:

ValueError – When the trajectory carries no positions.