OCANNL: OCaml Compiles Algorithms for Neural Networks Learning
115
stars
4,683
commits
OCaml
primary language
Sep 10, 2026
updated
OCANNL is sponsored by Ahrefs! Visit the Ahrefs website.
for loops.einsum notation, integrated with shape inference. See comparison with einops for how this relates to the popular einops library. Supports static indexing, with a built-in operation to take a slice of the batch axes, integrated with shape inference. Extensible to more static indexing patterns as needs arise.
%op syntax extension.
%op stands for "operation", it's meant to express tensors: Tensor.t, and tensor functions.%cd syntax extension. It integrates the arrayjit backend library with shape inference.
%cd stands for "code", it's meant to express assignment computations: Assignments.comp.The CUDA backend requires at least CUDA version 12.8. The Metal backend requires at least MSL version 3.1. The HIP backend (AMD GPUs) requires ROCm / the AMD HIP SDK, via the hipjit bindings (opam install hipjit).
API documentation entry point.
A possible route to learning OCANNL:
Context.To use debugging as provided by configuring Utils.settings.debug_log_from_routines <- true with the cuda or hip backend, wrap the code that schedules work and synchronizes the GPU with Utils.capture_stdout_logs. Both GPU APIs expose device-side printf, but not fprintf; the runtime drains the device printing buffer to process stdout around synchronization. Synchronize the context inside the capture window so all device output is available before stdout is restored.
NOTE: debug logging from CUDA or HIP in complex settings is a bit tricky, as it involves another thread (domain) intercepting and filtering stdout. If facing issues, try the setting never_capture_stdout=true (see ocannl_config.reference).
See ROADMAP.md for the detailed schedule. GitHub issue assignments are the source of truth for release scope. v1.0 was released on August 13, 2026; the next target is v1.1, soft-dated August 24, 2026. Release dates are now project-internal and aspirational — through v1.0 they were pinned to conference deadlines.
Note (July 2026): v0.7 shipped on July 3, 2026 as the consolidated paper-ready release. v0.6.4 was skipped as a release — its work (concatenation, RoPE, transformer toy) shipped inside v0.7 — and v0.7.2 was consolidated into v0.7. v0.7.1 was dissolved: its AMD HIP backend (#411) shipped in v0.8; completed examples and tokenizer work landed subsequently, while remaining work follows the current GitHub milestone assignments.
Update (August 2026): v0.9 shipped on August 3, 2026 with its milestone fully closed, so v1.0 becomes the next paper-deadline release and v1.1 follows it. CUDA/HIP graph capture (#488) moved from v0.9 to v1.0, and the training/deployment utilities (#96, #97, #122, #465, #467) plus the
lib/design study (#435) moved from v1.0 to v1.1.Venue history (August 2026): the OCaml Workshop submission was not accepted — the article reads as a research report rather than an introductory demonstration, which put it outside that audience's scope. IFL 2026 was then considered and decided against as a poor fit. No conference submission is currently scheduled; the workshop article stays in the repository unchanged, as a historical artifact of the project at v0.8, and the formal core technical report continues as live work.
Update (August 13, 2026): v1.0 shipped, with its milestone fully closed (49 issues). Release dates are no longer pinned to paper deadlines: v1.1's soft target is August 24, 2026 (the OCaml Workshop date, used as an anchor rather than as a submission). v1.1 and v1.2 were rebalanced along a different seam than the original split — v1.1 is the compiler work plus the training-loop mechanics it needs, v1.2 the consumers and explorations, including the training experience a user sees. The sequence is
0.9 → 1.0 → 1.1 → 1.2.
array field of Tnode.t and the hosted memory mode (#333).a^b), generalizing tensor stacking; shifting (1^i=>i) and padding (i=>1^i) as fixed-index special cases (#49).Tile_mma, and explicit SIMD codegen.gpt2_mini arc: attribute the step (#531), then judge companion coverage at the site's arity (#569) — tuned step 107.4 → 52.4 ms on CUDA, 45.6 → 25.4 ms on HIP; batched/rank-3 sites seeded (#528).cc worker-pool uniformity on hybrid CPUs (#530).stretch (#544), config profiles (#559), the tuner's honest reference point (#552), routine-name collision policy (#513).gpt2_mini residue (#573, #574).lib/, PoPE, and inference binaries (#404, #435, #444, #97).For more details, see CHANGES.
cp.async on CUDA), budget-driven rematerialization, and the CUDA tensor-core profile's remaining shapes (fp8, ldmatrix over swizzled staging).gpt2_mini step roughly halved on both GPU backends by judging companion coverage at the site's arity, after a time-attribution profile located 70% of the step in five declined kernels.reproducible / performance), cc worker-pool uniformity on hybrid CPUs, and use-site row resolution narrowed to the leaf-tensor rule it always was.Schedule.op_legality oracle; an analytic roofline cost model that picks untuned defaults and pre-filters the autotune beam (advisory throughout).Stage, blocked tile flavors, epilogue twins, compacting strided-row staging, and clamped-window pooling.uniform that is total over shapes and now backs default parameter initialization.benchmarks/.cc / multidev_cc.Tile_mma microkernels, SIMD vector-extension codegen, and CUDA WMMA / Metal simdgroup-matrix / HIP rocWMMA tensor-core paths.Autotune.tune) with a digest-guarded cache, per-segment schedules, sketch seeding, and placement A/B tuning.%cd and %op support both curried and uncurried operator application syntax.%cd syntax (better projections inference).Condition-based events for CPU backends.Gccjit backend, single and double precision floats, code compiled as a monolithic update step function.OCANNL follows different design choices than OWL. For example:
Tensor implements "putting pieces together".Train has the optimization "frontend" and utilities.arrayjit, which may one day become a standalone library: generates the code, performs backend-agnostic optimizations (virtual nodes whose computation is inlined), implements the backends.Although the project is called ocannl, the main package is called neural_nets_lib, to avoid the (opam linter's) complaint that the name can be confused with other packages. This also clarifies that ocannl is composed of arrayjit and neural_nets_lib.
The dependency on cudajit is optional so you have to install it first to enable the CUDA backend. The dependency on metal is MacOS-specific but automatic.
The codebase is organized to separate user-facing recipes from framework internals:
lib/: User-facing recipes and utilities
train.ml - Training utilities and optimizersnn_blocks.ml - Neural network building blocks (transformers, attention, convolution, etc.)ocannl.ml - Re-exports for backward compatibilitytensor/: Framework internals (separate library ocannl_tensor)
tensor.ml/mli - Core tensor type and operationsshape.ml/mli - Shape inference systemoperation.ml - Tensor operations and DSL modulesppx_*.ml - Syntax extensions implementationarrayjit/: Low-level optimizing compiler with multiple backends
NOTE TO POTENTIAL CONTRIBUTORS: while I am might be slowly starting to work with PRs in separate branches rather than just a stream of commits on the main branch, design migrations will be broken into small PRs to avoid main (master) branch staleness; and many changes will still be commits on the main branch. We allow for failing tests on the main branch, although going forward this would hopefully be happening less. Tagged i.e. released versions of the code are guaranteed to work as well as the given stage of the project permitted, the policy is that all tests must pass for releases with the backend cc and must have the behavior expected of a backend with all other backends. We try to minimize discrepancy across backends but prefer more stringent tests even if some backends only pass them "in spirit" rather than with exact expectations of the cc backend.
OCANNL uses ppx_minidebug for debugging. Currently, we migrated to a per-file opt-in scheme for enabling ppx_minidebug at compile time (via environment variables, see the top of .ml files in question), and then a unified log level configuration (ocannl_log_level) for tuning logging at runtime. Due to the compile-time nature of the per-file settings, run dune clean after setting/exporting one of these environment variables.
OCaml
95.4%
Standard ML
2.3%
Python
1.2%
OCANNL: OCaml Compiles Algorithms for Neural Networks Learning
115
stars
4,683
commits
OCaml
primary language
Sep 10, 2026
updated
OCANNL is sponsored by Ahrefs! Visit the Ahrefs website.
for loops.einsum notation, integrated with shape inference. See comparison with einops for how this relates to the popular einops library. Supports static indexing, with a built-in operation to take a slice of the batch axes, integrated with shape inference. Extensible to more static indexing patterns as needs arise.
%op syntax extension.
%op stands for "operation", it's meant to express tensors: Tensor.t, and tensor functions.%cd syntax extension. It integrates the arrayjit backend library with shape inference.
%cd stands for "code", it's meant to express assignment computations: Assignments.comp.The CUDA backend requires at least CUDA version 12.8. The Metal backend requires at least MSL version 3.1. The HIP backend (AMD GPUs) requires ROCm / the AMD HIP SDK, via the hipjit bindings (opam install hipjit).
API documentation entry point.
A possible route to learning OCANNL:
Context.To use debugging as provided by configuring Utils.settings.debug_log_from_routines <- true with the cuda or hip backend, wrap the code that schedules work and synchronizes the GPU with Utils.capture_stdout_logs. Both GPU APIs expose device-side printf, but not fprintf; the runtime drains the device printing buffer to process stdout around synchronization. Synchronize the context inside the capture window so all device output is available before stdout is restored.
NOTE: debug logging from CUDA or HIP in complex settings is a bit tricky, as it involves another thread (domain) intercepting and filtering stdout. If facing issues, try the setting never_capture_stdout=true (see ocannl_config.reference).
See ROADMAP.md for the detailed schedule. GitHub issue assignments are the source of truth for release scope. v1.0 was released on August 13, 2026; the next target is v1.1, soft-dated August 24, 2026. Release dates are now project-internal and aspirational — through v1.0 they were pinned to conference deadlines.
Note (July 2026): v0.7 shipped on July 3, 2026 as the consolidated paper-ready release. v0.6.4 was skipped as a release — its work (concatenation, RoPE, transformer toy) shipped inside v0.7 — and v0.7.2 was consolidated into v0.7. v0.7.1 was dissolved: its AMD HIP backend (#411) shipped in v0.8; completed examples and tokenizer work landed subsequently, while remaining work follows the current GitHub milestone assignments.
Update (August 2026): v0.9 shipped on August 3, 2026 with its milestone fully closed, so v1.0 becomes the next paper-deadline release and v1.1 follows it. CUDA/HIP graph capture (#488) moved from v0.9 to v1.0, and the training/deployment utilities (#96, #97, #122, #465, #467) plus the
lib/design study (#435) moved from v1.0 to v1.1.Venue history (August 2026): the OCaml Workshop submission was not accepted — the article reads as a research report rather than an introductory demonstration, which put it outside that audience's scope. IFL 2026 was then considered and decided against as a poor fit. No conference submission is currently scheduled; the workshop article stays in the repository unchanged, as a historical artifact of the project at v0.8, and the formal core technical report continues as live work.
Update (August 13, 2026): v1.0 shipped, with its milestone fully closed (49 issues). Release dates are no longer pinned to paper deadlines: v1.1's soft target is August 24, 2026 (the OCaml Workshop date, used as an anchor rather than as a submission). v1.1 and v1.2 were rebalanced along a different seam than the original split — v1.1 is the compiler work plus the training-loop mechanics it needs, v1.2 the consumers and explorations, including the training experience a user sees. The sequence is
0.9 → 1.0 → 1.1 → 1.2.
array field of Tnode.t and the hosted memory mode (#333).a^b), generalizing tensor stacking; shifting (1^i=>i) and padding (i=>1^i) as fixed-index special cases (#49).Tile_mma, and explicit SIMD codegen.gpt2_mini arc: attribute the step (#531), then judge companion coverage at the site's arity (#569) — tuned step 107.4 → 52.4 ms on CUDA, 45.6 → 25.4 ms on HIP; batched/rank-3 sites seeded (#528).cc worker-pool uniformity on hybrid CPUs (#530).stretch (#544), config profiles (#559), the tuner's honest reference point (#552), routine-name collision policy (#513).gpt2_mini residue (#573, #574).lib/, PoPE, and inference binaries (#404, #435, #444, #97).For more details, see CHANGES.
cp.async on CUDA), budget-driven rematerialization, and the CUDA tensor-core profile's remaining shapes (fp8, ldmatrix over swizzled staging).gpt2_mini step roughly halved on both GPU backends by judging companion coverage at the site's arity, after a time-attribution profile located 70% of the step in five declined kernels.reproducible / performance), cc worker-pool uniformity on hybrid CPUs, and use-site row resolution narrowed to the leaf-tensor rule it always was.Schedule.op_legality oracle; an analytic roofline cost model that picks untuned defaults and pre-filters the autotune beam (advisory throughout).Stage, blocked tile flavors, epilogue twins, compacting strided-row staging, and clamped-window pooling.uniform that is total over shapes and now backs default parameter initialization.benchmarks/.cc / multidev_cc.Tile_mma microkernels, SIMD vector-extension codegen, and CUDA WMMA / Metal simdgroup-matrix / HIP rocWMMA tensor-core paths.Autotune.tune) with a digest-guarded cache, per-segment schedules, sketch seeding, and placement A/B tuning.%cd and %op support both curried and uncurried operator application syntax.%cd syntax (better projections inference).Condition-based events for CPU backends.Gccjit backend, single and double precision floats, code compiled as a monolithic update step function.OCANNL follows different design choices than OWL. For example:
Tensor implements "putting pieces together".Train has the optimization "frontend" and utilities.arrayjit, which may one day become a standalone library: generates the code, performs backend-agnostic optimizations (virtual nodes whose computation is inlined), implements the backends.Although the project is called ocannl, the main package is called neural_nets_lib, to avoid the (opam linter's) complaint that the name can be confused with other packages. This also clarifies that ocannl is composed of arrayjit and neural_nets_lib.
The dependency on cudajit is optional so you have to install it first to enable the CUDA backend. The dependency on metal is MacOS-specific but automatic.
The codebase is organized to separate user-facing recipes from framework internals:
lib/: User-facing recipes and utilities
train.ml - Training utilities and optimizersnn_blocks.ml - Neural network building blocks (transformers, attention, convolution, etc.)ocannl.ml - Re-exports for backward compatibilitytensor/: Framework internals (separate library ocannl_tensor)
tensor.ml/mli - Core tensor type and operationsshape.ml/mli - Shape inference systemoperation.ml - Tensor operations and DSL modulesppx_*.ml - Syntax extensions implementationarrayjit/: Low-level optimizing compiler with multiple backends
NOTE TO POTENTIAL CONTRIBUTORS: while I am might be slowly starting to work with PRs in separate branches rather than just a stream of commits on the main branch, design migrations will be broken into small PRs to avoid main (master) branch staleness; and many changes will still be commits on the main branch. We allow for failing tests on the main branch, although going forward this would hopefully be happening less. Tagged i.e. released versions of the code are guaranteed to work as well as the given stage of the project permitted, the policy is that all tests must pass for releases with the backend cc and must have the behavior expected of a backend with all other backends. We try to minimize discrepancy across backends but prefer more stringent tests even if some backends only pass them "in spirit" rather than with exact expectations of the cc backend.
OCANNL uses ppx_minidebug for debugging. Currently, we migrated to a per-file opt-in scheme for enabling ppx_minidebug at compile time (via environment variables, see the top of .ml files in question), and then a unified log level configuration (ocannl_log_level) for tuning logging at runtime. Due to the compile-time nature of the per-file settings, run dune clean after setting/exporting one of these environment variables.
OCaml
95.4%
Standard ML
2.3%
Python
1.2%