vkML 0.1.0

Adding an operator

Every file that has to change, derived from the commit that added erfc — the cheapest operator this project can have.

What it costs

erfc is a unary function that slots into an existing shader's switch. It needs no new dispatch path, no new push-constant block and no new memory pattern. It touched 15 files and 145 lines. Anything harder touches strictly more.

That number is worth knowing before starting, and it is the reason the project's roadmap puts reducing this cost ahead of adding features.

The fifteen files

Identity — the graph's vocabulary

FileWhat to add
include/vkml/graph/op.h An OpKind enumerator. Append it — do not insert. The value reaches the shader as a specialisation constant, so renumbering changes what an already cached pipeline computes.
src/graph/op.cppIts name, for errors and the coverage report.

Public API

include/vkml/api/ops.h The declaration, and a /// block explaining anything non-obvious about it. That block is rendered on this site, so it is documentation rather than a comment.
src/api/ops.cpp Shape and dtype checks, then the node. Compose from existing operators if you can — most losses do, and they needed no new kernel on either backend as a result.

Kernels — both backends

src/backend/cpu/kernels_*.cpp The CPU implementation. Required. CPU support must be a superset of Vulkan support, and a test enforces it — a GPU-only operator has no oracle.
shaders/*.comp The GLSL. Add an OP_ constant and a case, or a new shader if the memory pattern differs.
src/backend/vulkan/vulkan_backend.cpp The supports() entry and the dispatch. If the operator cannot run on the GPU, leave it out of supports() — it will then raise NotImplementedError, which is the designed behaviour rather than a gap.

Gradient

src/autograd/autograd.cpp A case building the gradient from forward operators. A dedicated backward kernel needs an argument for why the gradient genuinely cannot be composed — only four operators have earned one, and all four are scatters.

Python surface

bindings/module.cppThe nanobind binding.
python/vkml/__init__.pyThe re-export.

Tests and policy

tests/python/tolerance.py A tolerance entry with a citable justification, not a number that makes the test pass. Which kind — exact, ULP, relative or backward — is itself the argument.
tests/python/test_ops_vs_torch.pyAgreement with PyTorch.
tests/python/test_vulkan_kernels.pyAgreement with the CPU oracle.
tests/python/test_invariants.py Anything the operator promises that a value comparison cannot express — NaN behaviour, a bound, an exactness claim.
docs/coverage-baseline.json Regenerate only after confirming the run has no new gaps. Writing a baseline from a failing run accepts the regression it was meant to catch.

Documentation

The site's implementation table, backend chips and cross-links are generated, so a new operator appears automatically. What is not generated is what it does: add an entry to the matching file in web/content/, and the build will report the coverage percentage.

Examples are executed by a gate. Paste what the interpreter printed — that gate's first run found 17 invented outputs, including .shape written as a list when it returns a tuple.

Before pushing

ctest --preset release
python -m pytest tests/python -q
VKML_MIN_SPEC=1 python -m pytest tests/python -q

python scripts/check_layering.py
python scripts/check_push_constants.py
python scripts/check_cpu_only_build.py
python scripts/check_docs_examples.py
python scripts/check_docs_references.py
python scripts/check_versions.py
python scripts/coverage_matrix.py

docs/PRE-COMMIT-CHECKLIST.md is the authority, and every item on it exists because something got through without it.

Two habits the project treats as standard

vkML — Vulkan-first machine learning in C++20. Apache-2.0. Signatures on this page are generated from the installed module.