Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
2440c42
fix(uec): make ValidationResult iterable for log_envelope
m2papierz Jan 13, 2026
b60d23d
feat(utils): add deterministic JSON serialization
m2papierz Jan 13, 2026
f5a3ec2
feat(uec): add ProducerInfo for SDK stack tracking
m2papierz Jan 13, 2026
167dd1d
feat(uec): add production result types
m2papierz Jan 13, 2026
0a5af29
feat(schema): update UEC JSON schema
m2papierz Jan 13, 2026
988ba7f
chore(uec): update uec __init__ docstring
m2papierz Jan 13, 2026
134b953
chore: update schemas version
m2papierz Jan 13, 2026
f313344
fix(uec): schema-code consistency
m2papierz Jan 13, 2026
d7c2c9c
fix(qiskit): UEC 1.0 compliance and failure-path envelope
m2papierz Jan 13, 2026
3b09c9b
chore: update run schema version in Run
m2papierz Jan 13, 2026
8b36445
refactor(qiskit): split adapter.py into logical modules
m2papierz Jan 13, 2026
7309a8a
feat(qiskit-runtime): UEC compilance migration and module reorganisation
m2papierz Jan 13, 2026
0b6238a
feat(pennylane): UEC compilance migration and module reorganisation
m2papierz Jan 13, 2026
e304ea7
feat(cirq): UEC compilance migration and module reorganisation
m2papierz Jan 13, 2026
35da58b
feat: UEC compilance migration and module reorganisation
m2papierz Jan 13, 2026
534c825
chore(braket): comments clean-up, minor adjustments, and formatting
m2papierz Jan 13, 2026
7ee32e5
chore(cirq): comments clean-up, minor adjustments, and formatting
m2papierz Jan 13, 2026
463d0a7
chore(pennylane): comments clean-up, minor adjustments, and formatting
m2papierz Jan 13, 2026
e13ceb2
chore(qiskit): comments clean-up, minor adjustments, and formatting
m2papierz Jan 13, 2026
4ca5023
chore(qiskit-runtime): comments clean-up, minor adjustments, and form…
m2papierz Jan 13, 2026
34aaa70
chore: update the changelog file content
m2papierz Jan 13, 2026
d04a833
test: update uec tests to the new api
m2papierz Jan 13, 2026
fbdf4dd
docs: update uec-related documentation
m2papierz Jan 13, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/14.changed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Breaking: Align UEC Envelope 1.0 schemas/models and update all adapters to emit consistent producer/device/program/execution snapshots and unified artifact references.
72 changes: 51 additions & 21 deletions docs/concepts/uec.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,25 +8,47 @@ To keep runs reproducible and comparable, adapters produce a standardized **Exec

```
ExecutionEnvelope
├── device: DeviceSnapshot # Backend state and calibration
├── program: ProgramSnapshot # Circuit artifacts and hashes
├── execution: ExecutionSnapshot # Job metadata and settings
└── result: ResultSnapshot # Normalized measurement results
├── schema: "devqubit.envelope/1.0"
├── envelope_id, created_at # Envelope metadata
├── producer: ProducerInfo # SDK stack + versions
├── device: DeviceSnapshot # Backend state and calibration
├── program: ProgramSnapshot # Circuit artifacts and hashes
├── execution: ExecutionSnapshot # Job metadata and settings
└── result: ResultSnapshot # Normalized results (items[])
```

The envelope is stored as an artifact with role `envelope` (typically kind `devqubit.envelope.json`).

The envelope schema is `devqubit.envelope/1.0` and requires `envelope_id`, `created_at`, `producer`, and `result`.


## Snapshots

### ProducerInfo

Captures the complete SDK/toolchain stack that produced the envelope:

| Field | Description |
|-------|-------------|
| `name` | Producer name (always `"devqubit"`) |
| `engine_version` | devqubit-engine version |
| `adapter` | Adapter package name (e.g., `devqubit-qiskit`) |
| `adapter_version` | Adapter version |
| `sdk` | Lowest/primary SDK (e.g., `qiskit`, `braket-sdk`, `cirq`) |
| `sdk_version` | Primary SDK version |
| `frontends` | Ordered SDK stack from highest to lowest layer |
| `build` | Optional build identifier (commit/dirty flag) |


### DeviceSnapshot

Captures backend state at execution time:

| Field | Description |
|-------|-------------|
| `backend_name` | Backend identifier (e.g., "ibm_brisbane", "aer_simulator") |
| `backend_type` | "simulator" or "hardware" |
| `provider` | Provider name (e.g., "ibm_quantum", "aer") |
| `backend_type` | Backend type (e.g., `"hardware"`, `"simulator"`, `"emulator"`, `"unknown"`) |
| `provider` | Physical provider (e.g., `"ibm_quantum"`, `"aws_braket"`, `"local"`) |
| `num_qubits` | Number of qubits |
| `connectivity` | Qubit coupling map as edge list |
| `native_gates` | Supported gate set |
Expand All @@ -50,8 +72,8 @@ Captures circuit/program artifacts:

Each artifact in `logical`/`physical` includes:
- `format`: Circuit format (QPY, QASM3, etc.)
- `artifact_ref`: Reference to stored artifact
- `circuit_index`: Index in multi-circuit batch
- `ref`: Reference to stored artifact
- `index`: Index in multi-circuit batch
- `name`: Circuit name (if available)

### ExecutionSnapshot
Expand All @@ -66,22 +88,23 @@ Captures submission and job metadata:
| `execution_count` | Execution counter within run |
| `transpilation` | Transpilation info (mode, transpiled_by) |
| `options` | Raw execution options (args, kwargs) |
| `sdk` | SDK used for execution |
| `sdk` | Optional legacy field (prefer `producer.sdk` / `producer.frontends`) |

### ResultSnapshot

Captures normalized execution results:
Captures normalized execution results (always as a list of per-item results):

| Field | Description |
|-------|-------------|
| `result_type` | Type of result (counts, quasi_dist, expectation, etc.) |
| `raw_result_ref` | Reference to full serialized result artifact |
| `counts` | Normalized measurement counts per circuit |
| `num_experiments` | Number of experiments in result |
| `success` | Whether execution succeeded |
| `error_message` | Error message if failed |
| `success` | Overall execution success |
| `status` | `"completed"`, `"failed"`, `"cancelled"`, or `"partial"` |
| `items` | List of `ResultItem` (one per circuit/parameter-set) |
| `error` | Structured error info when failed |
| `raw_result_ref` | Reference to the full serialized SDK result (optional) |
| `metadata` | Additional result metadata |

Each `ResultItem` may contain one primary payload, e.g. `counts`, `quasi_probability`, or `expectation`. If `counts` are present, they include `format` metadata (source SDK + bit ordering) to make results comparable across SDKs.

## Why UEC matters

The Uniform Execution Contract makes it easier to:
Expand Down Expand Up @@ -135,10 +158,17 @@ if envelope_artifact:
device = envelope["device"]
print(f"Backend: {device['backend_name']}")
print(f"Qubits: {device['num_qubits']}")

# Access results
for counts in envelope["result"]["counts"]:
print(f"Circuit {counts['circuit_index']}: {counts['counts']}")
# Access results (per-item)
for item in envelope["result"]["items"]:
if "counts" in item:
counts = item["counts"]["counts"]
print(f"Item {item['item_index']}: {counts}")
elif "quasi_probability" in item:
dist = item["quasi_probability"]["distribution"]
print(f"Item {item['item_index']} quasi: {dist}")
elif "expectation" in item:
value = item["expectation"]["value"]
print(f"Item {item['item_index']} expval: {value}")
```

See {doc}`../guides/adapters` for what each SDK adapter captures.
See `../guides/adapters` for what each SDK adapter captures.
48 changes: 29 additions & 19 deletions docs/guides/adapters.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ All adapters produce a standardized **ExecutionEnvelope** containing four canoni

| Snapshot | Description |
|----------|-------------|
| `DeviceSnapshot` | Backend state, calibration, topology, and SDK versions |
| `DeviceSnapshot` | Backend state, calibration, topology, and provider properties |
| `ProgramSnapshot` | Logical and physical circuit artifacts with hashes |
| `ExecutionSnapshot` | Submission metadata, transpilation info, job IDs |
| `ResultSnapshot` | Normalized measurement counts or expectation values |
Expand Down Expand Up @@ -92,9 +92,9 @@ with track(project="bell-state") as run:
| QPY binary | `qiskit.qpy.circuits` | `program` |
| OpenQASM 3 | `source.openqasm3` | `program` |
| Circuit diagram | `qiskit.circuits.diagram` | `program` |
| Counts | `result.counts.json` | `results` |
| Full result | `result.qiskit.result_json` | `results` |
| Raw backend properties | `device.qiskit.raw_properties.json` | `device_raw` |
| Counts | `result.counts.json` | `result` |
| Full result | `result.qiskit.result_json` | `result_raw` |
| Raw backend properties | `device.qiskit.raw_properties.json` | `device_snapshot` |
| Execution envelope | `devqubit.envelope.json` | `envelope` |

---
Expand Down Expand Up @@ -149,9 +149,9 @@ job = sampler.run([qc],
| Transpiled QPY | `qiskit.qpy.circuits.transpiled` | `program` |
| OpenQASM 3 | `source.openqasm3` | `program` |
| PUB structure | `qiskit_runtime.pubs.json` | `program` |
| Sampler counts | `result.counts.json` | `results` |
| Estimator values | `result.qiskit_runtime.estimator.json` | `results` |
| Raw runtime properties | `device.qiskit_runtime.raw_properties.json` | `device_raw` |
| Sampler counts | `result.counts.json` | `result` |
| Estimator values | `result.qiskit_runtime.estimator.json` | `result` |
| Raw runtime properties | `device.qiskit_runtime.raw_properties.json` | `device_snapshot` |
| Execution envelope | `devqubit.envelope.json` | `envelope` |

---
Expand All @@ -177,9 +177,9 @@ with track(project="braket-experiment") as run:
|----------|------|------|
| OpenQASM 3 | `source.openqasm3` | `program` |
| Circuit diagram | `braket.circuits.diagram` | `program` |
| Counts | `result.counts.json` | `results` |
| Raw result | `result.braket.raw.json` | `results` |
| Raw device properties | `device.braket.raw_properties.json` | `device_raw` |
| Counts | `result.counts.json` | `result` |
| Raw result | `result.braket.raw.json` | `result_raw` |
| Raw device properties | `device.braket.raw_properties.json` | `device_snapshot` |
| Execution envelope | `devqubit.envelope.json` | `envelope` |

---
Expand Down Expand Up @@ -225,8 +225,8 @@ with track(project="sweep") as run:
|----------|------|------|
| Cirq JSON | `cirq.circuit.json` | `program` |
| Circuit diagram | `cirq.circuits.txt` | `program` |
| Counts | `result.counts.json` | `results` |
| Raw device properties | `device.cirq.raw_properties.json` | `device_raw` |
| Counts | `result.counts.json` | `result` |
| Raw device properties | `device.cirq.raw_properties.json` | `device_snapshot` |
| Execution envelope | `devqubit.envelope.json` | `envelope` |

---
Expand Down Expand Up @@ -261,8 +261,8 @@ with track(project="vqe") as run:
|----------|------|------|
| Tape JSON | `pennylane.tapes.json` | `program` |
| Tape diagram | `pennylane.tapes.txt` | `program` |
| Results | `result.pennylane.output.json` | `results` |
| Raw device properties | `device.pennylane.raw_properties.json` | `device_raw` |
| Results | `result.pennylane.output.json` | `result` |
| Raw device properties | `device.pennylane.raw_properties.json` | `device_snapshot` |
| Execution envelope | `devqubit.envelope.json` | `envelope` |

### Multi-Layer Stack
Expand Down Expand Up @@ -400,7 +400,7 @@ with track(project="custom-sdk") as run:
run.log_json(
name="counts",
obj={"00": 500, "11": 500},
role="results",
role="result",
kind="result.counts.json",
)
```
Expand All @@ -411,18 +411,27 @@ For full UEC compliance, create an ExecutionEnvelope:

```python
from devqubit.uec import (
ProducerInfo,
DeviceSnapshot,
ExecutionEnvelope,
ExecutionSnapshot,
ProgramSnapshot,
ResultSnapshot,
)

producer = ProducerInfo.create(
adapter="devqubit-custom",
adapter_version="0.1.0",
sdk="custom-sdk",
sdk_version="1.0.0",
frontends=["custom-sdk"],
)

# Build snapshots
device = DeviceSnapshot(
backend_name="custom_device",
backend_type="simulator",
provider="custom",
provider="local",
captured_at=utc_now_iso(),
)

Expand All @@ -435,13 +444,14 @@ program = ProgramSnapshot(
execution = ExecutionSnapshot(
submitted_at=utc_now_iso(),
shots=1000,
sdk="custom",
)

# Create and log envelope
envelope = ExecutionEnvelope(
schema_version="devqubit.envelope/0.1",
adapter="custom",
schema_version="devqubit.envelope/1.0",
envelope_id="01J0EXAMPLEENVELOPEID0000",
created_at=utc_now_iso(),
producer=producer,
device=device,
program=program,
execution=execution,
Expand Down
Loading