Equations

Asgard uses LEAN-style mathematical syntax for defining equations. This clean, functional notation supports arithmetic, calculus, elementary functions, stochastic differential equations, and lambda calculus.

Syntax Overview

Operation Syntax Example
Integration int(term, var) int(f, x)
Differentiation diff(term, var) diff(f, x)
Exponential exp(term) exp(x)
Square root sqrt(term) sqrt(x)
Logarithm log(term) log(x)
Absolute value abs(term) abs(x)
SDE sde(drift, diffusion, var) sde(r * X, sigma * X, t)
Parameter $name $alpha
Lambda abstraction var. body abstraction x. x * x
Application apply(fn, arg) apply(f, 5)
Arithmetic +, -, *, /, ^ x + y * 2

Creating Equations

from gimle.asgard.equation.equation import Equation

# Simple equation
eq = Equation.from_string("x + y = z")

# Fundamental theorem of calculus
eq = Equation.from_string("diff(int(f, x), x) = f")

# Differential equation with parameter
eq = Equation.from_string("diff(y, t) = $alpha * y")

Integration

Define integrals using the int(term, variable) syntax:

# Integrate f with respect to x
eq = Equation.from_string("int(f, x) = g")

# Nested integrals
eq = Equation.from_string("int(int(f, x), y) = g")

# Integration with arithmetic
eq = Equation.from_string("int(a + b, x) = int(a, x) + int(b, x)")

Differentiation

Define derivatives using the diff(term, variable) syntax:

# Differentiate f with respect to x
eq = Equation.from_string("diff(f, x) = g")

# Partial derivatives
eq = Equation.from_string("diff(diff(u, x), y) = 0")

# Chain with integration (fundamental theorem)
eq = Equation.from_string("diff(int(f, x), x) = f")

Elementary Functions

Elementary and smooth transcendental functions are supported as first-class syntax:

# Exponential
eq = Equation.from_string("diff(exp(x), x) = exp(x)")

# Square root
eq = Equation.from_string("sqrt(x * x) = abs(x)")

# Natural logarithm
eq = Equation.from_string("log(exp(x)) = x")

# Absolute value
eq = Equation.from_string("abs(x) = y")

# Trigonometric and hyperbolic functions
eq = Equation.from_string("sin(x) + cos(x) = y")
eq = Equation.from_string("sinh(x) + cosh(x) = y")
eq = Equation.from_string("tanh(x) = y")

These compile to corresponding circuit atomics (exp, sin, cos, sinh, cosh, tanh, power(0.5), log, abs) and can appear anywhere a term is expected. Discontinuous functions such as sign, floor, and Heaviside are not coefficient atomics and are rejected rather than silently approximated.

Parameters

Parameters are named values supplied at runtime, prefixed with $:

# Growth rate as a parameter
eq = Equation.from_string("diff(y, t) = $rate * y")

# Multiple parameters
eq = Equation.from_string("sde($mu * X, $sigma * X, t) = Y")

Parameters are resolved from a dictionary when the compiled circuit executes. This separates the equation structure from its numeric values, enabling parameter sweeps and optimization.

Stochastic Differential Equations

Define SDEs using the sde(drift, diffusion, variable) syntax:

# Geometric Brownian motion: dX = r*X*dt + sigma*X*dW
eq = Equation.from_string("sde($r * X, $sigma * X, t) = Y")

# Ornstein-Uhlenbeck process: dX = -theta*(X - mu)*dt + sigma*dW
eq = Equation.from_string("sde($theta * ($mu - X), $sigma, t) = Y")

# Constant drift and diffusion
eq = Equation.from_string("sde($drift, $sigma, t) = Y")

The sde operation compiles to circuits with stochastic_register for the diffusion term, enabling Monte Carlo simulation at runtime.

Compiling to Circuits

Equations can be compiled to executable circuits:

from gimle.asgard.equation.equation import Equation
from gimle.asgard.compile.compiler import compile_equation_to_circuit

# Define the equation
eq = Equation.from_string("diff(int(f, x), x) = f")

# Compile to circuit (variable isolation is automatic)
circuit, metadata = compile_equation_to_circuit(eq)

print(circuit)
# Output: composition(register(x), deregister(x)) [1->1]

The compiler handles variable isolation (solving for the unknown), trace wiring (connecting feedback loops), and structural validation automatically.

Supported differential isolation forms

Automatic isolation accepts a complete derivative chain of one state, such as diff(f,t) = f or g = diff(diff(f,t),t). It also accepts a bounded first-order affine form a * diff(f,t) + b = rhs, moving the residual b and numeric scale a before integration. Either equation orientation is supported.

The derivative scale must come from numeric literals. Signed literals, unary signs, and literal-only products and quotients are accepted when every divisor is nonzero and the effective scale and its reciprocal remain finite and nonzero. Thus 2 * (3 * diff(f,t)) and (2 * 3) * diff(f,t) agree. The affine form must contain exactly one first-order evolution derivative, and residual terms must be free of evolution derivatives. These transformations apply with general equation normalization enabled or disabled.

Supported input Equivalent bare-derivative form
2 * diff(f,t) = f diff(f,t) = 0.5 * f
diff(f,t) + f = 0 diff(f,t) = -1.0 * f
2 * diff(u,t) = diff(diff(u,x),x) diff(u,t) = 0.5 * diff(diff(u,x),x)

For time evolution with spatial derivatives, select the evolution axis. Spatial derivatives may appear on the opposite side or in the additive residual:

circuit = Equation.from_string(
    "2 * diff(u,t) - diff(diff(u,x),x) = 0"
).compile(evolve_along="t")

Unknown or zero derivative coefficients, nonlinear derivative uses, repeated derivative atoms, competing evolution derivatives, and affine higher-order derivatives raise CompilationError. For example, $a * diff(f,t) = f, diff(f,t) + diff(f,t) = f, and 2 * diff(diff(f,t),t) = f remain unsupported. Coefficient sums and powers, such as (2 + 3) * diff(f,t), are not evaluated by this bounded preparation; supply the resulting numeric literal explicitly. Scaling a composite side such as 2 * (diff(f,t) + f) = 0 is also outside this bounded fragment; write 2 * diff(f,t) + 2 * f = 0 explicitly.

Parentheses and exact same-axis derivative-after-integral cancellation are allowed. Initial-condition wiring retains its existing contract; higher bare derivative chains do not imply support for arbitrary boundary data. Legacy whole-side mixed derivatives such as diff(diff(f,x),y) = g retain their repeated-integral particular-solution behavior. When validating the selected differential side, integral-after-derivative expressions are not treated as inverse cancellation because they carry boundary terms.

This restriction applies to automatic differential isolation. Parsing an equation still accepts broader symbolic expressions; isolate=False retains direct circuit translation semantics.

Differential Equations

Express ODEs and PDEs naturally:

# Simple ODE: dy/dt = -y (exponential decay)
eq = Equation.from_string("diff(y, t) = -1.0 * y")

# Exponential growth: dy/dt = ky
eq = Equation.from_string("diff(y, t) = $k * y")

# Heat equation: du/dt = d^2u/dx^2
eq = Equation.from_string("diff(u, t) = diff(diff(u, x), x)")

# Wave equation
eq = Equation.from_string("diff(diff(u, t), t) = diff(diff(u, x), x)")

Arithmetic Operations

Standard mathematical operators with familiar precedence:

# Basic operations
eq = Equation.from_string("x + y = z")
eq = Equation.from_string("a - b = c")
eq = Equation.from_string("2 * x = y")
eq = Equation.from_string("a / b = c")
eq = Equation.from_string("x ^ 2 = y")

# Compound expressions (use parentheses for grouping)
eq = Equation.from_string("(x + 2) * y = z")

Operator Precedence:

  1. ^ (exponentiation) - highest
  2. *, / (multiplication, division)
  3. +, - (addition, subtraction) - lowest

Lambda Calculus

Define anonymous functions using abstraction and application:

# Square function
eq = Equation.from_string("apply(abstraction x. x * x, 5) = 25")

# Nested abstractions (curried function)
eq = Equation.from_string("abstraction x. abstraction y. x + y")

# Function application
eq = Equation.from_string("apply(abstraction x. x + 1, y) = z")

Identifiers and Numbers

Identifiers

# Single letter
eq = Equation.from_string("x = y")

# Multi-letter with underscores
eq = Equation.from_string("initial_value = final_value")

# With numbers (must start with letter)
eq = Equation.from_string("x1 + x2 = y")

Rules:

Numbers

# Integers
eq = Equation.from_string("x = 42")

# Decimals
eq = Equation.from_string("pi = 3.14159")

# Negative numbers
eq = Equation.from_string("rate = -0.05")

Next Steps