Skip to content

Commit 574c384

Browse files
authored
Merge pull request #14 from devqubit-labs/feat/uec-1.0-update
feat: align UEC Envelope 1.0 + update all adapters
2 parents 9f0344f + fbdf4dd commit 574c384

57 files changed

Lines changed: 5851 additions & 3547 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

changelog.d/14.changed.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
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.

docs/concepts/uec.md

Lines changed: 51 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -8,25 +8,47 @@ To keep runs reproducible and comparable, adapters produce a standardized **Exec
88

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

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

22+
The envelope schema is `devqubit.envelope/1.0` and requires `envelope_id`, `created_at`, `producer`, and `result`.
23+
24+
1925
## Snapshots
2026

27+
### ProducerInfo
28+
29+
Captures the complete SDK/toolchain stack that produced the envelope:
30+
31+
| Field | Description |
32+
|-------|-------------|
33+
| `name` | Producer name (always `"devqubit"`) |
34+
| `engine_version` | devqubit-engine version |
35+
| `adapter` | Adapter package name (e.g., `devqubit-qiskit`) |
36+
| `adapter_version` | Adapter version |
37+
| `sdk` | Lowest/primary SDK (e.g., `qiskit`, `braket-sdk`, `cirq`) |
38+
| `sdk_version` | Primary SDK version |
39+
| `frontends` | Ordered SDK stack from highest to lowest layer |
40+
| `build` | Optional build identifier (commit/dirty flag) |
41+
42+
2143
### DeviceSnapshot
2244

2345
Captures backend state at execution time:
2446

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

5173
Each artifact in `logical`/`physical` includes:
5274
- `format`: Circuit format (QPY, QASM3, etc.)
53-
- `artifact_ref`: Reference to stored artifact
54-
- `circuit_index`: Index in multi-circuit batch
75+
- `ref`: Reference to stored artifact
76+
- `index`: Index in multi-circuit batch
5577
- `name`: Circuit name (if available)
5678

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

7193
### ResultSnapshot
7294

73-
Captures normalized execution results:
95+
Captures normalized execution results (always as a list of per-item results):
7496

7597
| Field | Description |
7698
|-------|-------------|
77-
| `result_type` | Type of result (counts, quasi_dist, expectation, etc.) |
78-
| `raw_result_ref` | Reference to full serialized result artifact |
79-
| `counts` | Normalized measurement counts per circuit |
80-
| `num_experiments` | Number of experiments in result |
81-
| `success` | Whether execution succeeded |
82-
| `error_message` | Error message if failed |
99+
| `success` | Overall execution success |
100+
| `status` | `"completed"`, `"failed"`, `"cancelled"`, or `"partial"` |
101+
| `items` | List of `ResultItem` (one per circuit/parameter-set) |
102+
| `error` | Structured error info when failed |
103+
| `raw_result_ref` | Reference to the full serialized SDK result (optional) |
83104
| `metadata` | Additional result metadata |
84105

106+
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.
107+
85108
## Why UEC matters
86109

87110
The Uniform Execution Contract makes it easier to:
@@ -135,10 +158,17 @@ if envelope_artifact:
135158
device = envelope["device"]
136159
print(f"Backend: {device['backend_name']}")
137160
print(f"Qubits: {device['num_qubits']}")
138-
139-
# Access results
140-
for counts in envelope["result"]["counts"]:
141-
print(f"Circuit {counts['circuit_index']}: {counts['counts']}")
161+
# Access results (per-item)
162+
for item in envelope["result"]["items"]:
163+
if "counts" in item:
164+
counts = item["counts"]["counts"]
165+
print(f"Item {item['item_index']}: {counts}")
166+
elif "quasi_probability" in item:
167+
dist = item["quasi_probability"]["distribution"]
168+
print(f"Item {item['item_index']} quasi: {dist}")
169+
elif "expectation" in item:
170+
value = item["expectation"]["value"]
171+
print(f"Item {item['item_index']} expval: {value}")
142172
```
143173

144-
See {doc}`../guides/adapters` for what each SDK adapter captures.
174+
See `../guides/adapters` for what each SDK adapter captures.

docs/guides/adapters.md

Lines changed: 29 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ All adapters produce a standardized **ExecutionEnvelope** containing four canoni
4242

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

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

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

185185
---
@@ -225,8 +225,8 @@ with track(project="sweep") as run:
225225
|----------|------|------|
226226
| Cirq JSON | `cirq.circuit.json` | `program` |
227227
| Circuit diagram | `cirq.circuits.txt` | `program` |
228-
| Counts | `result.counts.json` | `results` |
229-
| Raw device properties | `device.cirq.raw_properties.json` | `device_raw` |
228+
| Counts | `result.counts.json` | `result` |
229+
| Raw device properties | `device.cirq.raw_properties.json` | `device_snapshot` |
230230
| Execution envelope | `devqubit.envelope.json` | `envelope` |
231231

232232
---
@@ -261,8 +261,8 @@ with track(project="vqe") as run:
261261
|----------|------|------|
262262
| Tape JSON | `pennylane.tapes.json` | `program` |
263263
| Tape diagram | `pennylane.tapes.txt` | `program` |
264-
| Results | `result.pennylane.output.json` | `results` |
265-
| Raw device properties | `device.pennylane.raw_properties.json` | `device_raw` |
264+
| Results | `result.pennylane.output.json` | `result` |
265+
| Raw device properties | `device.pennylane.raw_properties.json` | `device_snapshot` |
266266
| Execution envelope | `devqubit.envelope.json` | `envelope` |
267267

268268
### Multi-Layer Stack
@@ -400,7 +400,7 @@ with track(project="custom-sdk") as run:
400400
run.log_json(
401401
name="counts",
402402
obj={"00": 500, "11": 500},
403-
role="results",
403+
role="result",
404404
kind="result.counts.json",
405405
)
406406
```
@@ -411,18 +411,27 @@ For full UEC compliance, create an ExecutionEnvelope:
411411

412412
```python
413413
from devqubit.uec import (
414+
ProducerInfo,
414415
DeviceSnapshot,
415416
ExecutionEnvelope,
416417
ExecutionSnapshot,
417418
ProgramSnapshot,
418419
ResultSnapshot,
419420
)
420421

422+
producer = ProducerInfo.create(
423+
adapter="devqubit-custom",
424+
adapter_version="0.1.0",
425+
sdk="custom-sdk",
426+
sdk_version="1.0.0",
427+
frontends=["custom-sdk"],
428+
)
429+
421430
# Build snapshots
422431
device = DeviceSnapshot(
423432
backend_name="custom_device",
424433
backend_type="simulator",
425-
provider="custom",
434+
provider="local",
426435
captured_at=utc_now_iso(),
427436
)
428437

@@ -435,13 +444,14 @@ program = ProgramSnapshot(
435444
execution = ExecutionSnapshot(
436445
submitted_at=utc_now_iso(),
437446
shots=1000,
438-
sdk="custom",
439447
)
440448

441449
# Create and log envelope
442450
envelope = ExecutionEnvelope(
443-
schema_version="devqubit.envelope/0.1",
444-
adapter="custom",
451+
schema_version="devqubit.envelope/1.0",
452+
envelope_id="01J0EXAMPLEENVELOPEID0000",
453+
created_at=utc_now_iso(),
454+
producer=producer,
445455
device=device,
446456
program=program,
447457
execution=execution,

0 commit comments

Comments
 (0)