COMPILED EXTENSIONS

Plug-in ABI.

0.1.1 · development ABI revision 1

Register native CPU and CUDA kernels through a development C11 boundary. The public header is hyperfold/plugin/abi.h; the optional hyperfold/plugin/sdk.h layer adds small C++17 helpers.

Set each struct_size to sizeof(record_type) and export hyperfold_plugin_init. The loader requires an exact HYPERFOLD_PLUGIN_ABI_REVISION match. Hyperfold promises no API or ABI compatibility during development: the revision is a mismatch guard, and there are no legacy entry points or compatibility aliases. Rebuild plug-ins against the current headers.

LIFETIME

Loading is transactional.

  1. 01
    Resolve the entry point

    The host loads the shared library and resolves hyperfold_plugin_init.

  2. 02
    Register descriptors

    The initialiser calls host->register_kernel. Hyperfold copies kernel descriptors before each registration call returns.

  3. 03
    Publish metadata

    Fill hyperfold_plugin_metadata; publish state and shutdown before any initialisation that can fail. The host validates metadata before committing registrations. If initialisation or any registration fails, the whole load fails and no partial catalogue is published.

Strings, descriptor arrays and invocation views are borrowed only for the call that supplies them. Callback state remains owned by the plug-in. Hyperfold retains the library while a context, materialised process or in-flight execution can reach its callbacks, then calls shutdown(plugin_state) once if supplied.

Callbacks may run concurrently. Synchronise mutable plug-in state. A CPU callback must finish all work before returning. A CUDA callback must enqueue work on invocation.native_stream for invocation.cuda_device and report launch or argument errors before returning; do not substitute a private stream.

DESCRIPTORS

Register kernels to define an operation.

An extension owns an operation namespace; a plug-in is its binary deployment form. Each plug-in must register at least one kernel in its own namespace. An operation ID combines a dotted name, such as acme.double, and an independent major version.

The host exposes only register_kernel. The first hyperfold_kernel_spec for an operation defines its role/access/kind contract. Later implementations must agree on that contract, while backends, storage and compute types, locations and layouts may differ.

Registration and invocation arrays contain zero or more contiguous, ordered, read-only inputs followed by one or more contiguous, ordered, mandatory write or read-write outputs. Input index N is encoded as N; output index N as 0x80000000 | N, with indices from 0 to 0x7fffffff. Operand roles identify contract positions; buffer roles identify physical storage within one operand.

There is no separate operation declaration or validator callback in the ABI. Hyperfold checks operand contracts and physical buffer representations. The CPU callback or CUDA host launcher must check operation-specific shape constraints after dispatch and adaptation, before writing outputs or launching work. Python and typed C++ validators retain their existing behaviour.

hyperfold_operand_view.signature describes actual adapted storage using the same hyperfold_backend_operand record used at registration. Scalars have extents 1/1, vectors length/1, matrices rows/columns, and LU and eigen operands order/order. Vector, matrix, LU and eigen storage is dense; there are no format selectors or sparse formats in this ABI.

Every operand requires an explicit, nonzero compute_type and a complete size-tagged record. Storage and compute types are logical and backend-neutral: complex single precision uses HYPERFOLD_SCALAR_CF32 on host and CUDA, split or interleaved. Physical split buffers use HYPERFOLD_SCALAR_F32; interleaved buffers use HYPERFOLD_SCALAR_CF32. Pivot and swap-count buffers declare their own integer element types.

Low-precision storage includes HYPERFOLD_SCALAR_F16 (IEEE binary16), HYPERFOLD_SCALAR_BF16 (bfloat16) and HYPERFOLD_SCALAR_CF16 (complex32, two binary16 components). The built-in policies compute F16 and BF16 in F32, and CF16 in CF32. Split CF16 buffers use F16 elements; interleaved buffers use CF16 elements, with real then imaginary components. Compile against the headers from the host installation, since this reference describes 0.1.1 and the development ABI does not guarantee compatibility across releases.

Kernel registrations carry no declarative numerical metadata. Plug-in descriptions expose description.numerics is None and implementation.numerics is None; authors must document algorithmic accuracy and stability guarantees.

C11

A complete single-kernel plug-in.

This example registers acme.double major version 1 for dense, pageable-host float32 vectors. It validates the size-tagged invocation before using borrowed storage.

Cacme_plugin.c
#include <hyperfold/plugin/abi.h>
#include <stdint.h>

#define OP_NAME "acme.double"

static hyperfold_operation_id op_id(void) {
  hyperfold_operation_id id = {
      sizeof(hyperfold_operation_id),
      {OP_NAME, sizeof(OP_NAME) - 1}, 1};
  return id;
}

static const hyperfold_backend_operand cpu_operands[] = {
    {sizeof(hyperfold_backend_operand),
     HYPERFOLD_OPERAND_ROLE_INPUT_0, HYPERFOLD_OPERAND_READ,
     HYPERFOLD_OPERAND_VECTOR, HYPERFOLD_LOCATION_PAGEABLE_HOST,
     HYPERFOLD_SCALAR_F32, HYPERFOLD_COMPLEX_NONE,
     HYPERFOLD_MATRIX_LAYOUT_NONE,
     HYPERFOLD_SCALAR_F32},
    {sizeof(hyperfold_backend_operand),
     HYPERFOLD_OPERAND_ROLE_OUTPUT, HYPERFOLD_OPERAND_WRITE,
     HYPERFOLD_OPERAND_VECTOR, HYPERFOLD_LOCATION_PAGEABLE_HOST,
     HYPERFOLD_SCALAR_F32, HYPERFOLD_COMPLEX_NONE,
     HYPERFOLD_MATRIX_LAYOUT_NONE,
     HYPERFOLD_SCALAR_F32}};

static hyperfold_status run(void *state,
    const hyperfold_invocation *call, hyperfold_error_sink *errors) {
  (void)state; (void)errors;
  if (!call || call->struct_size < sizeof(*call) ||
      call->backend != HYPERFOLD_BACKEND_CPU_SCALAR ||
      !call->operands || call->operand_count != 2)
    return HYPERFOLD_STATUS_INVALID_ARGUMENT;

  const hyperfold_operand_view *input = &call->operands[0];
  const hyperfold_operand_view *output = &call->operands[1];
  if (input->struct_size < sizeof(*input) ||
      output->struct_size < sizeof(*output) || !input->buffers ||
      !output->buffers || input->buffer_count != 1 ||
      output->buffer_count != 1 || input->extent0 != output->extent0 ||
      input->extent1 != 1 || output->extent1 != 1)
    return HYPERFOLD_STATUS_INVALID_ARGUMENT;
  const hyperfold_buffer_view *src = input->buffers;
  const hyperfold_buffer_view *dst = output->buffers;
  if (src->struct_size < sizeof(*src) || dst->struct_size < sizeof(*dst) ||
      src->role != HYPERFOLD_BUFFER_DATA ||
      dst->role != HYPERFOLD_BUFFER_DATA ||
      src->element_type != HYPERFOLD_SCALAR_F32 ||
      dst->element_type != HYPERFOLD_SCALAR_F32 ||
      src->element_size != sizeof(float) ||
      dst->element_size != sizeof(float) ||
      src->element_count != input->extent0 ||
      dst->element_count != output->extent0 ||
      (src->element_count && (!src->data || !dst->mutable_data)))
    return HYPERFOLD_STATUS_INVALID_ARGUMENT;

  const float *x = (const float *)src->data;
  float *out = (float *)dst->mutable_data;
  for (uint64_t i = 0; i < src->element_count; ++i) out[i] = 2.0F * x[i];
  return HYPERFOLD_STATUS_OK;
}

HYPERFOLD_PLUGIN_EXPORT hyperfold_status hyperfold_plugin_init(
    const hyperfold_host_api *host, hyperfold_plugin_metadata *metadata,
    hyperfold_error_sink *errors) {
  if (!host || host->struct_size < sizeof(*host) ||
      host->abi_revision != HYPERFOLD_PLUGIN_ABI_REVISION ||
      !host->register_kernel || !metadata)
    return HYPERFOLD_STATUS_INVALID_ARGUMENT;

  hyperfold_kernel_spec kernel = {sizeof(kernel), op_id(),
      HYPERFOLD_BACKEND_CPU_SCALAR, cpu_operands, 2, run, 0};
  hyperfold_status status = host->register_kernel(host->host_context, &kernel, errors);
  if (status != HYPERFOLD_STATUS_OK) return status;

  *metadata = (hyperfold_plugin_metadata){
      sizeof(*metadata), HYPERFOLD_PLUGIN_ABI_REVISION,
      {"acme", 4}, {"1.0.0", 5},
      0, 0, 0, 0};
  return HYPERFOLD_STATUS_OK;
}

C++17 SDK

Keep exceptions inside the library.

hyperfold/plugin/sdk.h is header-only. Only the C ABI crosses the shared-library boundary. Its helpers create borrowed strings and operation IDs, report errors, translate exceptions, and declare the entry point.

C++Complete C++17 example
#include <hyperfold/plugin/sdk.h>
#include <stdexcept>
using namespace Hyperfold::Plugin;

static const hyperfold_backend_operand cpu[] = {{
    sizeof(hyperfold_backend_operand),
    HYPERFOLD_OPERAND_ROLE_OUTPUT, HYPERFOLD_OPERAND_READ_WRITE,
    HYPERFOLD_OPERAND_SCALAR, HYPERFOLD_LOCATION_PAGEABLE_HOST,
    HYPERFOLD_SCALAR_I32, HYPERFOLD_COMPLEX_NONE,
    HYPERFOLD_MATRIX_LAYOUT_NONE,
    HYPERFOLD_SCALAR_I32}};

static hyperfold_status run(void *, const hyperfold_invocation *call,
                               hyperfold_error_sink *errors) {
  return translate_exceptions(errors, [&] {
    if (!call || call->struct_size < sizeof(*call) ||
        call->backend != HYPERFOLD_BACKEND_CPU_SCALAR ||
        call->operand_count != 1 || !call->operands)
      throw std::runtime_error("invalid toggle invocation");
    const auto &operand = call->operands[0];
    if (operand.struct_size < sizeof(operand) || operand.buffer_count != 1 ||
        !operand.buffers || operand.extent0 != 1 || operand.extent1 != 1) throw std::runtime_error("invalid toggle operand");
    const auto &buffer = operand.buffers[0];
    if (buffer.struct_size < sizeof(buffer) || !buffer.mutable_data ||
        buffer.role != HYPERFOLD_BUFFER_DATA ||
        buffer.element_type != HYPERFOLD_SCALAR_I32 ||
        buffer.element_count != 1 || buffer.element_size != sizeof(int32_t))
      throw std::runtime_error("invalid toggle storage");
    auto *value = static_cast<int32_t *>(buffer.mutable_data);
    *value = *value == 1 ? 0 : 1;
  });
}

HYPERFOLD_PLUGIN_ENTRY(const hyperfold_host_api *host,
                          hyperfold_plugin_metadata *metadata,
                          hyperfold_error_sink *errors) {
  if (!host || host->struct_size < sizeof(*host) ||
      host->abi_revision != HYPERFOLD_PLUGIN_ABI_REVISION ||
      !host->register_kernel || !metadata)
    return HYPERFOLD_STATUS_INVALID_ARGUMENT;

  const auto id = operation_id("acme.toggle", 1);
  const hyperfold_kernel_spec kernel{
      sizeof(hyperfold_kernel_spec), id, HYPERFOLD_BACKEND_CPU_SCALAR,
      cpu, 1, run, nullptr};
  hyperfold_status status = host->register_kernel(host->host_context, &kernel, errors);
  if (status != HYPERFOLD_STATUS_OK) return status;

  *metadata = hyperfold_plugin_metadata{
      sizeof(hyperfold_plugin_metadata), HYPERFOLD_PLUGIN_ABI_REVISION,
      string("acme"), string("1.0.0"),
      0, 0, nullptr, nullptr};
  return HYPERFOLD_STATUS_OK;
}
HelperPurpose
Hyperfold::Plugin::string(value)Creates a borrowed hyperfold_string from a std::string_view.
set_error(sink, status, message)Calls a compatible error sink when one is available.
translate_exceptions(errors, callback)Returns the callback status, maps void to OK, and converts escaped exceptions to runtime errors.
operation_id(name, major=1)Creates a fully size-tagged operation ID.
HYPERFOLD_PLUGIN_ENTRYDeclares the exported C entry point from C++.

ABI REFERENCE

Types, enumerations and constants.

Core records

SymbolFields or values
hyperfold_statusHYPERFOLD_STATUS_OK 0, HYPERFOLD_STATUS_INVALID_ARGUMENT 1, HYPERFOLD_STATUS_UNSUPPORTED 2, HYPERFOLD_STATUS_RUNTIME_ERROR 3, HYPERFOLD_STATUS_INTERNAL_ERROR 4.
hyperfold_stringdata, byte size; no terminator is required.
hyperfold_error_sinkstruct_size, context, set_error.
hyperfold_operation_idstruct_size, name, major.
ABI macrosHYPERFOLD_PLUGIN_EXPORT, HYPERFOLD_PLUGIN_ABI_REVISION = 1, HYPERFOLD_PLUGIN_INIT_SYMBOL.

Descriptor enumerations

EnumerationValues
hyperfold_operand_accessHYPERFOLD_OPERAND_READ, HYPERFOLD_OPERAND_WRITE, HYPERFOLD_OPERAND_READ_WRITE.
hyperfold_operand_roleHYPERFOLD_OPERAND_ROLE_INPUT_0, HYPERFOLD_OPERAND_ROLE_INPUT_1, HYPERFOLD_OPERAND_ROLE_INPUT_2; HYPERFOLD_OPERAND_ROLE_INDEX_MAX and HYPERFOLD_OPERAND_ROLE_OUTPUT_MASK; HYPERFOLD_OPERAND_ROLE_OUTPUT_0/HYPERFOLD_OPERAND_ROLE_OUTPUT, HYPERFOLD_OPERAND_ROLE_OUTPUT_1, HYPERFOLD_OPERAND_ROLE_OUTPUT_2. Use hyperfold_operand_input_role or hyperfold_operand_output_role for higher indices.
hyperfold_operand_kindHYPERFOLD_OPERAND_SCALAR, HYPERFOLD_OPERAND_VECTOR, HYPERFOLD_OPERAND_MATRIX, HYPERFOLD_OPERAND_LU, HYPERFOLD_OPERAND_EIGEN.
hyperfold_backend_kindHYPERFOLD_BACKEND_CPU_SCALAR, HYPERFOLD_BACKEND_CPU_SIMD, HYPERFOLD_BACKEND_CUDA.
hyperfold_data_locationHYPERFOLD_LOCATION_PAGEABLE_HOST, HYPERFOLD_LOCATION_PINNED_HOST, HYPERFOLD_LOCATION_CUDA_DEVICE.
hyperfold_scalar_typeHYPERFOLD_SCALAR_F16, HYPERFOLD_SCALAR_BF16, HYPERFOLD_SCALAR_F32, HYPERFOLD_SCALAR_F64, HYPERFOLD_SCALAR_I32, HYPERFOLD_SCALAR_I64, HYPERFOLD_SCALAR_U32, HYPERFOLD_SCALAR_U64, HYPERFOLD_SCALAR_CF16, HYPERFOLD_SCALAR_CF32, HYPERFOLD_SCALAR_CF64, HYPERFOLD_SCALAR_CI32, HYPERFOLD_SCALAR_CI64.
hyperfold_complex_layoutHYPERFOLD_COMPLEX_NONE, HYPERFOLD_COMPLEX_SPLIT, HYPERFOLD_COMPLEX_INTERLEAVED.
hyperfold_matrix_layoutHYPERFOLD_MATRIX_LAYOUT_NONE, HYPERFOLD_MATRIX_ROW_MAJOR, HYPERFOLD_MATRIX_COLUMN_MAJOR.
hyperfold_buffer_roleHYPERFOLD_BUFFER_DATA, HYPERFOLD_BUFFER_REAL, HYPERFOLD_BUFFER_IMAGINARY, HYPERFOLD_BUFFER_LU_PIVOTS, HYPERFOLD_BUFFER_LU_FACTORS, HYPERFOLD_BUFFER_LU_SWAP_COUNT, HYPERFOLD_BUFFER_EIGENVALUES, HYPERFOLD_BUFFER_EIGENVECTORS.

Descriptor and invocation records

RecordFields
hyperfold_backend_operandstruct_size, role/access/kind, location/storage type/complex layout/matrix layout, compute_type.
hyperfold_buffer_viewstruct_size, role, read-only data, mutable_data, element_count, element_size, element_type. Split-complex buffers use the real component type.
hyperfold_operand_viewstruct_size, signature, extent0, extent1, buffers, buffer_count.
hyperfold_invocationstruct_size, backend, cuda_device, native_stream, operands, operand_count.
hyperfold_kernel_specstruct_size, operation, backend, operands, operand_count, callback, callback_state.

CALLABLE SURFACE

Callbacks, metadata and host functions.

SymbolContract
hyperfold_kernel_callbackReceives callback state, the invocation and error sink. Validate borrowed views before use.
hyperfold_plugin_metadatastruct_size, abi_revision, plugin_id, plugin_version; inclusive minimum and maximum CUDA runtime versions (zero means unspecified); plug-in state and shutdown callback.
hyperfold_host_apistruct_size, abi_revision, opaque host_context, and register_kernel. Check the host size and exact revision before calling registration.
hyperfold_plugin_init / hyperfold_plugin_init_fnThe exported declaration and its matching initialiser function type.

Report the most specific status available and use the supplied error sink for a diagnostic. Never retain the error sink or pointers received through an invocation.

TOOLCHAIN

Build against the installed headers.

cmake_minimum_required(VERSION 3.18)
project(acme_plugin C)
find_package(Python COMPONENTS Interpreter REQUIRED)
execute_process(
  COMMAND "${Python_EXECUTABLE}" -c
          "import hyperfold; print(hyperfold.get_include())"
  OUTPUT_VARIABLE HYPERFOLD_INCLUDE_DIR OUTPUT_STRIP_TRAILING_WHITESPACE)
add_library(acme_plugin MODULE acme_plugin.c)
target_include_directories(acme_plugin PRIVATE "${HYPERFOLD_INCLUDE_DIR}")
set_target_properties(acme_plugin PROPERTIES C_STANDARD 11 C_STANDARD_REQUIRED YES)
set_target_properties(acme_plugin PROPERTIES PREFIX "")
from pathlib import Path
import hyperfold

context = (hyperfold.ContextBuilder()
           .load_plugin(Path("build/acme_plugin.so"))
           .build())
print(context.describe("acme.double"))

The example loads the Linux or macOS CMake module. On Windows, use the generated acme_plugin.dll path, including the configuration directory when using a multi-configuration generator.

Native installations expose the header-only CMake target Hyperfold::plugin_sdk via find_package(Hyperfold CONFIG REQUIRED). The C++17 SDK is a source helper that must be recompiled with the plug-in.

A C ABI plug-in has no Python ABI dependency and does not link against Hyperfold. Build for the target operating system, architecture, calling convention, CPU instruction baseline and C runtime; CUDA plug-ins must also declare a compatible runtime range. The host's runtime AVX2/AVX-512 selection does not make a plug-in compiled for a higher instruction baseline safe on older CPUs. CPU/CUDA requirements are inferred from registered backends. A CUDA registration fails to load on a CPU-only host or when its runtime is outside the declared range. CPU-only plug-ins leave both runtime bounds zero. Rebuild against the current headers whenever the binary contract changes.