Experiment Basics
The fundamental structure of an Athanor experiment.
Every Athanor experiment is an Elixir module that implements two callbacks: experiment/0 and run/1.
Minimal Example
defmodule MyExperiment do
use Athanor.Experiment
alias Athanor.Runtime
alias Athanor.Experiment.{Definition, ConfigSchema}
@impl true
def experiment do
Definition.new()
|> Definition.name("my_experiment")
|> Definition.description("A minimal experiment example")
|> Definition.configuration(config_schema())
end
@impl true
def run(ctx) do
config = Runtime.config(ctx)
iterations = config["iterations"]
for i <- 1..iterations do
Runtime.progress(ctx, i, iterations)
Runtime.log(ctx, :info, "Running iteration #{i}")
result = do_work(i)
Runtime.result(ctx, "iteration_#{i}", result)
end
Runtime.complete(ctx)
end
defp config_schema do
ConfigSchema.new()
|> ConfigSchema.field(:iterations, :integer,
default: 10,
min: 1,
max: 100,
label: "Iterations",
description: "Number of iterations to run"
)
end
defp do_work(iteration) do
# Your experiment logic here
%{iteration: iteration, value: :rand.uniform()}
end
endThe experiment/0 Callback
This callback returns an Experiment.Definition struct that describes your experiment to the system:
@impl true
def experiment do
Definition.new()
|> Definition.name("unique_name") # Required: unique identifier
|> Definition.description("...") # Optional: shown in UI
|> Definition.configuration(config_schema) # Optional: parameter schema
end
The definition is read when:
- Listing available experiments in the UI
- Creating new instances
- Validating configuration values
The run/1 Callback
This is your experiment's entry point. It receives a RunContext struct:
@impl true
def run(ctx) do
# ctx contains:
# - ctx.run: the Run database record
# - ctx.instance: the Instance database record
# - ctx.configuration: map of config values
# - ctx.experiment_module: this module's name
# Always end with complete or fail
Runtime.complete(ctx)
endImportant Rules
- Always call
complete/1orfail/2— This finalizes the run status - Check for cancellation in long loops — Respect user requests to stop
- Don't swallow exceptions — Let them propagate so the run fails cleanly
- Use the Runtime API — Don't write directly to the database
Where to Put Experiments
Experiments are typically organized as umbrella apps:
athanor/
├── apps/
│ ├── athanor/ # Core (don't modify)
│ ├── athanor_web/ # Web UI (don't modify)
│ └── my_experiment/ # Your experiment
│ ├── lib/
│ │ └── my_experiment.ex
│ ├── test/
│ └── mix.exs
Or as modules within an existing app:
apps/experiments/
├── lib/
│ ├── experiments/
│ │ ├── experiment_one.ex
│ │ └── experiment_two.ex
│ └── experiments.ex
└── mix.exs
Either way, as long as the module is compiled and loaded, Athanor will discover it.