Xenakis integration
Xenakis is an automated quantum circuit composer using genetic algorithms. Three sub-project variants are modelled in src/qpubench/schemas/mirrors/mqsdk_xenakis.py.
Sub-projects and genome types
| Sub-project | Location | Genome type | Key files |
|---|---|---|---|
| Original Xenakis | QST-HACK-Xenakis-exploration/ |
BitstringGenome |
genetic_algorithm.py, sweep_results.csv |
| qarchga (GA simplified) | QST-HACK-GA-simplified/ |
LayerGenome |
best_genome.json, history.csv |
| Xenakis+qNEAT | QST-HACK-Xenakis+qNEAT/ |
QNEATGenome |
qNEAT/genome.py, benchmark.py |
LayerGenome (qarchga)
Structured layer-based genome. Directly round-trips with best_genome.json.
import json
from qpubench.schemas import LayerGenome, CircuitSpec
# Parse best_genome.json
with open("results/run_20260129_230314/best_genome.json") as f:
raw = json.load(f)
genome = LayerGenome.from_struct(raw)
print(f"Depth: {genome.depth}")
print(f"Gate counts: {genome.gate_counts}") # {'rz': 1}
# Convert to a CircuitSpec for qpubench execution
spec: CircuitSpec = genome.to_circuit_spec()
print(spec.serialized)
# OPENQASM 2.0;
# include "qelib1.inc";
# qreg q[4];
# rz(2.269239756377628) q[3];
Serialisation round-trip
struct = genome.to_struct() # back to best_genome.json format
genome2 = LayerGenome.from_struct(struct)
assert genome2.depth == genome.depth
BitstringGenome (original Xenakis)
Binary string genome from sweep_results.csv. Decoding to an actual circuit requires the Xenakis library — this model is a data holder only.
from qpubench.schemas import BitstringGenome
# From a sweep_results.csv row (population=20, n_gates=4, n_qubits=4)
genome = BitstringGenome.from_sweep_row(
genome="10000000011111010110110111011011011001100010",
n_qubits=4,
n_gates=4,
fitness=1.135789,
energy=-1.1361891625101652,
is_best=True,
)
# gene_length is inferred: len(bitstring) / n_gates = 11 (6 gate + 5 qubit bits)
print(genome.gene_length) # 11
QNEATGenome
NEAT-style genome with innovation-numbered gate genes.
from qpubench.schemas import (
QNEATGenome, QNEATLayerEntry, QNEATGateGene, QNEATGateType,
)
genome = QNEATGenome(
n_qubits=4,
layers=[
QNEATLayerEntry(layer_index=0, gates=[
QNEATGateGene(innovation_number=1, gate_type=QNEATGateType.ROT,
qubit=0, parameters=[0.1, 0.2, 0.3]),
QNEATGateGene(innovation_number=2, gate_type=QNEATGateType.CNOT,
qubit=1, parameters=[]),
]),
],
fitness=-1.113,
)
# Compatibility distance between two genomes
dist = genome.compatibility_distance(other_genome)
# Convert to LayerGenome (ROT → rx/ry/rz) then to CircuitSpec
spec = genome.to_circuit_spec()
GA run configuration and history
Config snapshot
from qpubench.schemas import (
XenakisRunConfig, GAConfig, GenomeConfig, XenakisMolecule,
)
config = XenakisRunConfig(
seed=7,
backend="pennylane",
objective="vqe_molecule",
molecule=XenakisMolecule(
name="H2",
symbols=["H", "H"],
coordinates_angstrom=[(0.0, 0.0, 0.0), (0.0, 0.0, 0.7414)],
basis="sto-3g",
),
ga=GAConfig(
generations=25,
population_size=40,
elitism=2,
selection="tournament",
tournament_k=4,
crossover_rate=0.65,
mutation_rate=0.35,
lambda_depth=0.01,
lambda_2q=0.03,
),
genome=GenomeConfig(
max_layers=14,
init_layers=(4, 8),
gate_set_1q=["rx", "ry", "rz"],
gate_set_2q=["cx"],
),
param_restarts=2,
local_opt_steps=30,
)
History CSV
from qpubench.schemas import GAGenerationRecord
# Parse a history.csv row
record = GAGenerationRecord(
generation=5,
best_fitness=1.0858713,
mean_fitness=0.5245426,
best_depth=3,
best_n2q=0,
unique=40,
)
Full GA run result
from qpubench.schemas import GARunResult
result = GARunResult(
run_id="run_20260129_230314",
algo="qarchga",
config=config,
history=[...], # list[GAGenerationRecord]
best_genome=genome, # LayerGenome
best_fitness=1.1066843,
best_energy=-1.1067,
run_dir="results/run_20260129_230314/",
)
# Get a CircuitSpec from whatever genome type was stored
spec = result.best_circuit_spec()
# Link to a BenchmarkRecord
from qpubench.schemas import BenchmarkRecord, VQAConfig, VQAResult
vqa = VQAConfig(
problem_type="chemistry",
molecule="H2",
basis="sto-3g",
ga_run_id=result.run_id,
)
vqa_result = VQAResult(final_eigenvalue=result.best_energy)
XenakisMolecule ↔ MolecularGeometry
XenakisMolecule uses coordinates_angstrom as a list of (x, y, z) tuples (matching the YAML config format). MolecularGeometry (Cebule) uses a flat list.
flat = mol.flat_coordinates() # [x0, y0, z0, x1, y1, z1, …]
Tagging benchmark records from evolutionary runs
Use VQAConfig.ga_run_id, genome_hash, and best_complexity to link a qpubench record back to a Xenakis run:
from qpubench.schemas.utils.hashing import stable_hash # qarchga utility
vqa = VQAConfig(
problem_type="chemistry",
molecule="H2",
algorithm="qarchga",
optimizer="tournament_selection",
ga_run_id="run_20260129_230314",
genome_hash=stable_hash(genome.to_struct()),
)
vqa_result = VQAResult(
best_complexity=0.01 * genome.depth + 0.03 * genome.count_2q(),
final_eigenvalue=-1.1067,
)