Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
142 changes: 142 additions & 0 deletions docs/source/_static/announcements.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
/* Scoped announcement styles for the Sphinx RTD theme. */

#announcements > h1,
#announcements > p,
#announcements > .announcement-toolbar,
#announcements > .announcement-grid,
#announcements > .announcement-empty,
#announcements > .announcement-pager,
#announcements > .toctree-wrapper {
max-width: 860px;
margin-left: auto;
margin-right: auto;
}

.announcement-toolbar {
border: 1px solid #d6d8dc;
border-radius: 4px;
margin: 1.5rem 0;
padding: 1rem;
background: #f8f9fb;
}

.announcement-search-label {
display: block;
font-weight: 700;
margin-bottom: 0.35rem;
}

.announcement-search {
box-sizing: border-box;
width: 100%;
padding: 0.55rem 0.65rem;
border: 1px solid #b8bdc6;
border-radius: 4px;
font-size: 1rem;
}

.announcement-tags {
display: flex;
flex-wrap: wrap;
gap: 0.45rem;
margin-top: 0.75rem;
}

.announcement-tag {
border: 1px solid #9aa1ad;
border-radius: 999px;
padding: 0.28rem 0.65rem;
background: #fff;
color: #2f343d;
cursor: pointer;
font-size: 0.86rem;
}

.announcement-tag.is-active,
.announcement-tag:hover {
border-color: #76b900;
background: #76b900;
color: #111;
}

.announcement-grid {
display: grid;
grid-template-columns: minmax(0, 1fr);
gap: 1rem;
margin: 1rem 0 1.25rem;
}

.announcement-card {
border-bottom: 1px solid #d6d8dc;
padding: 0 0 1rem;
}

.announcement-card:last-child {
border-bottom: 0;
}

.announcement-card h2 {
margin-top: 0.25rem;
font-size: 1.2rem;
line-height: 1.35;
}

.announcement-card p {
margin-bottom: 0.75rem;
}

.announcement-card-meta {
color: #6b7280;
font-size: 0.85rem;
}

.announcement-card-tags {
display: flex;
flex-wrap: wrap;
gap: 0.35rem;
}

.announcement-card-tags span {
border: 1px solid #d6d8dc;
border-radius: 999px;
color: #4b5563;
font-size: 0.78rem;
padding: 0.15rem 0.45rem;
}

.announcement-empty {
border-left: 4px solid #76b900;
padding-left: 0.75rem;
}


.announcement-pager {
align-items: center;
display: flex;
gap: 0.75rem;
justify-content: flex-end;
margin: 0 0 2rem;
}

.announcement-page-button {
border: 1px solid #9aa1ad;
border-radius: 4px;
background: #fff;
color: #2f343d;
cursor: pointer;
padding: 0.35rem 0.7rem;
}

.announcement-page-button:disabled {
cursor: not-allowed;
opacity: 0.45;
}

.announcement-page-status {
color: #4b5563;
font-size: 0.9rem;
}

.toctree-wrapper.compound:empty {
display: none;
}
105 changes: 105 additions & 0 deletions docs/source/_static/announcements.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
document.addEventListener('DOMContentLoaded', () => {
const trimAnnouncementPostSidebar = () => {
if (!window.location.pathname.includes('/announcements/')) {
return;
}

const menu = document.querySelector('.wy-menu-vertical');
if (!menu) {
return;
}

menu.innerHTML = `
<p class="caption" role="heading"><span class="caption-text">Announcements</span></p>
<ul>
<li class="toctree-l1"><a class="reference internal" href="../index.html">Announcements</a></li>
<li class="toctree-l1"><a class="reference internal" href="../reference/1_modelopt_api.html">API Docs</a></li>
</ul>
`;
};

trimAnnouncementPostSidebar();

const search = document.querySelector('#announcement-search');
const cards = Array.from(document.querySelectorAll('.announcement-card')).sort((left, right) => {
return (right.dataset.date || '').localeCompare(left.dataset.date || '');
});
const tags = Array.from(document.querySelectorAll('.announcement-tag'));
const empty = document.querySelector('#announcement-empty');
const pager = document.querySelector('#announcement-pager');
const prev = document.querySelector('#announcement-prev');
const next = document.querySelector('#announcement-next');
const status = document.querySelector('#announcement-page-status');
const pageSize = 5;

cards.forEach((card) => card.parentNode.appendChild(card));
let activeTag = 'all';
let currentPage = 1;

if (!search || cards.length === 0) {
return;
}

const matchingCards = () => {
const query = search.value.trim().toLowerCase();
return cards.filter((card) => {
const haystack = [card.dataset.title, card.dataset.summary, card.dataset.tags].join(' ').toLowerCase();
const tagMatch = activeTag === 'all' || (card.dataset.tags || '').split(' ').includes(activeTag);
const searchMatch = !query || haystack.includes(query);
return tagMatch && searchMatch;
});
};

const update = () => {
const matches = matchingCards();
const pageCount = Math.max(1, Math.ceil(matches.length / pageSize));
currentPage = Math.min(currentPage, pageCount);
const start = (currentPage - 1) * pageSize;
const pageCards = new Set(matches.slice(start, start + pageSize));

cards.forEach((card) => {
card.hidden = !pageCards.has(card);
});

if (empty) {
empty.hidden = matches.length !== 0;
}

if (pager && prev && next && status) {
pager.hidden = matches.length <= pageSize;
prev.disabled = currentPage <= 1;
next.disabled = currentPage >= pageCount;
status.textContent = `Page ${currentPage} of ${pageCount}`;
}
};

tags.forEach((button) => {
button.addEventListener('click', () => {
activeTag = button.dataset.tag || 'all';
currentPage = 1;
tags.forEach((tag) => tag.classList.toggle('is-active', tag === button));
update();
});
});

search.addEventListener('input', () => {
currentPage = 1;
update();
});

if (prev) {
prev.addEventListener('click', () => {
currentPage -= 1;
update();
});
}

if (next) {
next.addEventListener('click', () => {
currentPage += 1;
update();
});
}

update();
});
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
107 changes: 107 additions & 0 deletions docs/source/announcements/dspark-vs-domino.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
:orphan:

DSpark vs Domino: Same DFlash Backbone, Different Correction Heads
##################################################################

:Author: ModelOpt Team
:Date: June 29, 2026
:Tags: speculative-decoding, dflash, dspark, domino, architecture

DSpark (DeepSpec) and Domino both build on block-parallel DFlash draft generation but diverge sharply in their token-level correction heads. DSpark uses a stateless VanillaMarkov head that is fast and parallelizable during training; Domino uses a GRU that is more expressive but sequential at inference.

Highlights
**********

* Both systems share the DFlash block-parallel backbone, so their parallel draft throughput starts from a similar foundation.
* DSpark defaults to VanillaMarkov: stateless ``W1`` and ``W2`` embedding lookups with no hidden state to thread through.
* Domino uses ``nn.GRU`` and carries recurrent state across draft positions.
* Both correction heads are sequential at inference because ``x_{k-1}`` must be sampled before step ``k``.
* DSpark adds a hardware-aware prefix scheduler through ``confidence_head``; Domino does not include this mechanism.

Shared Foundation: DFlash Block-Parallel Backbone
*************************************************

Both systems use DFlash: a draft backbone that runs a single causal attention forward pass over all draft positions in parallel, producing per-position hidden states and base draft logits. This is the expensive step; the correction head adds token-level adjustment on top of those outputs.

.. image:: assets/dspark_fig1.png
:alt: DSpark overall architecture and decoding cycle
:width: 100%

Where They Diverge: The Correction Head
***************************************

DSpark uses a first-order Markov transition. For each draft position ``k``:

.. code-block:: text

e_{k-1} = W1[x_{k-1}]
bias_k = W2 * e_{k-1}
p_k = softmax(U_k + bias_k)
x_k ~ p_k

The correction at position ``k`` depends only on ``x_{k-1}``; no RNN hidden state threads across steps. The dominant work is a table lookup and projection rather than a recurrent rollout.

Domino uses a GRU correction head. A recurrent hidden state accumulates information about the draft prefix and is concatenated at readout:

.. code-block:: text

gru_h_k = GRU(input_k, gru_h_{k-1})
p_k = softmax(U_k + W * [h_k; gru_h_k])
x_k ~ p_k

.. image:: assets/domino_fig.png
:alt: Domino pipeline with a DFlash backbone and GRU causal correction head
:width: 100%

The figure below compares training acceptance length on Qwen3-8B across the DFlash baseline, Domino GRU, and a DSpark implementation.

.. image:: assets/dspark_domino_al_qwen3_8b.png
:alt: Training acceptance length on Qwen3-8B: DFlash baseline vs Domino GRU vs DSpark
:width: 100%

Correction Head Overhead
************************

.. list-table::
:header-rows: 1

* - System
- Per-step compute
- State carried
* - DSpark VanillaMarkov
- ``W1[x_{k-1}]`` plus transition projection
- None
* - Domino GRU
- Full GRU cell over a high-dimensional input
- Recurrent hidden state

Both heads must unroll left-to-right at inference. The practical difference is per-step cost: VanillaMarkov is much lighter, while the GRU can condition on a richer prefix history.

Additional DSpark Machinery
***************************

DSpark includes a hardware-aware prefix scheduler through a confidence head. The scheduler estimates acceptance probability and selects how many draft tokens to submit for verification. It is a serving-time throughput optimization, not a draft-quality feature.

For MoE models, DSpark checkpoints also include manifold-constrained Hyper-Connections. Dense models use the simpler backbone plus Markov-head path.

.. image:: assets/dspark_fig7.png
:alt: DSpark throughput and TPS Pareto frontier
:width: 100%

Takeaways
*********

#. DFlash draft generation is shared; the correction head is the main differentiator.
#. VanillaMarkov is cheaper per step than GRU, although both are sequential at inference.
#. GRU is more expressive, but the extra recurrence may not translate into a large acceptance-rate gain.
#. DSpark's design is broader: VanillaMarkov, GatedMarkov, and RNN-style heads all fit the same family.
#. The prefix scheduler affects serving throughput decisions, not correctness.

Links
*****

* `DeepSpec / DSpark repo <https://github.com/deepseek-ai/DeepSpec>`_
* `DeepSeek-V4-Pro-DSpark checkpoint <https://huggingface.co/deepseek-ai/DeepSeek-V4-Pro-DSpark>`_
* `Domino repo <https://github.com/jianuo-huang/Domino>`_
* `Domino checkpoint: Qwen3-8B-Domino-b16 <https://huggingface.co/Huang2020/Qwen3-8B-Domino-b16>`_
* `ModelOpt PR #1710 <https://github.com/NVIDIA/Model-Optimizer/pull/1710>`_
23 changes: 23 additions & 0 deletions docs/source/announcements/github-pages-announcements.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
:orphan:

Model Optimizer Announcements Are Moving to GitHub Pages
#########################################################

:Author: Model Optimizer Team
:Date: July 13, 2026
:Tags: release, docs, github-pages

The Model Optimizer GitHub Pages site is expanding from API documentation into a lightweight announcement hub. The goal is to make releases, technical notes, examples, and deployment writeups easier to discover without introducing a separate publishing system.

What Changes
************

* Announcements live in the documentation source and are reviewed through pull requests.
* The landing page defaults to announcements.
* Existing API documentation remains available from the Sphinx left navigation.
* Announcement pages support tags, search, filtering, and embedded images.

Authoring Flow
**************

Add a Sphinx page under ``docs/source/announcements/`` and link it from the announcements toctree in ``docs/source/index.rst``. The GitHub Pages workflow rebuilds the static site from committed source, so every announcement follows the same review path as code and docs.
3 changes: 2 additions & 1 deletion docs/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,8 @@
html_static_path = ["_static"]

html_title = f"Model Optimizer {version}"
html_css_files = ["custom.css"]
html_css_files = ["custom.css", "announcements.css"]
html_js_files = ["announcements.js"]
html_permalinks_icon = "#" # default icon not rendering properly

# TODO: left here as reference for future
Expand Down
Loading
Loading