Isomorphism: A Backend-Agnostic C++ Tensor Library

Isomorphism is a hardware-agnostic C++ tensor math library with a pluggable backend architecture. Write your mathematical logic once with a unified API, and run it on Apple Silicon, the CPU or the GPU by choosing a backend at compile time.

The public API is fully decoupled from the backend (the Pimpl pattern), so switching from MLX to PyTorch is a one-word change in target_link_libraries, with no changes to application code. It is the foundation for my manifold optimisation libraries, Involute and the Riemannian Gaussian Sampler.

View on GitHub

🔌 Backends

BackendHardwareDependency
MLXApple Silicon (Metal GPU)MLX, with qr, eigh and svd on the GPU through metal-linalg
EigenCPU (any platform)Eigen3
TorchCPU / CUDA / MPSLibTorch

A SYCL / oneMKL backend for PC GPUs is in progress.

💻 Installation

Each backend is a separate Homebrew formula:

brew tap c0rmac/homebrew-isomorphism
brew install isomorphism-mlx      # or isomorphism-eigen, isomorphism-torch

Then in your CMakeLists.txt:

find_package(isomorphism REQUIRED)
target_link_libraries(my_app PRIVATE isomorphism::mlx)    # or ::eigen, ::torch

⚡️ Quick example

#include <isomorphism/math.hpp>
#include <isomorphism/tensor.hpp>
#include <iostream>

namespace iso = isomorphism;
using namespace iso::math;

int main() {
    // Create a batch of 4 random 3×3 matrices
    iso::Tensor A = random_normal({4, 3, 3}, iso::DType::Float32);
    iso::Tensor I = eye(3, iso::DType::Float32);

    // Batched matmul — shape stays {4, 3, 3}
    iso::Tensor result = matmul(A, broadcast_to(I, {4, 3, 3}));

    // Pull a scalar to CPU
    std::cout << "trace[0] = " << to_double(slice(trace(result), 0, 1, 0)) << "\n";
    return 0;
}

The API covers element-wise arithmetic, batched linear algebra (solve, svd, qr, inv, det, matrix_exp), reductions, random sampling and FFTs. Native MLX arrays, torch tensors and Eigen matrices can be wrapped and unwrapped without copying.