Skip to content

Pin DASC policy validation to CPU - #2399

Merged
kaix-nv merged 2 commits into
feature/dasc-state-sparsity-review-device-independentfrom
feature/dasc-state-sparsity-review-policy-device
Sep 11, 2026
Merged

kaix-nv merged 2 commits into
feature/dasc-state-sparsity-review-device-independentfrom
feature/dasc-state-sparsity-review-policy-device

Conversation

@kaix-nv

@kaix-nv kaix-nv commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

Summary

Closes the remaining full-package device-independence finding on #2398:

  • explicitly allocate stored policy horizons on CPU before comparing with CPU bounds
  • exercise calibration and modelopt_state() under a non-CPU ambient default-device context
  • retain the existing low-level horizon default-device regression

A full factory-call scan confirms this was the only remaining ambient-device tensor allocation in state_sparsity.

Validation

  • focused DASC suite: 52 passed, 1 optional Megatron skip
  • pre-commit hooks on both changed files
  • signed commit with DCO sign-off

Summary by CodeRabbit

  • Bug Fixes

    • Improved DASC calibration and checkpoint metadata generation when PyTorch’s default device is set to meta.
    • Ensured decay-parameter validation remains CPU-backed for reliable processing.
  • Tests

    • Added regression coverage for CPU-backed DASC calibration and checkpoint metadata.

Signed-off-by: kaix-nv <kaix@nvidia.com>
@kaix-nv
kaix-nv requested review from a team as code owners September 11, 2026 04:38
@kaix-nv
kaix-nv requested review from rohansjoshi and removed request for a team September 11, 2026 04:38
@kaix-nv

kaix-nv commented Sep 11, 2026

Copy link
Copy Markdown
Contributor Author

/claude review

@kaix-nv

kaix-nv commented Sep 11, 2026

Copy link
Copy Markdown
Contributor Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

Review Change StackReview Change Stack

Warning

Review limit reached

Next included review available in 1 minute.

Check out review usage here.

View limit details

Limit details: You’ve used all 12 included reviews currently available.

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 72a9fd32-b0e6-40c2-8357-882c22f695f6

📥 Commits

Reviewing files that changed from the base of the PR and between 48daa95 and 431bb04.

📒 Files selected for processing (1)
  • tests/unit/torch/sparsity/state_sparsity/test_dasc.py

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: daea5af4-b56c-4f91-a1b0-b11cb096fdd6

📥 Commits

Reviewing files that changed from the base of the PR and between 81cc09e and 48daa95.

📒 Files selected for processing (2)
  • modelopt/torch/sparsity/state_sparsity/policy.py
  • tests/unit/torch/sparsity/state_sparsity/test_dasc.py

Included review availability: Your plan provides up to 12 included reviews per hour; 0 remain after this review.


📝 Walkthrough

Walkthrough

DASC decay-parameter validation now creates the stored horizon tensor on the CPU. A regression test covers calibration and ModelOpt state extraction when PyTorch uses meta as the default device.

Changes

DASC CPU placement

Layer / File(s) Summary
CPU horizon creation and regression coverage
modelopt/torch/sparsity/state_sparsity/policy.py, tests/unit/torch/sparsity/state_sparsity/test_dasc.py
The stored horizon tensor is created on the CPU. The regression test runs DASC calibration and state extraction under the meta default device and verifies the dasc mode metadata.

Priority: ⬇️ Low

Estimated code review effort: 2 (Simple) | ~10 minutes

Change: Bug fix

Merge Risk: ⚪ Minimal · up to 48daa

The DASC change preserves calibration and state extraction under a non-CPU default-device context, with focused validation passing.

🚥 Pre-merge checks | ✅ 6
✅ Passed checks (6 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: pinning DASC policy validation to CPU.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 4 functions across 2 files.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Security Anti-Patterns ✅ Passed PASS. The authoritative diff changes only policy.py and test_dasc.py. Added code creates a CPU torch.tensor and adds a device-context regression test. No added lines use `torch.load(..., weights…
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/dasc-state-sparsity-review-policy-device

Comment @coderabbitai help to get the list of available commands.

@coderabbitai

coderabbitai Bot commented Sep 11, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Full review finished.

Comment on lines +514 to +517
with torch.device("meta"):
calibrated = mtss.calibrate(model, _config(wmax_candidates=[7]), [_candidate(7)])
state = mto.modelopt_state(calibrated)
assert state["modelopt_state_dict"][0][0] == "dasc"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[SUGGESTION] This regression guard only works by accident of the error type that escapes.

The line being fixed lives in validate_dasc_decay_parameters, and the only path from mto.modelopt_state() to it is update_dasc_metadata (conversion.py:118-127), which deliberately downgrades validation failures to warnings:

    else:
        try:
            validate_dasc_decay_parameters(model, policy)
        except ApplyModeError as error:
            warnings.warn(f"{error}. The restored DASC policy is stale; ...")

Today the un-pinned torch.tensor(...) fails with a device-mismatch RuntimeError, which slips past that except ApplyModeError and fails the test — so the test does currently catch the regression. But nothing in the assertion depends on validation having succeeded: state["modelopt_state_dict"][0][0] == "dasc" is true whether the horizon check passed or was swallowed into a staleness warning. If a future change wraps device/dtype errors in ApplyModeError (which the file already does for ValueError at policy.py:500-503), this test goes green while the device bug is back.

Two ways to make the guard load-bearing:

  1. Assert the validator was actually satisfied, by rejecting the staleness warning:
def test_policy_lifecycle_ignores_the_default_device():
    """Keep calibration and checkpoint metadata validation on their declared CPU path."""
    model = TinyGatedDeltaNetForCausalLM()
    with torch.device("meta"), warnings.catch_warnings():
        warnings.simplefilter("error")
        calibrated = mtss.calibrate(model, _config(wmax_candidates=[7]), [_candidate(7)])
        state = mto.modelopt_state(calibrated)
    assert state["modelopt_state_dict"][0][0] == "dasc"
  1. Or drive the changed line through export_policy (api.py:86-87), the one caller that propagates ApplyModeError instead of warning — so any failure of the horizon check surfaces regardless of exception type:
    with torch.device("meta"):
        calibrated = mtss.calibrate(model, _config(wmax_candidates=[7]), [_candidate(7)])
        state = mto.modelopt_state(calibrated)
        policy = mtss.export_policy(calibrated)
    assert state["modelopt_state_dict"][0][0] == "dasc"
    assert policy["layers"]["linear_attn"]["static_horizons"]

Either keeps the higher-level lifecycle coverage you wanted while making the assertion fail for the reason the test names. Non-blocking.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in #2400. The default-device lifecycle regression now calls export_policy() and asserts validated static horizons, so an ApplyModeError cannot be silently downgraded by the checkpoint metadata path.

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude review passed — no blocking issues found. LGTM

Scope

Full review; the PR is 2 files, +10/-1. Both files reviewed in full context (modelopt/torch/sparsity/state_sparsity/policy.py, tests/unit/torch/sparsity/state_sparsity/test_dasc.py), plus the surrounding call graph needed to judge the fix: api.py, conversion.py (restore / update_dasc_metadata paths), and the rest of policy.py.

Findings

  • CRITICAL: 0
  • IMPORTANT: 0
  • SUGGESTION: 1 — the new lifecycle test's guard is only load-bearing by accident of exception type (inline)

Assessment

The fix is correct and correctly scoped. layer.static_horizons is a plain Python list, so torch.tensor(...) was the one allocation in the module that honored PyTorch's ambient default-device context — under a non-CPU torch.device() context the resulting tensor would not be comparable with the CPU lower/upper bounds from _storage_cast_horizon_bounds, making validate_dasc_decay_parameters fail for a reason unrelated to policy validity. Pinning device="cpu" matches the function's documented CPU-float64 contract and the explicit .to(device="cpu", ...) idiom used everywhere else in the file.

I independently confirmed the PR's completeness claim rather than taking it on trust. Scanning state_sparsity/ for tensor factory calls (torch.tensor/zeros/ones/empty/full/arange/eye/rand/randn/linspace/as_tensor/from_numpy) and for other device-sensitive construction (torch.Tensor(, new_*, scalar_tensor, nn.Parameter() returns line 523 as the only factory call in the package. Every other tensor in the module derives from a module parameter and is explicitly moved with .to(device="cpu", ...) (policy.py:121-122, 214-215, 281-285, 306, 331-332), so it inherits nothing from the ambient device. The device-independence work on this file is complete.

Also checked and found clean: no modelopt_state schema change (metadata is still {"policy": ...}, so existing DASC checkpoints round-trip unaffected), no public API or config surface change, no mode-registration change, and no new CPU-GPU sync — the pinned tensor and its comparison operands are all CPU-resident by construction. No CHANGELOG.rst entry is warranted: this is a fix within the same unreleased cycle as the feature it touches.

Note on verification: I was not able to execute the test suite in this environment (Python execution was declined by the sandbox), so the 52-passed result in the PR description is unverified by me; my analysis of the fix and of the test's coverage is from reading the code paths. Tracing them, the new test does reach the changed line — mto.modelopt_state() -> update_dasc_metadata -> validate_dasc_decay_parameters — and a device-mismatch RuntimeError escapes the except ApplyModeError there, so the test does fail without the fix. The inline SUGGESTION is about making that dependence explicit rather than incidental.

Risk: low.

🤖 Generated with Claude Code

@github-actions

github-actions Bot commented Sep 11, 2026

Copy link
Copy Markdown
Contributor
PR Preview Action v1.8.1
Preview removed because the pull request was closed.
2026-09-11 04:50 UTC

@codecov

codecov Bot commented Sep 11, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 78.78%. Comparing base (81cc09e) to head (431bb04).
⚠️ Report is 1 commits behind head on feature/dasc-state-sparsity-review-device-independent.

Additional details and impacted files
@@                                    Coverage Diff                                     @@
##           feature/dasc-state-sparsity-review-device-independent    #2399       +/-   ##
==========================================================================================
+ Coverage                                                  68.75%   78.78%   +10.02%     
==========================================================================================
  Files                                                        548      548               
  Lines                                                      64244    64243        -1     
==========================================================================================
+ Hits                                                       44173    50615     +6442     
+ Misses                                                     20071    13628     -6443     
Flag Coverage Δ
examples-gpt-oss 13.39% <0.00%> (+<0.01%) ⬆️
examples-hf_ptq 21.78% <0.00%> (+0.01%) ⬆️
examples-llm_distill 13.46% <0.00%> (+<0.01%) ⬆️
examples-llm_eval 17.25% <0.00%> (+0.02%) ⬆️
examples-llm_qat 17.59% <0.00%> (+0.04%) ⬆️
examples-llm_sparsity 15.94% <0.00%> (+<0.01%) ⬆️
examples-megatron_bridge 26.25% <0.00%> (+<0.01%) ⬆️
examples-specdec_bench 13.14% <0.00%> (ø)
examples-speculative_decoding 17.67% <0.00%> (+<0.01%) ⬆️
examples-torch_onnx 21.82% <0.00%> (+0.01%) ⬆️
examples-torch_trt 15.15% <0.00%> (+<0.01%) ⬆️
gpu 58.35% <0.00%> (+37.44%) ⬆️
regression 15.15% <0.00%> (+<0.01%) ⬆️
unit 57.47% <100.00%> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

## Summary

Addresses the sole non-blocking suggestion on approved PR #2399:

- call `export_policy()` under the non-CPU ambient default-device
context
- assert validated static horizons are present
- make the regression fail even if a future checkpoint-save path
downgrades validation failures to warnings

## Validation

- targeted lifecycle regression passed
- pre-commit hooks passed
- signed commit with DCO sign-off

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Tests**
* Extended device lifecycle coverage to verify policy export while
operating on the `meta` device.
* Added validation that static horizon metadata is included in exported
policies.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Signed-off-by: kaix-nv <kaix@nvidia.com>
@kaix-nv
kaix-nv merged commit 1dcafa5 into feature/dasc-state-sparsity-review-device-independent Sep 11, 2026
11 of 12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant