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.
@@ -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
+ )