Skip to the content

A functional language with tensor shapes in its types.

Bagl checks every matrix operation at compile time, and its compiler writes derivatives for you. It is written in OCaml, and it also runs in the browser.

// Gradient descent on XOR, in your browser.
// The compiler writes the gradient of the loss,
// and it checks the shape of every step.
// The exact solution is [1, 1, -2].
let x = [[0.0, 0.0, 0.0],
         [0.0, 1.0, 0.0],
         [1.0, 0.0, 0.0],
         [1.0, 1.0, 1.0]] in
let y = [0.0, 1.0, 1.0, 0.0] in
let dloss = grad (fn w: tensor<float>[3] ->
  let e = dot(x, w) - y in
  dot(e, e)) in
letrec train = fn n ->
  if n < 0.5 then [0.0, 0.0, 0.0] else
  let w = train (n - 1.0) in
  w - 0.1 * dloss w
in train 200.0

Output: = [0.999987, 0.999987, -1.99996] : tensor<float>[3]

Run

Features

Shapes in the types

The size of each dimension is part of a tensor's type, so a product of a 2x3 and a 2x2 matrix doesn't compile.

let a = [[1.0, 2.0, 3.0],
         [4.0, 5.0, 6.0]] in
let b = [[1.0, 2.0],
         [3.0, 4.0]] in
dot(a, b)

Output: error: Dimension mismatch: 3 vs 2

Run

Derivatives from the compiler

grad turns a function into its derivative. Over tensors it uses reverse mode, which gives every partial derivative in one pass.

let x = [[1.0, 2.0], [3.0, 4.0]] in
let y = [1.0, 2.0] in
grad (fn w: tensor<float>[2] ->
  let e = dot(x, w) - y in
  dot(e, e)) [0.0, 0.0]

Output: = [-14, -20] : tensor<float>[2]

Run

Inferred types

Hindley-Milner inference, as in ML and OCaml, finds every type. You annotate only to fix a tensor's rank or to ask for floats.

let id = fn x -> x in
if id true then id 1 else id 2

Output: = 1 : int

Run

Install

Bagl builds from source with OCaml 5.2 and dune.

Installation and first steps

$ git clone https://github.com/aibrahm/bagl
$ cd bagl && dune build && dune install
$ baglc examples/train_xor.bagl