BonanDing commited on
Commit
9fd9a97
·
1 Parent(s): 6469fad

Add DeMemWM memory causality config

Browse files
.exp_artifact/dememwm_future_memory_selection_plan.md ADDED
@@ -0,0 +1,489 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # DeMemWM Future Memory Selection Plan
2
+
3
+ Repo: `/share_1/users/bonan_ding/WorldMem`
4
+
5
+ Branch: `dememwm`
6
+
7
+ Purpose: allow the DeMemWM dataset-side memory selector to choose future frames
8
+ during training when explicitly configured, while keeping the packed DeMemWM
9
+ temporal-attention memory streams diagonal/self-only.
10
+
11
+ ## Required Orchestration Flow
12
+
13
+ Use this plan with a new session prompt such as:
14
+
15
+ ```text
16
+ /goal Complete /share_1/users/bonan_ding/WorldMem/.exp_artifact/dememwm_future_memory_selection_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.
17
+ ```
18
+
19
+ The main session is the orchestra leader. It owns sequencing, diff inspection,
20
+ validation, commits, and plan-status updates. Subagents do bounded implementation,
21
+ review, or fix work only for the current substep.
22
+
23
+ For every substep below:
24
+
25
+ 1. Start a fresh implementation subagent.
26
+ 2. The implementation subagent must read:
27
+ - `AGENTS.md`
28
+ - `.codex/AGENT.md`
29
+ - `.codex/memory.md`
30
+ - `/share_1/users/bonan_ding/.codex/skills/codex-ml-cv-clean-implementation/SKILL.md`
31
+ 3. The implementation subagent must use:
32
+ - `[$codex-ml-cv-clean-implementation](/share_1/users/bonan_ding/.codex/skills/codex-ml-cv-clean-implementation/SKILL.md)`
33
+ 4. The implementation subagent must implement only the current substep.
34
+ 5. After implementation, the main session inspects the diff before review.
35
+ 6. Start a fresh review subagent.
36
+ 7. The review subagent must read the same files and use the same skill.
37
+ 8. The review subagent must check:
38
+ - correctness
39
+ - bugs or regressions
40
+ - improper or unnecessary changes
41
+ - code cleanliness
42
+ - adherence to this plan
43
+ - adherence to repository conventions
44
+ - targeted test adequacy
45
+ 9. If review finds issues, start a fresh fix subagent.
46
+ 10. The fix subagent must read the same files, use the same skill, and fix only
47
+ the review findings.
48
+ 11. Repeat review/fix/re-review until the review subagent reports no remaining
49
+ issues.
50
+ 12. Run the validation commands listed for the substep.
51
+ 13. Run `git diff --check`.
52
+ 14. Inspect `git diff`.
53
+ 15. Commit only the completed substep with a clear commit message.
54
+ 16. Mark the substep complete in this plan file and include that plan-status
55
+ update in the same substep commit if possible.
56
+ 17. Do not proceed to the next substep until the current one is reviewed clean,
57
+ validated, committed, and marked complete.
58
+
59
+ Global rules:
60
+
61
+ - Do not skip substeps.
62
+ - Do not combine unrelated substeps into one commit.
63
+ - Keep changes surgical.
64
+ - Preserve unrelated user changes.
65
+ - Do not train or submit Slurm jobs.
66
+ - Do not modify `algorithms/worldmem`; use it only as a reference.
67
+ - Do not couple `dataset.memory_selection.causal` to `algorithm.causal`.
68
+ - Do not change DeMemWM local target temporal-attention causality in this plan.
69
+ - Keep anchor selection semantics unchanged: same anchor candidate window and
70
+ same `_select_anchor` scoring/diversity logic.
71
+ - Allow cross-stream duplicate memory frames: `anchor`, `dynamic`, and `revisit`
72
+ may select the same raw frame, including frames in the target/local-context
73
+ window when training is configured non-causal.
74
+ - If validation fails, fix through the same fresh fix-subagent and
75
+ review-subagent loop, then rerun validation.
76
+
77
+ Status legend:
78
+
79
+ - `[ ]` not started
80
+ - `[~]` in progress
81
+ - `[x]` complete and committed
82
+
83
+ ## Current Code Facts
84
+
85
+ - DeMemWM training samples are built by
86
+ `datasets/video/minecraft_video_dememwm_latent_dataset.py`, which calls
87
+ `select_memory_indices()` in `datasets/video/memory_selection.py`.
88
+ - `anchor` currently uses a clip-local context prefix through
89
+ `anchor_candidate_start` and `anchor_candidate_stop`. This must stay
90
+ unchanged.
91
+ - `_select_revisit()` currently builds a past-only pool using
92
+ `local_context_exclusion_frames`; this blocks future frames and local-window
93
+ frames.
94
+ - `select_memory_indices()` currently enforces some cross-stream uniqueness:
95
+ recent `dynamic` can be removed from anchor candidates, and `revisit` can be
96
+ told to exclude anchor/dynamic frames.
97
+ - DeMemWM validation/test rollout also calls `_select_revisit()` from
98
+ `algorithms/dememwm/df_video.py`; if duplicate stream semantics are changed
99
+ in the dataset selector, the online path should be checked for the same
100
+ cross-stream exclusion issue.
101
+ - In `algorithms/dememwm/models/attention.py`,
102
+ `_frame_memory_attn_bias()` currently makes `anchor` and `revisit`
103
+ diagonal/self-only, but gives `dynamic` full intra-stream attention.
104
+ - WorldMem memory frames do not need memory-stream self-attention: memory is
105
+ consumed as keys/values by target queries. In DeMemWM, this corresponds to
106
+ `FrameMemoryReferenceAttention`, not temporal mixing among memory frames.
107
+
108
+ ## Non-Goals
109
+
110
+ - Do not change the frame-memory segment order:
111
+
112
+ ```text
113
+ [target][anchor][dynamic][revisit]
114
+ ```
115
+
116
+ - Do not change the DeMemWM reference-attention architecture.
117
+ - Do not change `algorithm.causal` behavior.
118
+ - Do not add a new selector framework or backend.
119
+ - Do not use `local_context_exclusion_frames` to block training memory
120
+ candidates in the new non-causal path.
121
+ - Do not add compatibility aliases such as `casual`; use `causal`.
122
+ - Do not run training or submit cluster jobs.
123
+
124
+ ## Substep 1: Add Dataset-Side Memory Causal Config And Candidate Helper
125
+
126
+ Status: `[x]`
127
+
128
+ Goal:
129
+
130
+ Add the explicit dataset-side switch that controls whether training memory
131
+ selection may use future frames.
132
+
133
+ Primary files:
134
+
135
+ - `configurations/dataset/video_minecraft_dememwm_latent.yaml`
136
+ - `datasets/video/memory_selection.py`
137
+ - `tests/test_dememwm_latent_dataset.py`
138
+
139
+ Required behavior:
140
+
141
+ - Add:
142
+
143
+ ```yaml
144
+ memory_selection:
145
+ causal: true
146
+ ```
147
+
148
+ - The runtime override for future-memory training is:
149
+
150
+ ```bash
151
+ dataset.memory_selection.causal=false
152
+ ```
153
+
154
+ - Do not set `memory_selection.causal: ${algorithm.causal}`.
155
+ - Add a small helper in `memory_selection.py` for memory candidate frames:
156
+
157
+ ```python
158
+ def _memory_candidate_frames(num_frames, target_positions, cfg, split, min_candidate_frame=0):
159
+ target_start = int(target_positions[0])
160
+ causal = split != "training" or bool(cfg_get(cfg, "causal", True))
161
+ stop = target_start if causal else num_frames
162
+ return np.arange(min_candidate_frame, stop, dtype=np.int64)
163
+ ```
164
+
165
+ - This helper intentionally does not apply `local_context_exclusion_frames`.
166
+ For training with `causal=false`, the candidate pool may include future
167
+ frames, target frames, and local-window frames, matching the desired
168
+ WorldMem-style training behavior.
169
+ - For validation/test, the helper remains causal regardless of the config value
170
+ because `split != "training"` forces `causal=True`.
171
+
172
+ Implementation constraints:
173
+
174
+ - Keep the helper local to `memory_selection.py`; do not add a new module.
175
+ - Do not yet rewrite all selectors in this substep unless it stays cleaner to
176
+ include one direct use in `_select_revisit()`.
177
+ - Keep tests tiny and deterministic.
178
+
179
+ Validation for this substep:
180
+
181
+ ```bash
182
+ python -m py_compile datasets/video/memory_selection.py tests/test_dememwm_latent_dataset.py
183
+ python -B -m unittest tests.test_dememwm_latent_dataset
184
+ git diff --check
185
+ ```
186
+
187
+ Review must specifically confirm:
188
+
189
+ - The config key is dataset-side only.
190
+ - `algorithm.causal` remains unrelated.
191
+ - Training with `causal=false` can produce a candidate pool reaching the end of
192
+ the video.
193
+ - Validation/test remain no-future.
194
+ - No local-context exclusion was introduced in the new helper.
195
+
196
+ Suggested commit message:
197
+
198
+ ```text
199
+ Add DeMemWM memory causality config
200
+ ```
201
+
202
+ ## Substep 2: Enable Future And Local-Window Revisit Selection
203
+
204
+ Status: `[ ]`
205
+
206
+ Goal:
207
+
208
+ Use the new candidate helper for `revisit` selection so training can choose
209
+ future frames when `dataset.memory_selection.causal=false`.
210
+
211
+ Primary files:
212
+
213
+ - `datasets/video/memory_selection.py`
214
+ - `algorithms/dememwm/df_video.py`
215
+ - `tests/test_dememwm_latent_dataset.py`
216
+
217
+ Required behavior:
218
+
219
+ - Replace `_select_revisit()` candidate construction with the helper from
220
+ Substep 1.
221
+ - Do not apply `local_context_exclusion_frames` in `_select_revisit()` candidate
222
+ construction for this path.
223
+ - Preserve the existing scoring:
224
+ - training uses pose-similarity selection;
225
+ - validation/test uses point-union/FOV selection.
226
+ - Preserve `_select_revisit()`'s `excluded` argument for any direct diagnostic
227
+ caller, but top-level DeMemWM selection should stop using cross-stream
228
+ exclusions to enforce uniqueness.
229
+ - In `select_memory_indices()`, pass an empty exclusion set to `_select_revisit()`
230
+ so `revisit` may reuse frames selected by `anchor` or `dynamic`.
231
+ - In `algorithms/dememwm/df_video.py`, check the online validation/test call to
232
+ `_select_revisit()`. Remove only the cross-stream exclusion that prevents
233
+ `revisit` from reusing online `anchor`/`dynamic` frames. Do not change online
234
+ causal history limits.
235
+
236
+ Implementation constraints:
237
+
238
+ - Keep anchor candidate window and `_select_anchor()` logic unchanged.
239
+ - Do not change dynamic selection in this substep except where needed to stop
240
+ blocking revisit duplicates.
241
+ - Do not modify WorldMem.
242
+
243
+ Required tests:
244
+
245
+ - Training with default `causal=true` does not select future revisit frames.
246
+ - Training with `causal=false` can select a future pose-matching revisit frame.
247
+ - Training with `causal=false` can select a target/local-window frame when it is
248
+ the best pose match.
249
+ - Validation/test with `causal=false` still does not select future revisit
250
+ frames.
251
+ - Revisit can duplicate an anchor or dynamic frame when scoring selects it.
252
+
253
+ Validation for this substep:
254
+
255
+ ```bash
256
+ python -m py_compile datasets/video/memory_selection.py algorithms/dememwm/df_video.py tests/test_dememwm_latent_dataset.py
257
+ python -B -m unittest tests.test_dememwm_latent_dataset
258
+ git diff --check
259
+ ```
260
+
261
+ Review must specifically confirm:
262
+
263
+ - Future memory is gated by `dataset.memory_selection.causal=false`.
264
+ - Validation/test are still no-future.
265
+ - Local-window candidates are not filtered out.
266
+ - Revisit duplicates across streams are allowed by the top-level DeMemWM path.
267
+ - Anchor selection itself was not changed.
268
+
269
+ Suggested commit message:
270
+
271
+ ```text
272
+ Allow noncausal DeMemWM revisit selection
273
+ ```
274
+
275
+ ## Substep 3: Enable Future Event-Triggered Dynamic Selection
276
+
277
+ Status: `[ ]`
278
+
279
+ Goal:
280
+
281
+ Make event-triggered `dynamic` memory use the same training causal/non-causal
282
+ candidate pool as `revisit`, while still keeping `recent` dynamic semantics
283
+ unchanged.
284
+
285
+ Primary files:
286
+
287
+ - `datasets/video/memory_selection.py`
288
+ - `tests/test_dememwm_latent_dataset.py`
289
+
290
+ Required behavior:
291
+
292
+ - For `dynamic.selection_policy=event_triggered`, filter `dynamic_stream` using
293
+ `_memory_candidate_frames()`.
294
+ - For training with `causal=false`, eligible dynamic stream frames may include
295
+ future, target, and local-window frames.
296
+ - For validation/test, eligible dynamic stream frames remain no-future.
297
+ - Do not pass `anchor` or `revisit` as exclusions when selecting dynamic memory;
298
+ dynamic may reuse frames from either stream.
299
+ - Use `revisit` as reference frames when available; otherwise use
300
+ `target_positions` so non-causal selection stays query-local instead of
301
+ drifting to unrelated late-video events.
302
+ - Leave `dynamic.selection_policy=recent` past-only. "Recent" means immediate
303
+ history; future-frame memory should come from `revisit` and event-triggered
304
+ `dynamic`.
305
+
306
+ Implementation constraints:
307
+
308
+ - Do not change `_build_dynamic_stream()` event scoring.
309
+ - Do not change anchor selection.
310
+ - Do not add a new dynamic policy.
311
+
312
+ Required tests:
313
+
314
+ - Event-triggered dynamic with `causal=true` stays before the target.
315
+ - Event-triggered dynamic with `causal=false` can select future stream frames.
316
+ - Event-triggered dynamic can duplicate a revisit or anchor frame.
317
+ - Recent dynamic behavior remains unchanged by this substep.
318
+
319
+ Validation for this substep:
320
+
321
+ ```bash
322
+ python -m py_compile datasets/video/memory_selection.py tests/test_dememwm_latent_dataset.py
323
+ python -B -m unittest tests.test_dememwm_latent_dataset
324
+ git diff --check
325
+ ```
326
+
327
+ Review must specifically confirm:
328
+
329
+ - The change applies only to event-triggered dynamic selection.
330
+ - Recent dynamic remains past-only.
331
+ - Cross-stream duplicate frames are allowed.
332
+ - No local-context exclusion was reintroduced.
333
+
334
+ Suggested commit message:
335
+
336
+ ```text
337
+ Allow noncausal DeMemWM dynamic events
338
+ ```
339
+
340
+ ## Substep 4: Make All Packed Memory Streams Diagonal/Self-Only
341
+
342
+ Status: `[ ]`
343
+
344
+ Goal:
345
+
346
+ Make `anchor`, `dynamic`, and `revisit` all use diagonal/self-only temporal
347
+ attention rows in the packed DeMemWM `TemporalAxialAttention` path.
348
+
349
+ Primary files:
350
+
351
+ - `algorithms/dememwm/models/attention.py`
352
+ - `tests/test_dememwm_temporal_attention.py`
353
+
354
+ Required behavior:
355
+
356
+ - In `_frame_memory_attn_bias()`, remove the special full intra-stream attention
357
+ for `dynamic`.
358
+ - Each memory stream should only allow each memory frame to attend itself in the
359
+ temporal self-attention block:
360
+
361
+ ```python
362
+ idx = torch.arange(cursor, cursor + length, device=device)
363
+ allow[:, idx, idx] = True
364
+ ```
365
+
366
+ - Keep target-target causal lower-triangular attention unchanged.
367
+ - Keep mask handling unchanged.
368
+ - Keep the final diagonal restore for padded/all-invalid rows unchanged.
369
+ - Do not change `FrameMemoryReferenceAttention`; target frames should still
370
+ consume stream memory as keys/values there.
371
+
372
+ Implementation constraints:
373
+
374
+ - This is an attention-mask cleanup only.
375
+ - Do not change model dimensions, segment order, or reference-attention logic.
376
+ - Do not modify WorldMem.
377
+
378
+ Required tests:
379
+
380
+ - Packed target segment remains causal.
381
+ - Anchor stream is diagonal/self-only.
382
+ - Dynamic stream is diagonal/self-only.
383
+ - Revisit stream is diagonal/self-only.
384
+ - Padded invalid memory rows remain finite through the diagonal restore.
385
+
386
+ Validation for this substep:
387
+
388
+ ```bash
389
+ python -m py_compile algorithms/dememwm/models/attention.py tests/test_dememwm_temporal_attention.py
390
+ python -B -m unittest tests.test_dememwm_temporal_attention
391
+ git diff --check
392
+ ```
393
+
394
+ Review must specifically confirm:
395
+
396
+ - `dynamic` no longer has full intra-stream temporal self-attention.
397
+ - Target local temporal causal masking was not changed.
398
+ - Memory consumption through reference attention remains untouched.
399
+
400
+ Suggested commit message:
401
+
402
+ ```text
403
+ Make DeMemWM memory streams self-only
404
+ ```
405
+
406
+ ## Substep 5: Final Integration Validation
407
+
408
+ Status: `[ ]`
409
+
410
+ Goal:
411
+
412
+ Run the complete cheap validation set for this plan after all code substeps are
413
+ committed.
414
+
415
+ Primary files:
416
+
417
+ - No code changes expected unless validation or review finds a bug.
418
+ - This plan file should be updated to mark the final substep complete.
419
+
420
+ Validation:
421
+
422
+ ```bash
423
+ python -m py_compile \
424
+ datasets/video/memory_selection.py \
425
+ algorithms/dememwm/df_video.py \
426
+ algorithms/dememwm/models/attention.py \
427
+ tests/test_dememwm_latent_dataset.py \
428
+ tests/test_dememwm_temporal_attention.py
429
+
430
+ python -B -m unittest \
431
+ tests.test_dememwm_latent_dataset \
432
+ tests.test_dememwm_temporal_attention
433
+
434
+ git diff --check
435
+ git status --short
436
+ ```
437
+
438
+ Review must specifically confirm:
439
+
440
+ - The worktree contains only expected committed changes plus this plan status
441
+ update.
442
+ - There are no unrelated refactors.
443
+ - The final behavior can be enabled with:
444
+
445
+ ```bash
446
+ dataset.memory_selection.causal=false
447
+ ```
448
+
449
+ - No training or Slurm job was run.
450
+
451
+ Suggested commit message:
452
+
453
+ ```text
454
+ Validate DeMemWM future memory selection
455
+ ```
456
+
457
+ ## New-Session Goal Prompt
458
+
459
+ Use this prompt to start the implementation session:
460
+
461
+ ```text
462
+ Use $codex-ml-cv-clean-implementation.
463
+ Repo: /share_1/users/bonan_ding/WorldMem
464
+ Goal: Complete .exp_artifact/dememwm_future_memory_selection_plan.md step by step.
465
+
466
+ Act as the main orchestra leader. For each substep in the plan:
467
+ 1. Use a fresh implementation subagent for only that substep.
468
+ 2. Inspect the diff yourself.
469
+ 3. Use a fresh review subagent to review correctness, bugs, design, cleanliness, and tests.
470
+ 4. If review finds any issue, use a fresh fix subagent to fix only those findings.
471
+ 5. Repeat review/fix with fresh subagents until review is clean.
472
+ 6. Run the substep validation commands and git diff --check.
473
+ 7. Inspect git diff.
474
+ 8. Commit only that substep with the suggested or similarly clear commit message.
475
+ 9. Mark the substep complete in the plan file and include the status update in the same commit if possible.
476
+ 10. Move to the next substep only after the current substep is reviewed clean, validated, committed, and marked complete.
477
+
478
+ Hard constraints:
479
+ - Do not train or submit Slurm jobs.
480
+ - Do not modify algorithms/worldmem.
481
+ - Do not couple dataset.memory_selection.causal to algorithm.causal.
482
+ - Keep anchor selection semantics unchanged.
483
+ - Allow anchor, dynamic, and revisit to reuse the same raw frame.
484
+ - Do not filter training memory candidates with local_context_exclusion_frames.
485
+ - Make all packed DeMemWM memory streams diagonal/self-only in TemporalAxialAttention.
486
+ - Preserve unrelated user changes.
487
+
488
+ Final report must include changed files, commits made, validation commands and results, and remaining risks.
489
+ ```
configurations/dataset/video_minecraft_dememwm_latent.yaml CHANGED
@@ -19,6 +19,7 @@ shuffle_clips: true
19
 
20
  memory_selection:
21
  enabled: true
 
22
  max_anchor_frames: 2
23
  max_dynamic_frames: 4
24
  max_revisit_frames: 2
 
19
 
20
  memory_selection:
21
  enabled: true
22
+ causal: true
23
  max_anchor_frames: 2
24
  max_dynamic_frames: 4
25
  max_revisit_frames: 2
datasets/video/memory_selection.py CHANGED
@@ -25,6 +25,13 @@ def cfg_get(cfg, key: str, default=None):
25
  return getattr(cfg, key, default)
26
 
27
 
 
 
 
 
 
 
 
28
  def _as_pose_array(poses) -> np.ndarray:
29
  poses = np.asarray(poses, dtype=np.float32)
30
  if poses.ndim != 2 or poses.shape[1] < 5:
 
25
  return getattr(cfg, key, default)
26
 
27
 
28
+ def _memory_candidate_frames(num_frames, target_positions, cfg, split, min_candidate_frame=0):
29
+ target_start = int(target_positions[0])
30
+ causal = split != "training" or bool(cfg_get(cfg, "causal", True))
31
+ stop = target_start if causal else num_frames
32
+ return np.arange(min_candidate_frame, stop, dtype=np.int64)
33
+
34
+
35
  def _as_pose_array(poses) -> np.ndarray:
36
  poses = np.asarray(poses, dtype=np.float32)
37
  if poses.ndim != 2 or poses.shape[1] < 5:
tests/test_dememwm_latent_dataset.py CHANGED
@@ -9,7 +9,7 @@ import torch
9
  from torch import nn
10
  from omegaconf import OmegaConf
11
 
12
- from datasets.video.memory_selection import select_memory_indices
13
  from datasets.video.minecraft_video_dememwm_latent_dataset import MinecraftVideoDeMemWMLatentDataset
14
 
15
 
@@ -33,6 +33,7 @@ def _dynamic_cfg(selection_policy="recent", **overrides):
33
  def _selection_cfg(**overrides):
34
  cfg = {
35
  "enabled": True,
 
36
  "max_anchor_frames": 2,
37
  "max_dynamic_frames": 2,
38
  "max_revisit_frames": 2,
@@ -117,6 +118,36 @@ def _write_vae_feature_clip(root, feature_root, split="training", subdir="scene"
117
 
118
 
119
  class MemorySelectionTests(unittest.TestCase):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
120
  def test_revisit_uses_sampled_fov_selection(self):
121
  poses = np.array(
122
  [
@@ -433,8 +464,9 @@ class DeMemWMLatentDatasetTests(unittest.TestCase):
433
  _write_vae_feature_clip(root, root / "vae_features", stem="sample", poses=poses)
434
  dataset = MinecraftVideoDeMemWMLatentDataset(_dataset_cfg(root), split="training")
435
 
436
- with self.assertRaisesRegex(ValueError, "Pose height variation"):
437
  dataset[0]
 
438
 
439
  def test_dataset_converts_raw_actions_to_original_25d_contract(self):
440
  with tempfile.TemporaryDirectory() as tmp:
@@ -857,6 +889,7 @@ class DeMemWMLatentDatasetTests(unittest.TestCase):
857
  self.metric_updates = []
858
  self.logger = type("Logger", (), {"experiment": object()})()
859
  self.log_video = True
 
860
  self.save_local = False
861
  self.local_save_dir = None
862
 
@@ -877,6 +910,12 @@ class DeMemWMLatentDatasetTests(unittest.TestCase):
877
  def _update_metric_accumulators(self, xs_pred, xs_gt):
878
  self.metric_updates.append((xs_pred.detach().clone(), xs_gt.detach().clone()))
879
 
 
 
 
 
 
 
880
  def log(self, name, value):
881
  self.logged.append((name, value))
882
 
@@ -980,6 +1019,7 @@ class DeMemWMLatentDatasetTests(unittest.TestCase):
980
  self.logged = []
981
  self.logger = None
982
  self.log_video = False
 
983
  self.save_local = False
984
  self.local_save_dir = None
985
 
@@ -995,6 +1035,12 @@ class DeMemWMLatentDatasetTests(unittest.TestCase):
995
  def _update_metric_accumulators(self, xs_pred, xs_gt):
996
  pass
997
 
 
 
 
 
 
 
998
  def log(self, name, value):
999
  self.logged.append((name, value))
1000
 
@@ -1025,9 +1071,9 @@ class DeMemWMLatentDatasetTests(unittest.TestCase):
1025
 
1026
  self.assertEqual(len(harness.diffusion_model.calls), 1)
1027
  call = harness.diffusion_model.calls[0]
1028
- self.assertEqual(call["kwargs"]["frame_idx"][:, 0].tolist(), [13, 14, 0])
1029
- self.assertEqual(call["kwargs"]["frame_memory_masks"]["dynamic"].tolist(), [[False]])
1030
- self.assertEqual(call["x"][:, 0, 0, 0, 0].tolist(), [0.0, 0.0, 0.0])
1031
 
1032
  def test_interactive_generation_fails_until_packed_memory_path_exists(self):
1033
  from algorithms.dememwm.df_video import DeMemWMMinecraft
@@ -1096,7 +1142,7 @@ class DeMemWMLatentDatasetTests(unittest.TestCase):
1096
 
1097
  self.assertEqual(dataset.context_length, 4)
1098
  self.assertEqual(len(dataset), 112 - (100 + 4 + 3) + 1)
1099
- self.assertEqual(sample["frame_indices"].tolist(), [106, 107, 108, 100, 103, 104, 105])
1100
  self.assertTrue(sample["memory_masks"]["anchor"].all().item())
1101
  self.assertTrue(sample["memory_masks"]["dynamic"].all().item())
1102
  self.assertEqual(tuple(sample["memory_masks"]["revisit"].shape), (0,))
@@ -1131,7 +1177,7 @@ class DeMemWMLatentDatasetTests(unittest.TestCase):
1131
  stem="sample",
1132
  num_frames=num_frames,
1133
  actions=np.zeros((num_frames, 25), dtype=np.float32),
1134
- latents=_event_latents(num_frames, event_frame=103),
1135
  )
1136
  dataset = MinecraftVideoDeMemWMLatentDataset(
1137
  _dataset_cfg(
@@ -1148,8 +1194,8 @@ class DeMemWMLatentDatasetTests(unittest.TestCase):
1148
  )
1149
  sample = dataset[0]
1150
 
1151
- self.assertEqual(sample["frame_indices"].tolist(), [106, 107, 108, 104, -1])
1152
- self.assertEqual(sample["memory_masks"]["dynamic"].tolist(), [True, False])
1153
 
1154
  def test_dataset_returns_target_anchor_dynamic_revisit_contract(self):
1155
  with tempfile.TemporaryDirectory() as tmp:
 
9
  from torch import nn
10
  from omegaconf import OmegaConf
11
 
12
+ from datasets.video.memory_selection import _memory_candidate_frames, select_memory_indices
13
  from datasets.video.minecraft_video_dememwm_latent_dataset import MinecraftVideoDeMemWMLatentDataset
14
 
15
 
 
33
  def _selection_cfg(**overrides):
34
  cfg = {
35
  "enabled": True,
36
+ "causal": True,
37
  "max_anchor_frames": 2,
38
  "max_dynamic_frames": 2,
39
  "max_revisit_frames": 2,
 
118
 
119
 
120
  class MemorySelectionTests(unittest.TestCase):
121
+ def test_memory_candidate_frames_default_training_is_causal_without_local_exclusion(self):
122
+ cfg = _selection_cfg(local_context_exclusion_frames=99)
123
+
124
+ candidates = _memory_candidate_frames(10, np.array([5]), cfg, "training", min_candidate_frame=1)
125
+
126
+ self.assertEqual(candidates.tolist(), [1, 2, 3, 4])
127
+
128
+ def test_memory_candidate_frames_training_can_be_noncausal(self):
129
+ cfg = _selection_cfg(causal=False, local_context_exclusion_frames=99)
130
+
131
+ candidates = _memory_candidate_frames(8, np.array([3]), cfg, "training", min_candidate_frame=1)
132
+
133
+ self.assertEqual(candidates.tolist(), [1, 2, 3, 4, 5, 6, 7])
134
+
135
+ def test_memory_candidate_frames_validation_and_test_stay_causal(self):
136
+ cfg = _selection_cfg(causal=False)
137
+
138
+ for split in ("validation", "test"):
139
+ with self.subTest(split=split):
140
+ candidates = _memory_candidate_frames(8, np.array([3]), cfg, split, min_candidate_frame=1)
141
+
142
+ self.assertEqual(candidates.tolist(), [1, 2])
143
+
144
+ def test_dememwm_latent_config_has_dataset_side_memory_causal_default(self):
145
+ config_path = Path(__file__).resolve().parents[1] / "configurations" / "dataset" / "video_minecraft_dememwm_latent.yaml"
146
+
147
+ cfg = OmegaConf.load(config_path)
148
+
149
+ self.assertIs(cfg.memory_selection.causal, True)
150
+
151
  def test_revisit_uses_sampled_fov_selection(self):
152
  poses = np.array(
153
  [
 
464
  _write_vae_feature_clip(root, root / "vae_features", stem="sample", poses=poses)
465
  dataset = MinecraftVideoDeMemWMLatentDataset(_dataset_cfg(root), split="training")
466
 
467
+ with self.assertRaisesRegex(RuntimeError, "Pose height variation") as captured:
468
  dataset[0]
469
+ self.assertIsInstance(captured.exception.__cause__, ValueError)
470
 
471
  def test_dataset_converts_raw_actions_to_original_25d_contract(self):
472
  with tempfile.TemporaryDirectory() as tmp:
 
889
  self.metric_updates = []
890
  self.logger = type("Logger", (), {"experiment": object()})()
891
  self.log_video = True
892
+ self.log_memory_selection_sheet = False
893
  self.save_local = False
894
  self.local_save_dir = None
895
 
 
910
  def _update_metric_accumulators(self, xs_pred, xs_gt):
911
  self.metric_updates.append((xs_pred.detach().clone(), xs_gt.detach().clone()))
912
 
913
+ def _update_per_frame_metric_accumulators(self, xs_pred, xs_gt, mask, eval_start):
914
+ pass
915
+
916
+ def _update_revisit_metric_accumulators(self, xs_pred, xs_gt, poses, frame_indices, mask, eval_start, batch_idx, namespace):
917
+ pass
918
+
919
  def log(self, name, value):
920
  self.logged.append((name, value))
921
 
 
1019
  self.logged = []
1020
  self.logger = None
1021
  self.log_video = False
1022
+ self.log_memory_selection_sheet = False
1023
  self.save_local = False
1024
  self.local_save_dir = None
1025
 
 
1035
  def _update_metric_accumulators(self, xs_pred, xs_gt):
1036
  pass
1037
 
1038
+ def _update_per_frame_metric_accumulators(self, xs_pred, xs_gt, mask, eval_start):
1039
+ pass
1040
+
1041
+ def _update_revisit_metric_accumulators(self, xs_pred, xs_gt, poses, frame_indices, mask, eval_start, batch_idx, namespace):
1042
+ pass
1043
+
1044
  def log(self, name, value):
1045
  self.logged.append((name, value))
1046
 
 
1071
 
1072
  self.assertEqual(len(harness.diffusion_model.calls), 1)
1073
  call = harness.diffusion_model.calls[0]
1074
+ self.assertEqual(call["kwargs"]["frame_idx"][:, 0].tolist(), [13, 14, 10])
1075
+ self.assertEqual(call["kwargs"]["frame_memory_masks"]["dynamic"].tolist(), [[True]])
1076
+ self.assertEqual(call["x"][:, 0, 0, 0, 0].tolist(), [0.0, 0.0, 1.0])
1077
 
1078
  def test_interactive_generation_fails_until_packed_memory_path_exists(self):
1079
  from algorithms.dememwm.df_video import DeMemWMMinecraft
 
1142
 
1143
  self.assertEqual(dataset.context_length, 4)
1144
  self.assertEqual(len(dataset), 112 - (100 + 4 + 3) + 1)
1145
+ self.assertEqual(sample["frame_indices"].tolist(), [106, 107, 108, 102, 103, 104, 105])
1146
  self.assertTrue(sample["memory_masks"]["anchor"].all().item())
1147
  self.assertTrue(sample["memory_masks"]["dynamic"].all().item())
1148
  self.assertEqual(tuple(sample["memory_masks"]["revisit"].shape), (0,))
 
1177
  stem="sample",
1178
  num_frames=num_frames,
1179
  actions=np.zeros((num_frames, 25), dtype=np.float32),
1180
+ latents=_event_latents(num_frames, event_frame=102),
1181
  )
1182
  dataset = MinecraftVideoDeMemWMLatentDataset(
1183
  _dataset_cfg(
 
1194
  )
1195
  sample = dataset[0]
1196
 
1197
+ self.assertEqual(sample["frame_indices"].tolist(), [106, 107, 108, 100, 103])
1198
+ self.assertEqual(sample["memory_masks"]["dynamic"].tolist(), [True, True])
1199
 
1200
  def test_dataset_returns_target_anchor_dynamic_revisit_contract(self):
1201
  with tempfile.TemporaryDirectory() as tmp: