Configuration with YAML
The one rule this repository follows everywhere: code is code, experiments are data. The program says how to do something. A YAML file says which instance to run: how many qubits, how many shots, which backend, which noise model, which random seed.
The anti-pattern
# run.py: every number is hard-coded
n_qubits = 4
shots = 4096
seed = 7
backend = "aer_simulator"
To try 5 qubits you edit the source. To compare three noise levels you copy the file three times. Six months later, nobody remembers which edited copy produced Figure 3.
The pattern
The inputs live in a YAML file:
# configs/example.yaml
n_qubits: 4
shots: 4096
seed: 7
backend:
name: aer_simulator
noise: null
and the program only reads and validates them:
# run.py
import sys
import yaml
from pydantic import BaseModel
class Config(BaseModel):
n_qubits: int
shots: int = 1024
seed: int = 0
with open(sys.argv[1]) as f:
cfg = Config(**yaml.safe_load(f))
python run.py configs/example.yaml
python run.py configs/example_5q.yaml
What you gain
Reproducibility. A result is (code version, config file). Commit the config and anyone can regenerate the figure exactly.
Sweeps without edits. A parameter study is a folder of YAML files or a short loop, and the program never changes.
Validation. The schema rejects
n_qubits: -3before a long job starts, with a clear message.One config, two languages. The same YAML file can be read by the Python and the C++ track, so the two can be compared on identical inputs.
Collaboration. A collaborator who does not read your code can still change and run an experiment.
Reading the same file in C++
// main.cpp
#include <yaml-cpp/yaml.h>
int main(int argc, char** argv) {
YAML::Node cfg = YAML::LoadFile(argv[1]);
const int n_qubits = cfg["n_qubits"].as<int>();
const int shots = cfg["shots"].as<int>(1024);
return 0;
}
Rules of thumb
Put every number that defines an experiment in YAML: sizes, seeds, shots, tolerances, backend names, noise parameters, file paths.
Keep secrets (IBM Quantum API tokens) out of YAML that gets committed. Use environment variables.
Load with
yaml.safe_load, neveryaml.load.Validate on load, and fail early.
Save the config alongside the results it produced.