Skip to content

[TRTLLM-14865][feat] Support occurrence penalties with beam search for TorchSampler - #17189

Merged
lori-ren merged 7 commits into
NVIDIA:mainfrom
lori-ren:feat/beam-search-penalties
Aug 13, 2026
Merged

[TRTLLM-14865][feat] Support occurrence penalties with beam search for TorchSampler#17189
lori-ren merged 7 commits into
NVIDIA:mainfrom
lori-ren:feat/beam-search-penalties

Conversation

@lori-ren

@lori-ren lori-ren commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Dev Engineer Review

  • Added beam-aware penalty state, beam re-parenting, prompt sharing, and packed-logit handling.
  • Updated PenaltyStore, PenaltyHandler, and fused operation APIs for beam metadata.
  • Preserved the existing single-beam execution path.
  • Allowed occurrence penalties with beam search.
  • Review should verify workspace sizing, beam index bounds, request staging, and update performance.
  • No configuration or test-list files changed.

QA Engineer Review

  • Added test_beam_occurrence_counts_follow_each_beam_history.
  • Added test_single_beam_slot_sharing_a_beam_engine_is_routed_correctly.
  • Added parameterized test_beam_search_penalties_e2e.
  • Removed tests that expected beam-search penalty requests to be rejected.
  • No test-db/ or qa/ coverage entries were provided for the new tests. Verdict: needs follow-up.

Description

TorchSampler rejects repetition / presence / frequency penalties combined with beam search at admission (penalties.py:222) because PenaltyStore holds per-request token counts ([max_num_sequences]), while beam search needs them per beam. This PR extends PenaltyStore and adds repetition / presence / frequency penalties for beam search requests.

Test Coverage

  • extended test_penalties.py to test beam search with penalties.

PR Checklist

Please review the following before submitting your PR:

  • PR description clearly explains what and why. If using CodeRabbit's summary, please make sure it makes sense.

  • PR Follows TRT-LLM CODING GUIDELINES to the best of your knowledge.

  • Test cases are provided for new code paths (see test instructions)

  • If PR introduces API changes, an appropriate PR label is added - either api-compatible or api-breaking. For api-breaking, include BREAKING in the PR title.

  • Any new dependencies have been scanned for license and vulnerabilities

  • CODEOWNERS updated if ownership changes

  • Documentation updated as needed

  • Update tava architecture diagram if there is a significant design change in PR.

  • The reviewers assigned automatically/manually are appropriate for the PR.

  • Please check this after reviewing the above items as appropriate for this PR.

GitHub Bot Help

To see a list of available CI bot commands, please comment /bot help.

@zhaoyangwang-nvidia zhaoyangwang-nvidia left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Reviewed the support occurrence penalties with beam search commit only (b8db3c7). I found no correctness issues.

Worth calling out what's done well: the docs were updated in the same commit; asserts vs. raises are used correctly (internal invariants asserted, the admission-time check dropped outright rather than left half-supported); and the tests compare the whole counts tensor at rtol=0, atol=0 against a host replay, so untouched slots are proven untouched — plus the e2e test's assert expected != _replay_beam_search(1.0) guards against the penalty silently not applying at all.

Five non-blocking comments inline.

Comment thread tensorrt_llm/_torch/pyexecutor/sampler/penalties.py Outdated
Comment thread tensorrt_llm/_torch/pyexecutor/sampler/ops/vanilla.py Outdated
Comment thread tensorrt_llm/_torch/pyexecutor/sampler/ops/vanilla.py Outdated
Comment thread tensorrt_llm/_torch/pyexecutor/sampler/penalties.py Outdated
Comment thread tensorrt_llm/_torch/pyexecutor/sampler/ops/vanilla.py
Comment thread tensorrt_llm/_torch/pyexecutor/sampler/ops/vanilla.py Outdated
…nalties_e2e.py

The case drives a real LLM through TorchSampler, so it belongs with the other
end-to-end penalty tests rather than in the op/handler unit-test module. It
keeps its dummy checkpoint -- and therefore no high_cuda_memory marker, unlike
its new neighbours.

Leaving test_penalties.py's dynamo recompile-headroom fixture behind is a win
rather than a loss: sharing a process with that module's shape-sweeping op
tests was what made the case slow, and it drops from 202s to 87s.

Signed-off-by: Lori Ren <lorir@nvidia.com>
@lori-ren
lori-ren force-pushed the feat/beam-search-penalties branch from 9d8d9a8 to 9139366 Compare August 13, 2026 03:59
@lori-ren
lori-ren marked this pull request as ready for review August 13, 2026 04:00
@lori-ren
lori-ren requested review from a team as code owners August 13, 2026 04:00
@lori-ren

Copy link
Copy Markdown
Contributor Author

@zhaoyangwang-nvidia I rebased this PR to main, pls check and review :)

@coderabbitai

coderabbitai Bot commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

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: 9669c093-11e0-493d-888c-fb0c4a6a5838

📥 Commits

Reviewing files that changed from the base of the PR and between 0c19520 and 41cf62e.

📒 Files selected for processing (1)
  • tensorrt_llm/_torch/pyexecutor/sampler/penalties.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • tensorrt_llm/_torch/pyexecutor/sampler/penalties.py

Walkthrough

Occurrence penalties now support beam search. Per-beam token counts are re-parented and folded before fused penalty application. Sampler wiring, tests, and sampling documentation were updated.

Changes

Beam occurrence penalties

Layer / File(s) Summary
Beam-aware penalty state
tensorrt_llm/_torch/pyexecutor/sampler/penalties.py
PenaltyStore allocates per-beam count rows and stages beam metadata. Request admission, slot reuse, prompt initialization, and pending state handle beam slots.
Beam count re-parenting and penalty application
tensorrt_llm/_torch/pyexecutor/sampler/ops/vanilla.py, tensorrt_llm/_torch/pyexecutor/sampler/penalties.py, tensorrt_llm/_torch/pyexecutor/sampler/sampler.py, tensorrt_llm/_torch/pyexecutor/sampler/sampler_strategy.py
The sampler passes beam widths and predecessor mappings to penalty processing. Fused operations re-parent histories, fold tokens, and apply penalties to beam-major packed logits.
Beam penalty validation and documentation
tests/unittest/_torch/sampler/test_penalties.py, tests/unittest/_torch/sampler/test_penalties_e2e.py, docs/source/features/sampling.md
Tests cover beam history scenarios, mixed beam and single-beam slots, scheduling modes, and generated results. Documentation describes per-beam occurrence histories and prompt seeding.

Estimated code review effort: 4 (Complex) | ~45 minutes

Mergeability Score: 🔵 Low · up to 41cf6

This PR adds occurrence penalties to beam-search sampling, but the current tests do not cover the case where an unpenalized beam slot must be excluded from count re-parenting. The change is otherwise mergeable with explicit owner awareness of this bounded correctness risk.

Sequence Diagram(s)

sequenceDiagram
  participant TorchSampler
  participant PenaltyHandler
  participant Fusions
  TorchSampler->>PenaltyHandler: apply beam widths and predecessor map
  PenaltyHandler->>Fusions: update beam occurrence counts
  Fusions->>Fusions: re-parent and fold beam token counts
  PenaltyHandler->>Fusions: apply packed occurrence penalties
  Fusions-->>TorchSampler: modified logits
Loading

Possibly related PRs

Suggested reviewers: chang-l, laikhtewari, asfiyab-nvidia

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 72.41% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes adding occurrence penalties for beam search in TorchSampler.
Description check ✅ Passed The description explains the problem, solution, and relevant test coverage using the required sections.
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.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
tensorrt_llm/_torch/pyexecutor/sampler/penalties.py (1)

138-148: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value

Consider caching the beam-index arange.

counts_rows allocates a new torch.arange on every call. The rest of PenaltyStore persists its buffers precisely to avoid per-call allocations. The call site is the admission path, not the per-step hot path, so the cost is small. If you want the same discipline here, cache the arange once in create and reuse it.

♻️ Proposed refactor
     max_num_sequences: int
     max_beam_width: int
     device: torch.device
+    _beam_ids_cuda: torch.Tensor | None = None
         if self.max_beam_width == 1:
             return slots_cuda
-        beams = torch.arange(self.max_beam_width, device=slots_cuda.device)
-        return (slots_cuda.unsqueeze(1) * self.max_beam_width + beams).reshape(-1)
+        if self._beam_ids_cuda is None:
+            with torch.inference_mode(False):
+                self._beam_ids_cuda = torch.arange(self.max_beam_width, device=self.device)
+        return (slots_cuda.unsqueeze(1) * self.max_beam_width + self._beam_ids_cuda).reshape(-1)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tensorrt_llm/_torch/pyexecutor/sampler/penalties.py` around lines 138 - 148,
Cache the beam-index arange during PenaltyStore.create and store it on the
instance, then update counts_rows to reuse that buffer instead of allocating
torch.arange on each call. Preserve the existing single-beam fast path and
ensure the cached tensor uses the appropriate device.
tests/unittest/_torch/sampler/test_penalties.py (1)

652-697: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Add negative coverage for the active_cuda gate.

active is True for every slot listed in seq_slots, and the mixed-slot test sets active all-True as well. So the active_cuda[seq_slots] term in both armed and fold_ok never masks anything in this suite. That gate is what stops an unpenalized request from having its counts re-parented and folded, which matters because a beam engine calls the op for the whole batch.

Include one slot in seq_slots with active[slot] = False and assert its count rows stay unchanged.

💚 Sketch of the added case
def test_beam_occurrence_counts_skip_inactive_slots() -> None:
    """An inactive slot in the batch must not be re-parented or folded."""
    counts = torch.zeros((BEAM_SLOTS * BEAM_WIDTH, BEAM_VOCAB), dtype=torch.int32, device="cuda")
    active = torch.tensor([True, False], dtype=torch.bool, device="cuda").repeat(BEAM_SLOTS)[
        :BEAM_SLOTS
    ]
    inactive = int((~active).nonzero()[0].item())
    counts[inactive * BEAM_WIDTH + 2, 5] = 7  # only reachable via a re-parent
    before = counts.clone()

    seq_slots = torch.arange(BEAM_SLOTS, dtype=torch.int64, device="cuda")
    predecessor_beams = torch.full(
        (BEAM_SLOTS, BEAM_WIDTH), 2, dtype=torch.int32, device="cuda"
    )
    new_tokens = torch.full((1, BEAM_SLOTS, BEAM_WIDTH), 3, dtype=torch.int32, device="cuda")

    Fusions.update_beam_occurrence_counts(
        counts,
        active,
        torch.ones(BEAM_SLOTS, dtype=torch.bool, device="cuda"),
        torch.ones(BEAM_SLOTS, dtype=torch.bool, device="cuda"),
        new_tokens,
        predecessor_beams,
        seq_slots,
        torch.full((BEAM_SLOTS,), BEAM_WIDTH, dtype=torch.int32, device="cuda"),
        BEAM_WIDTH,
    )

    base = inactive * BEAM_WIDTH
    torch.testing.assert_close(
        counts[base : base + BEAM_WIDTH],
        before[base : base + BEAM_WIDTH],
        rtol=0,
        atol=0,
    )
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/unittest/_torch/sampler/test_penalties.py` around lines 652 - 697, Add
a dedicated test for update_beam_occurrence_counts that includes an inactive
slot in seq_slots, seeds a count reachable only through re-parenting, and
invokes the operation with representative predecessor and token data. Capture
the inactive slot’s beam rows before the call and assert they remain unchanged
afterward, while preserving the existing active-slot coverage.

Apply the same fix in `@tests/unittest/_torch/sampler/test_penalties.py` around
lines 490 - 790.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@tensorrt_llm/_torch/pyexecutor/sampler/penalties.py`:
- Around line 138-148: Cache the beam-index arange during PenaltyStore.create
and store it on the instance, then update counts_rows to reuse that buffer
instead of allocating torch.arange on each call. Preserve the existing
single-beam fast path and ensure the cached tensor uses the appropriate device.

In `@tests/unittest/_torch/sampler/test_penalties.py`:
- Around line 652-697: Add a dedicated test for update_beam_occurrence_counts
that includes an inactive slot in seq_slots, seeds a count reachable only
through re-parenting, and invokes the operation with representative predecessor
and token data. Capture the inactive slot’s beam rows before the call and assert
they remain unchanged afterward, while preserving the existing active-slot
coverage.

Apply the same fix in `@tests/unittest/_torch/sampler/test_penalties.py` around
lines 490 - 790.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: ae4014e1-7c1e-4541-969f-2524fb487c8d

📥 Commits

Reviewing files that changed from the base of the PR and between 5fc252b and 9139366.

📒 Files selected for processing (6)
  • docs/source/features/sampling.md
  • tensorrt_llm/_torch/pyexecutor/sampler/ops/vanilla.py
  • tensorrt_llm/_torch/pyexecutor/sampler/penalties.py
  • tensorrt_llm/_torch/pyexecutor/sampler/sampler.py
  • tests/unittest/_torch/sampler/test_penalties.py
  • tests/unittest/_torch/sampler/test_penalties_e2e.py
💤 Files with no reviewable changes (1)
  • docs/source/features/sampling.md

@lori-ren lori-ren changed the title [TRTLLM-14865] Support occurrence penalties with beam search for TorchSampler [TRTLLM-14865][feat] Support occurrence penalties with beam search for TorchSampler Aug 13, 2026

@zhaoyangwang-nvidia zhaoyangwang-nvidia left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Approve with nits.

Comment thread docs/source/features/sampling.md
Comment thread tensorrt_llm/_torch/pyexecutor/sampler/penalties.py Outdated
…m history

Signed-off-by: Lori Ren <lorir@nvidia.com>
@lori-ren

Copy link
Copy Markdown
Contributor Author

/bot run

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #65808 [ run ] triggered by Bot. Commit: 8c1bcdf Link to invocation

…er_strategy.py

Signed-off-by: Lori Ren <lorir@nvidia.com>
@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #65808 [ run ] completed with state FAILURE. Commit: 8c1bcdf
/LLM/main/L0_MergeRequest_PR pipeline #53513 completed with status: 'FAILURE'

CI Report

⚠️ Action Required:

  • Please check the failed tests and fix your PR
  • If you cannot view the failures, ask the CI triggerer to share details
  • Once fixed, request an NVIDIA team member to trigger CI again

CI Agent Failure Analysis

Link to invocation

@lori-ren

Copy link
Copy Markdown
Contributor Author

/bot run --disable-fail-fast

@nv-guomingz nv-guomingz left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

LGTM for doc part

@lori-ren
lori-ren enabled auto-merge (squash) August 13, 2026 06:43
@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #65816 [ run ] triggered by Bot. Commit: 0c19520 Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #65816 [ run ] completed with state FAILURE. Commit: 0c19520
/LLM/main/L0_MergeRequest_PR pipeline #53518 completed with status: 'FAILURE'

CI Report

⚠️ Action Required:

  • Please check the failed tests and fix your PR
  • If you cannot view the failures, ask the CI triggerer to share details
  • Once fixed, request an NVIDIA team member to trigger CI again

CI Agent Failure Analysis

Link to invocation

@lori-ren

Copy link
Copy Markdown
Contributor Author

/bot run --disable-fail-fast

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #65859 [ run ] triggered by Bot. Commit: 0c19520 Link to invocation

…py check

Signed-off-by: Lori Ren <lorir@nvidia.com>
@lori-ren

Copy link
Copy Markdown
Contributor Author

/bot run

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #65867 [ run ] triggered by Bot. Commit: 41cf62e Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #65859 [ run ] completed with state ABORTED. Commit: 0c19520

Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #65867 [ run ] completed with state FAILURE. Commit: 41cf62e
/LLM/main/L0_MergeRequest_PR pipeline #53563 completed with status: 'FAILURE'

CI Report

⚠️ Action Required:

  • Please check the failed tests and fix your PR
  • If you cannot view the failures, ask the CI triggerer to share details
  • Once fixed, request an NVIDIA team member to trigger CI again

CI Agent Failure Analysis

Link to invocation

@lori-ren

Copy link
Copy Markdown
Contributor Author

/bot run --disable-fail-fast

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #65903 [ run ] triggered by Bot. Commit: 41cf62e Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #65903 [ run ] completed with state SUCCESS. Commit: 41cf62e
/LLM/main/L0_MergeRequest_PR pipeline #53594 completed with status: 'SUCCESS'

CI Report

Link to invocation

@lori-ren
lori-ren merged commit 2e109b0 into NVIDIA:main Aug 13, 2026
7 checks passed
@lori-ren
lori-ren deleted the feat/beam-search-penalties branch August 17, 2026 08:57
yihwang-nv pushed a commit to yihwang-nv/TensorRT-LLM that referenced this pull request Aug 18, 2026
…r TorchSampler (NVIDIA#17189)

Signed-off-by: Lori Ren <lorir@nvidia.com>
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.

5 participants