Initial website deployment
All checks were successful
Deploy Website / build-and-deploy (push) Successful in 1m44s
7
.dockerignore
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
.git
|
||||||
|
.github
|
||||||
|
.gitea
|
||||||
|
.hugo_build.lock
|
||||||
|
.DS_Store
|
||||||
|
public
|
||||||
|
resources/_gen
|
||||||
31
.gitea/workflows/deploy.yaml
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
name: Deploy Website
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
- master
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-and-deploy:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Check out repository code
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Build Docker image
|
||||||
|
run: |
|
||||||
|
docker build --pull -t akkolli-website:latest .
|
||||||
|
|
||||||
|
- name: Stop existing container
|
||||||
|
run: |
|
||||||
|
docker stop website-container || true
|
||||||
|
docker rm website-container || true
|
||||||
|
|
||||||
|
- name: Run new container
|
||||||
|
run: |
|
||||||
|
docker run -d \
|
||||||
|
--name website-container \
|
||||||
|
--restart unless-stopped \
|
||||||
|
-p 8080:80 \
|
||||||
|
akkolli-website:latest
|
||||||
4
.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
.hugo_build.lock
|
||||||
|
resources/_gen/
|
||||||
|
public/
|
||||||
|
.DS_Store
|
||||||
10
Dockerfile
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
FROM ghcr.io/gohugoio/hugo:v0.163.3 AS build
|
||||||
|
|
||||||
|
WORKDIR /src
|
||||||
|
COPY . .
|
||||||
|
RUN hugo --minify --cleanDestinationDir
|
||||||
|
|
||||||
|
FROM nginx:1.27-alpine
|
||||||
|
|
||||||
|
COPY --from=build /src/public /usr/share/nginx/html
|
||||||
|
EXPOSE 80
|
||||||
12
archetypes/apps.md
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
---
|
||||||
|
title: "{{ replace .File.ContentBaseName "-" " " | title }}"
|
||||||
|
date: "{{ .Date }}"
|
||||||
|
draft: true
|
||||||
|
description: ""
|
||||||
|
tags: []
|
||||||
|
status: ""
|
||||||
|
weight: 10
|
||||||
|
links: []
|
||||||
|
toc: true
|
||||||
|
math: false
|
||||||
|
---
|
||||||
5
archetypes/default.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
date: '{{ .Date }}'
|
||||||
|
draft: true
|
||||||
|
title: '{{ replace .File.ContentBaseName "-" " " | title }}'
|
||||||
|
---
|
||||||
9
archetypes/posts.md
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
---
|
||||||
|
title: "{{ replace .File.ContentBaseName "-" " " | title }}"
|
||||||
|
date: "{{ .Date }}"
|
||||||
|
draft: true
|
||||||
|
description: ""
|
||||||
|
tags: []
|
||||||
|
toc: true
|
||||||
|
math: false
|
||||||
|
---
|
||||||
12
archetypes/projects.md
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
---
|
||||||
|
title: "{{ replace .File.ContentBaseName "-" " " | title }}"
|
||||||
|
date: "{{ .Date }}"
|
||||||
|
draft: true
|
||||||
|
description: ""
|
||||||
|
tags: []
|
||||||
|
status: ""
|
||||||
|
weight: 10
|
||||||
|
links: []
|
||||||
|
toc: true
|
||||||
|
math: false
|
||||||
|
---
|
||||||
5
content/_index.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
title: "Akshay Kolli"
|
||||||
|
---
|
||||||
|
|
||||||
|
CS PhD at UMass Lowell, working on World Models & RL
|
||||||
6
content/apps/_index.md
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
title: "Apps"
|
||||||
|
build:
|
||||||
|
render: never
|
||||||
|
list: always
|
||||||
|
---
|
||||||
37
content/apps/clipbored.md
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
---
|
||||||
|
title: "ClipBored"
|
||||||
|
date: 2026-07-01
|
||||||
|
draft: false
|
||||||
|
description: "A local-only macOS clipboard manager with a keyboard-first bottom panel."
|
||||||
|
tags: ["Swift", "AppKit", "macOS", "Clipboard", "SQLite"]
|
||||||
|
status: "open beta"
|
||||||
|
weight: 20
|
||||||
|
links:
|
||||||
|
- label: "GitHub"
|
||||||
|
url: "https://github.com/akkolli/clipbored"
|
||||||
|
toc: true
|
||||||
|
math: false
|
||||||
|
---
|
||||||
|
|
||||||
|
ClipBored is a small native macOS clipboard manager. It captures local clipboard history and opens a keyboard-first responsive bottom panel for search, sorting, copy, paste, pinning, deletion, and organization.
|
||||||
|
|
||||||
|
{{< figure src="/images/apps/clipbored/panel.png" alt="ClipBored clipboard panel snapshot." />}}
|
||||||
|
|
||||||
|
## What It Does
|
||||||
|
|
||||||
|
- Runs as a dockless menu-bar utility by default.
|
||||||
|
- Opens with a global shortcut.
|
||||||
|
- Captures text, links, images, media, PDFs, files, and rich text.
|
||||||
|
- Supports search, sorting, pinning, collections, copy, paste, preview, and deletion.
|
||||||
|
- Keeps storage local and dependency-light.
|
||||||
|
- Uses SQLite persistence with bounded history and encrypted app-managed payloads.
|
||||||
|
|
||||||
|
## Technical Shape
|
||||||
|
|
||||||
|
ClipBored is built with Swift Package Manager, AppKit, Carbon hotkeys, SQLite, and system frameworks. It avoids network APIs and telemetry. Clipboard history is stored locally under Application Support, with privacy controls for ignored apps, content kinds, and sensitive-content exclusion.
|
||||||
|
|
||||||
|
The project is designed as a small native utility rather than a heavy Electron-style clipboard database.
|
||||||
|
|
||||||
|
## Status
|
||||||
|
|
||||||
|
ClipBored is in open beta. Current work focuses on interaction polish, keyboard navigation, persistence, privacy controls, and reliable local packaging.
|
||||||
35
content/apps/feedme.md
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
---
|
||||||
|
title: "FeedMe"
|
||||||
|
date: 2026-07-01
|
||||||
|
draft: false
|
||||||
|
description: "A local-first native SwiftUI feed reader for macOS and iOS."
|
||||||
|
tags: ["Swift", "SwiftUI", "RSS", "macOS", "iOS"]
|
||||||
|
status: "open beta"
|
||||||
|
weight: 10
|
||||||
|
links: []
|
||||||
|
toc: true
|
||||||
|
math: false
|
||||||
|
---
|
||||||
|
|
||||||
|
FeedMe is a small native SwiftUI feed reader for macOS and iOS. It is local-first, account-free, and built around a shared Swift package for feed parsing, OPML import/export, refresh orchestration, and SQLite persistence.
|
||||||
|
|
||||||
|
{{< figure src="/images/apps/feedme/demo.png" alt="FeedMe running with a local demo library on iOS." />}}
|
||||||
|
|
||||||
|
## What It Does
|
||||||
|
|
||||||
|
- Reads RSS, Atom, and JSON Feed sources.
|
||||||
|
- Imports and exports OPML.
|
||||||
|
- Stores the feed library locally in SQLite.
|
||||||
|
- Tracks folders, unread/read state, starred items, refresh history, and retention cleanup.
|
||||||
|
- Shares one SwiftUI reader interface across macOS and iOS.
|
||||||
|
- Includes a real-feed validation CLI for release checks.
|
||||||
|
|
||||||
|
## Technical Shape
|
||||||
|
|
||||||
|
The app is split into a reusable feed core and a shared UI package. The core owns parsing, feed discovery, refresh behavior, OPML, fetching, and persistence. The UI layer handles the reader, feed organization, article rendering, search, and platform commands.
|
||||||
|
|
||||||
|
The release build is intentionally size-conscious: local package code is statically linked where practical, release symbols are stripped, and the verification script checks app size as part of the release gate.
|
||||||
|
|
||||||
|
## Status
|
||||||
|
|
||||||
|
FeedMe is in open beta. Current work is focused on release hardening, real-feed validation, and keeping the app small while preserving a useful local reader workflow.
|
||||||
38
content/apps/ihatepdfs.md
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
---
|
||||||
|
title: "I Hate PDFs"
|
||||||
|
date: 2026-07-01
|
||||||
|
draft: false
|
||||||
|
description: "A tiny native macOS PDF reader for local reading, highlighting, commenting, and review."
|
||||||
|
tags: ["Swift", "SwiftUI", "PDFKit", "macOS"]
|
||||||
|
status: "open beta"
|
||||||
|
weight: 30
|
||||||
|
links:
|
||||||
|
- label: "GitHub"
|
||||||
|
url: "https://github.com/akkolli/ihatepdfs"
|
||||||
|
- label: "Download"
|
||||||
|
url: "https://github.com/akkolli/ihatepdfs/releases/latest"
|
||||||
|
toc: true
|
||||||
|
math: false
|
||||||
|
---
|
||||||
|
|
||||||
|
I Hate PDFs is a small native macOS PDF reader for local reading, highlighting, commenting, and review. It uses SwiftUI, AppKit, and PDFKit, keeps documents on your Mac, and avoids accounts, tracking, and cloud upload.
|
||||||
|
|
||||||
|
{{< figure src="/images/apps/ihatepdfs/default-reading.png" alt="I Hate PDFs default reading mode." />}}
|
||||||
|
|
||||||
|
## What It Does
|
||||||
|
|
||||||
|
- Opens local PDFs without accounts, analytics, tracking, or cloud upload.
|
||||||
|
- Supports highlighting, comments, replies, bookmarks, search, and sidebars.
|
||||||
|
- Writes standards-compatible annotations back into PDFs.
|
||||||
|
- Stays intentionally small by relying on system frameworks.
|
||||||
|
- Ships as a direct-download macOS app, with App Store packaging support.
|
||||||
|
|
||||||
|
## Technical Shape
|
||||||
|
|
||||||
|
The project is a Swift Package with a core PDF annotation target and a SwiftUI macOS app target. The app uses PDFKit for rendering and annotation behavior, AppKit bridges where needed, and strict release-size checks to keep the bundle small.
|
||||||
|
|
||||||
|
The design rule is simple: stay native, local, and small unless a feature clearly justifies its weight.
|
||||||
|
|
||||||
|
## Status
|
||||||
|
|
||||||
|
I Hate PDFs is in open beta and actively maintained. The current public release line focuses on fast local PDF review, standards-compatible annotations, and small distribution artifacts.
|
||||||
3
content/contact/_index.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
---
|
||||||
|
title: "Contact"
|
||||||
|
---
|
||||||
6
content/posts/_index.md
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
title: "Posts"
|
||||||
|
build:
|
||||||
|
render: never
|
||||||
|
list: always
|
||||||
|
---
|
||||||
119
content/posts/blackwell_datacenter_vs_geforce.md
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
---
|
||||||
|
title: 'The RTX 5090 Is Blackwell, But Not That Blackwell'
|
||||||
|
date: '2026-02-27'
|
||||||
|
description: 'Why GeForce and datacenter Blackwell GPUs can share a name but expose different tensor-core programming paths.'
|
||||||
|
tags: ['Nvidia', 'GPU']
|
||||||
|
---
|
||||||
|
|
||||||
|
I bought an RTX 5090 FE mostly for machine learning work. I still play games on it, but the real reason I wanted the card was FP4 support on Blackwell Tensor Cores.
|
||||||
|
|
||||||
|
FP4 matters because deep learning is mostly a story about moving numbers through matrix multiplications. If you can use fewer bits per number without breaking the model, you can often run faster and fit more work into the same memory. For someone interested in low-precision compute, a consumer card with Blackwell Tensor Cores sounded like a very good deal.
|
||||||
|
|
||||||
|
Then I saw people in the GPU Mode Discord calling GeForce Blackwell "fake Blackwell."
|
||||||
|
|
||||||
|
That sounded ridiculous at first. The RTX 5090 is a Blackwell GPU. It runs Blackwell-era FP4 kernels. It is also very fast. But after digging through Nvidia's docs and running a few benchmarks, I understand what people were reacting to.
|
||||||
|
|
||||||
|
The short version: the 5090 is real Blackwell, but it is not a small B200.
|
||||||
|
|
||||||
|
## The takeaway
|
||||||
|
|
||||||
|
If you only remember one thing, remember this:
|
||||||
|
|
||||||
|
- GeForce Blackwell and datacenter Blackwell share a product-generation name.
|
||||||
|
- They do not expose the same low-level tensor-core programming model.
|
||||||
|
- The RTX 5090 can run very fast FP4 workloads.
|
||||||
|
- It does not expose datacenter Blackwell's `tcgen05` path or tensor memory.
|
||||||
|
- Compute capability is not a simple "higher number means all the features" ranking.
|
||||||
|
|
||||||
|
That last point is what fooled me.
|
||||||
|
|
||||||
|
The GeForce cards are `sm_120`, with compute capability 12. Datacenter Blackwell cards such as B200 are `sm_100`, with compute capability 10. A normal person would look at 12 versus 10 and assume the consumer card is newer, broader, or at least a superset.
|
||||||
|
|
||||||
|
That is not how this works.
|
||||||
|
|
||||||
|
Compute capability is a target for CUDA code generation. It tells the compiler what family of instructions and hardware behavior to expect. It is not a feature score. Starting with Blackwell, Nvidia is also leaning harder on family-specific feature sets, which means some low-level features exist for one Blackwell family and not another. Nvidia explains this in its writeup on [family-specific architecture features](https://developer.nvidia.com/blog/nvidia-blackwell-and-nvidia-cuda-12-9-introduce-family-specific-architecture-features/).
|
||||||
|
|
||||||
|
## A quick vocabulary reset
|
||||||
|
|
||||||
|
Here is the minimum context for the rest of the post.
|
||||||
|
|
||||||
|
- **Tensor Cores** are specialized GPU units built for the matrix multiplications that dominate deep learning.
|
||||||
|
- **Precision** means how many bits you use to store each number. FP32 uses 32 bits. FP16 uses 16. FP4 uses 4.
|
||||||
|
- **FP4** is interesting because it can reduce memory traffic and increase throughput, if the model can tolerate the lower precision.
|
||||||
|
- **NVFP4** is Nvidia's FP4 format for deep learning workloads.
|
||||||
|
- **A kernel** is a small program that runs on the GPU.
|
||||||
|
- **CUTLASS** is Nvidia's library for writing fast matrix multiplication kernels.
|
||||||
|
- **PTX** is Nvidia's low-level instruction language for CUDA GPUs.
|
||||||
|
- **An SM**, or streaming multiprocessor, is one of the GPU's main compute blocks.
|
||||||
|
- **Shared memory** is fast memory inside an SM.
|
||||||
|
- **TMEM**, or tensor memory, is extra memory near the Tensor Cores on datacenter Blackwell.
|
||||||
|
|
||||||
|
With that out of the way, the argument becomes much easier to follow.
|
||||||
|
|
||||||
|
## What datacenter Blackwell gets
|
||||||
|
|
||||||
|
Datacenter Blackwell introduces a new Tensor Core instruction family called `tcgen05`. Nvidia's CUTLASS documentation describes Blackwell SM100 GEMMs as targeting `tcgen05.mma` instructions, including support for 4-bit, 6-bit, and 8-bit floating point data types.
|
||||||
|
|
||||||
|
Those instructions are important because they are not just a new spelling for old matrix multiply code. They are part of a different programming path for Tensor Cores.
|
||||||
|
|
||||||
|
The big extra piece is TMEM. You can think of shared memory as the fast staging area CUDA programmers already use to feed GPU work efficiently. TMEM adds another staging area closer to the Tensor Cores. For the kind of low-precision matrix multiplication deep learning cares about, that matters because the math units can be so fast that feeding them becomes the problem.
|
||||||
|
|
||||||
|
This is where the GeForce and datacenter stories split.
|
||||||
|
|
||||||
|
The RTX 5090 has Blackwell Tensor Cores and can run NVFP4 workloads. But when I looked through the [CUDA PTX documentation](https://docs.nvidia.com/CUDA/parallel-thread-execution/), the `tcgen05` features I cared about were tied to the datacenter Blackwell family, not GeForce Blackwell. The Blackwell tuning guide also lists B200 shared-memory configurations up to 228 KB per SM; GeForce does not get the same TMEM story.
|
||||||
|
|
||||||
|
So the practical distinction is not "does this GPU support FP4 at all?" It does. The distinction is "does this GPU expose the datacenter Blackwell Tensor Core programming model?" It does not.
|
||||||
|
|
||||||
|
## What the 5090 actually does
|
||||||
|
|
||||||
|
I still wanted to measure the card instead of just reading target tables. So I pulled CUTLASS and ran its NVFP4 matrix multiplication example on the RTX 5090.
|
||||||
|
|
||||||
|
{{< figure src="/images/1_blackwell_dc_vs_gf/5090_65536_cropped.png" alt="A screenshot of a CUTLASS NVFP4 matrix multiplication benchmark on an RTX 5090" />}}
|
||||||
|
|
||||||
|
That is over a petaflop of NVFP4 compute.
|
||||||
|
|
||||||
|
A petaflop means one quadrillion floating-point operations per second. For a desktop GPU, that is not a fake result in any meaningful everyday sense. The card is doing serious low-precision work.
|
||||||
|
|
||||||
|
But the next question is whether the Tensor Cores are being fed efficiently. Nsight Compute gives the more interesting picture.
|
||||||
|
|
||||||
|
{{< figure src="/images/1_blackwell_dc_vs_gf/geforce_ncu.png" alt="Nsight Compute showing register pressure and memory bottlenecks on a GeForce GPU" />}}
|
||||||
|
|
||||||
|
The short read is: memory is the bottleneck. The Tensor Cores can chew through math faster than the rest of the kernel can keep them supplied. Shared memory pressure shows up immediately.
|
||||||
|
|
||||||
|
That is exactly the problem the datacenter path is designed to address.
|
||||||
|
|
||||||
|
## What happens on B200
|
||||||
|
|
||||||
|
To compare against datacenter Blackwell, I rented a B200 instance on Vast.ai and ran the same kind of matrix multiplication with CUTLASS kernels targeting `sm_100a`.
|
||||||
|
|
||||||
|
{{< figure src="/images/1_blackwell_dc_vs_gf/nvtop_b200.png" alt="nvtop showing B200 GPU memory capacity" />}}
|
||||||
|
|
||||||
|
{{< figure src="/images/1_blackwell_dc_vs_gf/b200_65536_cropped.png" alt="A CUTLASS benchmark result from a B200 GPU" />}}
|
||||||
|
|
||||||
|
That run gets past 2 petaflops, and I suspect better kernels can push it further. The point is not just the number. The point is that the B200 has access to the datacenter Blackwell path that the 5090 does not.
|
||||||
|
|
||||||
|
This is the part that makes the "fake Blackwell" complaint emotionally understandable, even if I would not phrase it that way.
|
||||||
|
|
||||||
|
The RTX 5090 is not fake. It is a powerful GeForce card with real NVFP4 capability. But if you heard "Blackwell" and expected the Tensor Core programming model described in SM100 docs, you bought the wrong mental model.
|
||||||
|
|
||||||
|
## Why this should be clearer
|
||||||
|
|
||||||
|
I do not think the problem is that the 5090 is bad. I like the card.
|
||||||
|
|
||||||
|
The problem is that Nvidia's naming makes it easy to assume one Blackwell label means one Blackwell feature set. It does not. The details are discoverable, but they are scattered across CUDA docs, CUTLASS docs, tuning guides, target suffixes, and benchmark behavior.
|
||||||
|
|
||||||
|
That is fine for compiler engineers. It is not fine for people buying expensive hardware for machine learning.
|
||||||
|
|
||||||
|
If a GPU is marketed into a world where students, researchers, indie labs, and small companies are all trying to run deep learning workloads locally, the feature split should be much easier to understand before purchase.
|
||||||
|
|
||||||
|
## Bottom line
|
||||||
|
|
||||||
|
The RTX 5090 gives you a lot of low-precision compute for a desktop machine. It can run NVFP4 kernels, and in my tests it crossed a petaflop.
|
||||||
|
|
||||||
|
But GeForce Blackwell is not datacenter Blackwell. The missing `tcgen05` and TMEM path is the real split.
|
||||||
|
|
||||||
|
So yes, the 5090 is Blackwell.
|
||||||
|
|
||||||
|
It is just not that Blackwell.
|
||||||
|
|
||||||
|
Why Jensen, why.
|
||||||
265
content/posts/qwen36_tokens_per_kwh.md
Normal file
@@ -0,0 +1,265 @@
|
|||||||
|
---
|
||||||
|
title: 'Can an RTX 5090 Be an Always On Local Agent Box?'
|
||||||
|
date: '2026-05-26'
|
||||||
|
description: 'Qwen3.6-27B, MTP, clock limits, and trying to make a 5090 something I would leave running all day.'
|
||||||
|
tags: ['LLM Inference', 'GPU', 'Local Agents']
|
||||||
|
---
|
||||||
|
|
||||||
|
I wanted a local agent I could leave running all day.
|
||||||
|
|
||||||
|
Not a benchmark setup. Not a machine I turn on for one prompt, screenshot the result, then shut down. I wanted something that could sit there with a coding agent or research agent running for hours.
|
||||||
|
|
||||||
|
A lot of people use Macs for this. That makes sense.
|
||||||
|
|
||||||
|
The idle draw is very low. Energy Star lists the 2025 M3 Ultra Mac Studio at **7.7 W short idle** and **6.3 W long idle** for the 512 GB model. Apple lists the same class of machine at **9 W idle** and **270 W max** from the wall.
|
||||||
|
|
||||||
|
That is a good power envelope for local agents. Agents spend a lot of time waiting on tools, tests, file reads, and network calls. Peak speed matters, but so does idle power. So does the power draw during a long decode.
|
||||||
|
|
||||||
|
And **30 to 40 generated tokens per second** is fine for a lot of agent work. It may not feel great if you are staring at every token during interactive coding. But if the agent is reading files, running commands, and waiting on tests, the model is only one part of the loop.
|
||||||
|
|
||||||
|
My question was simple. I wanted to see whether my RTX 5090 could fit into that kind of all day setup.
|
||||||
|
|
||||||
|
Not at Mac idle power. That is not going to happen. But low enough that I would be comfortable leaving it on.
|
||||||
|
|
||||||
|
## Why decode is awkward
|
||||||
|
|
||||||
|
LLMs generate one token at a time.
|
||||||
|
|
||||||
|
The model reads the prompt, predicts a token, adds that token to the context, then runs again. Each new token depends on what came before it.
|
||||||
|
|
||||||
|
That is awkward for a large GPU. A 5090 has a lot of compute, but a single decode stream often cannot use all of it. The GPU keeps moving model weights and KV cache around to produce one new token. Then it repeats.
|
||||||
|
|
||||||
|
So raw GPU size does not always turn into efficient local inference.
|
||||||
|
|
||||||
|
{{< figure src="/images/2_qwen36_tokens_per_kwh/fig5_autoregressive_decode.svg" alt="Autoregressive decoding diagram showing one model pass producing one accepted token at a time" />}}
|
||||||
|
|
||||||
|
Speculative decoding tries to fix this shape.
|
||||||
|
|
||||||
|
A cheap drafter guesses future tokens. The main model checks those guesses. Accepted tokens stay. Bad guesses get dropped.
|
||||||
|
|
||||||
|
The goal is simple. Get more than one useful token out of an expensive model pass.
|
||||||
|
|
||||||
|
## Why MTP helps
|
||||||
|
|
||||||
|
MTP means multi token prediction.
|
||||||
|
|
||||||
|
Instead of using a fully separate draft model, an MTP model has a lookahead path built in. It can draft future tokens, then the main model verifies them.
|
||||||
|
|
||||||
|
That is why the Qwen3.6 MTP model caught my attention. `Qwen3.6-27B` is a dense 27B model with a long context window. The model card recommends at least 128K context for complex thinking work. That is the kind of model I want for a local agent.
|
||||||
|
|
||||||
|
Large enough to be useful. Small enough to run locally.
|
||||||
|
|
||||||
|
{{< figure src="/images/2_qwen36_tokens_per_kwh/fig6_mtp_decode.svg" alt="MTP speculative decoding diagram showing a lookahead head proposing several draft tokens and the target model verifying them" />}}
|
||||||
|
|
||||||
|
## The 5090 settings
|
||||||
|
|
||||||
|
I used the Qwen MTP GGUF setup. I also locked the clocks instead of letting the card boost freely.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo nvidia-smi -lgc 1200,1200
|
||||||
|
sudo nvidia-smi -lmc 7001,7001
|
||||||
|
```
|
||||||
|
|
||||||
|
This part matters. On my card, `nvidia-smi -pl` cannot go low enough.
|
||||||
|
|
||||||
|
```text
|
||||||
|
Default Power Limit: 575 W
|
||||||
|
Min Power Limit: 400 W
|
||||||
|
Max Power Limit: 600 W
|
||||||
|
```
|
||||||
|
|
||||||
|
The normal power limit knob can stop the card from going past 400 W. It cannot ask the card to behave like a 215 W inference card.
|
||||||
|
|
||||||
|
Clock limiting is what got me there.
|
||||||
|
|
||||||
|
The operating point I liked was this.
|
||||||
|
|
||||||
|
```text
|
||||||
|
Qwen3.6-27B MTP
|
||||||
|
87.2 generated tok/s
|
||||||
|
215 W GPU power
|
||||||
|
```
|
||||||
|
|
||||||
|
The same power settings without MTP gave me **32.1 tok/s**.
|
||||||
|
|
||||||
|
So MTP was not a small change. It moved the setup from usable to comfortable.
|
||||||
|
|
||||||
|
{{< figure src="/images/2_qwen36_tokens_per_kwh/author_nvidia_mtp_x6_215w_report.png" alt="Author measurement screenshot showing Qwen3.6-27B MTP throughput and a 215 watt GPU power reading" />}}
|
||||||
|
|
||||||
|
## The math
|
||||||
|
|
||||||
|
The metric I care about is generated tokens per watt second.
|
||||||
|
|
||||||
|
A watt second is a joule. So I will write it as **tokens/J**.
|
||||||
|
|
||||||
|
```text
|
||||||
|
tokens/J = generated tok/s / watts
|
||||||
|
```
|
||||||
|
|
||||||
|
For the 215 W MTP run, the math is this.
|
||||||
|
|
||||||
|
```text
|
||||||
|
87.2 tok/s / 215 W = 0.4056 tokens/J
|
||||||
|
1 / 0.4056 = 2.47 J/token
|
||||||
|
```
|
||||||
|
|
||||||
|
For the no MTP run at the same power settings, it is this.
|
||||||
|
|
||||||
|
```text
|
||||||
|
32.1 tok/s / 215 W = 0.1493 tokens/J
|
||||||
|
1 / 0.1493 = 6.70 J/token
|
||||||
|
```
|
||||||
|
|
||||||
|
So MTP improved generated token efficiency by **2.72x** at the same 215 W operating point.
|
||||||
|
|
||||||
|
```text
|
||||||
|
0.4056 / 0.1493 = 2.72x
|
||||||
|
```
|
||||||
|
|
||||||
|
There is a second question. Did clock limiting itself improve efficiency compared with stock clocks?
|
||||||
|
|
||||||
|
I do not have a clean stock clock run with both throughput and power recorded. So I will not claim that yet.
|
||||||
|
|
||||||
|
But the cutoff is easy to calculate.
|
||||||
|
|
||||||
|
```text
|
||||||
|
To match 0.4056 tokens/J at 400 W, stock would need 162.2 tok/s.
|
||||||
|
To match 0.4056 tokens/J at 450 W, stock would need 182.5 tok/s.
|
||||||
|
```
|
||||||
|
|
||||||
|
My MTP x4 screenshot shows about **151.5 tok/s**, but I did not record watts for that run. If that run was near 400 W, the 215 W point is more efficient. If a stock or high power run gets past **162.2 tok/s at 400 W**, it matches the 215 W point. At **450 W**, it needs **182.5 tok/s**.
|
||||||
|
|
||||||
|
That is the measurement I still need.
|
||||||
|
|
||||||
|
## Daily power
|
||||||
|
|
||||||
|
At 87.2 tok/s, the machine can generate this much text in a day.
|
||||||
|
|
||||||
|
```text
|
||||||
|
87.2 tok/s * 86,400 sec/day = 7,534,080 tokens/day
|
||||||
|
```
|
||||||
|
|
||||||
|
The GPU only number is this.
|
||||||
|
|
||||||
|
```text
|
||||||
|
0.4056 tokens/J * 3,600,000 J/kWh = 1.46M tokens/kWh
|
||||||
|
215 W * 24 h / 1000 = 5.16 kWh/day
|
||||||
|
7,534,080 / 5.16 = 1.46M tokens/kWh
|
||||||
|
```
|
||||||
|
|
||||||
|
That is clean, but it is not whole machine power.
|
||||||
|
|
||||||
|
My CPU pulls about **60 W** by default. If I add that, I get a rough box number.
|
||||||
|
|
||||||
|
```text
|
||||||
|
215 W GPU + 60 W CPU = 275 W
|
||||||
|
275 W * 24 h / 1000 = 6.60 kWh/day
|
||||||
|
7,534,080 / 6.60 = 1.14M tokens/kWh
|
||||||
|
```
|
||||||
|
|
||||||
|
That gives two comparison points.
|
||||||
|
|
||||||
|
```text
|
||||||
|
GPU only: 87.2 tok/s / 215 W = 0.4056 tokens/J
|
||||||
|
GPU plus CPU: 87.2 tok/s / 275 W = 0.3171 tokens/J
|
||||||
|
```
|
||||||
|
|
||||||
|
Adding my current CPU baseline moves the run from **2.47 J/token** to **3.15 J/token**.
|
||||||
|
|
||||||
|
That is still fine for me. I would leave that running.
|
||||||
|
|
||||||
|
Could I push the GPU lower? Yes. I can get the 5090 closer to **150 W**. I need a proper sweep before I say where the best point is. For now, 215 W is the point I like. It is fast enough, and it does not feel wasteful.
|
||||||
|
|
||||||
|
{{< figure src="/images/2_qwen36_tokens_per_kwh/fig1_tokens_per_day.png" alt="Bar chart comparing generated tokens per day across the measured NVIDIA run and M3 Ultra benchmark rows" />}}
|
||||||
|
|
||||||
|
## The Mac comparison
|
||||||
|
|
||||||
|
The public Apple rows I found are oMLX results for M3 Ultra. They include throughput. They do not include wall power during the run.
|
||||||
|
|
||||||
|
The strongest 80 core rows I found for `Qwen3.6-27B-oQ4-mtp` were these.
|
||||||
|
|
||||||
|
```text
|
||||||
|
8k context 42.0 generated tok/s
|
||||||
|
16k context 41.1 generated tok/s
|
||||||
|
32k context 38.6 generated tok/s
|
||||||
|
```
|
||||||
|
|
||||||
|
I also found a `Qwen3.6-27B-MXFP4-MTP` row at **25.7 tok/s** for 8k.
|
||||||
|
|
||||||
|
Those are good numbers for agent work. The missing number is power.
|
||||||
|
|
||||||
|
Against my GPU only 215 W point, the M3 Ultra 80 core oQ4 MTP rows need to run around **95 to 104 W at the wall** to match the 5090 on generated tokens per kWh.
|
||||||
|
|
||||||
|
{{< figure src="/images/2_qwen36_tokens_per_kwh/fig3_m3_break_even_power.png" alt="Bar chart showing the M3 Ultra wall-power levels needed to match the measured NVIDIA GPU-side tokens per kWh" />}}
|
||||||
|
|
||||||
|
Against my rough 275 W whole box estimate, the break even numbers move up.
|
||||||
|
|
||||||
|
```text
|
||||||
|
M3 Ultra 80 core oQ4 MTP at 8k 42.0 tok/s / 0.3171 tokens/J = 132.5 W
|
||||||
|
M3 Ultra 80 core oQ4 MTP at 16k 41.1 tok/s / 0.3171 tokens/J = 129.6 W
|
||||||
|
M3 Ultra 80 core oQ4 MTP at 32k 38.6 tok/s / 0.3171 tokens/J = 121.7 W
|
||||||
|
M3 Ultra 80 core MXFP4 MTP at 8k 25.7 tok/s / 0.3171 tokens/J = 81.0 W
|
||||||
|
```
|
||||||
|
|
||||||
|
So there are two fair comparisons.
|
||||||
|
|
||||||
|
Against GPU only, the Mac needs about **95 to 104 W** for the oQ4 MTP rows.
|
||||||
|
|
||||||
|
Against my rough whole box number, it needs about **122 to 132 W**.
|
||||||
|
|
||||||
|
That is why I do not want to turn this into a clean win for either side. The Mac has very low idle power, a low peak envelope, and unified memory. The 5090 has much more decode headroom if I tune it.
|
||||||
|
|
||||||
|
The real comparison needs outlet power during the same workload.
|
||||||
|
|
||||||
|
{{< figure src="/images/2_qwen36_tokens_per_kwh/fig4_m3_power_sensitivity.png" alt="Line chart showing M3 Ultra tokens per kWh across different assumed wall-power levels" />}}
|
||||||
|
|
||||||
|
## What changed for me
|
||||||
|
|
||||||
|
Before this, I thought of the 5090 as a burst machine.
|
||||||
|
|
||||||
|
Use it for a heavy run. Finish the job. Shut it down.
|
||||||
|
|
||||||
|
After this, I am more willing to treat it as a local agent box.
|
||||||
|
|
||||||
|
Not at stock settings. Not with the motherboard and GPU doing whatever they want. But with clock limits, MTP, and a power point I chose, the setup lands somewhere I can live with.
|
||||||
|
|
||||||
|
```text
|
||||||
|
87.2 generated tok/s
|
||||||
|
215 W GPU power
|
||||||
|
about 275 W with my current CPU baseline
|
||||||
|
7.53M generated tokens/day
|
||||||
|
```
|
||||||
|
|
||||||
|
That is enough throughput for an agent to read, edit, run tools, make mistakes, and keep going.
|
||||||
|
|
||||||
|
I do not need the maximum token rate. I need a machine that can keep working for hours without feeling wasteful.
|
||||||
|
|
||||||
|
## Next test
|
||||||
|
|
||||||
|
MTP is one way to deal with the one token at a time problem.
|
||||||
|
|
||||||
|
Next I want to test diffusion based speculative decoding.
|
||||||
|
|
||||||
|
DFlash is the paper I am looking at. It uses a lightweight block diffusion model as the drafter. The draft side can propose a block of tokens in parallel. The target model still verifies the draft.
|
||||||
|
|
||||||
|
The paper reports lossless acceleration. That is useful. But the question I care about is narrower.
|
||||||
|
|
||||||
|
```text
|
||||||
|
Does this improve tokens/J for a local agent that runs for hours?
|
||||||
|
```
|
||||||
|
|
||||||
|
That is the next article.
|
||||||
|
|
||||||
|
## Sources
|
||||||
|
|
||||||
|
Apple power numbers came from [Apple Support](https://support.apple.com/en-us/102027).
|
||||||
|
|
||||||
|
Energy Star idle numbers came from [Energy Star product 4513877](https://www.energystar.gov/productfinder/product/certified-computers/details/4513877/export/pdf).
|
||||||
|
|
||||||
|
The Qwen context note came from the [`Qwen3.6-27B` model card](https://huggingface.co/Qwen/Qwen3.6-27B).
|
||||||
|
|
||||||
|
The MTP explanation follows the [vLLM MTP docs](https://docs.vllm.ai/en/latest/features/speculative_decoding/mtp/) and the [vLLM speculative decoding docs](https://docs.vllm.ai/usage/speculative_decoding/).
|
||||||
|
|
||||||
|
The diffusion speculation follow up is based on [DFlash](https://arxiv.org/abs/2602.06036).
|
||||||
|
|
||||||
|
The Apple throughput comparison uses the oMLX [quantization table](https://omlx.ai/benchmarks?order=desc&page=7143&sort=quantization) and [memory table](https://omlx.ai/benchmarks?order=desc&sort=memory_gb).
|
||||||
3
content/projects/_index.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
---
|
||||||
|
title: "Projects"
|
||||||
|
---
|
||||||
3
content/publications/_index.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
---
|
||||||
|
title: "Publications"
|
||||||
|
---
|
||||||
9
data/publications.yaml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
- title: "Graph Attention Inference of Network Topology in Multi-Agent Systems"
|
||||||
|
year: 2024
|
||||||
|
authors: "Akshay Kolli, Reza Azadeh, Kshitij Jerath"
|
||||||
|
venue: "IFAC-PapersOnLine 58(28), 1037-1042"
|
||||||
|
links:
|
||||||
|
- label: "ScienceDirect"
|
||||||
|
url: "https://www.sciencedirect.com/science/article/pii/S2405896325001338"
|
||||||
|
- label: "DOI"
|
||||||
|
url: "https://doi.org/10.1016/j.ifacol.2025.01.133"
|
||||||
67
docs/apple-ii-interface.md
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
# Apple II Interface Theme
|
||||||
|
|
||||||
|
## Target
|
||||||
|
|
||||||
|
The site should feel like a black-and-white Apple II desktop interface rendered on a CRT, not a modern terminal and not a normal blog with a monospace font. The visual language is based on low-resolution desktop UI: black outlines, gray dither fills, compact list rows, square controls, and high-contrast pixel-like text. Keep it minimal: do not add fake menus, fake window controls, decorative title bars, icons, or OS chrome unless they directly serve the content.
|
||||||
|
|
||||||
|
## Reference Traits
|
||||||
|
|
||||||
|
- **Desktop surface:** medium gray dithered background, not a flat white or black page.
|
||||||
|
- **Content panels:** white rectangular content areas with hard black borders, square corners, and no shadows or blur.
|
||||||
|
- **No fake chrome:** avoid menu bars, title bars, desktop icons, and inactive controls. Use Apple II constraints, not Apple II decoration.
|
||||||
|
- **Controls:** checkboxes, buttons, and tag filters should be square, black-and-white, and visibly low resolution.
|
||||||
|
- **Cursor:** use small raster PNG cursor assets for the page pointer and interactive hand state; avoid SVG cursors because Safari may rasterize them badly.
|
||||||
|
- **Typography:** use the bundled `VT323` bitmap-style webfont for headings and title-like labels only. Body text, dates, tags, controls, captions, and code should use a sharper monospace so small text does not wash out.
|
||||||
|
- **Color:** black, white, and gray only. No green phosphor, amber phosphor, gradients, or colorful accents.
|
||||||
|
- **Resolution:** layout should use coarse units, hard edges, and visible dither patterns so it reads as low-res.
|
||||||
|
- **CRT:** apply one consistent screen overlay across the whole site: scanlines, slight vignette, and pixel grid. Do not apply separate unrelated glows to individual elements.
|
||||||
|
- **Figures:** technical figures and plots must stay legible. The theme should protect figures with a clear panel, border, and caption treatment, but should not require hand-tuning individual SVGs.
|
||||||
|
- **Math:** math can keep its own rendering. Do not force KaTeX into a fake bitmap style if that reduces clarity.
|
||||||
|
|
||||||
|
## Theme Modes
|
||||||
|
|
||||||
|
- `displayMode: "crt"` is the default: gray dither desktop, subtle scanlines, slight vignette.
|
||||||
|
- `displayMode: "clean"` keeps the monochrome interface but removes dither and CRT overlay.
|
||||||
|
- `displayMode: "plain"` is the least styled reading/printing baseline.
|
||||||
|
- `density: "compact"` is the default low-res layout.
|
||||||
|
- `density: "comfortable"` increases panel padding, section gaps, and row spacing without changing the component language.
|
||||||
|
|
||||||
|
## Home Page Rules
|
||||||
|
|
||||||
|
- The whole home page sits inside one primary content panel.
|
||||||
|
- The intro is the top content region, not a giant hero.
|
||||||
|
- Posts are a Finder-style list: date column, title column, inline tags.
|
||||||
|
- Post tags appear inline next to post titles as compact metadata. There is no separate tag filter panel on the home page.
|
||||||
|
- Tag toggles are real square checkboxes with on/off state.
|
||||||
|
- Spacing is compact, but rows must not collide or wrap dates.
|
||||||
|
|
||||||
|
## Post Page Rules
|
||||||
|
|
||||||
|
- A post page uses one primary document panel.
|
||||||
|
- The post title and date live in the document panel.
|
||||||
|
- The table of contents is a compact fixed side panel in the left margin on wide screens, outside the bordered article box.
|
||||||
|
- Code blocks use bordered listing panels with compact title strips and light code fields, not large black slabs.
|
||||||
|
- Inline code uses hard recessed rectangular boxes.
|
||||||
|
- Figures use a protected light paper panel with no CRT shadow inside.
|
||||||
|
|
||||||
|
## Component Rules
|
||||||
|
|
||||||
|
- **Links:** no underline by default; hover and keyboard focus invert black/white.
|
||||||
|
- **Inline code:** recessed monochrome field treatment, never a soft rounded badge.
|
||||||
|
- **Code blocks:** monochrome program-listing panel with square border, a compact title strip, readable text size, and no colored syntax requirement.
|
||||||
|
- **Tables:** square outer border and strong horizontal rules so rows survive the CRT treatment.
|
||||||
|
- **Sidenotes:** high-contrast text with coarse superscript markers; mobile fallback uses a square bordered panel.
|
||||||
|
- **TOC:** compact bordered navigation panel with a Macintosh-style striped title strip, tight rows, subordinate nested headings, and inverted active state.
|
||||||
|
- **Tags:** compact inline metadata next to post titles; do not render a separate tag filter box.
|
||||||
|
- **Captions:** larger, darker, and heavier than normal modern captions so they do not wash out.
|
||||||
|
- **Blockquotes:** square bordered callout panel, not a thin modern quote line.
|
||||||
|
|
||||||
|
## Implementation Notes
|
||||||
|
|
||||||
|
- `VT323` is bundled from Google Fonts under the SIL Open Font License 1.1; keep its license at `static/licenses/VT323-OFL.txt`, but do not make it the global body font.
|
||||||
|
- Use CSS variables for theme colors and dither patterns.
|
||||||
|
- Use `image-rendering: pixelated`, square corners, and `-webkit-font-smoothing: none` where useful.
|
||||||
|
- CRT overlay belongs in one common CSS layer, not scattered across unrelated components.
|
||||||
|
- Honor reduced-transparency, increased-contrast, and print contexts by removing the CRT/dither layer.
|
||||||
|
- Avoid modern UI motifs: rounded cards, soft shadows, gradients, glass, pill tags, or animated decoration.
|
||||||
|
- Keep the design readable first; the Apple II treatment should constrain the UI, not make the content hard to scan.
|
||||||
44
hugo.yaml
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
baseURL: "https://akkolli.net/"
|
||||||
|
locale: "en-us"
|
||||||
|
title: "Akshay Kolli"
|
||||||
|
|
||||||
|
enableRobotsTXT: true
|
||||||
|
|
||||||
|
params:
|
||||||
|
author: "Akshay Kolli"
|
||||||
|
description: "PhD student at UMass Lowell -- World Models & Reinforcement Learning."
|
||||||
|
displayMode: "crt"
|
||||||
|
density: "compact"
|
||||||
|
social:
|
||||||
|
- name: "GitHub"
|
||||||
|
url: "https://github.com/akkolli"
|
||||||
|
icon: "github"
|
||||||
|
- name: "LinkedIn"
|
||||||
|
url: "https://www.linkedin.com/in/akshay-kolli-/"
|
||||||
|
icon: "linkedin"
|
||||||
|
- name: "Google Scholar"
|
||||||
|
url: "https://scholar.google.com/citations?user=aAezuzMAAAAJ&hl=en"
|
||||||
|
icon: "google-scholar"
|
||||||
|
- name: "Twitter"
|
||||||
|
url: "https://twitter.com/thekolliakshay"
|
||||||
|
icon: "twitter"
|
||||||
|
- name: "Gitea"
|
||||||
|
url: "https://code.akkolli.net/explore/repos"
|
||||||
|
icon: "gitea"
|
||||||
|
- name: "RSS"
|
||||||
|
url: "/index.xml"
|
||||||
|
icon: "rss"
|
||||||
|
internal: true
|
||||||
|
|
||||||
|
disableKinds:
|
||||||
|
- taxonomy
|
||||||
|
- term
|
||||||
|
|
||||||
|
markup:
|
||||||
|
tableOfContents:
|
||||||
|
startupLevel: 2
|
||||||
|
endLevel: 3
|
||||||
|
ordered: false
|
||||||
|
highlight:
|
||||||
|
guessSyntax: true
|
||||||
|
noClasses: false
|
||||||
6
layouts/_default/list.html
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{{ define "main" }}
|
||||||
|
<main class="home">
|
||||||
|
{{ partial "home-intro.html" . }}
|
||||||
|
{{ partial "file-index.html" (dict "active" .Section) }}
|
||||||
|
</main>
|
||||||
|
{{ end }}
|
||||||
63
layouts/_default/single.html
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
{{ define "main" }}
|
||||||
|
<article class="post" id="top">
|
||||||
|
{{ $hasToc := and (ne .Params.toc false) (gt (len (findRE "<li>" .TableOfContents)) 0) }}
|
||||||
|
{{ $backHref := "/" | relURL }}
|
||||||
|
{{ $backLabel := "Home" }}
|
||||||
|
{{ if in (slice "apps" "projects" "websites") .Section }}
|
||||||
|
{{ $backHref = "/projects/" | relURL }}
|
||||||
|
{{ $backLabel = "Projects" }}
|
||||||
|
{{ else if eq .Section "publications" }}
|
||||||
|
{{ $backHref = "/publications/" | relURL }}
|
||||||
|
{{ $backLabel = "Publications" }}
|
||||||
|
{{ end }}
|
||||||
|
<div class="post-layout{{ if $hasToc }} has-toc{{ end }}">
|
||||||
|
{{ if $hasToc }}
|
||||||
|
<aside class="post-margin" aria-label="Table of contents">
|
||||||
|
<div class="toc">
|
||||||
|
<div class="toc-title">Contents</div>
|
||||||
|
{{ .TableOfContents }}
|
||||||
|
</div>
|
||||||
|
</aside>
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
<div class="post-shell">
|
||||||
|
<header class="post-header">
|
||||||
|
<a class="back-link" href="{{ $backHref }}" aria-label="Back to {{ $backLabel }}" title="Back to {{ $backLabel }}">< {{ $backLabel }}</a>
|
||||||
|
|
||||||
|
<h1>{{ .Title }}</h1>
|
||||||
|
|
||||||
|
<div class="post-meta">
|
||||||
|
{{ if eq .Section "posts" }}
|
||||||
|
<time datetime="{{ .Date.Format "2006-01-02" }}">
|
||||||
|
{{ .Date.Format "02 Jan 2006" }}
|
||||||
|
</time>
|
||||||
|
{{ else }}
|
||||||
|
<span>{{ .Section | singularize | title }}</span>
|
||||||
|
{{ with .Params.status }}
|
||||||
|
<span class="dot">·</span>
|
||||||
|
<span>{{ . }}</span>
|
||||||
|
{{ end }}
|
||||||
|
{{ end }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{ with .Params.links }}
|
||||||
|
<div class="item-links article-links">
|
||||||
|
{{ $pageTitle := $.Title }}
|
||||||
|
{{ range . }}
|
||||||
|
<a href="{{ .url }}" aria-label="{{ .label }} for {{ $pageTitle }}" title="{{ .label }} for {{ $pageTitle }}">{{ .label }}</a>
|
||||||
|
{{ end }}
|
||||||
|
</div>
|
||||||
|
{{ end }}
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<main class="post-content">
|
||||||
|
{{ .Content }}
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<footer class="post-footer">
|
||||||
|
<a class="post-button" href="#top" aria-label="Scroll to top" title="Scroll to top">^ Top</a>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
{{ end }}
|
||||||
4
layouts/_shortcodes/figref.html
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{{ $id := .Get 0 }}
|
||||||
|
{{ $figures := .Page.Scratch.Get "figures" | default dict }}
|
||||||
|
{{ $number := index $figures $id }}
|
||||||
|
<a class="xref" href="#{{ $id }}">Figure {{ with $number }}{{ . }}{{ else }}{{ $id }}{{ end }}</a>
|
||||||
20
layouts/_shortcodes/figure.html
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
{{ $id := .Get "id" | default (printf "figure-%d" .Ordinal) }}
|
||||||
|
{{ $src := .Get "src" }}
|
||||||
|
{{ $caption := .Get "caption" }}
|
||||||
|
{{ $alt := .Get "alt" | default $caption }}
|
||||||
|
{{ $figures := .Page.Scratch.Get "figures" | default dict }}
|
||||||
|
{{ $number := add (len $figures) 1 }}
|
||||||
|
{{ .Page.Scratch.SetInMap "figures" $id $number }}
|
||||||
|
|
||||||
|
<figure id="{{ $id }}" class="figure">
|
||||||
|
{{ with $src }}
|
||||||
|
<img src="{{ . | relURL }}" alt="{{ $alt }}">
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
{{ if or $caption .Inner }}
|
||||||
|
<figcaption>
|
||||||
|
<span class="figure-label">Figure {{ $number }}.</span>
|
||||||
|
{{ with $caption }}{{ . | markdownify }}{{ else }}{{ .Inner | markdownify }}{{ end }}
|
||||||
|
</figcaption>
|
||||||
|
{{ end }}
|
||||||
|
</figure>
|
||||||
10
layouts/_shortcodes/sidenote.html
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<a
|
||||||
|
id="snref-{{ .Ordinal }}"
|
||||||
|
class="sidenote-number"
|
||||||
|
href="#sn-{{ .Ordinal }}"
|
||||||
|
role="doc-noteref"
|
||||||
|
aria-label="Sidenote {{ add .Ordinal 1 }}"
|
||||||
|
></a>
|
||||||
|
<span id="sn-{{ .Ordinal }}" class="sidenote" role="doc-endnote">
|
||||||
|
{{ .Inner | markdownify }}
|
||||||
|
</span>
|
||||||
39
layouts/baseof.html
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html
|
||||||
|
lang="{{ site.Language.Locale | default "en-us" }}"
|
||||||
|
data-display="{{ site.Params.displayMode | default "crt" }}"
|
||||||
|
data-density="{{ site.Params.density | default "compact" }}"
|
||||||
|
>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>
|
||||||
|
{{- if .IsHome -}}
|
||||||
|
{{ site.Title }} · {{ site.Params.author }}
|
||||||
|
{{- else -}}
|
||||||
|
{{ .Title }} · {{ site.Title }}
|
||||||
|
{{- end -}}
|
||||||
|
</title>
|
||||||
|
<meta name="description" content="{{ .Description | default site.Params.description }}">
|
||||||
|
<link rel="alternate" type="application/rss+xml" title="{{ site.Title }}" href="{{ "index.xml" | absURL }}">
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="{{ "css/main.css" | relURL }}">
|
||||||
|
|
||||||
|
{{ if .Param "math" }}
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.css">
|
||||||
|
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.js"></script>
|
||||||
|
<script
|
||||||
|
defer
|
||||||
|
src="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/contrib/auto-render.min.js"
|
||||||
|
onload="renderMathInElement(document.body, { delimiters: [{left: '$$', right: '$$', display: true}, {left: '$', right: '$', display: false}, {left: '\\(', right: '\\)', display: false}, {left: '\\[', right: '\\]', display: true}], throwOnError: false });"
|
||||||
|
></script>
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
<script defer src="{{ "js/site.js" | relURL }}"></script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
{{ block "main" . }}{{ end }}
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
6
layouts/home.html
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{{ define "main" }}
|
||||||
|
<main class="home">
|
||||||
|
{{ partial "home-intro.html" . }}
|
||||||
|
{{ partial "file-index.html" (dict "active" "posts") }}
|
||||||
|
</main>
|
||||||
|
{{ end }}
|
||||||
56
layouts/index.rss.xml
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
{{- $items := slice -}}
|
||||||
|
{{- range where site.RegularPages "Section" "in" (slice "posts" "projects" "apps") -}}
|
||||||
|
{{- $description := .Description | default (.Summary | plainify) -}}
|
||||||
|
{{- $section := title .Section -}}
|
||||||
|
{{- if eq .Section "apps" }}{{ $section = "Projects" }}{{ end -}}
|
||||||
|
{{- $items = $items | append (dict
|
||||||
|
"title" .Title
|
||||||
|
"link" .Permalink
|
||||||
|
"guid" .Permalink
|
||||||
|
"date" .Date
|
||||||
|
"section" $section
|
||||||
|
"description" $description
|
||||||
|
) -}}
|
||||||
|
{{- end -}}
|
||||||
|
|
||||||
|
{{- range hugo.Data.publications -}}
|
||||||
|
{{- $parts := slice -}}
|
||||||
|
{{- with .authors }}{{ $parts = $parts | append . }}{{ end -}}
|
||||||
|
{{- with .venue }}{{ $parts = $parts | append . }}{{ end -}}
|
||||||
|
{{- $link := ("/publications/" | absURL) -}}
|
||||||
|
{{- with .links }}{{ with index . 0 }}{{ $link = .url }}{{ end }}{{ end -}}
|
||||||
|
{{- $items = $items | append (dict
|
||||||
|
"title" .title
|
||||||
|
"link" $link
|
||||||
|
"guid" $link
|
||||||
|
"date" (time.AsTime (printf "%v-01-01T00:00:00Z" .year))
|
||||||
|
"section" "Publications"
|
||||||
|
"description" (delimit $parts ". ")
|
||||||
|
) -}}
|
||||||
|
{{- end -}}
|
||||||
|
|
||||||
|
{{- $items = sort $items "date" "desc" -}}
|
||||||
|
{{- $latest := now -}}
|
||||||
|
{{- with index $items 0 }}{{ $latest = .date }}{{ end -}}
|
||||||
|
{{- printf "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\"?>" | safeHTML }}
|
||||||
|
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
|
||||||
|
<channel>
|
||||||
|
<title>{{ site.Title | html }}</title>
|
||||||
|
<link>{{ site.Home.Permalink }}</link>
|
||||||
|
<description>{{ site.Params.description | html }}</description>
|
||||||
|
<generator>Hugo</generator>
|
||||||
|
<language>{{ site.Language.Locale | default "en-us" }}</language>
|
||||||
|
<lastBuildDate>{{ $latest.Format "Mon, 02 Jan 2006 15:04:05 -0700" }}</lastBuildDate>
|
||||||
|
<atom:link href="{{ "index.xml" | absURL }}" rel="self" type="application/rss+xml" />
|
||||||
|
{{ range $items }}
|
||||||
|
<item>
|
||||||
|
<title>{{ .title | html }}</title>
|
||||||
|
<link>{{ .link }}</link>
|
||||||
|
<pubDate>{{ .date.Format "Mon, 02 Jan 2006 15:04:05 -0700" }}</pubDate>
|
||||||
|
<guid>{{ .guid }}</guid>
|
||||||
|
<category>{{ .section | html }}</category>
|
||||||
|
<description>{{ .description | plainify | html }}</description>
|
||||||
|
</item>
|
||||||
|
{{ end }}
|
||||||
|
</channel>
|
||||||
|
</rss>
|
||||||
4
layouts/partials/contact-section.html
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<section class="contact-section" aria-labelledby="contact-heading">
|
||||||
|
<h2 id="contact-heading">Contact</h2>
|
||||||
|
<p>Email me at <a href="mailto:akshaykolli@hotmail.com">akshaykolli@hotmail.com</a></p>
|
||||||
|
</section>
|
||||||
35
layouts/partials/file-index.html
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
{{ $active := .active | default "posts" }}
|
||||||
|
{{ $tabs := slice
|
||||||
|
(dict "id" "posts" "label" "Posts" "href" ("/" | relURL))
|
||||||
|
(dict "id" "projects" "label" "Projects" "href" ("/projects/" | relURL))
|
||||||
|
(dict "id" "publications" "label" "Publications" "href" ("/publications/" | relURL))
|
||||||
|
(dict "id" "contact" "label" "Contact" "href" ("/contact/" | relURL))
|
||||||
|
}}
|
||||||
|
|
||||||
|
<div class="file-index">
|
||||||
|
<nav class="file-tabs" aria-label="Index views">
|
||||||
|
{{ range $tabs }}
|
||||||
|
<a
|
||||||
|
class="file-tab{{ if eq $active .id }} active{{ end }}"
|
||||||
|
href="{{ .href }}"
|
||||||
|
aria-label="Show {{ .label }}"
|
||||||
|
title="Show {{ .label }}"
|
||||||
|
{{ if eq $active .id }}aria-current="page"{{ end }}
|
||||||
|
>{{ .label }}</a>
|
||||||
|
{{ end }}
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<section class="file-panel" aria-labelledby="{{ $active }}-heading">
|
||||||
|
<h2 class="sr-only" id="{{ $active }}-heading">{{ title $active }}</h2>
|
||||||
|
|
||||||
|
{{ if eq $active "publications" }}
|
||||||
|
{{ partial "publication-list.html" hugo.Data.publications }}
|
||||||
|
{{ else if eq $active "projects" }}
|
||||||
|
{{ partial "page-list.html" (where site.RegularPages "Section" "in" (slice "projects" "apps")) }}
|
||||||
|
{{ else if eq $active "contact" }}
|
||||||
|
{{ partial "contact-section.html" . }}
|
||||||
|
{{ else }}
|
||||||
|
{{ partial "post-list.html" (where site.RegularPages "Section" "posts") }}
|
||||||
|
{{ end }}
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
9
layouts/partials/home-intro.html
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<section class="home-intro">
|
||||||
|
<h1>{{ site.Title }}</h1>
|
||||||
|
{{ with site.Home.Content }}
|
||||||
|
{{ . }}
|
||||||
|
{{ else with site.Params.description }}
|
||||||
|
<p>{{ . }}</p>
|
||||||
|
{{ end }}
|
||||||
|
{{ partial "social-links.html" . }}
|
||||||
|
</section>
|
||||||
25
layouts/partials/page-list-section.html
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
{{ with .pages }}
|
||||||
|
<section class="home-section" id="{{ $.id }}" aria-labelledby="{{ $.id }}-heading">
|
||||||
|
<h2 id="{{ $.id }}-heading">{{ $.title }}</h2>
|
||||||
|
|
||||||
|
<div class="item-list">
|
||||||
|
{{ range .ByWeight }}
|
||||||
|
<article class="item-row">
|
||||||
|
<div class="item-year">{{ with .Date }}{{ .Format "2006" }}{{ end }}</div>
|
||||||
|
<div>
|
||||||
|
<a class="item-title" href="{{ .RelPermalink }}">{{ .Title }}</a>
|
||||||
|
{{ with .Description }}<p class="row-summary">{{ . }}</p>{{ end }}
|
||||||
|
{{ with .Params.status }}<div class="item-meta">{{ . }}</div>{{ end }}
|
||||||
|
{{ with .Params.links }}
|
||||||
|
<div class="item-links">
|
||||||
|
{{ range . }}
|
||||||
|
<a href="{{ .url }}">{{ .label }}</a>
|
||||||
|
{{ end }}
|
||||||
|
</div>
|
||||||
|
{{ end }}
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
{{ end }}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
{{ end }}
|
||||||
26
layouts/partials/page-list.html
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
{{ with . }}
|
||||||
|
<div class="item-list">
|
||||||
|
{{ range .ByWeight }}
|
||||||
|
{{ $pageTitle := .Title }}
|
||||||
|
<article class="item-row" data-row-href="{{ .RelPermalink }}" role="link" tabindex="0" aria-label="Open {{ $pageTitle }}">
|
||||||
|
<div class="item-year">{{ with .Date }}{{ .Format "2006" }}{{ end }}</div>
|
||||||
|
<div class="item-body">
|
||||||
|
<div class="item-head">
|
||||||
|
<a class="item-title" href="{{ .RelPermalink }}" aria-label="Open {{ $pageTitle }}" title="Open {{ $pageTitle }}">{{ $pageTitle }}</a>
|
||||||
|
{{ with .Params.status }}
|
||||||
|
<span class="item-status">{{ . }}</span>
|
||||||
|
{{ end }}
|
||||||
|
</div>
|
||||||
|
{{ with .Params.links }}
|
||||||
|
<span class="item-links">
|
||||||
|
{{ range . }}<a href="{{ .url }}" aria-label="{{ .label }} for {{ $pageTitle }}" title="{{ .label }} for {{ $pageTitle }}">{{ .label }}</a>{{ end }}
|
||||||
|
</span>
|
||||||
|
{{ end }}
|
||||||
|
{{ with .Description }}<p class="row-summary">{{ . }}</p>{{ end }}
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
{{ end }}
|
||||||
|
</div>
|
||||||
|
{{ else }}
|
||||||
|
<p class="empty-list">No projects yet.</p>
|
||||||
|
{{ end }}
|
||||||
27
layouts/partials/post-list.html
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
{{ with . }}
|
||||||
|
<div class="tag-filter-status" data-tag-filter-status hidden>
|
||||||
|
<span>Showing <span data-tag-filter-name></span></span>
|
||||||
|
<a href="{{ "/" | relURL }}" aria-label="Clear tag filter" title="Clear tag filter">clear</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="post-list" data-post-list>
|
||||||
|
{{ range .ByDate.Reverse }}
|
||||||
|
{{ $tags := .Params.tags | default (slice) }}
|
||||||
|
<article class="post-row" data-tags='{{ jsonify $tags }}'>
|
||||||
|
<time datetime="{{ .Date.Format "2006-01-02" }}">{{ .Date.Format "02 Jan 2006" }}</time>
|
||||||
|
<div class="post-row-body">
|
||||||
|
<a class="post-title" href="{{ .RelPermalink }}" aria-label="Read {{ .Title }}" title="Read {{ .Title }}">{{ .Title }}</a>
|
||||||
|
{{ with $tags }}
|
||||||
|
<span class="post-title-tags">
|
||||||
|
{{ range . }}
|
||||||
|
<a class="post-tag" href="{{ "/" | relURL }}?tag={{ . | urlquery }}" data-tag="{{ . }}" aria-label="Filter posts by {{ . }}" title="Filter posts by {{ . }}">{{ . }}</a>
|
||||||
|
{{ end }}
|
||||||
|
</span>
|
||||||
|
{{ end }}
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
{{ end }}
|
||||||
|
</div>
|
||||||
|
{{ else }}
|
||||||
|
<p class="empty-list">No posts yet.</p>
|
||||||
|
{{ end }}
|
||||||
19
layouts/partials/publication-list.html
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
{{ with . }}
|
||||||
|
<div class="item-list">
|
||||||
|
{{ range sort . "year" "desc" }}
|
||||||
|
{{ $pubTitle := .title }}
|
||||||
|
{{ $authors := .authors }}
|
||||||
|
<article class="item-row">
|
||||||
|
<div class="item-year">{{ .year }}</div>
|
||||||
|
<div class="item-body">
|
||||||
|
<div class="item-head">
|
||||||
|
<div class="item-title">{{ $pubTitle }}</div>
|
||||||
|
</div>
|
||||||
|
{{ with $authors }}<p class="row-summary">{{ . }}</p>{{ end }}
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
{{ end }}
|
||||||
|
</div>
|
||||||
|
{{ else }}
|
||||||
|
<p class="empty-list">No publications yet.</p>
|
||||||
|
{{ end }}
|
||||||
18
layouts/partials/social-links.html
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
{{ with site.Params.social }}
|
||||||
|
<nav class="social-links" aria-label="Social links">
|
||||||
|
{{ range . }}
|
||||||
|
{{ if .url }}
|
||||||
|
<a
|
||||||
|
class="social-link"
|
||||||
|
href="{{ .url }}"
|
||||||
|
{{ if not .internal }}target="_blank" rel="me noopener noreferrer"{{ end }}
|
||||||
|
aria-label="{{ .name }}"
|
||||||
|
title="{{ .name }}"
|
||||||
|
>
|
||||||
|
<img src="{{ printf "icons/%s.svg" .icon | relURL }}" alt="" width="14" height="14">
|
||||||
|
<span class="sr-only">{{ .name }}</span>
|
||||||
|
</a>
|
||||||
|
{{ end }}
|
||||||
|
{{ end }}
|
||||||
|
</nav>
|
||||||
|
{{ end }}
|
||||||
1316
static/css/main.css
Normal file
BIN
static/cursors/mac-arrow.png
Normal file
|
After Width: | Height: | Size: 457 B |
BIN
static/cursors/mac-hand.png
Normal file
|
After Width: | Height: | Size: 419 B |
BIN
static/fonts/VT323-Regular.ttf
Normal file
1
static/icons/gitea.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg fill="#000000" role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Gitea</title><path d="M4.209 4.603c-.247 0-.525.02-.84.088-.333.07-1.28.283-2.054 1.027C-.403 7.25.035 9.685.089 10.052c.065.446.263 1.687 1.21 2.768 1.749 2.141 5.513 2.092 5.513 2.092s.462 1.103 1.168 2.119c.955 1.263 1.936 2.248 2.89 2.367 2.406 0 7.212-.004 7.212-.004s.458.004 1.08-.394c.535-.324 1.013-.893 1.013-.893s.492-.527 1.18-1.73c.21-.37.385-.729.538-1.068 0 0 2.107-4.471 2.107-8.823-.042-1.318-.367-1.55-.443-1.627-.156-.156-.366-.153-.366-.153s-4.475.252-6.792.306c-.508.011-1.012.023-1.512.027v4.474l-.634-.301c0-1.39-.004-4.17-.004-4.17-1.107.016-3.405-.084-3.405-.084s-5.399-.27-5.987-.324c-.187-.011-.401-.032-.648-.032zm.354 1.832h.111s.271 2.269.6 3.597C5.549 11.147 6.22 13 6.22 13s-.996-.119-1.641-.348c-.99-.324-1.409-.714-1.409-.714s-.73-.511-1.096-1.52C1.444 8.73 2.021 7.7 2.021 7.7s.32-.859 1.47-1.145c.395-.106.863-.12 1.072-.12zm8.33 2.554c.26.003.509.127.509.127l.868.422-.529 1.075a.686.686 0 0 0-.614.359.685.685 0 0 0 .072.756l-.939 1.924a.69.69 0 0 0-.66.527.687.687 0 0 0 .347.763.686.686 0 0 0 .867-.206.688.688 0 0 0-.069-.882l.916-1.874a.667.667 0 0 0 .237-.02.657.657 0 0 0 .271-.137 8.826 8.826 0 0 1 1.016.512.761.761 0 0 1 .286.282c.073.21-.073.569-.073.569-.087.29-.702 1.55-.702 1.55a.692.692 0 0 0-.676.477.681.681 0 1 0 1.157-.252c.073-.141.141-.282.214-.431.19-.397.515-1.16.515-1.16.035-.066.218-.394.103-.814-.095-.435-.48-.638-.48-.638-.467-.301-1.116-.58-1.116-.58s0-.156-.042-.27a.688.688 0 0 0-.148-.241l.516-1.062 2.89 1.401s.48.218.583.619c.073.282-.019.534-.069.657-.24.587-2.1 4.317-2.1 4.317s-.232.554-.748.588a1.065 1.065 0 0 1-.393-.045l-.202-.08-4.31-2.1s-.417-.218-.49-.596c-.083-.31.104-.691.104-.691l2.073-4.272s.183-.37.466-.497a.855.855 0 0 1 .35-.077z"/></svg>
|
||||||
|
After Width: | Height: | Size: 1.8 KiB |
1
static/icons/github.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg fill="#000000" role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>GitHub</title><path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"/></svg>
|
||||||
|
After Width: | Height: | Size: 837 B |
1
static/icons/google-scholar.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg fill="#000000" role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Google Scholar</title><path d="M5.242 13.769L0 9.5 12 0l12 9.5-5.242 4.269C17.548 11.249 14.978 9.5 12 9.5c-2.977 0-5.548 1.748-6.758 4.269zM12 10a7 7 0 1 0 0 14 7 7 0 0 0 0-14z"/></svg>
|
||||||
|
After Width: | Height: | Size: 279 B |
1
static/icons/linkedin.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>LinkedIn</title><path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433c-1.144 0-2.063-.926-2.063-2.065 0-1.138.92-2.063 2.063-2.063 1.14 0 2.064.925 2.064 2.063 0 1.139-.925 2.065-2.064 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z"/></svg>
|
||||||
|
After Width: | Height: | Size: 610 B |
5
static/icons/rss.svg
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||||
|
<path d="M5 3c8.837 0 16 7.163 16 16h-4C17 12.373 11.627 7 5 7V3z"/>
|
||||||
|
<path d="M5 10c4.971 0 9 4.029 9 9h-4c0-2.761-2.239-5-5-5v-4z"/>
|
||||||
|
<path d="M8 18.5A2.5 2.5 0 1 1 3 18.5a2.5 2.5 0 0 1 5 0z"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 268 B |
1
static/icons/twitter.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg fill="#000000" role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>X</title><path d="M14.234 10.162 22.977 0h-2.072l-7.591 8.824L7.251 0H.258l9.168 13.343L.258 24H2.33l8.016-9.318L16.749 24h6.993zm-2.837 3.299-.929-1.329L3.076 1.56h3.182l5.965 8.532.929 1.329 7.754 11.09h-3.182z"/></svg>
|
||||||
|
After Width: | Height: | Size: 314 B |
BIN
static/images/1_blackwell_dc_vs_gf/5090_65536_cropped.png
Normal file
|
After Width: | Height: | Size: 8.2 KiB |
BIN
static/images/1_blackwell_dc_vs_gf/b200_65536_cropped.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
static/images/1_blackwell_dc_vs_gf/geforce_ncu.png
Normal file
|
After Width: | Height: | Size: 307 KiB |
BIN
static/images/1_blackwell_dc_vs_gf/nvtop_b200.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 313 KiB |
BIN
static/images/2_qwen36_tokens_per_kwh/fig1_tokens_per_day.png
Normal file
|
After Width: | Height: | Size: 64 KiB |
|
After Width: | Height: | Size: 80 KiB |
|
After Width: | Height: | Size: 150 KiB |
@@ -0,0 +1,49 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="520" viewBox="0 0 1200 520" role="img" aria-labelledby="title desc">
|
||||||
|
<title id="title">Autoregressive decoding</title>
|
||||||
|
<desc id="desc">A simplified diagram showing one target model pass producing one accepted token, repeated sequentially.</desc>
|
||||||
|
<defs>
|
||||||
|
<style>
|
||||||
|
.bg { fill: #f7f4ed; }
|
||||||
|
.panel { fill: #fffdf7; stroke: #cfc8ba; stroke-width: 2; }
|
||||||
|
.ink { fill: #181612; font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; }
|
||||||
|
.muted { fill: #6b6254; font-family: ui-monospace, "SFMono-Regular", Consolas, monospace; }
|
||||||
|
.box-title { fill: #181612; font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; text-anchor: middle; }
|
||||||
|
.box-sub { fill: #6b6254; font-family: ui-monospace, "SFMono-Regular", Consolas, monospace; text-anchor: middle; }
|
||||||
|
.accent { fill: #0f6f77; }
|
||||||
|
.arrow-line { stroke: #5c554a; stroke-width: 3; fill: none; stroke-linecap: round; stroke-linejoin: round; }
|
||||||
|
.arrow-head { fill: #5c554a; }
|
||||||
|
.dash { stroke: #b8ad9b; stroke-width: 2; stroke-dasharray: 8 8; fill: none; }
|
||||||
|
</style>
|
||||||
|
</defs>
|
||||||
|
|
||||||
|
<rect class="bg" width="1200" height="520" rx="24"/>
|
||||||
|
|
||||||
|
<text class="ink" x="56" y="72" font-size="34" font-weight="650">Autoregressive decode</text>
|
||||||
|
<text class="muted" x="56" y="112" font-size="18">one target model pass gives one accepted token</text>
|
||||||
|
|
||||||
|
<rect class="panel" x="72" y="188" width="220" height="118" rx="18"/>
|
||||||
|
<text class="box-title" x="182" y="238" font-size="24" font-weight="650">context</text>
|
||||||
|
<text class="box-sub" x="182" y="274" font-size="17">prompt + KV cache</text>
|
||||||
|
|
||||||
|
<path class="arrow-line" d="M292 247 L385 247"/>
|
||||||
|
<path class="arrow-head" d="M410 247 L385 234 L385 260 Z"/>
|
||||||
|
|
||||||
|
<rect class="panel" x="430" y="168" width="280" height="158" rx="20"/>
|
||||||
|
<text class="box-title" x="570" y="228" font-size="25" font-weight="650">target model</text>
|
||||||
|
<text class="box-sub" x="570" y="265" font-size="17">full forward pass</text>
|
||||||
|
<rect class="accent" x="485" y="287" width="170" height="8" rx="4"/>
|
||||||
|
|
||||||
|
<path class="arrow-line" d="M710 247 L810 247"/>
|
||||||
|
<path class="arrow-head" d="M835 247 L810 234 L810 260 Z"/>
|
||||||
|
|
||||||
|
<rect class="panel" x="855" y="188" width="166" height="118" rx="18"/>
|
||||||
|
<text class="box-title" x="938" y="238" font-size="24" font-weight="650">token</text>
|
||||||
|
<text class="box-sub" x="938" y="274" font-size="17">+1</text>
|
||||||
|
|
||||||
|
<path class="arrow-line" d="M1021 247 C1110 247 1102 397 940 417 C650 452 290 427 210 333"/>
|
||||||
|
<path class="arrow-head" d="M188 309 L214 320 L201 346 Z"/>
|
||||||
|
|
||||||
|
<path class="dash" d="M430 344 L710 344"/>
|
||||||
|
<text class="muted" x="458" y="382" font-size="16">the GPU pays this cost one token at a time</text>
|
||||||
|
<text class="muted" x="454" y="456" font-size="17">append token, then run again</text>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.8 KiB |
73
static/images/2_qwen36_tokens_per_kwh/fig6_mtp_decode.svg
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="560" viewBox="0 0 1200 560" role="img" aria-labelledby="title desc">
|
||||||
|
<title id="title">MTP speculative decoding</title>
|
||||||
|
<desc id="desc">A simplified diagram showing a multi token prediction path proposing draft tokens and the target model verifying them together.</desc>
|
||||||
|
<defs>
|
||||||
|
<style>
|
||||||
|
.bg { fill: #f7f4ed; }
|
||||||
|
.panel { fill: #fffdf7; stroke: #cfc8ba; stroke-width: 2; }
|
||||||
|
.ink { fill: #181612; font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; }
|
||||||
|
.muted { fill: #6b6254; font-family: ui-monospace, "SFMono-Regular", Consolas, monospace; }
|
||||||
|
.box-title { fill: #181612; font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; text-anchor: middle; }
|
||||||
|
.box-sub { fill: #6b6254; font-family: ui-monospace, "SFMono-Regular", Consolas, monospace; text-anchor: middle; }
|
||||||
|
.accent { fill: #0f6f77; }
|
||||||
|
.warm { fill: #c06922; }
|
||||||
|
.cool { fill: #315f9f; }
|
||||||
|
.arrow-line { stroke: #5c554a; stroke-width: 3; fill: none; stroke-linecap: round; stroke-linejoin: round; }
|
||||||
|
.arrow-head { fill: #5c554a; }
|
||||||
|
.thin { stroke: #b8ad9b; stroke-width: 2; fill: none; }
|
||||||
|
</style>
|
||||||
|
</defs>
|
||||||
|
|
||||||
|
<rect class="bg" width="1200" height="560" rx="24"/>
|
||||||
|
|
||||||
|
<text class="ink" x="56" y="72" font-size="34" font-weight="650">MTP speculative decode</text>
|
||||||
|
<text class="muted" x="56" y="112" font-size="18">draft several tokens, verify them, keep the accepted prefix</text>
|
||||||
|
|
||||||
|
<rect class="panel" x="72" y="218" width="220" height="118" rx="18"/>
|
||||||
|
<text class="box-title" x="182" y="268" font-size="24" font-weight="650">context</text>
|
||||||
|
<text class="box-sub" x="182" y="304" font-size="17">prompt + KV cache</text>
|
||||||
|
|
||||||
|
<path class="arrow-line" d="M292 277 C342 277 342 196 395 196"/>
|
||||||
|
<path class="arrow-head" d="M420 196 L395 183 L395 209 Z"/>
|
||||||
|
|
||||||
|
<path class="arrow-line" d="M292 277 C342 277 342 374 395 374"/>
|
||||||
|
<path class="arrow-head" d="M420 374 L395 361 L395 387 Z"/>
|
||||||
|
|
||||||
|
<rect class="panel" x="440" y="138" width="270" height="116" rx="20"/>
|
||||||
|
<text class="box-title" x="575" y="188" font-size="24" font-weight="650">MTP lookahead</text>
|
||||||
|
<text class="box-sub" x="575" y="224" font-size="17">propose draft tokens</text>
|
||||||
|
|
||||||
|
<rect class="panel" x="440" y="316" width="270" height="116" rx="20"/>
|
||||||
|
<text class="box-title" x="575" y="366" font-size="24" font-weight="650">target model</text>
|
||||||
|
<text class="box-sub" x="575" y="402" font-size="17">verify in one pass</text>
|
||||||
|
|
||||||
|
<path class="arrow-line" d="M710 196 L738 196"/>
|
||||||
|
<path class="arrow-head" d="M763 196 L738 183 L738 209 Z"/>
|
||||||
|
|
||||||
|
<g transform="translate(782 170)">
|
||||||
|
<rect class="warm" x="0" y="0" width="54" height="54" rx="12"/>
|
||||||
|
<rect class="warm" x="66" y="0" width="54" height="54" rx="12"/>
|
||||||
|
<rect class="warm" x="132" y="0" width="54" height="54" rx="12"/>
|
||||||
|
<rect class="warm" x="198" y="0" width="54" height="54" rx="12"/>
|
||||||
|
<rect class="warm" x="264" y="0" width="54" height="54" rx="12"/>
|
||||||
|
<rect class="warm" x="330" y="0" width="54" height="54" rx="12"/>
|
||||||
|
</g>
|
||||||
|
<text class="muted" x="874" y="252" font-size="17">draft block</text>
|
||||||
|
|
||||||
|
<path class="arrow-line" d="M710 374 L748 374"/>
|
||||||
|
<path class="arrow-head" d="M773 374 L748 361 L748 387 Z"/>
|
||||||
|
|
||||||
|
<path class="arrow-line" d="M1030 232 L1030 340"/>
|
||||||
|
<path class="arrow-head" d="M1030 365 L1017 340 L1043 340 Z"/>
|
||||||
|
|
||||||
|
<rect class="panel" x="790" y="356" width="344" height="102" rx="18"/>
|
||||||
|
<rect class="accent" x="820" y="384" width="50" height="50" rx="11"/>
|
||||||
|
<rect class="accent" x="880" y="384" width="50" height="50" rx="11"/>
|
||||||
|
<rect class="accent" x="940" y="384" width="50" height="50" rx="11"/>
|
||||||
|
<rect class="cool" x="1000" y="384" width="50" height="50" rx="11"/>
|
||||||
|
<rect class="cool" x="1060" y="384" width="50" height="50" rx="11"/>
|
||||||
|
|
||||||
|
<path class="thin" d="M820 448 L990 448"/>
|
||||||
|
<text class="muted" x="820" y="492" font-size="16">accepted prefix, retry on mismatch</text>
|
||||||
|
<text class="muted" x="720" y="530" font-size="16">best case, one pass advances several tokens</text>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 4.0 KiB |
BIN
static/images/apps/clipbored/icon.png
Normal file
|
After Width: | Height: | Size: 6.6 KiB |
BIN
static/images/apps/clipbored/panel.png
Normal file
|
After Width: | Height: | Size: 244 KiB |
BIN
static/images/apps/feedme/demo.png
Normal file
|
After Width: | Height: | Size: 79 KiB |
BIN
static/images/apps/ihatepdfs/default-reading.png
Normal file
|
After Width: | Height: | Size: 875 KiB |
BIN
static/images/apps/ihatepdfs/icon.png
Normal file
|
After Width: | Height: | Size: 7.1 KiB |
135
static/js/site.js
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
(() => {
|
||||||
|
const $$ = (selector, root = document) => [...root.querySelectorAll(selector)];
|
||||||
|
|
||||||
|
function setupToc() {
|
||||||
|
const headings = $$(".post-content h2[id], .post-content h3[id]");
|
||||||
|
const links = $$("#TableOfContents a");
|
||||||
|
if (!headings.length || !links.length) return;
|
||||||
|
|
||||||
|
let queued = false;
|
||||||
|
|
||||||
|
function update() {
|
||||||
|
const y = window.scrollY + window.innerHeight * 0.35;
|
||||||
|
let activeId = headings[0].id;
|
||||||
|
|
||||||
|
for (const heading of headings) {
|
||||||
|
if (heading.offsetTop > y) break;
|
||||||
|
activeId = heading.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const link of links) {
|
||||||
|
link.classList.toggle(
|
||||||
|
"active",
|
||||||
|
decodeURIComponent(link.hash.slice(1)) === activeId
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
queued = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function queue() {
|
||||||
|
if (queued) return;
|
||||||
|
queued = true;
|
||||||
|
window.requestAnimationFrame(update);
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener("scroll", queue, { passive: true });
|
||||||
|
window.addEventListener("resize", queue);
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
function setupPostTags() {
|
||||||
|
const list = document.querySelector("[data-post-list]");
|
||||||
|
if (!list) return;
|
||||||
|
|
||||||
|
const rows = $$(".post-row", list);
|
||||||
|
const links = $$("[data-tag]");
|
||||||
|
const status = document.querySelector("[data-tag-filter-status]");
|
||||||
|
const clear = status?.querySelector("a");
|
||||||
|
|
||||||
|
function setUrl(tag) {
|
||||||
|
const url = new URL(window.location.href);
|
||||||
|
if (tag) {
|
||||||
|
url.searchParams.set("tag", tag);
|
||||||
|
} else {
|
||||||
|
url.searchParams.delete("tag");
|
||||||
|
}
|
||||||
|
window.history.pushState(null, "", `${url.pathname}${url.search}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyTag(tag) {
|
||||||
|
for (const link of links) {
|
||||||
|
link.classList.toggle("active", link.dataset.tag === tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
let visible = 0;
|
||||||
|
for (const row of rows) {
|
||||||
|
const tags = JSON.parse(row.dataset.tags || "[]");
|
||||||
|
const matched = !tag || tags.includes(tag);
|
||||||
|
row.hidden = !matched;
|
||||||
|
if (matched) visible += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status) {
|
||||||
|
const name = status.querySelector("[data-tag-filter-name]");
|
||||||
|
if (name) name.textContent = tag ? `[${tag}]` : "";
|
||||||
|
status.hidden = !tag;
|
||||||
|
status.classList.toggle("is-empty", visible === 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const link of links) {
|
||||||
|
link.addEventListener("click", (event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
const nextTag = link.classList.contains("active") ? null : link.dataset.tag;
|
||||||
|
applyTag(nextTag);
|
||||||
|
setUrl(nextTag);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clear) {
|
||||||
|
clear.addEventListener("click", (event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
applyTag(null);
|
||||||
|
setUrl(null);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
applyTag(new URLSearchParams(window.location.search).get("tag"));
|
||||||
|
}
|
||||||
|
|
||||||
|
function setupClickableRows() {
|
||||||
|
const rows = $$("[data-row-href]");
|
||||||
|
|
||||||
|
function openRow(row) {
|
||||||
|
const href = row.dataset.rowHref;
|
||||||
|
if (href) window.location.href = href;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const row of rows) {
|
||||||
|
row.addEventListener("click", (event) => {
|
||||||
|
if (event.target.closest("a")) return;
|
||||||
|
openRow(row);
|
||||||
|
});
|
||||||
|
|
||||||
|
row.addEventListener("keydown", (event) => {
|
||||||
|
if (event.key !== "Enter" && event.key !== " ") return;
|
||||||
|
event.preventDefault();
|
||||||
|
openRow(row);
|
||||||
|
});
|
||||||
|
|
||||||
|
row.addEventListener("pointerdown", (event) => {
|
||||||
|
if (event.target.closest(".item-links a")) return;
|
||||||
|
row.classList.add("is-pressed");
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const eventName of ["pointerup", "pointercancel", "pointerleave", "blur"]) {
|
||||||
|
row.addEventListener(eventName, () => row.classList.remove("is-pressed"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setupToc();
|
||||||
|
setupPostTags();
|
||||||
|
setupClickableRows();
|
||||||
|
})();
|
||||||
93
static/licenses/VT323-OFL.txt
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
Copyright 2011, The VT323 Project Authors (peter.hull@oikoi.com)
|
||||||
|
|
||||||
|
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||||
|
This license is copied below, and is also available with a FAQ at:
|
||||||
|
http://scripts.sil.org/OFL
|
||||||
|
|
||||||
|
|
||||||
|
-----------------------------------------------------------
|
||||||
|
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||||
|
-----------------------------------------------------------
|
||||||
|
|
||||||
|
PREAMBLE
|
||||||
|
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||||
|
development of collaborative font projects, to support the font creation
|
||||||
|
efforts of academic and linguistic communities, and to provide a free and
|
||||||
|
open framework in which fonts may be shared and improved in partnership
|
||||||
|
with others.
|
||||||
|
|
||||||
|
The OFL allows the licensed fonts to be used, studied, modified and
|
||||||
|
redistributed freely as long as they are not sold by themselves. The
|
||||||
|
fonts, including any derivative works, can be bundled, embedded,
|
||||||
|
redistributed and/or sold with any software provided that any reserved
|
||||||
|
names are not used by derivative works. The fonts and derivatives,
|
||||||
|
however, cannot be released under any other type of license. The
|
||||||
|
requirement for fonts to remain under this license does not apply
|
||||||
|
to any document created using the fonts or their derivatives.
|
||||||
|
|
||||||
|
DEFINITIONS
|
||||||
|
"Font Software" refers to the set of files released by the Copyright
|
||||||
|
Holder(s) under this license and clearly marked as such. This may
|
||||||
|
include source files, build scripts and documentation.
|
||||||
|
|
||||||
|
"Reserved Font Name" refers to any names specified as such after the
|
||||||
|
copyright statement(s).
|
||||||
|
|
||||||
|
"Original Version" refers to the collection of Font Software components as
|
||||||
|
distributed by the Copyright Holder(s).
|
||||||
|
|
||||||
|
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||||
|
or substituting -- in part or in whole -- any of the components of the
|
||||||
|
Original Version, by changing formats or by porting the Font Software to a
|
||||||
|
new environment.
|
||||||
|
|
||||||
|
"Author" refers to any designer, engineer, programmer, technical
|
||||||
|
writer or other person who contributed to the Font Software.
|
||||||
|
|
||||||
|
PERMISSION & CONDITIONS
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||||
|
redistribute, and sell modified and unmodified copies of the Font
|
||||||
|
Software, subject to the following conditions:
|
||||||
|
|
||||||
|
1) Neither the Font Software nor any of its individual components,
|
||||||
|
in Original or Modified Versions, may be sold by itself.
|
||||||
|
|
||||||
|
2) Original or Modified Versions of the Font Software may be bundled,
|
||||||
|
redistributed and/or sold with any software, provided that each copy
|
||||||
|
contains the above copyright notice and this license. These can be
|
||||||
|
included either as stand-alone text files, human-readable headers or
|
||||||
|
in the appropriate machine-readable metadata fields within text or
|
||||||
|
binary files as long as those fields can be easily viewed by the user.
|
||||||
|
|
||||||
|
3) No Modified Version of the Font Software may use the Reserved Font
|
||||||
|
Name(s) unless explicit written permission is granted by the corresponding
|
||||||
|
Copyright Holder. This restriction only applies to the primary font name as
|
||||||
|
presented to the users.
|
||||||
|
|
||||||
|
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||||
|
Software shall not be used to promote, endorse or advertise any
|
||||||
|
Modified Version, except to acknowledge the contribution(s) of the
|
||||||
|
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||||
|
permission.
|
||||||
|
|
||||||
|
5) The Font Software, modified or unmodified, in part or in whole,
|
||||||
|
must be distributed entirely under this license, and must not be
|
||||||
|
distributed under any other license. The requirement for fonts to
|
||||||
|
remain under this license does not apply to any document created
|
||||||
|
using the Font Software.
|
||||||
|
|
||||||
|
TERMINATION
|
||||||
|
This license becomes null and void if any of the above conditions are
|
||||||
|
not met.
|
||||||
|
|
||||||
|
DISCLAIMER
|
||||||
|
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||||
|
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||||
|
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||||
|
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||||
|
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||||