factoriax.analysis.milestones#
Find when key events first happen in an episode, and plot them.
Two questions, each with a finder and its plots. When did each achievement unlock, and when did the agent first take a given action? The answers show how far a policy gets and how quickly, which is what makes two policies comparable.
Every timestep here is a 0-based index along the time axis of the
trajectory. An event that never happened reports -1, so a caller
masks with >= 0 before it takes a mean.
The two box plots drop the episodes where the event never happened, and put the share that did happen in the tick label. A box therefore describes the episodes that reached the event, and not the whole batch. A policy that reaches an achievement once, quickly, can look better than one that reaches it every time, slowly.
- factoriax.analysis.milestones.achievement_timing(traj: Trajectory) ndarray#
Find the first timestep at which each achievement unlocked.
- Parameters:
traj – A trajectory whose
achievementsfield holds a mask of shape(B, T, A). The mask is read as a boolean, so any non-zero value counts as unlocked.- Returns:
int32 of shape
(B, A). Entry(b, a)is the first timestep at which achievementawas set in episodeb, or-1when it never was.- Return type:
- Raises:
ValueError – When the trajectory carries no
achievementsfield.
Notes
The scan is a Python loop over every episode and achievement, so the cost grows with
BtimesA.
- factoriax.analysis.milestones.first_action_timestep(traj: Trajectory, action_id: int, player: int | None = None) ndarray#
Find the first timestep at which one action was taken.
- Parameters:
traj – The recording to read. Actions of shape
(B, T),(B, T, P), and(B, T, 1)all work.action_id – The action to look for.
player – Which player to read, or
Nonefor player 0.
- Returns:
int32 of shape
(B,). Entrybis the first timestep at which episodebtook the action, or-1when it never did.- Return type:
- Raises:
IndexError – When
playernames a slot the player axis does not have.
- factoriax.analysis.milestones.plot_achievement_progress(traj: Trajectory, achievement_labels: list[str] | None = None, ax: Axes | None = None, figsize: tuple[float, float] = (12, 5), title: str | None = None) tuple[Figure, Axes]#
Draw the share of episodes that unlocked each achievement.
Each line is a cumulative distribution over unlock time. At timestep
tit gives the share of all episodes that had unlocked that achievement byt.The share is over every episode, and not only over the episodes that unlocked the achievement. A line therefore flattens below 1.0 when some episodes never reached it, and that height is the success rate. This differs from
plot_achievement_timing(), which drops the episodes that never reached the achievement.- Parameters:
traj – A trajectory with an
achievementsfield.achievement_labels – The display name of each achievement, or
Nonefor"Achievement 0"and so on.ax – The axes to draw on, or
Nonefor a new figure.figsize – The size of that new figure in inches. It is ignored when
axis given.title – The figure title, or
Nonefor a default.
- Returns:
(figure, axes). The figure is not written and not closed, so the caller owns it.- Return type:
- factoriax.analysis.milestones.plot_achievement_timing(traj: Trajectory, achievement_labels: list[str] | None = None, ax: Axes | None = None, figsize: tuple[float, float] = (10, 5), title: str | None = None) tuple[Figure, Axes]#
Draw one box for each achievement, over its unlock timesteps.
An achievement that no episode unlocked gets no box. For the rest, the box covers the episodes that did unlock it, and the tick label gives that share as a percentage. Read the box and the percentage together: a low percentage means the box describes few episodes.
- Parameters:
traj – A trajectory with an
achievementsfield.achievement_labels – The display name of each achievement, or
Nonefor"Achievement 0"and so on. The list must be at least as long as the achievement axis.ax – The axes to draw on, or
Nonefor a new figure.figsize – The size of that new figure in inches. It is ignored when
axis given.title – The figure title, or
Nonefor a default.
- Returns:
(figure, axes). The figure is not written and not closed, so the caller owns it.- Return type:
- factoriax.analysis.milestones.plot_first_action_timing(traj: Trajectory, action_ids: list[int], action_labels: list[str] | None = None, player: int | None = None, ax: Axes | None = None, figsize: tuple[float, float] = (10, 4), title: str | None = None) tuple[Figure, Axes]#
Draw one box for each action, over its first-use timesteps.
This answers when an agent first mines, first crafts, or first places a machine.
An action that no episode took gets no box. For the rest, the box covers the episodes that did take it, and the tick label gives that share as a percentage. Read the box and the percentage together.
- Parameters:
traj – The recording to read.
action_ids – The actions to draw, one box each.
action_labels – The display name of each action, or
Noneto look the names up inDEFAULT_ACTION_LABELS. The list must be as long asaction_ids.player – Which player to read, or
Nonefor player 0.ax – The axes to draw on, or
Nonefor a new figure.figsize – The size of that new figure in inches. It is ignored when
axis given.title – The figure title, or
Nonefor a default.
- Returns:
(figure, axes). The figure is not written and not closed, so the caller owns it.- Return type: