BonanDing commited on
Commit
c8308b9
·
1 Parent(s): 3df3b5e

Fix DeMemWM nested split filtering

Browse files
.exp_artifact/dememwm_remaining_fix_plan.md ADDED
@@ -0,0 +1,368 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # DeMemWM Remaining Fix Plan
2
+
3
+ Repo: `/share_1/users/bonan_ding/WorldMem`
4
+
5
+ Branch: `dememwm`
6
+
7
+ Purpose: fix the remaining DeMemWM bugs one at a time. Each substep must be implemented, reviewed, fixed if needed, validated, committed, and marked complete before moving to the next substep.
8
+
9
+ ## Required Orchestration Flow
10
+
11
+ Use this plan with a new session prompt such as:
12
+
13
+ ```text
14
+ /goal Complete /share_1/users/bonan_ding/WorldMem/.exp_artifact/dememwm_remaining_fix_plan.md one substep at a time. For every substep, use a fresh implementation subagent, a fresh review subagent, and if needed fresh fix subagents until review is clean. Validate, inspect diff, commit the substep, and update this plan before moving to the next substep.
15
+ ```
16
+
17
+ For every substep below:
18
+
19
+ 1. Start a fresh implementation subagent.
20
+ 2. The implementation subagent must read:
21
+ - `AGENTS.md`
22
+ - `.codex/AGENT.md`
23
+ - `.codex/memory.md`
24
+ - `/share_1/users/bonan_ding/.codex/skills/codex-ml-cv-clean-implementation/SKILL.md`
25
+ 3. The implementation subagent must use:
26
+ - `[$codex-ml-cv-clean-implementation](/share_1/users/bonan_ding/.codex/skills/codex-ml-cv-clean-implementation/SKILL.md)`
27
+ 4. The implementation subagent must implement only the current substep.
28
+ 5. After implementation, inspect the diff before review.
29
+ 6. Start a fresh review subagent.
30
+ 7. The review subagent must read the same files and use the same skill.
31
+ 8. The review subagent must check:
32
+ - correctness
33
+ - bugs or regressions
34
+ - improper or unnecessary changes
35
+ - code cleanliness
36
+ - adherence to this plan
37
+ - adherence to repository conventions
38
+ - targeted test adequacy
39
+ 9. If review finds issues, start a fresh fix subagent.
40
+ 10. The fix subagent must read the same files, use the same skill, and fix only the review findings.
41
+ 11. Repeat review/fix/re-review until the review subagent reports no remaining issues.
42
+ 12. Run the validation commands listed for the substep.
43
+ 13. Run `git diff --check`.
44
+ 14. Inspect `git diff`.
45
+ 15. Commit only the completed substep with a clear commit message.
46
+ 16. Mark the substep complete in this plan file and include that plan status update in the same substep commit if possible.
47
+ 17. Do not proceed to the next substep until the current one is reviewed clean, validated, committed, and marked complete.
48
+
49
+ Global rules:
50
+
51
+ - Do not skip substeps.
52
+ - Do not combine unrelated substeps into one commit.
53
+ - Keep changes surgical.
54
+ - Preserve unrelated user changes.
55
+ - Do not train or submit Slurm jobs.
56
+ - Do not add config flags, new abstractions, compatibility fallbacks, or broad refactors unless the substep explicitly requires them.
57
+ - If validation fails, fix through the same fresh fix-subagent and review-subagent loop, then rerun validation.
58
+
59
+ Status legend:
60
+
61
+ - `[ ]` not started
62
+ - `[~]` in progress
63
+ - `[x]` complete and committed
64
+
65
+ ## Substep 1: Nested Validation/Test Split Filter Must Not Fall Back To Unfiltered Files
66
+
67
+ Status: `[x]`
68
+
69
+ Bug:
70
+
71
+ The DeMemWM latent dataset filters validation/test files by split type, such as `w_updown` or `wo_updown`. The direct-file path filtering was fixed, but when the direct filtered candidates are empty, the nested fallback can still add nested `.npz` files without applying the same split filter. That silently loads the wrong validation/test subset.
72
+
73
+ Primary files:
74
+
75
+ - `datasets/video/minecraft_video_dememwm_latent_dataset.py`
76
+ - `tests/test_dememwm_latent_dataset.py`
77
+
78
+ Known code area to inspect:
79
+
80
+ - `get_data_paths()` in `datasets/video/minecraft_video_dememwm_latent_dataset.py`
81
+ - Validation/test filtering around the existing `w_updown` and `wo_updown` logic
82
+ - Nested `.npz` fallback collection after the direct candidates are empty
83
+
84
+ Required behavior:
85
+
86
+ - Apply the same validation/test split filter to nested fallback files.
87
+ - Never re-add an unfiltered nested file list after validation/test filtering removed all candidates.
88
+ - If validation/test filtering leaves no files, emit a warning and then raise `FileNotFoundError`.
89
+ - The warning and error must make the reason clear: the selected split is empty because the split filter, such as `w_updown` or `wo_updown`, removed all candidates.
90
+ - Do not add a compatibility fallback that silently loads mismatched files.
91
+
92
+ Implementation constraints:
93
+
94
+ - Keep the dataset change local to path collection/filtering.
95
+ - Do not change sample loading semantics for valid datasets.
96
+ - Use standard `warnings.warn(...)` unless the local file already uses another warning pattern.
97
+ - Keep tests small and filesystem-local with temporary directories.
98
+
99
+ Validation for this substep:
100
+
101
+ ```bash
102
+ python -m py_compile datasets/video/minecraft_video_dememwm_latent_dataset.py tests/test_dememwm_latent_dataset.py
103
+ python -B -m unittest tests.test_dememwm_latent_dataset
104
+ git diff --check
105
+ ```
106
+
107
+ Review must specifically confirm:
108
+
109
+ - Mismatched nested validation/test files are rejected.
110
+ - A warning is emitted before `FileNotFoundError`.
111
+ - Positive filtering behavior still works.
112
+ - Train split behavior is not accidentally changed.
113
+
114
+ Suggested commit message:
115
+
116
+ ```text
117
+ Fix DeMemWM nested split filtering
118
+ ```
119
+
120
+ ## Substep 2: Remove Stale Raw-Video Helper Path From DeMemWM Algorithm
121
+
122
+ Status: `[ ]`
123
+
124
+ Bug:
125
+
126
+ The public DeMemWM train, validation, test, and interactive paths now use typed latent/memory dictionary batches, but stale legacy raw-video helper code remains in `algorithms/dememwm/df_video.py`. This dead path makes the implementation harder to audit and can accidentally preserve old tuple/raw-video assumptions.
127
+
128
+ Primary file:
129
+
130
+ - `algorithms/dememwm/df_video.py`
131
+
132
+ Known stale symbols to inspect:
133
+
134
+ - `_preprocess_batch`
135
+ - `_generate_condition_indices`
136
+ - `_prepare_conditions`
137
+ - `_prepare_noise_levels`
138
+
139
+ Required behavior:
140
+
141
+ - Make `_preprocess_batch()` strict for the current mapping/dictionary batch format.
142
+ - Remove unused legacy raw-video helper methods and dead code.
143
+ - Before deleting each helper, use `rg` to confirm it has no remaining references.
144
+ - Keep active encode/decode helpers only if they are still used by current DeMemWM code.
145
+ - Do not rewrite the training loop.
146
+ - Do not change current public train/validation/test/interactive behavior except removing stale unsupported paths.
147
+
148
+ Implementation constraints:
149
+
150
+ - Avoid broad refactors.
151
+ - Keep the diff centered on dead-code removal and the strict current-batch boundary.
152
+ - If a helper is still used, do not delete it. Instead, report why it must remain.
153
+
154
+ Validation for this substep:
155
+
156
+ ```bash
157
+ rg -n "_generate_condition_indices|_prepare_conditions|_prepare_noise_levels" algorithms/dememwm tests
158
+ python -m py_compile algorithms/dememwm/df_video.py
159
+ python -B -m unittest tests.test_checkpoint_loading tests.test_resume_checkpoint_logic
160
+ git diff --check
161
+ ```
162
+
163
+ Review must specifically confirm:
164
+
165
+ - No stale raw-video/tuple helper path remains in active DeMemWM preprocessing.
166
+ - No live references were broken.
167
+ - No unrelated training/eval behavior was changed.
168
+ - The code is cleaner, not merely rearranged.
169
+
170
+ Suggested commit message:
171
+
172
+ ```text
173
+ Remove stale DeMemWM raw-video helpers
174
+ ```
175
+
176
+ ## Substep 3: Reject Stale Experiment Flags For DeMemWM
177
+
178
+ Status: `[ ]`
179
+
180
+ Bug:
181
+
182
+ The generic experiment layer still accepts old experiment flags such as `zero_init_gate` and `only_tune_memory`. For `algorithm._name == "dememwm_base"`, these flags can override or corrupt DeMemWM trainability assumptions. The reference DeMemWM path rejects these stale flags instead of applying them.
183
+
184
+ Primary files:
185
+
186
+ - `experiments/exp_base.py`
187
+ - The smallest relevant existing test file, likely one of:
188
+ - `tests/test_checkpoint_loading.py`
189
+ - `tests/test_resume_checkpoint_logic.py`
190
+
191
+ Known code area to inspect:
192
+
193
+ - Experiment setup logic in `experiments/exp_base.py`
194
+ - Existing handling for `zero_init_gate`
195
+ - Existing handling for `only_tune_memory`
196
+ - How tests instantiate experiment/config objects
197
+
198
+ Required behavior:
199
+
200
+ - Add an early, surgical validation for DeMemWM experiments.
201
+ - If `algorithm._name == "dememwm_base"` and `zero_init_gate` is enabled, raise a clear `ValueError`.
202
+ - If `algorithm._name == "dememwm_base"` and `only_tune_memory` is enabled, raise a clear `ValueError`.
203
+ - Do not affect non-DeMemWM algorithms.
204
+ - Do not refactor experiment setup.
205
+
206
+ Implementation constraints:
207
+
208
+ - Keep the validation near existing flag handling or experiment initialization, whichever is cleanest in the current file.
209
+ - Do not add new config keys.
210
+ - Do not change default trainability behavior beyond rejecting these stale flags for DeMemWM.
211
+
212
+ Validation for this substep:
213
+
214
+ ```bash
215
+ python -m py_compile experiments/exp_base.py tests/test_checkpoint_loading.py tests/test_resume_checkpoint_logic.py
216
+ python -B -m unittest tests.test_checkpoint_loading tests.test_resume_checkpoint_logic
217
+ git diff --check
218
+ ```
219
+
220
+ Review must specifically confirm:
221
+
222
+ - DeMemWM rejects both stale flags with clear errors.
223
+ - Non-DeMemWM behavior is unchanged.
224
+ - The check is not duplicated in multiple messy locations.
225
+ - The patch does not introduce a broad config-validation layer.
226
+
227
+ Suggested commit message:
228
+
229
+ ```text
230
+ Reject stale DeMemWM experiment flags
231
+ ```
232
+
233
+ ## Substep 4: Add Minimal DeMemWM DiT Boundary Checks
234
+
235
+ Status: `[ ]`
236
+
237
+ Bug:
238
+
239
+ The DeMemWM DiT frame/memory splitting and stream-mask helpers are weaker than the reference implementation. Bad segment lengths or bad mask shapes can produce confusing downstream tensor failures. The fix should add only targeted checks at the model boundary, not broad internal guard clutter.
240
+
241
+ Primary files:
242
+
243
+ - `algorithms/dememwm/models/dit.py`
244
+ - `tests/test_dememwm_temporal_attention.py`
245
+
246
+ Known code area to inspect:
247
+
248
+ - `_split_frame_memory()`
249
+ - `_frame_memory_stream_mask()`
250
+
251
+ Required behavior:
252
+
253
+ - In `_split_frame_memory()`, validate that frame/memory segment lengths are nonnegative and sum to `x.shape[1]`.
254
+ - In `_frame_memory_stream_mask()`, validate that the stream mask shape matches the expected stream length.
255
+ - Error messages should be concise and useful for debugging a bad config or caller.
256
+ - Keep checks surgical. Do not add repetitive guards for every tensor property.
257
+
258
+ Implementation constraints:
259
+
260
+ - Follow the `codex-ml-cv-clean-implementation` guard policy.
261
+ - Add checks only where they catch this known realistic failure.
262
+ - Prefer clear local code over helper abstractions.
263
+ - Keep CPU-safe tests.
264
+
265
+ Validation for this substep:
266
+
267
+ ```bash
268
+ python -m py_compile algorithms/dememwm/models/dit.py tests/test_dememwm_temporal_attention.py
269
+ python -B -m unittest tests.test_dememwm_temporal_attention
270
+ git diff --check
271
+ ```
272
+
273
+ Review must specifically confirm:
274
+
275
+ - Bad segment sums fail early with a clear error.
276
+ - Bad stream-mask shapes fail early with a clear error.
277
+ - The checks do not sprawl into broad defensive programming.
278
+ - Existing temporal attention behavior is preserved.
279
+
280
+ Suggested commit message:
281
+
282
+ ```text
283
+ Validate DeMemWM DiT stream boundaries
284
+ ```
285
+
286
+ ## Substep 5: Compute Plucker Geometry In fp32 Under Mixed Precision
287
+
288
+ Status: `[ ]`
289
+
290
+ Bug:
291
+
292
+ The DeMemWM DiT Plucker/ray geometry path casts camera pose/ray math to the model activation dtype. With mixed precision training, for example `16-mixed`, this can run geometry in fp16/bf16 and lose precision. Geometry should be computed in fp32, then cast back to the activation dtype before use.
293
+
294
+ Primary files:
295
+
296
+ - `algorithms/dememwm/models/dit.py`
297
+ - `tests/test_dememwm_temporal_attention.py` or another existing focused model test file
298
+
299
+ Known code area to inspect:
300
+
301
+ - Camera pose handling in `algorithms/dememwm/models/dit.py`
302
+ - Ray or Plucker feature construction
303
+ - Any `dtype=x.dtype` or activation-dtype casting around pose/ray geometry
304
+
305
+ Required behavior:
306
+
307
+ - Compute camera/ray/Plucker geometry in fp32.
308
+ - Disable autocast only around the geometry math block if needed.
309
+ - Cast the final geometry tensor back to the activation/model dtype before concatenation or use.
310
+ - Keep the change local to the geometry path.
311
+ - Do not add a config flag.
312
+
313
+ Implementation constraints:
314
+
315
+ - Preserve device placement.
316
+ - Do not force CPU.
317
+ - Do not disrupt AMP for unrelated model operations.
318
+ - Avoid broad dtype guards; the important invariant is fp32 geometry math and final cast back to the caller's dtype.
319
+
320
+ Validation for this substep:
321
+
322
+ ```bash
323
+ python -m py_compile algorithms/dememwm/models/dit.py tests/test_dememwm_temporal_attention.py
324
+ python -B -m unittest tests.test_dememwm_temporal_attention
325
+ git diff --check
326
+ ```
327
+
328
+ Review must specifically confirm:
329
+
330
+ - Geometry math runs in fp32 under autocast/mixed precision.
331
+ - Final geometry output returns to the expected activation dtype before model use.
332
+ - The patch does not disable autocast for unrelated transformer/model compute.
333
+ - The test covers finite output and dtype behavior as far as the local environment allows.
334
+
335
+ Suggested commit message:
336
+
337
+ ```text
338
+ Use fp32 for DeMemWM Plucker geometry
339
+ ```
340
+
341
+ ## Final Audit After All Substeps
342
+
343
+ Status: `[ ]`
344
+
345
+ After all implementation substeps are complete and committed, run a final read-only audit.
346
+
347
+ Required commands:
348
+
349
+ ```bash
350
+ git status --short --branch
351
+ python -m py_compile datasets/video/minecraft_video_dememwm_latent_dataset.py algorithms/dememwm/df_video.py experiments/exp_base.py algorithms/dememwm/models/dit.py
352
+ python -B -m unittest tests.test_dememwm_latent_dataset tests.test_dememwm_temporal_attention tests.test_checkpoint_loading tests.test_resume_checkpoint_logic
353
+ git diff --check
354
+ ```
355
+
356
+ Final audit review must check:
357
+
358
+ - All substeps are marked `[x]`.
359
+ - Each substep has a separate commit.
360
+ - No unrelated files were changed.
361
+ - No stale raw-video helper path remains.
362
+ - No unfiltered nested validation/test fallback remains.
363
+ - DeMemWM rejects stale experiment flags.
364
+ - DeMemWM DiT boundary checks are concise and targeted.
365
+ - Plucker geometry is fp32 for math and returns to activation dtype for model use.
366
+ - There are no remaining uncommitted changes except intentional final plan status updates.
367
+
368
+ If the final audit passes, mark this final audit status `[x]` and commit that status update only if it is not already included in the last substep commit.
datasets/video/minecraft_video_dememwm_latent_dataset.py CHANGED
@@ -2,6 +2,7 @@
2
 
3
  from __future__ import annotations
4
 
 
5
  from pathlib import Path
6
  import numpy as np
7
  import torch
@@ -85,16 +86,28 @@ class MinecraftVideoDeMemWMLatentDataset(torch.utils.data.Dataset):
85
  if not split_dir.exists():
86
  return []
87
  paths = sorted((path for path in split_dir.glob("**/*.npz") if path.is_file()), key=lambda path: path.name)
 
88
 
89
- if self.wo_updown:
90
- paths = [path for path in paths if "w_updown" not in str(path)]
91
-
92
- if split in {"validation", "test"} and self.wo_updown:
 
 
 
 
93
  paths = [path for path in paths if "w_updown" not in str(path)]
94
- elif split in {"validation", "test"}:
95
- paths = [path for path in paths if "w_updown" in str(path)]
96
 
97
  if not paths:
 
 
 
 
 
 
 
 
 
98
  for sub_path in sorted(split_dir.iterdir(), key=lambda path: path.name):
99
  paths += sorted((path for path in sub_path.glob("**/*.npz") if path.is_file()), key=lambda path: path.name)
100
  return paths
 
2
 
3
  from __future__ import annotations
4
 
5
+ import warnings
6
  from pathlib import Path
7
  import numpy as np
8
  import torch
 
86
  if not split_dir.exists():
87
  return []
88
  paths = sorted((path for path in split_dir.glob("**/*.npz") if path.is_file()), key=lambda path: path.name)
89
+ num_candidates = len(paths)
90
 
91
+ split_filter = None
92
+ if split in {"validation", "test"}:
93
+ split_filter = "wo_updown" if self.wo_updown else "w_updown"
94
+ if self.wo_updown:
95
+ paths = [path for path in paths if "w_updown" not in str(path)]
96
+ else:
97
+ paths = [path for path in paths if "w_updown" in str(path)]
98
+ elif self.wo_updown:
99
  paths = [path for path in paths if "w_updown" not in str(path)]
 
 
100
 
101
  if not paths:
102
+ if split_filter is not None and num_candidates:
103
+ message = (
104
+ f"Selected split '{split}' under {split_dir} is empty because the "
105
+ f"{split_filter} split filter removed all {num_candidates} candidate .npz file(s)."
106
+ )
107
+ warnings.warn(message, stacklevel=2)
108
+ raise FileNotFoundError(message)
109
+ if split_filter is not None:
110
+ return paths
111
  for sub_path in sorted(split_dir.iterdir(), key=lambda path: path.name):
112
  paths += sorted((path for path in sub_path.glob("**/*.npz") if path.is_file()), key=lambda path: path.name)
113
  return paths
tests/test_dememwm_latent_dataset.py CHANGED
@@ -197,6 +197,33 @@ class DeMemWMLatentDatasetTests(unittest.TestCase):
197
  self.assertEqual([path.name for path in dataset.data_paths], ["clip_w_updown.npz"])
198
  self.assertEqual([path.name for path in dataset_wo.data_paths], ["plain.npz"])
199
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
200
  def test_dataset_rejects_files_shorter_than_original_frame_skip_window(self):
201
  with tempfile.TemporaryDirectory() as tmp:
202
  root = Path(tmp)
@@ -524,7 +551,7 @@ class DeMemWMLatentDatasetTests(unittest.TestCase):
524
  class Harness:
525
  def __init__(self):
526
  self.memory_condition_length, self.clip_noise = 3, 0.0
527
- self.context_frames, self.frame_stack, self.chunk_size = 2, 1, 2
528
  self.cfg = OmegaConf.create(
529
  {"memory_selection": {"enabled": True, "max_anchor_frames": 1, "max_dynamic_frames": 1, "max_revisit_frames": 1, "fov_overlap_threshold": 0.75}}
530
  )
@@ -592,26 +619,26 @@ class DeMemWMLatentDatasetTests(unittest.TestCase):
592
  self.assertTrue(all(call["kwargs"]["reference_length"] == 0 for call in calls))
593
  self.assertEqual(
594
  ([call["kwargs"]["current_frame"] for call in calls[::2]], [call["kwargs"]["frame_memory_segments"]["target"] for call in calls[::2]]),
595
- ([2, 4], [2, 1]),
596
  )
597
  self.assertEqual([call["kwargs"]["current_frame"] for call in calls], [2, 2, 4, 4])
598
  self.assertTrue(torch.equal(calls[0]["kwargs"]["frame_idx"], calls[1]["kwargs"]["frame_idx"]))
599
  self.assertTrue(torch.equal(calls[2]["kwargs"]["frame_idx"], calls[3]["kwargs"]["frame_idx"]))
600
  self.assertEqual(calls[0]["x"][:, 0, 0, 0, 0].tolist(), [0.0, 0.0, 0.0, 1.0, 0.0])
601
- self.assertEqual(calls[2]["x"][:, 0, 0, 0, 0].tolist(), [0.0, 0.0, 2.0, 2.0])
602
  self.assertEqual(calls[0]["action_cond"][:, 0, 0].tolist(), [102.0, 103.0, 0.0, 0.0, 0.0])
603
  self.assertEqual(calls[0]["kwargs"]["frame_idx"][:, 0].tolist(), [12, 13, 10, 11, 0])
604
- self.assertEqual(calls[2]["kwargs"]["frame_idx"][:, 0].tolist(), [14, 10, 13, 12])
605
  self.assertEqual(calls[0]["kwargs"]["frame_memory_masks"]["revisit"].tolist(), [[False]])
606
  self.assertEqual(calls[2]["kwargs"]["frame_memory_masks"]["revisit"].tolist(), [[True]])
607
  self.assertAlmostEqual(float(loss), 1.0 / 3.0)
608
  self.assertEqual([name for name, _ in harness.logged], ["test/latent_mse"])
609
  self.assertEqual(len(harness.decoded_inputs), 2)
610
- self.assertEqual(harness.decoded_inputs[0][:, 0, 0, 0, 0].tolist(), [2.0, 2.0, 4.0])
611
  self.assertEqual(harness.decoded_inputs[1][:, 0, 0, 0, 0].tolist(), [2.0, 3.0, 4.0])
612
  self.assertEqual(len(harness.metric_updates), 1)
613
  metric_pred, metric_gt = harness.metric_updates[0]
614
- self.assertEqual(metric_pred[:, 0, 0, 0, 0].tolist(), [12.0, 12.0, 14.0])
615
  self.assertEqual(metric_gt[:, 0, 0, 0, 0].tolist(), [12.0, 13.0, 14.0])
616
  self.assertEqual(len(video_calls), 1)
617
  self.assertEqual(video_calls[0][1]["namespace"], "test_vis")
 
197
  self.assertEqual([path.name for path in dataset.data_paths], ["clip_w_updown.npz"])
198
  self.assertEqual([path.name for path in dataset_wo.data_paths], ["plain.npz"])
199
 
200
+ def test_dataset_filters_nested_validation_files_by_selected_split(self):
201
+ with tempfile.TemporaryDirectory() as tmp:
202
+ root = Path(tmp)
203
+ _write_latent_npz(root / "validation" / "nested" / "plain.npz", 104)
204
+ _write_latent_npz(root / "validation" / "nested" / "clip_w_updown.npz", 104)
205
+
206
+ dataset = MinecraftVideoDeMemWMLatentDataset(_dataset_cfg(root), split="validation")
207
+ dataset_wo = MinecraftVideoDeMemWMLatentDataset(_dataset_cfg(root, wo_updown=True), split="validation")
208
+
209
+ self.assertEqual([path.name for path in dataset.data_paths], ["clip_w_updown.npz"])
210
+ self.assertEqual([path.name for path in dataset_wo.data_paths], ["plain.npz"])
211
+
212
+ def test_dataset_rejects_nested_validation_test_files_removed_by_split_filter(self):
213
+ cases = [
214
+ ("validation", False, "plain.npz", "w_updown"),
215
+ ("test", True, "clip_w_updown.npz", "wo_updown"),
216
+ ]
217
+ for split, wo_updown, filename, split_filter in cases:
218
+ with self.subTest(split=split, split_filter=split_filter), tempfile.TemporaryDirectory() as tmp:
219
+ root = Path(tmp)
220
+ _write_latent_npz(root / split / "nested" / filename, 104)
221
+ message = f"Selected split '{split}'.*{split_filter} split filter removed all"
222
+
223
+ with self.assertWarnsRegex(UserWarning, message):
224
+ with self.assertRaisesRegex(FileNotFoundError, message):
225
+ MinecraftVideoDeMemWMLatentDataset(_dataset_cfg(root, wo_updown=wo_updown), split=split)
226
+
227
  def test_dataset_rejects_files_shorter_than_original_frame_skip_window(self):
228
  with tempfile.TemporaryDirectory() as tmp:
229
  root = Path(tmp)
 
551
  class Harness:
552
  def __init__(self):
553
  self.memory_condition_length, self.clip_noise = 3, 0.0
554
+ self.context_frames, self.frame_stack, self.chunk_size, self.n_tokens = 2, 1, 2, 2
555
  self.cfg = OmegaConf.create(
556
  {"memory_selection": {"enabled": True, "max_anchor_frames": 1, "max_dynamic_frames": 1, "max_revisit_frames": 1, "fov_overlap_threshold": 0.75}}
557
  )
 
619
  self.assertTrue(all(call["kwargs"]["reference_length"] == 0 for call in calls))
620
  self.assertEqual(
621
  ([call["kwargs"]["current_frame"] for call in calls[::2]], [call["kwargs"]["frame_memory_segments"]["target"] for call in calls[::2]]),
622
+ ([2, 4], [2, 2]),
623
  )
624
  self.assertEqual([call["kwargs"]["current_frame"] for call in calls], [2, 2, 4, 4])
625
  self.assertTrue(torch.equal(calls[0]["kwargs"]["frame_idx"], calls[1]["kwargs"]["frame_idx"]))
626
  self.assertTrue(torch.equal(calls[2]["kwargs"]["frame_idx"], calls[3]["kwargs"]["frame_idx"]))
627
  self.assertEqual(calls[0]["x"][:, 0, 0, 0, 0].tolist(), [0.0, 0.0, 0.0, 1.0, 0.0])
628
+ self.assertEqual(calls[2]["x"][:, 0, 0, 0, 0].tolist(), [2.0, 0.0, 0.0, 2.0, 2.0])
629
  self.assertEqual(calls[0]["action_cond"][:, 0, 0].tolist(), [102.0, 103.0, 0.0, 0.0, 0.0])
630
  self.assertEqual(calls[0]["kwargs"]["frame_idx"][:, 0].tolist(), [12, 13, 10, 11, 0])
631
+ self.assertEqual(calls[2]["kwargs"]["frame_idx"][:, 0].tolist(), [13, 14, 10, 12, 12])
632
  self.assertEqual(calls[0]["kwargs"]["frame_memory_masks"]["revisit"].tolist(), [[False]])
633
  self.assertEqual(calls[2]["kwargs"]["frame_memory_masks"]["revisit"].tolist(), [[True]])
634
  self.assertAlmostEqual(float(loss), 1.0 / 3.0)
635
  self.assertEqual([name for name, _ in harness.logged], ["test/latent_mse"])
636
  self.assertEqual(len(harness.decoded_inputs), 2)
637
+ self.assertEqual(harness.decoded_inputs[0][:, 0, 0, 0, 0].tolist(), [2.0, 4.0, 4.0])
638
  self.assertEqual(harness.decoded_inputs[1][:, 0, 0, 0, 0].tolist(), [2.0, 3.0, 4.0])
639
  self.assertEqual(len(harness.metric_updates), 1)
640
  metric_pred, metric_gt = harness.metric_updates[0]
641
+ self.assertEqual(metric_pred[:, 0, 0, 0, 0].tolist(), [12.0, 14.0, 14.0])
642
  self.assertEqual(metric_gt[:, 0, 0, 0, 0].tolist(), [12.0, 13.0, 14.0])
643
  self.assertEqual(len(video_calls), 1)
644
  self.assertEqual(video_calls[0][1]["namespace"], "test_vis")