Skip to main content

QuYAML: a readable, compact quantum circuit format with Qiskit converter and CLI

Project description

QuYAML v1.0: A Human-Readable Standard for Quantum Circuits

QuYAML is a token-efficient, human-readable data format for defining quantum circuits, designed for the age of AI-driven quantum development.

Tests Python License Release QASM3 Import Benchmarks

This repository contains the official Python tooling for the QuYAML format. The parser targets the v0.4 specification. It converts .quyaml files or strings into executable Qiskit QuantumCircuit objects and provides a CLI for validation, conversion, diffs, and benchmarking.

Highlights in v1.0:

  • Native Parameterization: High-performance sweeps using Qiskit parameters.
  • Opt-Out Provenance: Auto-saving of results for reproducibility.
  • Zero-Trust Security: Secure backend resolution.
  • Structured control flow: if/elif/else, while, and for blocks
  • Composite conditions with && and ||
  • Safer YAML surface: rejects anchors, aliases, custom tags, and merge keys
  • Strict schema and canonical formatter
  • CI-friendly parse-time gates and token-count gates

Why QuYAML?

As we increasingly use Large Language Models (LLMs) to assist in quantum development, the verbosity of standard formats like JSON becomes a bottleneck. QuYAML solves this by being:

  • Token-Efficient: Achieves 73% fewer tokens compared to standard Qiskit JSON (measured with exact GPT-4 tokenization)
  • Human-Readable: Clean, minimal syntax makes it easy for researchers to write, read, and share circuit designs
  • Structured & Extensible: Built on YAML, it's easy to extend with new features like metadata and parameters
  • Production-Ready: Comprehensive test suite with metamorphic testing ensures mathematical correctness

Performance Comparison

Using exact GPT-4 tokenization across 8 diverse quantum circuits:

Format Avg Tokens vs QuYAML Cost per 100K calls
OpenQASM 2.0 84.9 -3.5% better $254.64 (-$9)
OpenQASM 3.0 83.9 -4.8% better $251.64 (-$12)
QuYAML (Optimized) 87.9 baseline $263.64
JSON (Qiskit) 325.0 +72.9% worse $975.00 (+$711)

Key Findings:

  • 73% more efficient than JSON - Save $711 per 100K API calls vs JSON
  • ⚠️ 3.5% behind OpenQASM 2.0 - Costs $9 more per 100K calls
  • ⚠️ 4.8% behind OpenQASM 3.0 - Costs $12 more per 100K calls
  • Wins on simple circuits - 15.8% better than OpenQASM for non-parameterized circuits (Bell, GHZ, QFT, Teleportation)
  • 📊 Loses on parameterized circuits - 17.2% worse than OpenQASM for circuits with symbolic parameters (QAOA, VQE, Max-Cut, Grover)

Trade-off: QuYAML prioritizes human readability with symbolic parameters ($gamma, 2*$beta) over maximum token efficiency. OpenQASM's pre-evaluated numeric values (1.0, 2.4) are more token-efficient but less readable for humans and LLMs working with parameterized circuits.

See benchmarks/README.md for detailed per-circuit analysis.

QuYAML v1.0 at a glance

QuYAML supports both original and optimized syntax (fully backward compatible):

Optimized Syntax (Recommended for LLMs)

# Bell state – minimal and token-efficient
version: 0.4
circuit: bell
qubits: q[2]
bits: c[2]
ops:
  - h 0
  - cx 0 1
  - measure

Original Syntax (Human-Readable)

# Bell state – explicit and descriptive
version: 0.4
circuit: BellState
metadata:
  description: Creates an entangled Bell pair
qreg: q[2]
creg: c[2]
instructions:
  - h q[0]
  - cx q[0], q[1]
  - measure

Parameterized circuits

version: 0.4
circuit: qaoa_p1
qubits: q[2]
params: {gamma: 0.5, beta: 1.2}
ops:
  - h 0
  - h 1
  - cphase(2*$gamma) 0 1
  - rx(2*$beta) 0
  - rx(2*$beta) 1

Control flow

Conditional blocks with composite conditions:

version: 0.4
qubits: q[2]
bits: c[2]
ops:
  - h 0
  - {measure: {q: 0, c: 0}}
  - if:
      cond: "c[0] == 1 && c[1] == 0"   # && and || supported
      then:
        - x 1
      else:
        - h 1

While and for-loops:

version: 0.4
qubits: q[1]
bits: c[1]
ops:
  - reset 0         # string form
  - while:
      cond: "c[0] == 0"
      body:
        - h 0
        - {measure: {q: 0, c: 0}}
  - for:
      range: [0, 3]  # start, stop (exclusive)
      body:
        - rx(pi/4) 0

Reset and measure forms:

  • reset 0 or {reset: {q: 0}}
  • measure (all qubits) or {measure: {q: 0, c: 0}} for mid-circuit bit-targeted measurement

Installation

# Clone the repository
git clone https://github.com/Ahmed-Samir11/QuYAML.git
cd QuYAML

# Install dependencies
pip install -r requirements.txt

# Optional: Install with visualizer support
pip install .[viz]

Dependencies: pyyaml, qiskit, numpy (plus optional: tiktoken, jsonschema, matplotlib)

Usage

Basic Example

from quyaml_parser import parse_quyaml_to_qiskit

# Optimized syntax for token efficiency
quyaml_string = """
circuit: bell
qubits: q[2]
ops:
  - h 0
  - cx 0 1
"""

quantum_circuit = parse_quyaml_to_qiskit(quyaml_string)
print(quantum_circuit)
quantum_circuit.draw('mpl')

Advanced example: Parameterized QAOA circuit

from quyaml_parser import parse_quyaml_to_qiskit

quyaml_string = """
version: 0.4
circuit: QAOA_Ansatz
qubits: q[2]
params: {gamma: 0.5, beta: 1.2}
ops:
  - h 0
  - h 1
  - barrier
  - cx 0 1
  - ry(2*$gamma) 1
  - cx 0 1
  - barrier
  - rx(2*$beta) 0
  - rx(2*$beta) 1
"""

qc = parse_quyaml_to_qiskit(quyaml_string)
print(qc)

PennyLane compatibility

QuYAML circuits can be used directly with PennyLane via an optional helper:

import pennylane as qml
from quyaml_pennylane import parse_quyaml_to_pennylane

quyaml_string = """
circuit: bell
qubits: q[2]
ops:
  - h 0
  - cx 0 1
"""

my_template = parse_quyaml_to_pennylane(quyaml_string)  # returns a PennyLane quantum function

dev = qml.device("default.qubit", wires=2)

@qml.qnode(dev)
def pl_circuit():
    my_template(wires=[0, 1])
    return qml.expval(qml.Z(0))

print(pl_circuit())

Notes:

  • Requires installing optional dependencies: pip install pennylane pennylane-qiskit
  • Under the hood, QuYAML -> Qiskit QuantumCircuit -> PennyLane via qml.from_qiskit()

## Testing & verification

To verify the parser's correctness, install the test dependencies and run pytest:

```bash
pip install -r requirements.txt
pytest

The project includes comprehensive tests for v0.4 features and QASM3 round-trips.

Dev setup notes

  • QASM3 round-trip tests require the optional OpenQASM 3 loader: pip install qiskit_qasm3_import.
  • We pin Qiskit to the 2.x line for compatibility with dynamic circuits and optional integrations: qiskit>=2.0,<2.2 (see requirements.txt).
  • If you use PennyLane’s qiskit plugin, ensure versions are compatible with the Qiskit pin.
  1. Unit Tests (tests/test_valid_circuits.py): Basic circuit parsing with metamorphic testing
  2. Advanced Circuit Tests (tests/test_advanced_circuits.py): QML feature maps and QAOA ansatz circuits
  3. Advanced Algorithm Tests (tests/test_advanced_algorithms.py): QFT, QAOA Max-Cut, Toffoli decomposition, VQE, Quantum Teleportation
  4. Failure Tests (tests/test_invalid_syntax.py): Error handling and edge cases
  5. Property-Based Tests (tests/test_property_based.py): Hypothesis-based generative testing

Metamorphic Testing

The test suite includes metamorphic tests that prove the parser produces circuits mathematically identical to those created by Qiskit. These tests compare unitary matrices using Operator.equiv(), ensuring correctness for complex algorithms like:

  • Quantum Fourier Transform (QFT)
  • QAOA optimization ansätze
  • Variational Quantum Eigensolver (VQE) circuits
  • Quantum Teleportation protocol

Run tests with verbose output:

pytest -v

Benchmarks

Running Benchmarks

# Recommended: Optimized syntax with exact GPT-4 tokenization
python benchmarks/benchmark_optimized.py

# Original syntax baseline
python benchmarks/benchmark_with_tiktoken.py

# Test syntax compatibility
python benchmarks/test_optimized_syntax.py

Results Summary

Using exact GPT-4 tokenization (tiktoken) across 8 diverse circuits:

Circuit Type OpenQASM QuYAML Improvement
Simple Circuits 62.8 53.5 +15.8%
Bell State 46 37 +19.6%
GHZ (3 qubits) 55 46 +16.4%
Teleportation 60 51 +15.0%
QFT (3 qubits) 90 79 +12.2%
Parameterized 107.0 125.3 -17.2%
QAOA (p=1) 65 82 -26.2%
VQE Ansatz 78 95 -21.8%
Overall Average 84.9 87.9 -3.5%

Compared to JSON:

  • QuYAML: 87.9 tokens (average)
  • JSON: 325.0 tokens (average)
  • Reduction: 73.0%

Cost Impact (GPT-4 API @ $0.03/1K tokens):

  • QuYAML vs OpenQASM: -$9 per 100K calls (negligible)
  • QuYAML vs JSON: +$711 per 100K calls (significant savings)

See benchmarks/README.md and OPTIMIZATION_RESULTS.md for detailed analysis.

Additional Docs

PennyLane-style Benchmarks (QuYAML vs JSON vs OpenQASM 3)

We also evaluated a set of harder, PennyLane-style circuits (QAOA Max-Cut, QFT, Grover, Phase Estimation, random entangling layers, and a shallow quantum-volume-like circuit), using exact GPT-4 tokenization and measuring parse times:

  • Tokens are counted for the QuYAML source, a compact Qiskit JSON representation, and OpenQASM 3 generated from the same circuit.
  • Parse times measure: QuYAML parse (QuYAML → Qiskit), JSON decode only, and QASM3 parse via Qiskit’s qasm3.loads.

Update (includes v0.3 control-flow case and JSON→Qiskit rebuild timing):

  • New case: Conditional full-register equality (2q)
    • Tokens: QuYAML 131, JSON 442, QASM3 90
    • Parse times (ms): QuYAML 3.545, JSON decode 0.013, JSON rebuild 0.168, QASM3 parse 8.464

Refreshed averages across all tests:

  • Tokens: QuYAML 424.6, JSON 633.8, QASM3 372.9
  • QuYAML vs JSON: +33.0% fewer tokens than JSON
  • QuYAML vs QASM3: −13.9% more tokens than QASM3 (QASM3 wins on average)
  • Parse times (ms): QuYAML 6.269, JSON decode 0.051, JSON rebuild 0.672, QASM3 parse 21.475

Per-circuit token counts and deltas:

Circuit QuYAML JSON QASM3 QuYAML vs JSON QuYAML vs QASM3
QAOA Max-Cut p=3 (ring-6) 678 715 484 +5.2% -40.1%
QFT (5 qubits) 191 346 195 +44.8% +2.1%
Grover (4q, oracle=1111) 268 527 236 +49.1% -13.6%
Phase Estimation (3+1) 122 225 126 +45.8% +3.2%
QAOA Max-Cut p=5 (ring-6) 948 1123 772 +15.6% -22.8%
Random entangling (n=6, d=4) 738 1152 718 +35.9% -2.8%
Quantum-volume-like (n=6, layers=2) 321 540 362 +40.6% +11.3%

Per-circuit parse times (milliseconds):

Circuit QuYAML parse JSON decode QASM3 parse
QAOA Max-Cut p=3 (ring-6) 7.409 0.068 30.593
QFT (5 qubits) 3.061 0.040 13.344
Grover (4q, oracle=1111) 3.443 0.035 20.074
Phase Estimation (3+1) 1.737 0.018 8.313
QAOA Max-Cut p=5 (ring-6) 11.093 0.076 36.120
Random entangling (n=6, d=4) 7.730 0.083 45.472
Quantum-volume-like (n=6, layers=2) 4.352 0.037 21.815

Reproduce locally:

# Activate venv (Windows PowerShell)
& F:/repos/QuYAML/quyaml/Scripts/Activate.ps1

# Run the PennyLane-style benchmark
F:/repos/QuYAML/quyaml/Scripts/python.exe benchmarks/benchmark_pennylane.py

The latest run output is saved to benchmarks/_last_pennylane_results.txt.

CLI utilities

The CLI supports validate, lint, format, convert (QuYAML↔QASM3), diff, compile (compact JSON), count-tokens, and time-parse. Use a file path or - for stdin.

# Validate and print a summary
F:/repos/QuYAML/quyaml/Scripts/python.exe scripts/quyaml_cli.py validate examples/bell.quyaml --summary

# Lint with JSON Schema (if jsonschema installed) and parse check
F:/repos/QuYAML/quyaml/Scripts/python.exe scripts/quyaml_cli.py lint examples/bell.quyaml

# Format to canonical key order and style (in-place)
F:/repos/QuYAML/quyaml/Scripts/python.exe scripts/quyaml_cli.py format examples/bell.quyaml -w

# Convert QuYAML -> QASM3
F:/repos/QuYAML/quyaml/Scripts/python.exe scripts/quyaml_cli.py convert examples/bell.quyaml --to qasm3 -o -

# Convert QASM3 -> QuYAML
Get-Content examples/bell.qasm | F:/repos/QuYAML/quyaml/Scripts/python.exe scripts/quyaml_cli.py convert --from qasm3 - > bell.quyaml

# Structural diff (human)
F:/repos/QuYAML/quyaml/Scripts/python.exe scripts/quyaml_cli.py diff a.quyaml b.quyaml

# Structural diff (JSON)
F:/repos/QuYAML/quyaml/Scripts/python.exe scripts/quyaml_cli.py diff a.quyaml b.quyaml --json

# Compile to compact JSON (for benchmarking/token counting)
F:/repos/QuYAML/quyaml/Scripts/python.exe scripts/quyaml_cli.py compile examples/bell.quyaml -o -

# Count tokens across QuYAML/JSON/QASM3 (requires tiktoken)
F:/repos/QuYAML/quyaml/Scripts/python.exe scripts/quyaml_cli.py count-tokens examples/bell.quyaml --json --qasm3

# Measure average parse time (ms) over N iterations
F:/repos/QuYAML/quyaml/Scripts/python.exe scripts/quyaml_cli.py time-parse examples/bell.quyaml -n 200

CI and performance gates

We ship a CI-friendly benchmark harness scripts/bench_ci.py to keep parse-time regressions and token budgets under control.

Examples:

# Parse-time gates for small, static samples
F:/repos/QuYAML/quyaml/Scripts/python.exe scripts/bench_ci.py --iters 300 --max-ms 100 benchmarks/samples/ghz.quyaml benchmarks/samples/qft3.quyaml benchmarks/samples/control_flow.quyaml

# Parse-time gates for dynamic circuits (looser threshold)
F:/repos/QuYAML/quyaml/Scripts/python.exe scripts/bench_ci.py --iters 300 --max-ms 200 benchmarks/samples/teleportation.quyaml benchmarks/samples/ipea.quyaml benchmarks/samples/rus.quyaml

# Token-count gates for static samples (500 tokens)
F:/repos/QuYAML/quyaml/Scripts/python.exe scripts/bench_ci.py --max-tokens 500 benchmarks/samples/ghz.quyaml benchmarks/samples/qft3.quyaml benchmarks/samples/control_flow.quyaml

# Token-count gates for dynamic circuits (higher threshold e.g. 1000)
F:/repos/QuYAML/quyaml/Scripts/python.exe scripts/bench_ci.py --max-tokens 1000 benchmarks/samples/teleportation.quyaml benchmarks/samples/ipea.quyaml benchmarks/samples/rus.quyaml

Our GitHub Actions workflow .github/workflows/ci.yml runs a matrix across Windows and Ubuntu with Python 3.11/3.12, executes unit tests, and enforces the gates above. Adjust thresholds per your project needs.

YAML safety and restrictions (enforced)

To keep parsing safe and predictable for both humans and LLMs, the parser enforces a restricted YAML subset and resource guardrails:

  • Not allowed: YAML anchors (&name), aliases (*name), custom tags (!tag), and merge keys (<<:)
  • Size limits: sane ceilings on input length and nesting depth
  • Safe loader: prefers CSafeLoader when available, otherwise safe_load

Violations fail parsing with a clear error. See the JSON Schema in docs/schema/quyaml.schema.json for structural validation.

YAML safety and restrictions

To keep parsing safe and predictable for both humans and LLMs, the parser enforces a restricted YAML subset:

  • Allowed: plain mappings/sequences/scalars used by the QuYAML fields
  • Not allowed: YAML anchors (&name) and aliases (*name)
  • Not allowed: YAML custom tags (!tag)

If any of these are present, parsing will fail with a clear error. We also use yaml.safe_load under the hood.

Editor tip: VS Code users get schema validation and completion by opening this repo; it includes .vscode/settings.json mapping the QuYAML schema (docs/schema/quyaml.schema.json) to *.quyaml files.

QuYAML language reference (selected)

Field Names (Aliases Supported)

Both original and optimized syntax work identically:

Feature Original Optimized
Quantum register qreg: q[n] qubits: q[n]
Classical register creg: c[n] bits: c[n]
Parameters parameters: params:
Instructions instructions: ops:
Qubit index q[0] 0 (implicit)

Required Fields

circuit: CircuitName         # Circuit identifier
qubits: q[n]                 # Quantum register (n qubits) [or qreg]
ops:                         # List of operations [or instructions]
  - gate_name args

Optional Fields

bits: c[n]                   # Classical register (n bits) [or creg]
params:                      # Symbolic parameters [or parameters]
  theta: 0.5
  gamma: 1.2
metadata:                    # Circuit metadata (optional, ignored by parser)
  description: "Circuit description"
  author: "Your Name"

Supported Gates

Gate Original Syntax Optimized Syntax Description
Hadamard h q[0] h 0 Single-qubit Hadamard
Pauli-X x q[0] x 0 Bit flip gate
CNOT cx q[0], q[1] cx 0 1 Controlled-NOT
SWAP swap q[0], q[1] swap 0 1 Swap two qubits
RX rx(theta) q[0] rx(theta) 0 X-axis rotation
RY ry(theta) q[0] ry(theta) 0 Y-axis rotation
Controlled-Phase cphase(theta) q[0], q[1] cphase(theta) 0 1 Controlled phase gate
Barrier barrier barrier Prevent optimization across barrier
Measure measure measure Measure all qubits

Parameter substitution

Use $variable to reference parameters, and expressions are evaluated with NumPy:

params: {theta: 0.5, gamma: 1.2}
ops:
  - rx(2*$theta) 0
  - ry($gamma+pi/4) 1
  - cphase(2*(pi-$theta)) 0 1

Supported constants: pi, e

Operators: +, -, *, /, **, %

Functions: a minimal safe subset sufficient for common arithmetic; expression evaluation is sandboxed.

For full details, browse the parser source in quyaml_parser.py.

Contributing

Contributions are welcome! Please:

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/AmazingFeature)
  3. Add tests for your changes
  4. Ensure all tests pass (pytest)
  5. Commit your changes (git commit -m 'Add some AmazingFeature')
  6. Push to the branch (git push origin feature/AmazingFeature)
  7. Open a Pull Request

License

This project is licensed under the MIT License - see the LICENSE file for details.

Migration

Coming from v0.2 or v0.3? See the migration guide: docs/MIGRATION_0.3_to_0.4.md. For v0.4 to v1.0, no breaking changes were introduced to the circuit syntax, but the execution model now supports native parameterization and auto-saving.

Citation

If you use QuYAML in your research, please cite:

@software{quyaml2025,
  author = {Ahmed Samir},
  title = {QuYAML: A Token-Efficient Standard for Quantum Circuits},
  year = {2025},
  url = {https://github.com/Ahmed-Samir11/QuYAML},
  version = {1.0.0}
}

Contact

Ahmed Samir
GitHub: @Ahmed-Samir11
Project Link: https://github.com/Ahmed-Samir11/QuYAML


Built for the age of AI-driven quantum development. 🚀

⚡ Parameter Sweeps (Batch Execution)

QuYAML v1.0 introduces Native Parameterization, allowing you to define symbolic parameters in your circuit and sweep over them efficiently at runtime. Unlike previous versions that re-parsed the circuit for every value, v1.0 uses Qiskit's assign_parameters to bind values to a single circuit object, enabling high-performance batch execution.

1. Define a Parameterized Circuit

Use the $variable syntax in your QuYAML file. You can define a default value in params, but it will be overridden during a sweep.

# sweep_example.quyaml
version: '0.4'
circuit: native_sweep
qubits: q[1]
bits: c[1]
params:
  theta: 0  # Default value (will be symbolic in sweep)
ops:
  - h 0
  - rx($theta) 0
  - measure q[0], c[0]

2. Execute with a Sweep

Define a parameter_sweep dictionary in the execution config (or pass it programmatically).

from quyaml_parser import parse_quyaml_job
import numpy as np

# Load the circuit
with open("sweep_example.quyaml", "r") as f:
    job = parse_quyaml_job(f.read())

# Define the sweep at runtime
# This overrides any 'parameter_sweep' defined in the YAML file
job.execution['parameter_sweep'] = {
    'theta': np.linspace(0, np.pi, 10).tolist()
}

# Execute
# This generates 10 bound circuits from 1 symbolic circuit (High Performance)
result = job.execute(log_dir="./runs")

print(f"Job ID: {result.job_id}")
print(f"Results saved to: ./runs/quyaml_job_{result.job_id}.json")

🛡️ Provenance & Reproducibility

QuYAML v1.0 enforces Opt-Out Provenance. By default, every job execution automatically saves a comprehensive JSON manifest to disk, ensuring you never lose the context of an experiment.

The saved file (./runs/quyaml_job_<id>.json) contains:

  • Job ID & Timestamp: When and where it ran.
  • Backend Info: Which quantum computer (or simulator) was used.
  • Full Results: The counts or probabilities returned.
  • Metadata: The exact shots, parameter sweep values, and QuYAML version used.

To customize the save location, pass log_dir to the execute method:

result = job.execute(log_dir="./experiments/2025_qaoa_runs")

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

quyaml-1.0.0.tar.gz (42.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

quyaml-1.0.0-py3-none-any.whl (27.8 kB view details)

Uploaded Python 3

File details

Details for the file quyaml-1.0.0.tar.gz.

File metadata

  • Download URL: quyaml-1.0.0.tar.gz
  • Upload date:
  • Size: 42.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.4

File hashes

Hashes for quyaml-1.0.0.tar.gz
Algorithm Hash digest
SHA256 fb76c55a31e1e9940160a215a3064a06e184cf66ddcabf3295c9c99bcaf07550
MD5 22c82ec89d379f5c5d8c16eacab35abb
BLAKE2b-256 fce7e9001326c2e0b086e6c6be38143ace0f95378fb9a922866744c20bd8714a

See more details on using hashes here.

File details

Details for the file quyaml-1.0.0-py3-none-any.whl.

File metadata

  • Download URL: quyaml-1.0.0-py3-none-any.whl
  • Upload date:
  • Size: 27.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.4

File hashes

Hashes for quyaml-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 e6fb8636850e0430630c89cdcdfd9f8937c8990eb0f3a3d37f515c394bd1a687
MD5 51fc42e8a547ddf3c3839f9aafc8900e
BLAKE2b-256 644807d3b56ecde8b8776312cc5d70770253fd3bf2e4262f6c94c49c9b5544c3

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page