#hypergraph #open #data-parallel #tensor #terms #transformation #reverse

open-hypergraphs

Data-Parallel Algorithms for Open Hypergraphs

3 releases

new 0.1.2 Mar 11, 2025
0.1.1 Feb 27, 2025
0.1.0 Feb 27, 2025

#562 in Algorithms

Download history 70/week @ 2025-02-21 222/week @ 2025-02-28 102/week @ 2025-03-07

394 downloads per month

MIT license

115KB
2K SLoC

Open Hypergraphs

An implementation of Data Parallel Algorithms for String Diagrams. See also the Python implementation.

Features:

  • Algebraic construction by tensor and composition
  • Functors, including optic transformation for reverse differentiation of morphisms
  • Data-parallel diagram layering

Examples:

An example for defining a simple expression language (polynomial circuits) and evaluating its terms is given here.


lib.rs:

Open Hypergraphs

An OpenHypergraph is a GPU-accelerated datastructure for representing large networks (circuits) of operations having multiple inputs and outputs. For example:

      ─────────────┐    ┌───┐    ┌───┐
                   └────│   │    │   │────
                        │ + │────│ Δ │
          ┌───┐    ┌────│   │    │   │────
      ────│ - │────┘    └───┘    └───┘
          └───┘

The above term represents the expression (x + (-y), x + (-y)), where Δ : 12 is an explicit copying operation. Note that multiple outputs are "native" to the representation: in the textual description, the arithmetic expression is repeated twice, while copying is explicit in the open hypergraph representation.

Theory & Example

Open Hypergraphs are a representation of cospans of hypergraphs which correspond directly to morphisms of a symmetric monoidal category presented by generators and equations.

Pragmatically, this means one can define a set of objects (e.g. types) and operations (e.g. functions), and represent terms over those generators.

For example, we can define a simple expression language of arithmetic over integers and constrct the example term above as follows:

use open_hypergraphs::prelude::*;

#[derive(PartialEq, Eq, Debug, Clone, Copy)]
enum Operation {
    Negate,
    Add,
    Copy,
}

#[derive(PartialEq, Eq, Debug, Clone)]
enum Object {
    Int,
}

// Return the type (inputs and outputs) of the specified op
fn op_type(op: Operation) -> (SemifiniteFunction<Object>, SemifiniteFunction<Object>) {
    use Operation::*;
    use Object::*;

    // Objects (or "types") in an open hypergraph are *lists* of generating types.
    // 'int' is the singleton list Int
    let int = SemifiniteFunction::new(VecArray(vec![Int]));
    // ... 'int2' is the list `Int ● Int` - the `●` denotes *tensor product* in the category;
    // you can think of `X₁ ● X₂ ● ... ● Xn` as a list of generating objects `[X₁, X₂, ..., Xn]`.
    let int2 = SemifiniteFunction::new(VecArray(vec![Int, Int]));

    match op {
        // `Negate : Int → Int` has 1 integer input and 1 integer output
        Negate => (int.clone(), int),
        // `Add : Int ● Int → Int` a binary operation
        Add => (int2, int),
        // `Copy : Int → Int ● Int` has a single integer input, but *two* outputs.
        Copy => (int, int2),
    }
}

// Create the OpenHypergraph corresponding to a given op
fn op(op: Operation) -> OpenHypergraph<Object, Operation> {
    let (s, t) = op_type(op);
    OpenHypergraph::singleton(op, s, t)
}

// Construct the example term ((x + (-y), x + (-y))
fn example_term() -> OpenHypergraph<Object, Operation> {
    use Operation::*;
    use Object::*;
    let int = SemifiniteFunction::new(VecArray(vec![Int]));
    let id = OpenHypergraph::identity(int);
    id.tensor(&op(Negate)).compose(&op(Add)).unwrap().compose(&op(Copy)).unwrap()
}

GPU Acceleration

The OpenHypergraph datastructure is a flat, array-based representation which works on both CPU and GPU.

Algorithms and datastructures are all parametrised by a type K implementing ArrayKind. This type is used to represent a kind K : ** rather than a concrete type. For example, the kind of [Vec]-backed arrays is given by VecKind, and type aliases for this backend are exported in the prelude.

To add a new array backend, create a new type implementing ArrayKind. Type alises for Open Hypergraphs using the VecKind array backend.

Dependencies

~150KB