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:
^(exponentiation) - highest*,/(multiplication, division)+,-(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:
- Must begin with a letter (a-z, A-Z)
- Can contain letters, numbers, underscores
- Case sensitive (
xandXare different)
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")