Runtime
The runtime executes circuits under different mathematical interpretations called calculi. The same circuit can be evaluated as Taylor series, stochastic processes, or discrete sequences.
The Three Calculi
RealCalculus
Taylor series expansion for deterministic computation. Use for ODEs, symbolic differentiation, and classical analysis.
StochasticCalculus
Monte Carlo simulation of stochastic differential equations. Use for finance, physics simulations, and SDEs.
DiscreteCalculus
Discrete sequences and finite differences. Use for time series, difference equations, and signal processing.
Execution Flow
Circuit (Parse Tree)
│
│ JAXCircuitCompiler.compile()
▼
JAX Function
│
│ execute(input_streams, state)
▼
Stream (coefficient representation)
│
│ StreamEvaluator.evaluate() with Calculus
▼
┌────┴────┬──────────────┐
▼ ▼ ▼
RealCalculus Stochastic Discrete
Taylor series SDE paths Sequences
Streams
Streams represent functions as coefficient expansions:
from gimle.asgard.runtime.stream import Stream
import jax.numpy as jnp
# Coefficients for f(x) = 2 + 3x + x^2/2
stream = Stream(
data=jnp.array([[2.0, 3.0, 1.0]]),
dim_labels=("x",),
chunk_size=1
)
Key Properties:
data: JAX array of coefficientsdim_labels: Dimension names (e.g.,("x",)or("x", "t"))chunk_size: Number of samples per chunk
RealCalculus (Deterministic)
Represents functions as Taylor series:
from gimle.asgard.runtime.stream_evaluator import StreamEvaluator, RealCalculus
import jax.numpy as jnp
# Coefficients: [2, 3, 1] means f(x) = 2 + 3x + x^2/2
stream = Stream(
data=jnp.array([[2.0, 3.0, 1.0]]),
dim_labels=("x",),
chunk_size=1
)
# Create evaluator
evaluator = StreamEvaluator(stream, {"x": RealCalculus(center=0.0)})
# Evaluate at specific points
x_points = jnp.array([0.0, 1.0, 2.0])
values = evaluator.evaluate(x=x_points)
for x, val in zip(x_points, values):
print(f"f({x}) = {val:.2f}")
# f(0.0) = 2.00
# f(1.0) = 5.50 (2 + 3 + 0.5)
# f(2.0) = 10.00 (2 + 6 + 2)
Use Cases:
- Solving ODEs symbolically
- Taylor series approximations
- Classical analysis and calculus
StochasticCalculus (Stochastic)
Simulates stochastic differential equations using Monte Carlo:
from gimle.asgard.runtime.stream_evaluator import StochasticCalculus
# Create stochastic calculus
calculus = StochasticCalculus(
drift=0.0, # Deterministic drift
diffusion=1.0, # Stochastic diffusion
n_paths=1000, # Number of Monte Carlo paths
dt=0.01, # Time step
seed=42, # Random seed for reproducibility
interpretation="ito" # "ito" (default) or "stratonovich"
)
# Generate Brownian motion paths
paths = calculus.generate_brownian_paths(t_start=0.0, t_end=2.0, n_steps=200)
# Returns: (1000, 201) array of Brownian motion paths
# Verify statistical properties
print(f"Mean at t=2: {jnp.mean(paths[:, -1]):.3f}") # ~ 0
print(f"Variance at t=2: {jnp.var(paths[:, -1]):.3f}") # ~ 2
Simulating SDEs
Solve stochastic differential equations of the form dX = mu(X,t)dt + sigma(X,t)dW:
# Ornstein-Uhlenbeck process: dX = -theta(X - mu)dt + sigma dW
theta = 0.5 # Mean reversion speed
mu = 1.0 # Long-term mean
sigma = 0.3 # Volatility
x0 = 2.0 # Initial value
calculus = StochasticCalculus(n_paths=10000, dt=0.01)
paths = calculus.simulate_sde(
x0=x0,
drift_fn=lambda x, t: -theta * (x - mu),
diffusion_fn=lambda x, t: sigma,
t_start=0.0,
t_end=5.0,
n_steps=500
)
Black-Scholes Example
# Geometric Brownian Motion: dS = mu*S*dt + sigma*S*dW
S0 = 100.0 # Initial stock price
mu = 0.05 # Expected return (5% annually)
sigma = 0.2 # Volatility (20% annually)
T = 1.0 # Time horizon
calculus = StochasticCalculus(n_paths=100000, dt=0.001, seed=123)
paths = calculus.simulate_sde(
x0=S0,
drift_fn=lambda x, t: mu * x,
diffusion_fn=lambda x, t: sigma * x,
t_start=0.0,
t_end=T,
n_steps=1000
)
# Price European call option
K = 100.0
payoffs = jnp.maximum(paths[:, -1] - K, 0)
option_price = jnp.mean(payoffs) * jnp.exp(-mu * T)
print(f"Option price: ${option_price:.2f}")
Use Cases:
- Financial modeling (option pricing, risk analysis)
- Physics simulations (Brownian motion, Langevin dynamics)
- Biology (population dynamics with noise)
Itô vs Stratonovich Interpretation
The interpretation parameter controls how the SDE is discretized:
Itô (default) — Uses the Euler-Maruyama method:
X(t+dt) = X(t) + drift(X(t), t) * dt + diffusion(X(t), t) * dW
- Single-pass evaluation per step (faster)
- Drift and diffusion evaluated at current state only
- Standard in mathematical finance
- Use
interpretation="ito"(the default)
Stratonovich — Uses the Heun predictor-corrector method:
K₁ = drift(X, t)*dt + diffusion(X, t)*dW
X̃ = X + K₁
K₂ = drift(X̃, t+dt)*dt + diffusion(X̃, t+dt)*dW (same dW)
X(t+dt) = X + 0.5 * (K₁ + K₂)
- Two-pass evaluation per step (more accurate, ~2x cost)
- Averages over start and predicted end state
- Standard in physics (preserves chain rule)
- Use
interpretation="stratonovich"
# Itô interpretation (default)
ito_calculus = StochasticCalculus(
n_paths=1000, dt=0.01, interpretation="ito"
)
# Stratonovich interpretation
strat_calculus = StochasticCalculus(
n_paths=1000, dt=0.01, interpretation="stratonovich"
)
When to choose which:
| Scenario | Interpretation |
|---|---|
| Finance (Black-Scholes, interest rates) | Itô |
| Physics (Langevin, Brownian motion) | Stratonovich |
| Fast prototyping / lower accuracy needed | Itô |
| Higher accuracy needed | Stratonovich |
Circuit-Driven SDEs
Asgard supports two ways to define stochastic differential equations:
1. Expression-based — Define drift and diffusion as Python functions:
calculus = StochasticCalculus(n_paths=1000, dt=0.01)
paths = calculus.simulate_sde(
x0=1.0,
drift_fn=lambda x, t: -0.5 * x,
diffusion_fn=lambda x, t: 0.3,
t_start=0.0, t_end=5.0, n_steps=500
)
2. Circuit-driven — Define the SDE as an equation and compile it:
from gimle.asgard.equation.equation import Equation
from gimle.asgard.compile.compiler import compile_equation_to_circuit
eq = Equation.from_string("Y = sde($drift, $sigma, X)")
circuit = compile_equation_to_circuit(eq)
In YAML examples, the circuit-driven approach uses the stochastic block:
equation: "Y = sde($drift, $sigma, X)"
stochastic:
calculus: stratonovich # or "ito"
n_paths: 1000
dt: 0.01
seed: 42
params:
drift: -0.5
sigma: 0.3
DiscreteCalculus (Sequences)
Represents discrete-time sequences:
from gimle.asgard.runtime.stream_evaluator import DiscreteCalculus
# Sequence: f(n) = n^2 for n = 0, 1, 2, 3
stream = Stream(
data=jnp.array([[0.0, 1.0, 4.0, 9.0]]),
dim_labels=("n",),
chunk_size=1
)
evaluator = StreamEvaluator(stream, {"n": DiscreteCalculus()})
# Evaluate at n = 2
result = evaluator.evaluate(n=2.0)
print(f"f(2) = {result}") # 4.0
Operations:
- Register (Integration): Cumulative sum
[a, b, c] -> [0, a, a+b] - Deregister (Differentiation): Finite differences
[a, b, c] -> [b-a, c-b]
Use Cases:
- Time series analysis
- Difference equations
- Signal processing
Mixed Calculi
Use different calculi for different dimensions:
# Discrete time, continuous space
evaluator = StreamEvaluator(
stream,
calculi={
"n": DiscreteCalculus(), # Discrete time
"x": RealCalculus(center=0.0) # Continuous space
}
)
# Evaluate at discrete time n=5, continuous position x=1.5
result = evaluator.evaluate(n=5.0, x=1.5)
Two-Phase Execution
Asgard separates compilation and execution for efficiency:
from gimle.asgard.circuit.circuit import Circuit
from gimle.asgard.runtime.stream import Stream, StreamState
# Phase 1: Compile once (slow)
circuit = Circuit.from_string("composition(register(x), deregister(x))")
# Phase 2: Execute many times (fast, JIT-compiled)
for input_data in dataset:
input_stream = Stream(data=input_data, dim_labels=("x",), chunk_size=1)
outputs, state = circuit.execute([input_stream], StreamState())
Benefits:
- Compile once, run many times
- JAX JIT optimization
- GPU/TPU acceleration
Performance Comparison
| Calculus | Speed | Memory | Accuracy |
|---|---|---|---|
| RealCalculus | Fast | Low | Exact (up to truncation) |
| StochasticCalculus | Slow | High | Statistical (1/sqrt(n_paths)) |
| DiscreteCalculus | Fast | Low | Exact |
Optimization Tips:
- Use fewer paths for testing (
n_paths=100) - Use more paths for production (
n_paths=10000+) - Leverage JAX's
vmapfor parallelization - Use larger
dtfor faster stochastic simulation (trade-off: accuracy)
Next Steps
- Equations - Define equations with LEAN syntax
- Circuits - Build computational circuits
- Getting Started - Complete walkthrough