[bugfix] v2: MatrixGame2/3 causal-loop progress counter + MG3 patch alignment

MatrixGame2 (causal DMD loop): bump st.step_idx on every executed work unit (each
DMD step and each clean-context pass). The loop drives its own control flow off
block_idx/dmd_idx/phase, but the runtime's no-progress watchdog keys on
st.step_idx, so a multi-block causal rollout was seen as stalled. Mirrors what
every other recipe loop does.

MatrixGame3 (5B WanModel): patch-align the latent H/W (patch_size (1,2,2)) before
denoise. The model folds (H/2, W/2) tokens, so an odd latent dim made the
unpatchified velocity come back one row/col short of the noise latent. Crop to
(latent // patch) * patch, faithful to MatrixGame3DenoisingStage.

v2 mini: 240 passed.
This commit is contained in:
SolitaryThinker
2026-06-19 11:58:17 +00:00
parent 9e8d515842
commit 685702e330
2 changed files with 19 additions and 1 deletions
+5
View File
@@ -291,6 +291,11 @@ class MatrixGame2CausalDMDLoop:
# advance #
# --------------------------------------------------------------------- #
def advance(self, st: LoopState, result: StepResult) -> LoopState:
# Monotonic progress counter: this loop drives control flow off block_idx/dmd_idx/phase (in
# scratch), but the runtime's no-progress watchdog (engine ProgramRunner._progress) keys on
# st.step_idx. Bump it on EVERY executed work unit (each DMD step AND each clean-context pass)
# so a multi-block causal rollout is seen to advance — every other recipe loop does the same.
st.step_idx += 1
out = result.output
start, num = out["start"], out["num"]
# Write the (denoised or unchanged) block back into the full latent.
+14 -1
View File
@@ -60,6 +60,12 @@ MG3_NUM_TRAIN_TIMESTEPS = 1000 # FlowUniPC timestep ~ sigma * num_train_timeste
MG3_CLIP_FRAME = 56
MG3_FIRST_CLIP_FRAME = MG3_CLIP_FRAME + 1 # 57
MG3_PAST_FRAME = 16
# DiT patch stride on the spatial latent dims (patch_size (1,2,2)). The 5B MatrixGame3WanModel folds
# (H/2, W/2) tokens, so the latent H/W must be patch-aligned (even) before denoise or the unpatchified
# velocity comes back one row/col short of the noise latent (faithful to MatrixGame3DenoisingStage's
# ``latent_h_aligned = (latent_h // patch_h) * patch_h`` crop). 720px -> latent_h 45 (odd) -> 44.
MG3_PATCH_H = 2
MG3_PATCH_W = 2
def _toy_latent_shape(req) -> tuple[int, int, int, int]:
@@ -76,7 +82,14 @@ def _toy_latent_shape(req) -> tuple[int, int, int, int]:
def _gpu_latent_shape(req, channels: int, spatial_ratio: int, temporal_ratio: int) -> tuple[int, int, int, int]:
d = req.diffusion
t = (max(1, d.num_frames) - 1) // temporal_ratio + 1
return (channels, max(1, t), max(1, d.height // spatial_ratio), max(1, d.width // spatial_ratio))
h = max(1, d.height // spatial_ratio)
w = max(1, d.width // spatial_ratio)
# Patch-align the spatial latent dims to the DiT patch stride (faithful to MatrixGame3DenoisingStage):
# an odd latent_h/w (e.g. 720px -> 45) would otherwise leave the unpatchified velocity one row short
# of the noise latent and break the flow-match step's elementwise add.
h = max(MG3_PATCH_H, (h // MG3_PATCH_H) * MG3_PATCH_H)
w = max(MG3_PATCH_W, (w // MG3_PATCH_W) * MG3_PATCH_W)
return (channels, max(1, t), h, w)
def _cond_compatible(latent: np.ndarray, img_cond: np.ndarray) -> bool: