Files
cs249r_book/instructors/customization.html
T

1926 lines
78 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en"><head>
<meta charset="utf-8">
<meta name="generator" content="quarto-1.9.38">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
<title>Customization Guide The Blueprint - Machine Learning Systems</title>
<style>
/* Default styles provided by pandoc.
** See https://pandoc.org/MANUAL.html#variables-for-html for config info.
*/
code{white-space: pre-wrap;}
span.smallcaps{font-variant: small-caps;}
div.columns{display: flex; gap: min(4vw, 1.5em);}
div.column{flex: auto; overflow-x: auto;}
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
ul.task-list{list-style: none;}
ul.task-list li input[type="checkbox"] {
width: 0.8em;
margin: 0 0.8em 0.2em -1em; /* quarto-specific, see https://github.com/quarto-dev/quarto-cli/issues/4556 */
vertical-align: middle;
}
</style>
<script src="site_libs/quarto-nav/quarto-nav.js"></script>
<script src="site_libs/clipboard/clipboard.min.js"></script>
<script src="site_libs/quarto-search/autocomplete.umd.js"></script>
<script src="site_libs/quarto-search/fuse.min.js"></script>
<script src="site_libs/quarto-search/quarto-search.js"></script>
<meta name="quarto:offset" content="./">
<link href="./pedagogy.html" rel="next">
<link href="./tinyml-syllabus.html" rel="prev">
<link href="./assets/images/logo.png" rel="icon" type="image/png">
<script src="site_libs/quarto-html/quarto.js" type="module"></script>
<script src="site_libs/quarto-html/tabsets/tabsets.js" type="module"></script>
<script src="site_libs/quarto-html/popper.min.js"></script>
<script src="site_libs/quarto-html/tippy.umd.min.js"></script>
<script src="site_libs/quarto-html/anchor.min.js"></script>
<link href="site_libs/quarto-html/tippy.css" rel="stylesheet">
<link href="site_libs/quarto-html/quarto-syntax-highlighting-07314a9cf6582ab18f4f031a2d4fd43d.css" rel="stylesheet" class="quarto-color-scheme" id="quarto-text-highlighting-styles">
<link href="site_libs/quarto-html/quarto-syntax-highlighting-dark-5b9c4d9438c2366fdf56ac55b12b357f.css" rel="stylesheet" class="quarto-color-scheme quarto-color-alternate" id="quarto-text-highlighting-styles">
<link href="site_libs/quarto-html/quarto-syntax-highlighting-07314a9cf6582ab18f4f031a2d4fd43d.css" rel="stylesheet" class="quarto-color-scheme-extra" id="quarto-text-highlighting-styles">
<script src="site_libs/bootstrap/bootstrap.min.js"></script>
<link href="site_libs/bootstrap/bootstrap-icons.css" rel="stylesheet">
<link href="site_libs/bootstrap/bootstrap-45390e6f2c2917d02ff9761a2a3930e8.min.css" rel="stylesheet" append-hash="true" class="quarto-color-scheme" id="quarto-bootstrap" data-mode="light">
<link href="site_libs/bootstrap/bootstrap-dark-d43015b1d7ede0eadfe3284c0da38244.min.css" rel="stylesheet" append-hash="true" class="quarto-color-scheme quarto-color-alternate" id="quarto-bootstrap" data-mode="light">
<link href="site_libs/bootstrap/bootstrap-45390e6f2c2917d02ff9761a2a3930e8.min.css" rel="stylesheet" append-hash="true" class="quarto-color-scheme-extra" id="quarto-bootstrap" data-mode="light">
<script id="quarto-search-options" type="application/json">{
"location": "navbar",
"copy-button": false,
"collapse-after": 3,
"panel-placement": "end",
"type": "overlay",
"limit": 50,
"keyboard-shortcut": [
"f",
"/",
"s"
],
"show-item-context": false,
"language": {
"search-no-results-text": "No results",
"search-matching-documents-text": "matching documents",
"search-copy-link-title": "Copy link to search",
"search-hide-matches-text": "Hide additional matches",
"search-more-match-text": "more match in this document",
"search-more-matches-text": "more matches in this document",
"search-clear-button-title": "Clear",
"search-text-placeholder": "",
"search-detached-cancel-button-title": "Cancel",
"search-submit-button-title": "Submit",
"search-label": "Search"
}
}</script>
<!-- Shared head includes — MLSysBook ecosystem -->
<!--
theme-persist (inline, sync) — applies stored color-scheme before first paint.
Inlined deliberately: runs before any other <script>, no extra round-trip,
and avoids per-subsite asset path differences. Canonical source:
shared/scripts/theme-persist.js (kept identical for documentation/testability).
If you change one, mirror the change to the other.
-->
<script>
(function () {
'use strict';
var STORAGE_KEY = 'quarto-color-scheme';
// Quarto's toggle writes 'alternate'/'default' (alternate = dark, the
// layered sheet); sibling non-Quarto sites write 'dark'/'light'. Both
// hit the same key, so we accept either. Quarto's own startup still
// checks `=== 'alternate'`, so we mirror onto <html> rather than rewrite.
function normalize(v) {
if (v === 'dark' || v === 'alternate') return 'dark';
if (v === 'light' || v === 'default') return 'light';
return null;
}
function preferredScheme() {
try {
var n = normalize(window.localStorage.getItem(STORAGE_KEY));
if (n) return n;
} catch (e) {}
if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
return 'dark';
}
return 'light';
}
function apply(scheme) {
var html = document.documentElement;
if (!html) return;
html.setAttribute('data-bs-theme', scheme);
html.setAttribute('data-quarto-color-scheme', scheme);
html.style.colorScheme = scheme;
// Bridge to legacy `.quarto-dark` body class — about.css,
// community.css, and newsletter.css key their dark-mode CSS
// variables off `.quarto-dark { ... }`. Quarto's own toggle adds
// that class on click, but the OS-pref / first-paint path never
// does. Mirror the scheme onto body so those CSS variables fire
// on every dark path, not just toggle clicks. body may not exist
// yet on the first call (this script runs in <head>); a
// MutationObserver below catches it as soon as it parses.
var body = document.body;
if (body) body.classList.toggle('quarto-dark', scheme === 'dark');
}
apply(preferredScheme());
// Catch the FOUC window between <head> apply() and DOMContentLoaded:
// <body> may not exist yet at first apply(), but as soon as the
// parser appends it we want the legacy class bridge in place so any
// CSS keyed on `.quarto-dark` paints correctly on first render.
if (!document.body) {
var earlyObserver = new MutationObserver(function () {
if (document.body) {
apply(preferredScheme());
earlyObserver.disconnect();
}
});
earlyObserver.observe(document.documentElement, { childList: true });
}
// Quarto's toggle updates localStorage but not <html>, so wrap it to
// mirror the new value onto data-bs-theme; otherwise clicking the toggle
// leaves CSS keyed off [data-bs-theme="dark"] rendering against the wrong
// stylesheet until the next reload.
function syncFromStorage() {
try {
var n = normalize(window.localStorage.getItem(STORAGE_KEY));
if (n) apply(n);
} catch (e) {}
}
function wrapToggle() {
var orig = window.quartoToggleColorScheme;
if (typeof orig !== 'function' || orig.__mlsbWrapped) return;
var wrapped = function () { var r = orig.apply(this, arguments); syncFromStorage(); return r; };
wrapped.__mlsbWrapped = true;
window.quartoToggleColorScheme = wrapped;
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', function () { wrapToggle(); syncFromStorage(); });
} else {
wrapToggle(); syncFromStorage();
}
// Defend the legacy `.quarto-dark` body class against Quarto's own
// `toggleBodyColor(mode)`, which runs *after* this script and re-asserts
// the body class based on `mode` — and `mode` defaults to 'light' when
// there's no stored quarto-color-scheme value, even when the OS / first
// paint already resolved to dark via `data-bs-theme`. Without this
// observer, on every fresh visit Quarto would clobber our bridge and
// restore `.quarto-light`, leaving `.quarto-dark`-keyed CSS variables
// (about/community/newsletter pages) at their light-mode defaults
// against the dark `data-bs-theme="dark"` background.
function bridgeBody() {
var body = document.body;
if (!body) return;
var wantDark = document.documentElement.getAttribute('data-bs-theme') === 'dark';
if (wantDark && !body.classList.contains('quarto-dark')) {
body.classList.add('quarto-dark');
body.classList.remove('quarto-light');
} else if (!wantDark && body.classList.contains('quarto-dark')) {
body.classList.remove('quarto-dark');
body.classList.add('quarto-light');
}
}
function watchBody() {
if (!document.body) return;
bridgeBody();
var bodyObs = new MutationObserver(bridgeBody);
bodyObs.observe(document.body, { attributes: true, attributeFilter: ['class'] });
var htmlObs = new MutationObserver(bridgeBody);
htmlObs.observe(document.documentElement, { attributes: true, attributeFilter: ['data-bs-theme'] });
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', watchBody);
} else {
watchBody();
}
window.addEventListener('storage', function (ev) {
if (ev.key !== STORAGE_KEY) return;
var n = normalize(ev.newValue);
if (n) apply(n);
});
if (window.matchMedia) {
try {
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', function (ev) {
try { if (window.localStorage.getItem(STORAGE_KEY)) return; } catch (e) {}
apply(ev.matches ? 'dark' : 'light');
});
} catch (e) {}
}
})();
</script>
<!--
Build-stamp styling. Tiny, neutral, dark-mode aware. The actual stamp text
is injected by shared/scripts/inject-build-stamp.sh into footer markup
containing the literal MLSB_BUILD_STAMP HTML-comment token (see that
script for the exact pattern — we cannot write it here because HTML
comments cannot nest and the inner closer would terminate this block).
-->
<style>
.mlsb-build-stamp {
display: inline-block;
margin-top: 0.25rem;
font-size: 0.75rem;
opacity: 0.6;
font-variant-numeric: tabular-nums;
}
[data-bs-theme="dark"] .mlsb-build-stamp { opacity: 0.5; }
</style>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&amp;family=JetBrains+Mono:wght@400;500&amp;display=swap" rel="stylesheet">
<link rel="icon" href="https://mlsysbook.ai/vol1/assets/images/icons/favicon.png" type="image/png">
<!--
navbar-theme-toggle-relocate — moves the Quarto theme toggle into the
navbar's right-side .navbar-nav group at page load, so it collapses into
the hamburger menu alongside Subscribe/Star/Support/GitHub at narrow widths
instead of being stranded in the middle by Quarto's mx-auto brand layout.
Why JS: Quarto auto-injects the toggle into .quarto-navbar-tools (outside
.navbar-collapse). CSS can't move DOM nodes between containers, and CSS
positioning hacks (absolute/right: 50px) fight Bootstrap's responsive
layout below 992px. Moving the node once at startup is the standard
approach used across Quarto-based sites with the same problem.
-->
<script>
(function () {
'use strict';
function relocateThemeToggle() {
var tools = document.querySelector('.quarto-navbar-tools');
if (!tools) return;
var toggle = tools.querySelector('.quarto-color-scheme-toggle');
if (!toggle) return;
var navRight = document.querySelector(
'.navbar .navbar-collapse .navbar-nav.ms-auto'
) || document.querySelector(
'.navbar .navbar-collapse .navbar-nav:last-of-type'
);
if (!navRight) return;
var li = document.createElement('li');
li.className = 'nav-item navbar-theme-toggle-item';
toggle.classList.add('nav-link');
li.appendChild(toggle);
navRight.appendChild(li);
tools.style.display = 'none';
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', relocateThemeToggle);
} else {
relocateThemeToggle();
}
})();
</script>
<!--
navbar-brand-href-fix — force the navbar brand link to the ecosystem root
(https://mlsysbook.ai/) on every subsite.
Quarto sets each subsite's `site-url` to `https://mlsysbook.ai/<subsite>/`
and then relativizes any navbar.href value that lives inside the
site-url's domain. The shared `href: https://mlsysbook.ai/` in
navbar-common.yml therefore gets emitted as `./index.html`, which on
vol1/vol2/tinytorch/etc. resolves to *that subsite's* root rather than
the ecosystem landing page. Override the brand link href in JS after
Quarto's render: this is a one-line user-experience fix that does not
fight Quarto's URL machinery.
-->
<script>
(function () {
'use strict';
function fixBrandHref() {
var brand = document.querySelectorAll(
'.navbar .navbar-brand, .navbar .navbar-brand-logo'
);
for (var i = 0; i < brand.length; i++) {
brand[i].setAttribute('href', 'https://mlsysbook.ai/');
}
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', fixBrandHref);
} else {
fixBrandHref();
}
})();
</script>
<!-- Release identity (rendered into the footer pill below). -->
<meta name="release-manifest" content="/instructors/release-manifest.json">
</head>
<body class="nav-sidebar floating nav-fixed quarto-light"><script id="quarto-html-before-body" type="application/javascript">
const toggleBodyColorMode = (bsSheetEl) => {
const mode = bsSheetEl.getAttribute("data-mode");
const bodyEl = window.document.querySelector("body");
if (mode === "dark") {
bodyEl.classList.add("quarto-dark");
bodyEl.classList.remove("quarto-light");
} else {
bodyEl.classList.add("quarto-light");
bodyEl.classList.remove("quarto-dark");
}
}
const toggleBodyColorPrimary = () => {
const bsSheetEl = window.document.querySelector("link#quarto-bootstrap:not([rel=disabled-stylesheet])");
if (bsSheetEl) {
toggleBodyColorMode(bsSheetEl);
}
}
const setColorSchemeToggle = (alternate) => {
const toggles = window.document.querySelectorAll('.quarto-color-scheme-toggle');
for (let i=0; i < toggles.length; i++) {
const toggle = toggles[i];
if (toggle) {
if (alternate) {
toggle.classList.add("alternate");
} else {
toggle.classList.remove("alternate");
}
}
}
};
const toggleColorMode = (alternate) => {
// Switch the stylesheets
const primaryStylesheets = window.document.querySelectorAll('link.quarto-color-scheme:not(.quarto-color-alternate)');
const alternateStylesheets = window.document.querySelectorAll('link.quarto-color-scheme.quarto-color-alternate');
manageTransitions('#quarto-margin-sidebar .nav-link', false);
if (alternate) {
// note: dark is layered on light, we don't disable primary!
enableStylesheet(alternateStylesheets);
for (const sheetNode of alternateStylesheets) {
if (sheetNode.id === "quarto-bootstrap") {
toggleBodyColorMode(sheetNode);
}
}
} else {
disableStylesheet(alternateStylesheets);
enableStylesheet(primaryStylesheets)
toggleBodyColorPrimary();
}
manageTransitions('#quarto-margin-sidebar .nav-link', true);
// Switch the toggles
setColorSchemeToggle(alternate)
// Hack to workaround the fact that safari doesn't
// properly recolor the scrollbar when toggling (#1455)
if (navigator.userAgent.indexOf('Safari') > 0 && navigator.userAgent.indexOf('Chrome') == -1) {
manageTransitions("body", false);
window.scrollTo(0, 1);
setTimeout(() => {
window.scrollTo(0, 0);
manageTransitions("body", true);
}, 40);
}
}
const disableStylesheet = (stylesheets) => {
for (let i=0; i < stylesheets.length; i++) {
const stylesheet = stylesheets[i];
stylesheet.rel = 'disabled-stylesheet';
}
}
const enableStylesheet = (stylesheets) => {
for (let i=0; i < stylesheets.length; i++) {
const stylesheet = stylesheets[i];
if(stylesheet.rel !== 'stylesheet') { // for Chrome, which will still FOUC without this check
stylesheet.rel = 'stylesheet';
}
}
}
const manageTransitions = (selector, allowTransitions) => {
const els = window.document.querySelectorAll(selector);
for (let i=0; i < els.length; i++) {
const el = els[i];
if (allowTransitions) {
el.classList.remove('notransition');
} else {
el.classList.add('notransition');
}
}
}
const isFileUrl = () => {
return window.location.protocol === 'file:';
}
const hasAlternateSentinel = () => {
let styleSentinel = getColorSchemeSentinel();
if (styleSentinel !== null) {
return styleSentinel === "alternate";
} else {
return false;
}
}
const setStyleSentinel = (alternate) => {
const value = alternate ? "alternate" : "default";
if (!isFileUrl()) {
window.localStorage.setItem("quarto-color-scheme", value);
} else {
localAlternateSentinel = value;
}
}
const getColorSchemeSentinel = () => {
if (!isFileUrl()) {
const storageValue = window.localStorage.getItem("quarto-color-scheme");
return storageValue != null ? storageValue : localAlternateSentinel;
} else {
return localAlternateSentinel;
}
}
const toggleGiscusIfUsed = (isAlternate, darkModeDefault) => {
const baseTheme = document.querySelector('#giscus-base-theme')?.value ?? 'light';
const alternateTheme = document.querySelector('#giscus-alt-theme')?.value ?? 'dark';
let newTheme = '';
if(authorPrefersDark) {
newTheme = isAlternate ? baseTheme : alternateTheme;
} else {
newTheme = isAlternate ? alternateTheme : baseTheme;
}
const changeGiscusTheme = () => {
// From: https://github.com/giscus/giscus/issues/336
const sendMessage = (message) => {
const iframe = document.querySelector('iframe.giscus-frame');
if (!iframe) return;
iframe.contentWindow.postMessage({ giscus: message }, 'https://giscus.app');
}
sendMessage({
setConfig: {
theme: newTheme
}
});
}
const isGiscussLoaded = window.document.querySelector('iframe.giscus-frame') !== null;
if (isGiscussLoaded) {
changeGiscusTheme();
}
};
const authorPrefersDark = false;
const queryPrefersDark = window.matchMedia('(prefers-color-scheme: dark)');
const darkModeDefault = queryPrefersDark.matches;
document.querySelector('link#quarto-text-highlighting-styles.quarto-color-scheme-extra').rel = 'disabled-stylesheet';
document.querySelector('link#quarto-bootstrap.quarto-color-scheme-extra').rel = 'disabled-stylesheet';
let localAlternateSentinel = darkModeDefault ? 'alternate' : 'default';
// Dark / light mode switch
window.quartoToggleColorScheme = () => {
// Read the current dark / light value
let toAlternate = !hasAlternateSentinel();
toggleColorMode(toAlternate);
setStyleSentinel(toAlternate);
toggleGiscusIfUsed(toAlternate, darkModeDefault);
window.dispatchEvent(new Event('resize'));
};
queryPrefersDark.addEventListener("change", e => {
if(window.localStorage.getItem("quarto-color-scheme") !== null)
return;
const alternate = e.matches
toggleColorMode(alternate);
localAlternateSentinel = e.matches ? 'alternate' : 'default'; // this is used alongside local storage!
toggleGiscusIfUsed(alternate, darkModeDefault);
});
// Switch to dark mode if need be
if (hasAlternateSentinel()) {
toggleColorMode(true);
} else {
toggleColorMode(false);
}
</script>
<div id="quarto-search-results"></div>
<header id="quarto-header" class="headroom fixed-top">
<nav class="navbar navbar-expand-xl " data-bs-theme="dark">
<div class="navbar-container container-fluid">
<div class="navbar-brand-container mx-auto">
<a href="./index.html" class="navbar-brand navbar-brand-logo">
<img src="./assets/images/logo.png" alt="" class="navbar-logo light-content">
<img src="./assets/images/logo.png" alt="" class="navbar-logo dark-content">
</a>
<a class="navbar-brand" href="./index.html">
<span class="navbar-title">Machine Learning Systems</span>
</a>
</div>
<div id="quarto-search" class="" title="Search"></div>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarCollapse" aria-controls="navbarCollapse" role="menu" aria-expanded="false" aria-label="Toggle navigation" onclick="if (window.quartoToggleHeadroom) { window.quartoToggleHeadroom(); }">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarCollapse">
<ul class="navbar-nav navbar-nav-scroll me-auto">
<li class="nav-item dropdown ">
<a class="nav-link dropdown-toggle" href="#" id="nav-menu-the-blueprint" role="link" data-bs-toggle="dropdown" aria-expanded="false">
<span class="menu-text">The Blueprint</span>
</a>
<ul class="dropdown-menu" aria-labelledby="nav-menu-the-blueprint">
<li>
<a class="dropdown-item" href="./index.html"><i class="bi bi-map" role="img">
</i>
<span class="dropdown-text">Blueprint Hub</span></a>
</li>
<li>
<a class="dropdown-item" href="./getting-started.html">
<span class="dropdown-text">Getting Started</span></a>
</li>
<li><hr class="dropdown-divider"></li>
<li>
<a class="dropdown-item" href="./foundations-syllabus.html">
<span class="dropdown-text">Foundations Syllabus</span></a>
</li>
<li>
<a class="dropdown-item" href="./scale-syllabus.html">
<span class="dropdown-text">Scale Syllabus</span></a>
</li>
<li>
<a class="dropdown-item" href="./tinyml-syllabus.html">
<span class="dropdown-text">TinyML Syllabus</span></a>
</li>
<li><hr class="dropdown-divider"></li>
<li>
<a class="dropdown-item" href="./pedagogy.html">
<span class="dropdown-text">Pedagogy</span></a>
</li>
<li>
<a class="dropdown-item" href="./assessment.html">
<span class="dropdown-text">Assessment &amp; Grading</span></a>
</li>
<li>
<a class="dropdown-item" href="./ta-guide.html">
<span class="dropdown-text">TA Guide</span></a>
</li>
<li><hr class="dropdown-divider"></li>
<li>
<a class="dropdown-item" href="./course-map.html">
<span class="dropdown-text">Course Map</span></a>
</li>
<li>
<a class="dropdown-item" href="./customization.html">
<span class="dropdown-text">Customization</span></a>
</li>
<li>
<a class="dropdown-item" href="./faq.html">
<span class="dropdown-text">FAQ</span></a>
</li>
</ul>
</li>
<li class="nav-item dropdown ">
<a class="nav-link dropdown-toggle" href="#" id="nav-menu-read" role="link" data-bs-toggle="dropdown" aria-expanded="false">
<span class="menu-text">Read</span>
</a>
<ul class="dropdown-menu" aria-labelledby="nav-menu-read">
<li>
<a class="dropdown-item" href="https://mlsysbook.ai/vol1/"><i class="bi bi-journal" role="img">
</i>
<span class="dropdown-text">Volume I: Foundations</span></a>
</li>
<li>
<a class="dropdown-item" href="https://mlsysbook.ai/vol2/"><i class="bi bi-journal" role="img">
</i>
<span class="dropdown-text">Volume II: At Scale</span></a>
</li>
<li><hr class="dropdown-divider"></li>
<li>
<a class="dropdown-item" href="https://mlsysbook.ai/vol1/assets/downloads/Machine-Learning-Systems-Vol1.pdf" target="_blank"><i class="bi bi-file-pdf" role="img">
</i>
<span class="dropdown-text">Volume I PDF</span></a>
</li>
<li>
<a class="dropdown-item" href="https://mlsysbook.ai/vol1/assets/downloads/Machine-Learning-Systems-Vol1.epub" target="_blank"><i class="bi bi-journal-text" role="img">
</i>
<span class="dropdown-text">Volume I EPUB</span></a>
</li>
<li><hr class="dropdown-divider"></li>
<li>
<a class="dropdown-item" href="https://mlsysbook.ai/vol2/assets/downloads/Machine-Learning-Systems-Vol2.pdf" target="_blank"><i class="bi bi-file-pdf" role="img">
</i>
<span class="dropdown-text">Volume II PDF</span></a>
</li>
<li>
<a class="dropdown-item" href="https://mlsysbook.ai/vol2/assets/downloads/Machine-Learning-Systems-Vol2.epub" target="_blank"><i class="bi bi-journal-text" role="img">
</i>
<span class="dropdown-text">Volume II EPUB</span></a>
</li>
</ul>
</li>
<li class="nav-item dropdown ">
<a class="nav-link dropdown-toggle" href="#" id="nav-menu-build" role="link" data-bs-toggle="dropdown" aria-expanded="false">
<span class="menu-text">Build</span>
</a>
<ul class="dropdown-menu" aria-labelledby="nav-menu-build">
<li>
<a class="dropdown-item" href="https://mlsysbook.ai/labs/"><i class="bi bi-sliders" role="img">
</i>
<span class="dropdown-text">Labs</span></a>
</li>
<li>
<a class="dropdown-item" href="https://mlsysbook.ai/tinytorch/"><i class="bi bi-fire" role="img">
</i>
<span class="dropdown-text">TinyTorch</span></a>
</li>
<li>
<a class="dropdown-item" href="https://mlsysbook.ai/kits/"><i class="bi bi-cpu" role="img">
</i>
<span class="dropdown-text">Hardware Kits</span></a>
</li>
<li>
<a class="dropdown-item" href="https://mlsysbook.ai/mlsysim/"><i class="bi bi-calculator" role="img">
</i>
<span class="dropdown-text">MLSys·im</span></a>
</li>
</ul>
</li>
<li class="nav-item dropdown ">
<a class="nav-link dropdown-toggle" href="#" id="nav-menu-teach" role="link" data-bs-toggle="dropdown" aria-expanded="false">
<span class="menu-text">Teach</span>
</a>
<ul class="dropdown-menu" aria-labelledby="nav-menu-teach">
<li>
<a class="dropdown-item" href="https://mlsysbook.ai/instructors/course-map.html"><i class="bi bi-signpost-split" role="img">
</i>
<span class="dropdown-text">Course Map</span></a>
</li>
<li>
<a class="dropdown-item" href="https://mlsysbook.ai/slides/"><i class="bi bi-easel" role="img">
</i>
<span class="dropdown-text">Lecture Slides</span></a>
</li>
<li>
<a class="dropdown-item" href="https://mlsysbook.ai/instructors/"><i class="bi bi-person-video3" role="img">
</i>
<span class="dropdown-text">Instructor Hub</span></a>
</li>
</ul>
</li>
<li class="nav-item dropdown ">
<a class="nav-link dropdown-toggle" href="#" id="nav-menu-prepare" role="link" data-bs-toggle="dropdown" aria-expanded="false">
<span class="menu-text">Prepare</span>
</a>
<ul class="dropdown-menu" aria-labelledby="nav-menu-prepare">
<li>
<a class="dropdown-item" href="https://mlsysbook.ai/staffml/"><i class="bi bi-mortarboard" role="img">
</i>
<span class="dropdown-text">StaffML</span></a>
</li>
<li>
<a class="dropdown-item" href="https://mlsysbook.ai/staffml/plans"><i class="bi bi-map" role="img">
</i>
<span class="dropdown-text">Study Plans</span></a>
</li>
<li>
<a class="dropdown-item" href="https://mlsysbook.ai/staffml/gauntlet"><i class="bi bi-lightning" role="img">
</i>
<span class="dropdown-text">Gauntlet Mode</span></a>
</li>
<li><hr class="dropdown-divider"></li>
<li>
<a class="dropdown-item" href="https://mlsysbook.ai/staffml/downloads/StaffML-Paper.pdf" target="_blank"><i class="bi bi-file-pdf" role="img">
</i>
<span class="dropdown-text">StaffML Paper</span></a>
</li>
</ul>
</li>
<li class="nav-item dropdown ">
<a class="nav-link dropdown-toggle" href="#" id="nav-menu-connect" role="link" data-bs-toggle="dropdown" aria-expanded="false">
<span class="menu-text">Connect</span>
</a>
<ul class="dropdown-menu" aria-labelledby="nav-menu-connect">
<li>
<a class="dropdown-item" href="https://mlsysbook.ai/newsletter/"><i class="bi bi-envelope" role="img">
</i>
<span class="dropdown-text">Newsletter</span></a>
</li>
<li>
<a class="dropdown-item" href="https://mlsysbook.ai/community/"><i class="bi bi-globe" role="img">
</i>
<span class="dropdown-text">Global Network</span></a>
</li>
<li>
<a class="dropdown-item" href="https://mlsysbook.ai/community/events.html"><i class="bi bi-calendar-event" role="img">
</i>
<span class="dropdown-text">Workshops &amp; Events</span></a>
</li>
<li>
<a class="dropdown-item" href="https://mlsysbook.ai/community/partners.html"><i class="bi bi-people" role="img">
</i>
<span class="dropdown-text">Partners &amp; Sponsors</span></a>
</li>
</ul>
</li>
<li class="nav-item dropdown ">
<a class="nav-link dropdown-toggle" href="#" id="nav-menu-about" role="link" data-bs-toggle="dropdown" aria-expanded="false">
<span class="menu-text">About</span>
</a>
<ul class="dropdown-menu" aria-labelledby="nav-menu-about">
<li>
<a class="dropdown-item" href="https://mlsysbook.ai/about/"><i class="bi bi-bullseye" role="img">
</i>
<span class="dropdown-text">Mission</span></a>
</li>
<li>
<a class="dropdown-item" href="https://mlsysbook.ai/about/#story"><i class="bi bi-book" role="img">
</i>
<span class="dropdown-text">Our Story</span></a>
</li>
<li><hr class="dropdown-divider"></li>
<li>
<a class="dropdown-item" href="https://mlsysbook.ai/about/people.html"><i class="bi bi-person-lines-fill" role="img">
</i>
<span class="dropdown-text">People</span></a>
</li>
<li>
<a class="dropdown-item" href="https://mlsysbook.ai/about/contributors.html"><i class="bi bi-people" role="img">
</i>
<span class="dropdown-text">Contributors</span></a>
</li>
<li><hr class="dropdown-divider"></li>
<li>
<a class="dropdown-item" href="https://mlsysbook.ai/about/license.html"><i class="bi bi-cc-circle" role="img">
</i>
<span class="dropdown-text">License</span></a>
</li>
</ul>
</li>
</ul>
<ul class="navbar-nav navbar-nav-scroll ms-auto">
<li class="nav-item">
<a class="nav-link" href="https://mlsysbook.ai/newsletter/"> <i class="bi bi-envelope" role="img">
</i>
<span class="menu-text">Subscribe</span></a>
</li>
<li class="nav-item">
<a class="nav-link" href="https://github.com/harvard-edge/cs249r_book" target="_blank"> <i class="bi bi-star" role="img">
</i>
<span class="menu-text">Star</span></a>
</li>
<li class="nav-item">
<a class="nav-link" href="https://opencollective.com/mlsysbook" target="_blank"> <i class="bi bi-heart" role="img">
</i>
<span class="menu-text">Support</span></a>
</li>
<li class="nav-item dropdown ">
<a class="nav-link dropdown-toggle" href="#" id="nav-menu-github" role="link" data-bs-toggle="dropdown" aria-expanded="false">
<i class="bi bi-github" role="img">
</i>
<span class="menu-text">GitHub</span>
</a>
<ul class="dropdown-menu dropdown-menu-end" aria-labelledby="nav-menu-github">
<li>
<a class="dropdown-item" href="https://github.com/harvard-edge/cs249r_book/discussions" target="_blank"><i class="bi bi-chat" role="img">
</i>
<span class="dropdown-text">Discussions</span></a>
</li>
<li>
<a class="dropdown-item" href="https://github.com/harvard-edge/cs249r_book" target="_blank"><i class="bi bi-pencil" role="img">
</i>
<span class="dropdown-text">Edit this page</span></a>
</li>
<li>
<a class="dropdown-item" href="https://github.com/harvard-edge/cs249r_book/issues/new" target="_blank"><i class="bi bi-bug" role="img">
</i>
<span class="dropdown-text">Report an issue</span></a>
</li>
<li>
<a class="dropdown-item" href="https://github.com/harvard-edge/cs249r_book" target="_blank"><i class="bi bi-code" role="img">
</i>
<span class="dropdown-text">View source</span></a>
</li>
</ul>
</li>
</ul>
</div> <!-- /navcollapse -->
<div class="quarto-navbar-tools">
<a href="" class="quarto-color-scheme-toggle quarto-navigation-tool px-1" onclick="window.quartoToggleColorScheme(); return false;" title="Toggle dark mode"><i class="bi"></i></a>
</div>
</div> <!-- /container-fluid -->
</nav>
<nav class="quarto-secondary-nav">
<div class="container-fluid d-flex">
<button type="button" class="quarto-btn-toggle btn" data-bs-toggle="collapse" role="button" data-bs-target=".quarto-sidebar-collapse-item" aria-controls="quarto-sidebar" aria-expanded="false" aria-label="Toggle sidebar navigation" onclick="if (window.quartoToggleHeadroom) { window.quartoToggleHeadroom(); }">
<i class="bi bi-layout-text-sidebar-reverse"></i>
</button>
<nav class="quarto-page-breadcrumbs" aria-label="breadcrumb"><ol class="breadcrumb"><li class="breadcrumb-item"><a href="./foundations-syllabus.html">Syllabi</a></li><li class="breadcrumb-item"><a href="./customization.html">Customization Guide</a></li></ol></nav>
<a class="flex-grow-1" role="navigation" data-bs-toggle="collapse" data-bs-target=".quarto-sidebar-collapse-item" aria-controls="quarto-sidebar" aria-expanded="false" aria-label="Toggle sidebar navigation" onclick="if (window.quartoToggleHeadroom) { window.quartoToggleHeadroom(); }">
</a>
<button type="button" class="btn quarto-search-button" aria-label="Search" onclick="window.quartoOpenSearch();">
<i class="bi bi-search"></i>
</button>
</div>
</nav>
<div id="quarto-announcement" data-announcement-id="b9d6329686913125e6bf36ba894aaa63" class="alert alert-primary hidden"><i class="bi bi-megaphone quarto-announcement-icon"></i><div class="quarto-announcement-content">
<p>👩‍🏫 <strong>Instructor Hub</strong> — complete course materials, slides, exercises, and grading tools.<br> 📘 <strong>The book:</strong> <a href="https://mlsysbook.ai/vol1/">Vol I: Foundations</a> · <a href="https://mlsysbook.ai/vol2/">Vol II: At Scale</a> — open access, free forever.<br> 🛠️ <strong>Build with your students:</strong> <a href="https://mlsysbook.ai/tinytorch/">TinyTorch</a> (framework) · <a href="https://mlsysbook.ai/kits/">Hardware Kits</a> (embedded) · <a href="https://mlsysbook.ai/slides/">Lecture Slides</a> (decks) · <a href="https://mlsysbook.ai/staffml/">StaffML</a> (practice)<br> 📬 <strong>Newsletter:</strong> ML Systems insights &amp; updates — <a href="https://mlsysbook.ai/newsletter/">Subscribe →</a></p>
</div><i class="bi bi-x-lg quarto-announcement-action"></i></div>
</header>
<!-- content -->
<div id="quarto-content" class="quarto-container page-columns page-rows-contents page-layout-article page-navbar">
<!-- sidebar -->
<nav id="quarto-sidebar" class="sidebar collapse collapse-horizontal quarto-sidebar-collapse-item sidebar-navigation floating overflow-auto">
<div class="mt-2 flex-shrink-0 align-items-center">
<div class="sidebar-search">
<div id="quarto-search" class="" title="Search"></div>
</div>
</div>
<div class="sidebar-menu-container">
<ul class="list-unstyled mt-1">
<li class="sidebar-item sidebar-item-section">
<div class="sidebar-item-container">
<a class="sidebar-item-text sidebar-link text-start" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-1" role="navigation" aria-expanded="true">
<span class="menu-text">Start Here</span></a>
<a class="sidebar-item-toggle text-start" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-1" role="navigation" aria-expanded="true" aria-label="Toggle section">
<i class="bi bi-chevron-right ms-2"></i>
</a>
</div>
<ul id="quarto-sidebar-section-1" class="collapse list-unstyled sidebar-section depth1 show">
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="./index.html" class="sidebar-item-text sidebar-link">
<span class="menu-text">The Blueprint</span></a>
</div>
</li>
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="./getting-started.html" class="sidebar-item-text sidebar-link">
<span class="menu-text">Getting Started</span></a>
</div>
</li>
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="./course-map.html" class="sidebar-item-text sidebar-link">
<span class="menu-text">Course Component Map</span></a>
</div>
</li>
</ul>
</li>
<li class="sidebar-item sidebar-item-section">
<div class="sidebar-item-container">
<a class="sidebar-item-text sidebar-link text-start" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-2" role="navigation" aria-expanded="true">
<span class="menu-text">Syllabi</span></a>
<a class="sidebar-item-toggle text-start" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-2" role="navigation" aria-expanded="true" aria-label="Toggle section">
<i class="bi bi-chevron-right ms-2"></i>
</a>
</div>
<ul id="quarto-sidebar-section-2" class="collapse list-unstyled sidebar-section depth1 show">
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="./foundations-syllabus.html" class="sidebar-item-text sidebar-link">
<span class="menu-text">AI Systems Foundations</span></a>
</div>
</li>
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="./scale-syllabus.html" class="sidebar-item-text sidebar-link">
<span class="menu-text">AI Engineering at Scale</span></a>
</div>
</li>
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="./tinyml-syllabus.html" class="sidebar-item-text sidebar-link">
<span class="menu-text">Tiny Machine Learning (TinyML)</span></a>
</div>
</li>
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="./customization.html" class="sidebar-item-text sidebar-link active">
<span class="menu-text">Customization Guide</span></a>
</div>
</li>
</ul>
</li>
<li class="sidebar-item sidebar-item-section">
<div class="sidebar-item-container">
<a class="sidebar-item-text sidebar-link text-start" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-3" role="navigation" aria-expanded="true">
<span class="menu-text">Teaching &amp; Assessment</span></a>
<a class="sidebar-item-toggle text-start" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-3" role="navigation" aria-expanded="true" aria-label="Toggle section">
<i class="bi bi-chevron-right ms-2"></i>
</a>
</div>
<ul id="quarto-sidebar-section-3" class="collapse list-unstyled sidebar-section depth1 show">
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="./pedagogy.html" class="sidebar-item-text sidebar-link">
<span class="menu-text">Pedagogy</span></a>
</div>
</li>
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="./assessment.html" class="sidebar-item-text sidebar-link">
<span class="menu-text">Assessment &amp; Grading</span></a>
</div>
</li>
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="./ta-guide.html" class="sidebar-item-text sidebar-link">
<span class="menu-text">TA Guide</span></a>
</div>
</li>
</ul>
</li>
<li class="sidebar-item sidebar-item-section">
<div class="sidebar-item-container">
<a class="sidebar-item-text sidebar-link text-start" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-4" role="navigation" aria-expanded="true">
<span class="menu-text">Resources</span></a>
<a class="sidebar-item-toggle text-start" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-4" role="navigation" aria-expanded="true" aria-label="Toggle section">
<i class="bi bi-chevron-right ms-2"></i>
</a>
</div>
<ul id="quarto-sidebar-section-4" class="collapse list-unstyled sidebar-section depth1 show">
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="./faq.html" class="sidebar-item-text sidebar-link">
<span class="menu-text">FAQ</span></a>
</div>
</li>
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="https://github.com/harvard-edge/cs249r_book/blob/dev/tinytorch/INSTRUCTOR.md" class="sidebar-item-text sidebar-link">
<span class="menu-text">TinyTorch Instructor Guide</span></a>
</div>
</li>
</ul>
</li>
</ul>
</div>
</nav>
<div id="quarto-sidebar-glass" class="quarto-sidebar-collapse-item" data-bs-toggle="collapse" data-bs-target=".quarto-sidebar-collapse-item"></div>
<!-- margin-sidebar -->
<div id="quarto-margin-sidebar" class="sidebar margin-sidebar">
<nav id="TOC" role="doc-toc" class="toc-active">
<h2 id="toc-title">On this page</h2>
<ul>
<li><a href="#week-quarter-version-foundations" id="toc-week-quarter-version-foundations" class="nav-link active" data-scroll-target="#week-quarter-version-foundations">10-Week Quarter Version (Foundations)</a></li>
<li><a href="#day-workshop-version" id="toc-day-workshop-version" class="nav-link" data-scroll-target="#day-workshop-version">3-Day Workshop Version</a></li>
<li><a href="#for-software-engineers" id="toc-for-software-engineers" class="nav-link" data-scroll-target="#for-software-engineers">For Software Engineers</a></li>
<li><a href="#for-computer-architects" id="toc-for-computer-architects" class="nav-link" data-scroll-target="#for-computer-architects">For Computer Architects</a></li>
<li><a href="#graduate-seminar-version" id="toc-graduate-seminar-version" class="nav-link" data-scroll-target="#graduate-seminar-version">Graduate Seminar Version</a></li>
<li><a href="#mixing-and-matching-components" id="toc-mixing-and-matching-components" class="nav-link" data-scroll-target="#mixing-and-matching-components">Mixing and Matching Components</a></li>
</ul>
</nav>
</div>
<!-- main -->
<main class="content" id="quarto-document-content">
<header id="title-block-header" class="quarto-title-block default"><nav class="quarto-page-breadcrumbs quarto-title-breadcrumbs d-none d-lg-block" aria-label="breadcrumb"><ol class="breadcrumb"><li class="breadcrumb-item"><a href="./foundations-syllabus.html">Syllabi</a></li><li class="breadcrumb-item"><a href="./customization.html">Customization Guide</a></li></ol></nav>
<div class="quarto-title">
<h1 class="title">Customization Guide</h1>
<p class="subtitle lead">Adapt the curriculum to your programs format and emphasis</p>
</div>
<div class="quarto-title-meta">
</div>
</header>
<p>The 16-week syllabi are designed as complete, ready-to-use courses. But not every program has 16 weeks, and not every audience has the same emphasis. This guide shows how to adapt.</p>
<hr>
<section id="week-quarter-version-foundations" class="level2">
<h2 class="anchored" data-anchor-id="week-quarter-version-foundations">10-Week Quarter Version (Foundations)</h2>
<p>For quarter systems, compress the 16-week Foundations syllabus:</p>
<table class="caption-top table">
<colgroup>
<col style="width: 33%">
<col style="width: 33%">
<col style="width: 33%">
</colgroup>
<thead>
<tr class="header">
<th style="text-align: left;">Quarter Week</th>
<th style="text-align: left;">Content (from 16-week)</th>
<th style="text-align: left;">What Changes</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td style="text-align: left;">1</td>
<td style="text-align: left;">Weeks 12</td>
<td style="text-align: left;">Introduction + ML Systems combined; Module 01 only</td>
</tr>
<tr class="even">
<td style="text-align: left;">2</td>
<td style="text-align: left;">Week 3</td>
<td style="text-align: left;">ML Workflow; Module 02 starts</td>
</tr>
<tr class="odd">
<td style="text-align: left;">3</td>
<td style="text-align: left;">Weeks 45</td>
<td style="text-align: left;">Data Engineering + Neural Computation; Module 02 + 03</td>
</tr>
<tr class="even">
<td style="text-align: left;">4</td>
<td style="text-align: left;">Week 6</td>
<td style="text-align: left;">NN Architectures; Module 04</td>
</tr>
<tr class="odd">
<td style="text-align: left;">5</td>
<td style="text-align: left;">Weeks 78</td>
<td style="text-align: left;">Frameworks + Training; Module 05 + 06</td>
</tr>
<tr class="even">
<td style="text-align: left;">6</td>
<td style="text-align: left;">Week 9</td>
<td style="text-align: left;">Data Selection; Module 07</td>
</tr>
<tr class="odd">
<td style="text-align: left;">7</td>
<td style="text-align: left;">Week 10</td>
<td style="text-align: left;">Model Compression + Lab 09 (Quantization); Module 08</td>
</tr>
<tr class="even">
<td style="text-align: left;">8</td>
<td style="text-align: left;">Week 11</td>
<td style="text-align: left;">HW Acceleration + Lab 10 (Roofline)</td>
</tr>
<tr class="odd">
<td style="text-align: left;">9</td>
<td style="text-align: left;">Weeks 1314</td>
<td style="text-align: left;">Serving + Operations; Labs 1213</td>
</tr>
<tr class="even">
<td style="text-align: left;">10</td>
<td style="text-align: left;">Week 16</td>
<td style="text-align: left;">Capstone (AI Olympics, reduced scope)</td>
</tr>
</tbody>
</table>
<p><strong>What gets dropped:</strong> Benchmarking (Week 12), Responsible Engineering (Week 15) — assign as optional reading. Integrate key responsibility points into the capstone rubric.</p>
<p><strong>What gets compressed:</strong> TinyTorch Modules 01+02 doubled up in Weeks 1+3. Labs 00, 01, and 03 become optional.</p>
<div class="callout callout-style-default callout-caution callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
<span class="screen-reader-only">Caution</span>Quarter Tradeoff
</div>
</div>
<div class="callout-body-container callout-body">
<p>The 10-week version sacrifices breathing room. Consider reducing Decision Logs to 100 words and assigning only 2 Design Challenges instead of 4.</p>
</div>
</div>
<hr>
</section>
<section id="day-workshop-version" class="level2">
<h2 class="anchored" data-anchor-id="day-workshop-version">3-Day Workshop Version</h2>
<p>For short workshops with experienced practitioners:</p>
<table class="caption-top table">
<colgroup>
<col style="width: 33%">
<col style="width: 33%">
<col style="width: 33%">
</colgroup>
<thead>
<tr class="header">
<th style="text-align: left;">Day</th>
<th style="text-align: left;">Focus</th>
<th style="text-align: left;">Materials</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td style="text-align: left;"><strong>Day 1</strong></td>
<td style="text-align: left;">The Physics of Inference</td>
<td style="text-align: left;">Iron Law introduction + Labs 01, 05, 09 (Magnitude Gap, Architecture Tradeoffs, Quantization)</td>
</tr>
<tr class="even">
<td style="text-align: left;"><strong>Day 2</strong></td>
<td style="text-align: left;">The Optimization Frontier</td>
<td style="text-align: left;">Labs 10, 11 (Roofline, Benchmarking) + TinyTorch Module 08 speed-run</td>
</tr>
<tr class="odd">
<td style="text-align: left;"><strong>Day 3</strong></td>
<td style="text-align: left;">Production Deployment</td>
<td style="text-align: left;">Labs 12, 13 (Tail Latency, Drift Detection) + mini Design Challenge</td>
</tr>
</tbody>
</table>
<p>Focus exclusively on the <strong>Iron Law</strong> and <strong>Interactive Labs</strong>. Skip TinyTorch (except as demo). No formal assessment — use labs for hands-on discovery.</p>
<hr>
</section>
<section id="for-software-engineers" class="level2">
<h2 class="anchored" data-anchor-id="for-software-engineers">For Software Engineers</h2>
<p>If your audience is experienced developers, lean into <strong>TinyTorch</strong>:</p>
<table class="caption-top table">
<colgroup>
<col style="width: 33%">
<col style="width: 33%">
<col style="width: 33%">
</colgroup>
<thead>
<tr class="header">
<th style="text-align: left;">Weeks</th>
<th style="text-align: left;">Focus</th>
<th style="text-align: left;">Modules</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td style="text-align: left;">14</td>
<td style="text-align: left;">Building the Autograd Engine</td>
<td style="text-align: left;">Modules 0106 (Tensor → Autograd)</td>
</tr>
<tr class="even">
<td style="text-align: left;">58</td>
<td style="text-align: left;">From CNNs to Transformers</td>
<td style="text-align: left;">Modules 0913 (Conv → Transformer)</td>
</tr>
<tr class="odd">
<td style="text-align: left;">912</td>
<td style="text-align: left;">Production Optimization</td>
<td style="text-align: left;">Modules 1419 (Profiling → Benchmarking)</td>
</tr>
<tr class="even">
<td style="text-align: left;">1316</td>
<td style="text-align: left;">Capstone: Torch Olympics</td>
<td style="text-align: left;">Module 20 + competition</td>
</tr>
</tbody>
</table>
<p>Use textbook chapters as background reading, not lecture material. Labs serve as validation checkpoints, not primary pedagogy.</p>
<hr>
</section>
<section id="for-computer-architects" class="level2">
<h2 class="anchored" data-anchor-id="for-computer-architects">For Computer Architects</h2>
<p>Shift the focus toward <strong>Hardware Acceleration</strong> and <strong>mlsysim</strong>:</p>
<ul>
<li>Use the hardware Zoo in <code>mlsysim</code> to compare architectures (H100, B200, edge devices)</li>
<li>Spend 2 weeks on the Roofline model — have students plot multiple workloads</li>
<li>Extend model compression to 2 weeks (quantization + pruning as hardware-aware optimizations)</li>
<li>Use hardware kits extensively — make them mandatory, not optional</li>
<li>Reduce TinyTorch to Modules 0103 (enough to understand what frameworks do)</li>
</ul>
<hr>
</section>
<section id="graduate-seminar-version" class="level2">
<h2 class="anchored" data-anchor-id="graduate-seminar-version">Graduate Seminar Version</h2>
<p>For a graduate-level seminar (assumes strong systems background):</p>
<table class="caption-top table">
<colgroup>
<col style="width: 25%">
<col style="width: 25%">
<col style="width: 25%">
<col style="width: 25%">
</colgroup>
<thead>
<tr class="header">
<th style="text-align: left;">Week</th>
<th style="text-align: left;">Topic</th>
<th style="text-align: left;">Textbook</th>
<th style="text-align: left;">Paper</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td style="text-align: left;">1</td>
<td style="text-align: left;">The Iron Law</td>
<td style="text-align: left;">Vol I: Intro + ML Systems</td>
<td style="text-align: left;">Hennessy &amp; Patterson, “A New Golden Age” (2019)</td>
</tr>
<tr class="even">
<td style="text-align: left;">2</td>
<td style="text-align: left;">Memory Hierarchy</td>
<td style="text-align: left;">Vol I: HW Acceleration</td>
<td style="text-align: left;">Williams et al., “Roofline” (2009)</td>
</tr>
<tr class="odd">
<td style="text-align: left;">3</td>
<td style="text-align: left;">Quantization</td>
<td style="text-align: left;">Vol I: Model Compression</td>
<td style="text-align: left;">Dettmers et al., “LLM.int8()” (2022)</td>
</tr>
<tr class="even">
<td style="text-align: left;">4</td>
<td style="text-align: left;">Serving Systems</td>
<td style="text-align: left;">Vol I: Model Serving</td>
<td style="text-align: left;">Yu et al., “Orca” (2022)</td>
</tr>
<tr class="odd">
<td style="text-align: left;">5</td>
<td style="text-align: left;">Distributed Training</td>
<td style="text-align: left;">Vol II: Distributed Training</td>
<td style="text-align: left;">Shoeybi et al., “Megatron-LM” (2020)</td>
</tr>
<tr class="even">
<td style="text-align: left;">6</td>
<td style="text-align: left;">3D Parallelism</td>
<td style="text-align: left;">Vol II: Distributed Training</td>
<td style="text-align: left;">Narayanan et al., “Efficient Large-Scale Training” (2021)</td>
</tr>
<tr class="odd">
<td style="text-align: left;">7</td>
<td style="text-align: left;">Collective Comms</td>
<td style="text-align: left;">Vol II: Collective Comm.</td>
<td style="text-align: left;">Patarasuk &amp; Yuan, “Bandwidth Optimal All-Reduce” (2009)</td>
</tr>
<tr class="even">
<td style="text-align: left;">8</td>
<td style="text-align: left;">Fault Tolerance</td>
<td style="text-align: left;">Vol II: Fault Tolerance</td>
<td style="text-align: left;">Jeon et al., “Large-Scale GPU Clusters” (2019)</td>
</tr>
<tr class="odd">
<td style="text-align: left;">9</td>
<td style="text-align: left;">Inference at Scale</td>
<td style="text-align: left;">Vol II: Inference</td>
<td style="text-align: left;">Kwon et al., “vLLM/PagedAttention” (2023)</td>
</tr>
<tr class="even">
<td style="text-align: left;">10</td>
<td style="text-align: left;">KV-Cache Optimization</td>
<td style="text-align: left;">Vol II: Inference</td>
<td style="text-align: left;">Ainslie et al., “GQA” (2023)</td>
</tr>
<tr class="odd">
<td style="text-align: left;">11</td>
<td style="text-align: left;">Edge Intelligence</td>
<td style="text-align: left;">Vol II: Edge Intelligence</td>
<td style="text-align: left;">Lin et al., “MCUNet” (2020)</td>
</tr>
<tr class="even">
<td style="text-align: left;">12</td>
<td style="text-align: left;">Fleet Operations</td>
<td style="text-align: left;">Vol II: Ops at Scale</td>
<td style="text-align: left;">Zhao et al., “ATC24 Fleet Analysis” (2024)</td>
</tr>
<tr class="odd">
<td style="text-align: left;">13</td>
<td style="text-align: left;">Sustainability</td>
<td style="text-align: left;">Vol II: Sustainable AI</td>
<td style="text-align: left;">Patterson et al., “Carbon Emissions and AI” (2021)</td>
</tr>
<tr class="even">
<td style="text-align: left;">14</td>
<td style="text-align: left;">Student presentations</td>
<td style="text-align: left;"></td>
<td style="text-align: left;"></td>
</tr>
</tbody>
</table>
<p><strong>Assessment:</strong> 40% paper presentations, 30% lab Decision Logs (selected labs only), 30% semester project (original system design or benchmarking study).</p>
<hr>
</section>
<section id="mixing-and-matching-components" class="level2">
<h2 class="anchored" data-anchor-id="mixing-and-matching-components">Mixing and Matching Components</h2>
<p>Each component is independently adoptable:</p>
<table class="caption-top table">
<colgroup>
<col style="width: 33%">
<col style="width: 33%">
<col style="width: 33%">
</colgroup>
<thead>
<tr class="header">
<th style="text-align: left;">Pattern</th>
<th style="text-align: left;">Components Used</th>
<th style="text-align: left;">Typical Context</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td style="text-align: left;"><strong>Textbook Only</strong></td>
<td style="text-align: left;">Vol I or II as required reading</td>
<td style="text-align: left;">Supplement for existing ML course</td>
</tr>
<tr class="even">
<td style="text-align: left;"><strong>Textbook + Labs</strong></td>
<td style="text-align: left;">Readings + interactive labs</td>
<td style="text-align: left;">Active learning without coding assignments</td>
</tr>
<tr class="odd">
<td style="text-align: left;"><strong>TinyTorch Only</strong></td>
<td style="text-align: left;">20 modules as programming assignments</td>
<td style="text-align: left;">Systems programming course</td>
</tr>
<tr class="even">
<td style="text-align: left;"><strong>Labs Only</strong></td>
<td style="text-align: left;">Interactive labs as in-class activities</td>
<td style="text-align: left;">Active learning supplement for any course</td>
</tr>
<tr class="odd">
<td style="text-align: left;"><strong>Hardware Kits Only</strong></td>
<td style="text-align: left;">Edge deployment labs</td>
<td style="text-align: left;">Embedded systems course</td>
</tr>
<tr class="even">
<td style="text-align: left;"><strong>Full Stack</strong></td>
<td style="text-align: left;">All components integrated</td>
<td style="text-align: left;">Dedicated ML Systems course</td>
</tr>
</tbody>
</table>
<div class="callout callout-style-default callout-tip callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
<span class="screen-reader-only">Tip</span>Start Small, Layer Up
</div>
</div>
<div class="callout-body-container callout-body">
<p>If adopting for the first time, start with <strong>Textbook + Labs</strong> for one semester. Add TinyTorch the second time you teach it. Add hardware kits the third. Each component is valuable on its own.</p>
</div>
</div>
</section>
<a onclick="window.scrollTo(0, 0); return false;" role="button" id="quarto-back-to-top"><i class="bi bi-arrow-up"></i> Back to top</a></main> <!-- /main -->
<script id="quarto-html-after-body" type="application/javascript">
window.document.addEventListener("DOMContentLoaded", function (event) {
// Ensure there is a toggle, if there isn't float one in the top right
if (window.document.querySelector('.quarto-color-scheme-toggle') === null) {
const a = window.document.createElement('a');
a.classList.add('top-right');
a.classList.add('quarto-color-scheme-toggle');
a.href = "";
a.onclick = function() { try { window.quartoToggleColorScheme(); } catch {} return false; };
const i = window.document.createElement("i");
i.classList.add('bi');
a.appendChild(i);
window.document.body.appendChild(a);
}
setColorSchemeToggle(hasAlternateSentinel())
const icon = "";
const anchorJS = new window.AnchorJS();
anchorJS.options = {
placement: 'right',
icon: icon
};
anchorJS.add('.anchored');
const isCodeAnnotation = (el) => {
for (const clz of el.classList) {
if (clz.startsWith('code-annotation-')) {
return true;
}
}
return false;
}
const onCopySuccess = function(e) {
// button target
const button = e.trigger;
// don't keep focus
button.blur();
// flash "checked"
button.classList.add('code-copy-button-checked');
var currentTitle = button.getAttribute("title");
button.setAttribute("title", "Copied!");
let tooltip;
if (window.bootstrap) {
button.setAttribute("data-bs-toggle", "tooltip");
button.setAttribute("data-bs-placement", "left");
button.setAttribute("data-bs-title", "Copied!");
tooltip = new bootstrap.Tooltip(button,
{ trigger: "manual",
customClass: "code-copy-button-tooltip",
offset: [0, -8]});
tooltip.show();
}
setTimeout(function() {
if (tooltip) {
tooltip.hide();
button.removeAttribute("data-bs-title");
button.removeAttribute("data-bs-toggle");
button.removeAttribute("data-bs-placement");
}
button.setAttribute("title", currentTitle);
button.classList.remove('code-copy-button-checked');
}, 1000);
// clear code selection
e.clearSelection();
}
const getTextToCopy = function(trigger) {
const outerScaffold = trigger.parentElement.cloneNode(true);
const codeEl = outerScaffold.querySelector('code');
for (const childEl of codeEl.children) {
if (isCodeAnnotation(childEl)) {
childEl.remove();
}
}
return codeEl.innerText;
}
const clipboard = new window.ClipboardJS('.code-copy-button:not([data-in-quarto-modal])', {
text: getTextToCopy
});
clipboard.on('success', onCopySuccess);
if (window.document.getElementById('quarto-embedded-source-code-modal')) {
const clipboardModal = new window.ClipboardJS('.code-copy-button[data-in-quarto-modal]', {
text: getTextToCopy,
container: window.document.getElementById('quarto-embedded-source-code-modal')
});
clipboardModal.on('success', onCopySuccess);
}
var localhostRegex = new RegExp(/^(?:http|https):\/\/localhost\:?[0-9]*\//);
var mailtoRegex = new RegExp(/^mailto:/);
var filterRegex = new RegExp("https:\/\/mlsysbook\.ai\/instructors\/");
var isInternal = (href) => {
return filterRegex.test(href) || localhostRegex.test(href) || mailtoRegex.test(href);
}
// Inspect non-navigation links and adorn them if external
var links = window.document.querySelectorAll('a[href]:not(.nav-link):not(.navbar-brand):not(.toc-action):not(.sidebar-link):not(.sidebar-item-toggle):not(.pagination-link):not(.no-external):not([aria-hidden]):not(.dropdown-item):not(.quarto-navigation-tool):not(.about-link)');
for (var i=0; i<links.length; i++) {
const link = links[i];
if (!isInternal(link.href)) {
// undo the damage that might have been done by quarto-nav.js in the case of
// links that we want to consider external
if (link.dataset.originalHref !== undefined) {
link.href = link.dataset.originalHref;
}
}
}
function tippyHover(el, contentFn, onTriggerFn, onUntriggerFn) {
const config = {
allowHTML: true,
maxWidth: 500,
delay: 100,
arrow: false,
appendTo: function(el) {
return el.parentElement;
},
interactive: true,
interactiveBorder: 10,
theme: 'quarto',
placement: 'bottom-start',
};
if (contentFn) {
config.content = contentFn;
}
if (onTriggerFn) {
config.onTrigger = onTriggerFn;
}
if (onUntriggerFn) {
config.onUntrigger = onUntriggerFn;
}
window.tippy(el, config);
}
const noterefs = window.document.querySelectorAll('a[role="doc-noteref"]');
for (var i=0; i<noterefs.length; i++) {
const ref = noterefs[i];
tippyHover(ref, function() {
// use id or data attribute instead here
let href = ref.getAttribute('data-footnote-href') || ref.getAttribute('href');
try { href = new URL(href).hash; } catch {}
const id = href.replace(/^#\/?/, "");
const note = window.document.getElementById(id);
if (note) {
return note.innerHTML;
} else {
return "";
}
});
}
const xrefs = window.document.querySelectorAll('a.quarto-xref');
const processXRef = (id, note) => {
// Strip column container classes
const stripColumnClz = (el) => {
el.classList.remove("page-full", "page-columns");
if (el.children) {
for (const child of el.children) {
stripColumnClz(child);
}
}
}
stripColumnClz(note)
if (id === null || id.startsWith('sec-')) {
// Special case sections, only their first couple elements
const container = document.createElement("div");
if (note.children && note.children.length > 2) {
container.appendChild(note.children[0].cloneNode(true));
for (let i = 1; i < note.children.length; i++) {
const child = note.children[i];
if (child.tagName === "P" && child.innerText === "") {
continue;
} else {
container.appendChild(child.cloneNode(true));
break;
}
}
if (window.Quarto?.typesetMath) {
window.Quarto.typesetMath(container);
}
return container.innerHTML
} else {
if (window.Quarto?.typesetMath) {
window.Quarto.typesetMath(note);
}
return note.innerHTML;
}
} else {
// Remove any anchor links if they are present
const anchorLink = note.querySelector('a.anchorjs-link');
if (anchorLink) {
anchorLink.remove();
}
if (window.Quarto?.typesetMath) {
window.Quarto.typesetMath(note);
}
if (note.classList.contains("callout")) {
return note.outerHTML;
} else {
return note.innerHTML;
}
}
}
for (var i=0; i<xrefs.length; i++) {
const xref = xrefs[i];
tippyHover(xref, undefined, function(instance) {
instance.disable();
let url = xref.getAttribute('href');
let hash = undefined;
if (url.startsWith('#')) {
hash = url;
} else {
try { hash = new URL(url).hash; } catch {}
}
if (hash) {
const id = hash.replace(/^#\/?/, "");
const note = window.document.getElementById(id);
if (note !== null) {
try {
const html = processXRef(id, note.cloneNode(true));
instance.setContent(html);
} finally {
instance.enable();
instance.show();
}
} else {
// See if we can fetch this
fetch(url.split('#')[0])
.then(res => res.text())
.then(html => {
const parser = new DOMParser();
const htmlDoc = parser.parseFromString(html, "text/html");
const note = htmlDoc.getElementById(id);
if (note !== null) {
const html = processXRef(id, note);
instance.setContent(html);
}
}).finally(() => {
instance.enable();
instance.show();
});
}
} else {
// See if we can fetch a full url (with no hash to target)
// This is a special case and we should probably do some content thinning / targeting
fetch(url)
.then(res => res.text())
.then(html => {
const parser = new DOMParser();
const htmlDoc = parser.parseFromString(html, "text/html");
const note = htmlDoc.querySelector('main.content');
if (note !== null) {
// This should only happen for chapter cross references
// (since there is no id in the URL)
// remove the first header
if (note.children.length > 0 && note.children[0].tagName === "HEADER") {
note.children[0].remove();
}
const html = processXRef(null, note);
instance.setContent(html);
}
}).finally(() => {
instance.enable();
instance.show();
});
}
}, function(instance) {
});
}
let selectedAnnoteEl;
const selectorForAnnotation = ( cell, annotation) => {
let cellAttr = 'data-code-cell="' + cell + '"';
let lineAttr = 'data-code-annotation="' + annotation + '"';
const selector = 'span[' + cellAttr + '][' + lineAttr + ']';
return selector;
}
const selectCodeLines = (annoteEl) => {
const doc = window.document;
const targetCell = annoteEl.getAttribute("data-target-cell");
const targetAnnotation = annoteEl.getAttribute("data-target-annotation");
const annoteSpan = window.document.querySelector(selectorForAnnotation(targetCell, targetAnnotation));
const lines = annoteSpan.getAttribute("data-code-lines").split(",");
const lineIds = lines.map((line) => {
return targetCell + "-" + line;
})
let top = null;
let height = null;
let parent = null;
if (lineIds.length > 0) {
//compute the position of the single el (top and bottom and make a div)
const el = window.document.getElementById(lineIds[0]);
top = el.offsetTop;
height = el.offsetHeight;
parent = el.parentElement.parentElement;
if (lineIds.length > 1) {
const lastEl = window.document.getElementById(lineIds[lineIds.length - 1]);
const bottom = lastEl.offsetTop + lastEl.offsetHeight;
height = bottom - top;
}
if (top !== null && height !== null && parent !== null) {
// cook up a div (if necessary) and position it
let div = window.document.getElementById("code-annotation-line-highlight");
if (div === null) {
div = window.document.createElement("div");
div.setAttribute("id", "code-annotation-line-highlight");
div.style.position = 'absolute';
parent.appendChild(div);
}
div.style.top = top - 2 + "px";
div.style.height = height + 4 + "px";
div.style.left = 0;
let gutterDiv = window.document.getElementById("code-annotation-line-highlight-gutter");
if (gutterDiv === null) {
gutterDiv = window.document.createElement("div");
gutterDiv.setAttribute("id", "code-annotation-line-highlight-gutter");
gutterDiv.style.position = 'absolute';
const codeCell = window.document.getElementById(targetCell);
const gutter = codeCell.querySelector('.code-annotation-gutter');
gutter.appendChild(gutterDiv);
}
gutterDiv.style.top = top - 2 + "px";
gutterDiv.style.height = height + 4 + "px";
}
selectedAnnoteEl = annoteEl;
}
};
const unselectCodeLines = () => {
const elementsIds = ["code-annotation-line-highlight", "code-annotation-line-highlight-gutter"];
elementsIds.forEach((elId) => {
const div = window.document.getElementById(elId);
if (div) {
div.remove();
}
});
selectedAnnoteEl = undefined;
};
// Handle positioning of the toggle
window.addEventListener(
"resize",
throttle(() => {
elRect = undefined;
if (selectedAnnoteEl) {
selectCodeLines(selectedAnnoteEl);
}
}, 10)
);
function throttle(fn, ms) {
let throttle = false;
let timer;
return (...args) => {
if(!throttle) { // first call gets through
fn.apply(this, args);
throttle = true;
} else { // all the others get throttled
if(timer) clearTimeout(timer); // cancel #2
timer = setTimeout(() => {
fn.apply(this, args);
timer = throttle = false;
}, ms);
}
};
}
// Attach click handler to the DT
const annoteDls = window.document.querySelectorAll('dt[data-target-cell]');
for (const annoteDlNode of annoteDls) {
annoteDlNode.addEventListener('click', (event) => {
const clickedEl = event.target;
if (clickedEl !== selectedAnnoteEl) {
unselectCodeLines();
const activeEl = window.document.querySelector('dt[data-target-cell].code-annotation-active');
if (activeEl) {
activeEl.classList.remove('code-annotation-active');
}
selectCodeLines(clickedEl);
clickedEl.classList.add('code-annotation-active');
} else {
// Unselect the line
unselectCodeLines();
clickedEl.classList.remove('code-annotation-active');
}
});
}
const findCites = (el) => {
const parentEl = el.parentElement;
if (parentEl) {
const cites = parentEl.dataset.cites;
if (cites) {
return {
el,
cites: cites.split(' ')
};
} else {
return findCites(el.parentElement)
}
} else {
return undefined;
}
};
var bibliorefs = window.document.querySelectorAll('a[role="doc-biblioref"]');
for (var i=0; i<bibliorefs.length; i++) {
const ref = bibliorefs[i];
const citeInfo = findCites(ref);
if (citeInfo) {
tippyHover(citeInfo.el, function() {
var popup = window.document.createElement('div');
citeInfo.cites.forEach(function(cite) {
var citeDiv = window.document.createElement('div');
citeDiv.classList.add('hanging-indent');
citeDiv.classList.add('csl-entry');
var biblioDiv = window.document.getElementById('ref-' + cite);
if (biblioDiv) {
citeDiv.innerHTML = biblioDiv.innerHTML;
}
popup.appendChild(citeDiv);
});
return popup.innerHTML;
});
}
}
});
</script>
<nav class="page-navigation">
<div class="nav-page nav-page-previous">
<a href="./tinyml-syllabus.html" class="pagination-link" aria-label="Tiny Machine Learning (TinyML)">
<i class="bi bi-arrow-left-short"></i> <span class="nav-page-text">Tiny Machine Learning (TinyML)</span>
</a>
</div>
<div class="nav-page nav-page-next">
<a href="./pedagogy.html" class="pagination-link" aria-label="Pedagogy">
<span class="nav-page-text">Pedagogy</span> <i class="bi bi-arrow-right-short"></i>
</a>
</div>
</nav>
</div> <!-- /content -->
<footer class="footer">
<div class="nav-footer">
<div class="nav-footer-left">
<p>© 2024-2026 Harvard University. Licensed under <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/">CC-BY-NC-SA 4.0</a></p>
</div>
<div class="nav-footer-center">
<p>Part of the <a href="https://mlsysbook.ai">Machine Learning Systems</a> ecosystem</p>
</div>
<div class="nav-footer-right">
<ul class="footer-items list-unstyled">
<li class="nav-item">
<span class="release-pill" data-release-pill=""></span>
</li>
<li class="nav-item compact">
<a class="nav-link" href="https://github.com/harvard-edge/cs249r_book">
<i class="bi bi-github" role="img" aria-label="View source on GitHub">
</i>
</a>
</li>
<li class="nav-item compact">
<a class="nav-link" href="https://github.com/harvard-edge/cs249r_book">
<i class="bi bi-star" role="img" aria-label="Star this repository">
</i>
</a>
</li>
</ul>
</div>
</div>
</footer>
<!--
MLSysBook release-pill — shared footer version chip for Quarto + static sites.
Renders a small "v0.1.0 · Apr 26, 2026" chip from a deployed
release-manifest.json. Best-effort chrome: silent on any fetch failure
(manifest missing, offline, CORS). Use at any site that emits a manifest
via scripts/version/release.py emit-manifest.
── Standardized placement (all MLSysBook sites) ──
The chip lives in the page-footer, right slot, so it reads as an
intentional part of the footer bar (not dangling below the page). Two
pieces per site:
1. This file loads the style + populator script site-wide:
format:
html:
include-in-header:
- text: |
<meta name="release-manifest" content="/<project-base>/release-manifest.json">
include-after-body:
- file: ../shared/release/release-pill.html
2. Each page-footer places the chip span where it should render:
website:
page-footer:
right:
- text: |
<span class="release-pill" data-release-pill></span>
- icon: github
href: ...
The meta tag tells the script where to find the manifest. The script
populates every span carrying data-release-pill, so the chip can sit in
the footer (canonical), an about page, or anywhere else.
── Manifest contract ──
The manifest is the JSON written by scripts/version/release.py
emit-manifest. Required keys: releaseId, releaseHash. Optional but
recommended: buildDate, project. Anything else is ignored.
── Why runtime fetch instead of build-time templating ──
Build-time templating would be deterministic but requires per-project
Lua/template machinery. Runtime fetch is project-agnostic and works
identically across Quarto, Next.js, and plain HTML. The one fetch per
page-load is cheap and the chip degrades to nothing on failure.
-->
<style>
/* Fixed muted gray on every site — the chip must look identical regardless of
each Quarto theme's accent colors (do NOT inherit --bs-secondary / link color). */
.release-pill {
display: inline-flex;
align-items: baseline;
gap: 0.25em;
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, monospace;
font-size: 0.75rem;
line-height: 1;
color: var(--release-pill-color, #6c757d);
}
.release-pill a,
.release-pill a:visited,
.release-pill-version,
.release-pill-date {
color: var(--release-pill-color, #6c757d);
text-decoration: none;
}
.release-pill a:hover {
text-decoration: underline;
text-underline-offset: 2px;
}
.release-pill-date {
opacity: 0.75;
}
</style>
<script>
(function () {
var pills = document.querySelectorAll('[data-release-pill]');
if (!pills.length) return;
// Manifest URL resolution order:
// 1. data-manifest attribute on the pill (per-page override)
// 2. <meta name="release-manifest" content="..."> in <head>
// 3. './release-manifest.json' relative to current page (fallback)
var meta = document.querySelector('meta[name="release-manifest"]');
var metaPath = meta && meta.getAttribute('content');
fetch(metaPath || './release-manifest.json', { cache: 'no-store' })
.then(function (res) { return res.ok ? res.json() : null; })
.then(function (m) {
if (!m || !m.releaseId || !m.releaseHash) return;
var date = m.buildDate
? new Date(m.buildDate).toLocaleDateString('en-US',
{ year: 'numeric', month: 'short', day: 'numeric' })
: '';
var shortHash = String(m.releaseHash).slice(0, 7);
var title =
(m.project ? m.project + ' ' : '') +
'v' + m.releaseId +
(date ? ' · built ' + date : '') +
' · hash ' + shortHash;
pills.forEach(function (el) {
var rawLink = el.getAttribute('data-link') || '#release';
// XSS hardening: allow only anchor, relative, or http(s) links; reject javascript:/data: URLs.
var linkHref = /^(?:#|\/|\.{1,2}\/|https?:\/\/)/i.test(rawLink) ? rawLink : '#release';
var a = document.createElement('a');
a.href = linkHref;
a.title = title;
a.setAttribute('aria-label',
'Release v' + m.releaseId + (date ? ', built ' + date : ''));
var v = document.createElement('span');
v.className = 'release-pill-version';
v.textContent = 'v' + m.releaseId;
a.appendChild(v);
if (date) {
var d = document.createElement('span');
d.className = 'release-pill-date';
d.textContent = ' · ' + date;
a.appendChild(d);
}
el.innerHTML = '';
el.appendChild(a);
});
})
.catch(function () { /* silent: best-effort chrome */ });
})();
</script>
</body></html>