Operations Reference

Detailed reference for all atomic operations and combinators in Asgard circuits.

Operation Categories

Category Operations Purpose
Arithmetic add, multiplication, scalar Mathematical operations
Structure id, split, const, var Circuit structure
Differential register, deregister Integration and differentiation
Combinators composition, monoidal, trace Circuit composition

Combinators

composition(f, g)

Signature: If f : A → B and g : B → C, then composition(f, g) : A → C

Description: Sequential composition - apply f first, then g.

Category Theory: Morphism composition in the circuit category.

Example:

# Fundamental theorem: d/dx(∫f dx) = f
circuit = Circuit.from_string("composition(register(x), deregister(x))")

Execution Semantics:

outputs_f, state_1 = f(inputs, state_0)
outputs_g, state_2 = g(outputs_f, state_1)
return outputs_g, state_2

Identity Laws:

composition(f, id) ≡ f  # Right identity
composition(id, f) ≡ f  # Left identity

Associativity:

composition(f, composition(g, h)) ≡ composition(composition(f, g), h)

monoidal(f, g)

Signature: If f : A → B and g : C → D, then monoidal(f, g) : A⊕C → B⊕D

Description: Parallel composition - apply f and g independently.

Category Theory: Tensor product in monoidal category.

Example:

# Apply different scalars to two inputs
circuit = Circuit.from_string("monoidal(scalar(2.0), scalar(3.0))")

Execution Semantics:

inputs_f = inputs[:n]  # First n inputs
inputs_g = inputs[n:]  # Remaining m inputs
outputs_f, state_f = f(inputs_f, state)
outputs_g, state_g = g(inputs_g, state)
outputs = concatenate([outputs_f, outputs_g])
return outputs, state_g

Interchange Law:

composition(
    monoidal(f, g),
    monoidal(h, k)
) ≡ monoidal(
    composition(f, h),
    composition(g, k)
)

trace(f)

Signature: If f : A⊕X → B⊕X, then trace(f) : A → B

Description: Feedback loop - connect last output back to last input.

Category Theory: Trace operator in traced monoidal category.

Example:

# Solve differential equation via feedback
circuit = Circuit.from_string(
    "trace(composition(monoidal(id, register(t)), add))"
)

Execution Semantics (Fixed-point iteration):

feedback = initialize_feedback()  # Usually zeros
for i in range(max_iterations=100):
    all_inputs = concatenate([inputs, feedback])
    all_outputs, state = f(all_inputs, state)
    regular_outputs = all_outputs[:-1]
    new_feedback = all_outputs[-1]
    if converged(feedback, new_feedback, tolerance=1e-6):
        return regular_outputs, state
    feedback = new_feedback

Use Cases:

Arithmetic Operations

add

Signature: 2 → 1

Description: Element-wise addition of two streams.

Syntax:

circuit = Circuit.from_string("add")

Behavior:

Input 1: [a₀, a₁, a₂, ...]
Input 2: [b₀, b₁, b₂, ...]
Output:  [a₀+b₀, a₁+b₁, a₂+b₂, ...]

Example:

from gimle.asgard.circuit.circuit import Circuit
from gimle.asgard.runtime.stream import Stream, StreamState
import jax.numpy as jnp

circuit = Circuit.from_string("add")

input1 = Stream(data=jnp.array([[1.0, 2.0, 3.0]]), dim_labels=(), chunk_size=1)
input2 = Stream(data=jnp.array([[4.0, 5.0, 6.0]]), dim_labels=(), chunk_size=1)

outputs, state = circuit.execute([input1, input2], StreamState())
# Output: [5.0, 7.0, 9.0]

multiplication

Signature: 2 → 1

Description: Element-wise multiplication of two streams.

Syntax:

circuit = Circuit.from_string("multiplication")

Behavior:

Input 1: [a₀, a₁, a₂, ...]
Input 2: [b₀, b₁, b₂, ...]
Output:  [a₀·b₀, a₁·b₁, a₂·b₂, ...]

Use Cases:

scalar(c)

Signature: 1 → 1

Description: Multiply all elements by a constant scalar.

Syntax:

circuit = Circuit.from_string("scalar(2.5)")

Parameters:

Behavior:

Input:  [a₀, a₁, a₂, ...]
Output: [c·a₀, c·a₁, c·a₂, ...]

Example:

circuit = Circuit.from_string("scalar(2.0)")

input_stream = Stream(data=jnp.array([[1.0, 2.0, 3.0]]), dim_labels=(), chunk_size=1)
outputs, state = circuit.execute([input_stream], StreamState())
# Output: [2.0, 4.0, 6.0]

Structure Operations

id

Signature: 1 → 1

Description: Identity operation - passes input unchanged.

Syntax:

circuit = Circuit.from_string("id")

Behavior:

Input:  [a₀, a₁, a₂, ...]
Output: [a₀, a₁, a₂, ...]

Use Cases:

split

Signature: 1 → 2

Description: Duplicates the input to create two identical outputs (fanout).

Syntax:

circuit = Circuit.from_string("split")

Behavior:

Input:    [a₀, a₁, a₂, ...]
Output 1: [a₀, a₁, a₂, ...]
Output 2: [a₀, a₁, a₂, ...]

Example:

# Square a value: x → [x, x] → x * x
circuit = Circuit.from_string("composition(split, multiplication)")

Status: Stub implementation

const(c)

Signature: 0 → 1

Description: Generates a constant stream (ignores inputs).

Syntax:

circuit = Circuit.from_string("const(3.14)")

Parameters:

Behavior:

(no input)
Output: [c, 0, 0, 0, ...]  # Constant in first coefficient

Use Cases:

var(name)

Signature: 1 → 1

Description: Variable lookup or identity.

Syntax:

circuit = Circuit.from_string("var(x)")

Parameters:

Behavior:

# If var_values = {"x": 5.0}:
Input:  [a₀, a₁, a₂, ...]
Output: [5.0, 0, 0, ...]

# If "x" not in var_values:
Input:  [a₀, a₁, a₂, ...]
Output: [a₀, a₁, a₂, ...]  # Identity

Differential Operations

register(dim)

Signature: 1 → 1

Description: Integration along a dimension (shifts coefficients right).

Syntax:

circuit = Circuit.from_string("register(x)")

Parameters:

Behavior:

Input:  [a, b, c]
Output: [0, a, b]  # Shift right, pad with 0

Mathematical Meaning by Calculus:

Calculus Operation Formula
RealCalculus Indefinite integral ∫f(x)dx
StochasticCalculus Stochastic integral ∫f dW_t
DiscreteCalculus Cumulative sum Σf(n)

Stateful: Yes - saves last element as boundary for next chunk

Example:

# Integration: ∫x dx = x²/2
# Coefficients: x = [0, 1] → ∫x dx = [0, 0, 1]
circuit = Circuit.from_string("register(x)")

deregister(dim)

Signature: 1 → 1

Description: Differentiation along a dimension (shifts coefficients left).

Syntax:

circuit = Circuit.from_string("deregister(x)")

Parameters:

Behavior:

Input:  [a, b, c]
Output: [b, c]  # Shift left, drop first

Mathematical Meaning by Calculus:

Calculus Operation Formula
RealCalculus Derivative df/dx
StochasticCalculus Ill-defined (Brownian motion non-differentiable)
DiscreteCalculus Finite difference Δf(n) = f(n+1) - f(n)

Stateful: No - differentiation loses information

Example:

# Differentiation: d/dx(x²) = 2x
# Coefficients: x² = [0, 0, 2] → d/dx = [0, 4]
circuit = Circuit.from_string("deregister(x)")

Performance Characteristics

Operation Complexity Notes
add, multiplication O(n) Element-wise, highly parallel
scalar, id, var O(n) Trivial operations
const O(1) Constant generation
register, deregister O(n) Array slicing/concatenation
split O(n) Memory copy (JAX optimizes)
composition Sequential No parallelization
monoidal Parallel Fully parallelizable
trace Iterative Fixed-point iteration overhead

Optimization Tips:

Equation-Level Operations

The following operations are available in the equation grammar but do not have direct circuit atomic equivalents. They are handled during compilation via rewrite rules.

sqrt(term)

Description: Square root of a term. Used in equations such as the chemical Langevin equation.

Syntax:

eq = Equation.from_string("sqrt(x)")

Compiles to: power(0.5) circuit atomic during equation-to-circuit translation.

Example:

# Chemical Langevin equation diffusion term
eq = Equation.from_string("sqrt(N)")

Complete Examples

Example 1: Polynomial Differentiation

# Differentiate f(x) = x³ + 2x² + 3x + 4
# Coefficients: [4, 3, 2, 1]
# Expected: f'(x) = 3x² + 4x + 3

circuit = Circuit.from_string("deregister(x)")
input_stream = Stream(
    data=jnp.array([[4.0, 3.0, 2.0, 1.0]]),
    dim_labels=("x",),
    chunk_size=1
)

outputs, state = circuit.execute([input_stream], StreamState())
print(outputs[0].data)  # [3.0, 4.0, 3.0]

Example 2: Arithmetic Pipeline

# Circuit: 2x + 3
circuit = Circuit.from_string(
    "composition(monoidal(scalar(2.0), const(3.0)), add)"
)

input_stream = Stream(data=jnp.array([[5.0]]), dim_labels=(), chunk_size=1)
outputs, state = circuit.execute([input_stream], StreamState())
# Output: 2.0 * 5.0 + 3.0 = 13.0

Example 3: Solving dy/dt = -y

# Differential equation: dy/dt = -y
# Integral form: y = y₀ - ∫y dt
# Use trace for feedback

circuit = Circuit.from_string(
    "trace(composition("
    "  monoidal(id, composition(scalar(-1.0), register(t))),"
    "  add"
    "))"
)

Next Steps