ExSystolic.Space behaviour (ex_systolic v0.2.0)

Copy Markdown View Source

Behaviour defining the spatial model of a systolic array.

A Space is responsible for four things:

  1. Coordinate validation -- is a given term a valid coordinate in this space?
  2. Neighbour relationships -- given a coordinate, what are its neighbours and which port connects to each?
  3. Port definitions -- which named ports does a coordinate expose?
  4. Link topology -- given a direction, produce the full set of internal and boundary links.

Design intent

The Space abstraction separates where things are from what they do. The PE behaviour, Clock, Link, and Trace modules remain completely unaware of the space. Only the Array module consults the space when constructing links or enumerating coordinates.

This means you can introduce a graph topology, a hierarchical layout, or a vector space in a future phase without touching execution logic.

Implementing a custom space

Any module that implements the callbacks below can serve as a space. The opts argument lets the same module serve multiple configurations (e.g. different grid sizes).

defmodule MyGraphSpace do
  @behaviour ExSystolic.Space

  @impl true
  def normalize({node_id, _} = coord), do: {:ok, coord}

  @impl true
  def neighbors(coord, adjacency) do
    Map.get(adjacency, coord, %{})
  end

  @impl true
  def ports(_coord, _opts), do: [:in, :out]

  @impl true
  def coords(adjacency), do: Map.keys(adjacency)

  @impl true
  def links(_adjacency, _direction), do: []
end

Summary

Callbacks

Returns all valid coordinates for a particular space configuration.

Returns the list of Link.t() for a given direction.

Returns the neighbours of a coordinate as a map of port => neighbour_coord.

Validates and normalizes a coordinate term.

Returns the list of port names that a coordinate exposes.

Types

coord()

@type coord() :: term()

Callbacks

coords(opts)

@callback coords(opts :: term()) :: [coord()]

Returns all valid coordinates for a particular space configuration.

The meaning and shape of opts is implementation-defined, but should match what is accepted by neighbors/2 and ports/2.

links(opts, direction)

@callback links(opts :: term(), direction :: atom()) :: [ExSystolic.Link.t()]

Returns the list of Link.t() for a given direction.

Includes both internal links (PE-to-PE) and boundary links (external-input-to-PE). The direction atom is space-specific (e.g. :west_to_east for Grid2D). Each space module defines the set of valid direction atoms and the resulting link topology.

Return an empty list if the direction is not recognized.

neighbors(coord, opts)

@callback neighbors(coord(), opts :: term()) :: %{optional(atom()) => coord() | nil}

Returns the neighbours of a coordinate as a map of port => neighbour_coord.

The shape and meaning of opts is defined by the concrete space implementation (for example, rows: / cols: for a rectangular grid, or an adjacency map for a graph space).

Ports that have no neighbour (boundary) should map to nil.

normalize(term)

@callback normalize(term()) :: {:ok, coord()} | {:error, term()}

Validates and normalizes a coordinate term.

Returns {:ok, coord} if the term is a valid coordinate in this space, or {:error, reason} otherwise. Normalization may transform the term into a canonical form.

ports(coord, opts)

@callback ports(coord(), opts :: term()) :: [atom()]

Returns the list of port names that a coordinate exposes.

The opts parameter is the same configuration that was passed when constructing the array's space (for example, grid dimensions). It is provided so implementations can vary the available ports with the configuration if needed.

This determines which input/output ports the PE at that coordinate will use.