westquant-qiskit 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- westquant_qiskit/__init__.py +28 -0
- westquant_qiskit/adapter.py +188 -0
- westquant_qiskit/batch1000.py +812 -0
- westquant_qiskit/challenges.py +117 -0
- westquant_qiskit/cli.py +73 -0
- westquant_qiskit/compiler.py +80 -0
- westquant_qiskit/config.py +135 -0
- westquant_qiskit/io.py +36 -0
- westquant_qiskit/search.py +569 -0
- westquant_qiskit/sequential.py +549 -0
- westquant_qiskit/stage.py +219 -0
- westquant_qiskit/verification.py +140 -0
- westquant_qiskit-0.1.0.dist-info/METADATA +155 -0
- westquant_qiskit-0.1.0.dist-info/RECORD +18 -0
- westquant_qiskit-0.1.0.dist-info/WHEEL +5 -0
- westquant_qiskit-0.1.0.dist-info/entry_points.txt +12 -0
- westquant_qiskit-0.1.0.dist-info/licenses/LICENSE +202 -0
- westquant_qiskit-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
from .adapter import QiskitAdapter, circuit_instructions, circuit_metrics, noise_aware_metrics, numeric_metrics
|
|
2
|
+
from .compiler import QiskitCompiler
|
|
3
|
+
from .config import PipelineConfig, SearchSpace
|
|
4
|
+
from .search import CandidateResult, DeterministicSearchEngine, SearchResult
|
|
5
|
+
from .verification import QiskitEquivalenceVerifier, VerificationReport
|
|
6
|
+
from .sequential import SequentialSearchEngine, SequentialSearchResult, SequentialSearchSpace
|
|
7
|
+
|
|
8
|
+
__version__ = "0.1.0a4"
|
|
9
|
+
|
|
10
|
+
__all__ = [
|
|
11
|
+
"QiskitAdapter",
|
|
12
|
+
"circuit_instructions",
|
|
13
|
+
"circuit_metrics",
|
|
14
|
+
"noise_aware_metrics",
|
|
15
|
+
"numeric_metrics",
|
|
16
|
+
"QiskitCompiler",
|
|
17
|
+
"PipelineConfig",
|
|
18
|
+
"SearchSpace",
|
|
19
|
+
"CandidateResult",
|
|
20
|
+
"DeterministicSearchEngine",
|
|
21
|
+
"SearchResult",
|
|
22
|
+
"QiskitEquivalenceVerifier",
|
|
23
|
+
"VerificationReport",
|
|
24
|
+
"SequentialSearchEngine",
|
|
25
|
+
"SequentialSearchResult",
|
|
26
|
+
"SequentialSearchSpace",
|
|
27
|
+
"__version__",
|
|
28
|
+
]
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
from westquant_core import Representation, RepresentationKind
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def _bit_index(circuit: Any, bit: Any) -> int | str:
|
|
8
|
+
try:
|
|
9
|
+
return int(circuit.find_bit(bit).index)
|
|
10
|
+
except Exception:
|
|
11
|
+
try:
|
|
12
|
+
return int(list(circuit.qubits).index(bit))
|
|
13
|
+
except Exception:
|
|
14
|
+
return str(bit)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _safe_param(value: Any) -> float | int | str:
|
|
18
|
+
if isinstance(value, bool):
|
|
19
|
+
return int(value)
|
|
20
|
+
if isinstance(value, int):
|
|
21
|
+
return value
|
|
22
|
+
if isinstance(value, float):
|
|
23
|
+
return value
|
|
24
|
+
try:
|
|
25
|
+
return float(value)
|
|
26
|
+
except Exception:
|
|
27
|
+
return str(value)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def circuit_instructions(circuit: Any) -> list[dict[str, Any]]:
|
|
31
|
+
out: list[dict[str, Any]] = []
|
|
32
|
+
for position, instruction in enumerate(circuit.data):
|
|
33
|
+
op = getattr(instruction, "operation", instruction)
|
|
34
|
+
qubits = getattr(instruction, "qubits", ())
|
|
35
|
+
clbits = getattr(instruction, "clbits", ())
|
|
36
|
+
out.append({
|
|
37
|
+
"position": position,
|
|
38
|
+
"name": str(getattr(op, "name", type(op).__name__)),
|
|
39
|
+
"qubits": [_bit_index(circuit, q) for q in qubits],
|
|
40
|
+
"clbits": [str(c) for c in clbits],
|
|
41
|
+
"params": [_safe_param(p) for p in getattr(op, "params", ())],
|
|
42
|
+
})
|
|
43
|
+
return out
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def circuit_metrics(circuit: Any) -> dict[str, Any]:
|
|
47
|
+
counts = {str(k): int(v) for k, v in circuit.count_ops().items()}
|
|
48
|
+
two_qubit = 0
|
|
49
|
+
for instruction in circuit.data:
|
|
50
|
+
if len(getattr(instruction, "qubits", ())) == 2:
|
|
51
|
+
two_qubit += 1
|
|
52
|
+
duration = getattr(circuit, "duration", None)
|
|
53
|
+
return {
|
|
54
|
+
"n_qubits": int(circuit.num_qubits),
|
|
55
|
+
"n_clbits": int(circuit.num_clbits),
|
|
56
|
+
"depth": int(circuit.depth() or 0),
|
|
57
|
+
"size": int(circuit.size()),
|
|
58
|
+
"two_qubit_gates": int(two_qubit),
|
|
59
|
+
"swap_gates": int(counts.get("swap", 0)),
|
|
60
|
+
"duration": float(duration) if isinstance(duration, (int, float)) else None,
|
|
61
|
+
"operations": counts,
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def numeric_metrics(metrics: dict[str, Any]) -> dict[str, float | int | None]:
|
|
66
|
+
keys = ("n_qubits", "n_clbits", "depth", "size", "two_qubit_gates", "swap_gates", "duration")
|
|
67
|
+
return {key: metrics.get(key) for key in keys}
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def noise_aware_metrics(
|
|
71
|
+
circuit: Any,
|
|
72
|
+
backend: Any = None,
|
|
73
|
+
target: Any = None,
|
|
74
|
+
) -> dict[str, Any]:
|
|
75
|
+
"""Compute noise/fidelity-aware metrics for a circuit.
|
|
76
|
+
|
|
77
|
+
Returns a dict with ``estimated_fidelity``, ``total_duration`` and
|
|
78
|
+
``noise_aware_depth``. When ``backend``/``target`` is ``None`` or does not
|
|
79
|
+
expose per-gate error/duration data, the corresponding fields are ``None``.
|
|
80
|
+
"""
|
|
81
|
+
result: dict[str, Any] = {
|
|
82
|
+
"estimated_fidelity": None,
|
|
83
|
+
"total_duration": None,
|
|
84
|
+
"noise_aware_depth": None,
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
# Collect gate names in execution order.
|
|
88
|
+
ops: list[str] = []
|
|
89
|
+
for instruction in getattr(circuit, "data", []):
|
|
90
|
+
op = getattr(instruction, "operation", instruction)
|
|
91
|
+
ops.append(str(getattr(op, "name", type(op).__name__)).lower())
|
|
92
|
+
|
|
93
|
+
if not ops:
|
|
94
|
+
return result
|
|
95
|
+
|
|
96
|
+
# Try to obtain a properties/props source from backend or target.
|
|
97
|
+
props = None
|
|
98
|
+
if backend is not None:
|
|
99
|
+
props = getattr(backend, "properties", None)
|
|
100
|
+
if callable(props):
|
|
101
|
+
try:
|
|
102
|
+
props = props()
|
|
103
|
+
except Exception:
|
|
104
|
+
props = None
|
|
105
|
+
if props is None and target is not None:
|
|
106
|
+
props = getattr(target, "props", None)
|
|
107
|
+
|
|
108
|
+
if props is None:
|
|
109
|
+
return result
|
|
110
|
+
|
|
111
|
+
# Build per-gate error rate and duration lookup tables.
|
|
112
|
+
def _gate_error(props: Any, name: str, qubits: tuple) -> float | None:
|
|
113
|
+
getter = getattr(props, "gate_error", None)
|
|
114
|
+
if callable(getter):
|
|
115
|
+
try:
|
|
116
|
+
return float(getter(name, qubits))
|
|
117
|
+
except Exception:
|
|
118
|
+
return None
|
|
119
|
+
return None
|
|
120
|
+
|
|
121
|
+
def _gate_length(props: Any, name: str, qubits: tuple) -> float | None:
|
|
122
|
+
for attr in ("gate_length", "gate_duration"):
|
|
123
|
+
getter = getattr(props, attr, None)
|
|
124
|
+
if callable(getter):
|
|
125
|
+
try:
|
|
126
|
+
return float(getter(name, qubits))
|
|
127
|
+
except Exception:
|
|
128
|
+
continue
|
|
129
|
+
return None
|
|
130
|
+
|
|
131
|
+
fidelity = 1.0
|
|
132
|
+
total_duration = 0.0
|
|
133
|
+
weighted_depth = 0.0
|
|
134
|
+
have_fidelity = False
|
|
135
|
+
have_duration = False
|
|
136
|
+
|
|
137
|
+
for instruction in getattr(circuit, "data", []):
|
|
138
|
+
op = getattr(instruction, "operation", instruction)
|
|
139
|
+
name = str(getattr(op, "name", type(op).__name__)).lower()
|
|
140
|
+
qubits = tuple(
|
|
141
|
+
int(getattr(circuit.find_bit(q), "index", 0))
|
|
142
|
+
for q in getattr(instruction, "qubits", ())
|
|
143
|
+
)
|
|
144
|
+
err = _gate_error(props, name, qubits)
|
|
145
|
+
length = _gate_length(props, name, qubits)
|
|
146
|
+
if err is not None:
|
|
147
|
+
fidelity *= (1.0 - err)
|
|
148
|
+
have_fidelity = True
|
|
149
|
+
if length is not None:
|
|
150
|
+
total_duration += length
|
|
151
|
+
have_duration = True
|
|
152
|
+
# Weight this gate's contribution to depth by its error rate.
|
|
153
|
+
weighted_depth += (1.0 - (err if err is not None else 0.0)) * length
|
|
154
|
+
|
|
155
|
+
if have_fidelity:
|
|
156
|
+
result["estimated_fidelity"] = fidelity
|
|
157
|
+
if have_duration:
|
|
158
|
+
result["total_duration"] = total_duration
|
|
159
|
+
result["noise_aware_depth"] = weighted_depth
|
|
160
|
+
return result
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
class QiskitAdapter:
|
|
164
|
+
framework = "qiskit"
|
|
165
|
+
|
|
166
|
+
def import_native(
|
|
167
|
+
self,
|
|
168
|
+
circuit: Any,
|
|
169
|
+
*,
|
|
170
|
+
representation_id: str = "qiskit:circuit",
|
|
171
|
+
semantic_root: str | None = None,
|
|
172
|
+
include_instructions: bool = True,
|
|
173
|
+
metadata: dict[str, Any] | None = None,
|
|
174
|
+
) -> Representation:
|
|
175
|
+
payload: dict[str, Any] = {"metrics": circuit_metrics(circuit)}
|
|
176
|
+
if include_instructions:
|
|
177
|
+
payload["instructions"] = circuit_instructions(circuit)
|
|
178
|
+
global_phase = getattr(circuit, "global_phase", None)
|
|
179
|
+
if global_phase is not None:
|
|
180
|
+
payload["global_phase"] = _safe_param(global_phase)
|
|
181
|
+
return Representation(
|
|
182
|
+
id=representation_id,
|
|
183
|
+
kind=RepresentationKind.CIRCUIT,
|
|
184
|
+
semantic_root=semantic_root,
|
|
185
|
+
framework=self.framework,
|
|
186
|
+
payload=payload,
|
|
187
|
+
metadata={"native_type": type(circuit).__name__, **(metadata or {})},
|
|
188
|
+
)
|