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 |
-
|
| 90 |
-
|
| 91 |
-
|
| 92 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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,
|
| 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,
|
| 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,
|
| 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,
|
| 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")
|