superquantx 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.
- superquantx/__init__.py +321 -0
- superquantx/algorithms/__init__.py +55 -0
- superquantx/algorithms/base_algorithm.py +413 -0
- superquantx/algorithms/hybrid_classifier.py +628 -0
- superquantx/algorithms/qaoa.py +406 -0
- superquantx/algorithms/quantum_agents.py +1006 -0
- superquantx/algorithms/quantum_kmeans.py +575 -0
- superquantx/algorithms/quantum_nn.py +544 -0
- superquantx/algorithms/quantum_pca.py +499 -0
- superquantx/algorithms/quantum_svm.py +346 -0
- superquantx/algorithms/vqe.py +553 -0
- superquantx/algorithms.py +863 -0
- superquantx/backends/__init__.py +265 -0
- superquantx/backends/base_backend.py +321 -0
- superquantx/backends/braket_backend.py +420 -0
- superquantx/backends/cirq_backend.py +466 -0
- superquantx/backends/ocean_backend.py +491 -0
- superquantx/backends/pennylane_backend.py +419 -0
- superquantx/backends/qiskit_backend.py +451 -0
- superquantx/backends/simulator_backend.py +455 -0
- superquantx/backends/tket_backend.py +519 -0
- superquantx/circuits.py +447 -0
- superquantx/cli/__init__.py +28 -0
- superquantx/cli/commands.py +528 -0
- superquantx/cli/main.py +254 -0
- superquantx/client.py +298 -0
- superquantx/config.py +326 -0
- superquantx/exceptions.py +287 -0
- superquantx/gates.py +588 -0
- superquantx/logging_config.py +347 -0
- superquantx/measurements.py +702 -0
- superquantx/ml.py +936 -0
- superquantx/noise.py +760 -0
- superquantx/utils/__init__.py +83 -0
- superquantx/utils/benchmarking.py +523 -0
- superquantx/utils/classical_utils.py +575 -0
- superquantx/utils/feature_mapping.py +467 -0
- superquantx/utils/optimization.py +410 -0
- superquantx/utils/quantum_utils.py +456 -0
- superquantx/utils/visualization.py +654 -0
- superquantx/version.py +33 -0
- superquantx-0.1.0.dist-info/METADATA +365 -0
- superquantx-0.1.0.dist-info/RECORD +46 -0
- superquantx-0.1.0.dist-info/WHEEL +4 -0
- superquantx-0.1.0.dist-info/entry_points.txt +2 -0
- superquantx-0.1.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,519 @@
|
|
1
|
+
"""TKET/Quantinuum backend implementation for SuperQuantX.
|
2
|
+
|
3
|
+
This module provides integration with TKET (Quantum Toolkit) and Quantinuum
|
4
|
+
hardware for advanced quantum circuit compilation and optimization.
|
5
|
+
"""
|
6
|
+
|
7
|
+
import logging
|
8
|
+
from typing import Any, Dict, List, Optional, Tuple, Union
|
9
|
+
|
10
|
+
import numpy as np
|
11
|
+
|
12
|
+
from .base_backend import BaseBackend
|
13
|
+
|
14
|
+
|
15
|
+
logger = logging.getLogger(__name__)
|
16
|
+
|
17
|
+
# Try to import TKET/pytket
|
18
|
+
try:
|
19
|
+
from pytket import Circuit, OpType
|
20
|
+
from pytket.backends import Backend
|
21
|
+
from pytket.backends.simulator import AerBackend
|
22
|
+
from pytket.circuit import Bit, Qubit
|
23
|
+
|
24
|
+
# Try to import Quantinuum backend
|
25
|
+
try:
|
26
|
+
from pytket.extensions.quantinuum import QuantinuumBackend
|
27
|
+
QUANTINUUM_AVAILABLE = True
|
28
|
+
except ImportError:
|
29
|
+
QUANTINUUM_AVAILABLE = False
|
30
|
+
|
31
|
+
# Try to import other TKET backends
|
32
|
+
try:
|
33
|
+
from pytket.extensions.cirq import CirqBackend as TKETCirqBackend
|
34
|
+
from pytket.extensions.qiskit import AerStateBackend
|
35
|
+
TKET_EXTENSIONS_AVAILABLE = True
|
36
|
+
except ImportError:
|
37
|
+
TKET_EXTENSIONS_AVAILABLE = False
|
38
|
+
|
39
|
+
TKET_AVAILABLE = True
|
40
|
+
|
41
|
+
except ImportError:
|
42
|
+
TKET_AVAILABLE = False
|
43
|
+
QUANTINUUM_AVAILABLE = False
|
44
|
+
TKET_EXTENSIONS_AVAILABLE = False
|
45
|
+
Circuit = None
|
46
|
+
OpType = None
|
47
|
+
|
48
|
+
class TKETBackend(BaseBackend):
|
49
|
+
"""TKET (Quantum Toolkit) backend for quantum computing operations.
|
50
|
+
|
51
|
+
This backend provides access to TKET's quantum circuit compilation,
|
52
|
+
optimization, and execution capabilities, including Quantinuum hardware.
|
53
|
+
|
54
|
+
Args:
|
55
|
+
device: Device name (e.g., 'aer_simulator', 'H1-1E', 'H1-2E', 'simulator')
|
56
|
+
shots: Number of measurement shots
|
57
|
+
machine: Specific Quantinuum machine for hardware execution
|
58
|
+
api_key: Quantinuum API key (required for hardware)
|
59
|
+
**kwargs: Additional backend configuration
|
60
|
+
|
61
|
+
Example:
|
62
|
+
>>> # Local simulator
|
63
|
+
>>> backend = TKETBackend(device='aer_simulator')
|
64
|
+
>>>
|
65
|
+
>>> # Quantinuum hardware (requires API key)
|
66
|
+
>>> backend = TKETBackend(
|
67
|
+
... device='H1-1E',
|
68
|
+
... api_key='your-quantinuum-api-key'
|
69
|
+
... )
|
70
|
+
|
71
|
+
"""
|
72
|
+
|
73
|
+
def __init__(
|
74
|
+
self,
|
75
|
+
device: str = 'aer_simulator',
|
76
|
+
shots: int = 1024,
|
77
|
+
machine: Optional[str] = None,
|
78
|
+
api_key: Optional[str] = None,
|
79
|
+
**kwargs
|
80
|
+
) -> None:
|
81
|
+
if not TKET_AVAILABLE:
|
82
|
+
raise ImportError(
|
83
|
+
"TKET not available. Install with: pip install pytket"
|
84
|
+
)
|
85
|
+
|
86
|
+
self.device_name = device
|
87
|
+
self.machine = machine or device
|
88
|
+
self.api_key = api_key
|
89
|
+
self._backend = None
|
90
|
+
|
91
|
+
super().__init__(device=device, shots=shots, **kwargs)
|
92
|
+
|
93
|
+
def _initialize_backend(self) -> None:
|
94
|
+
"""Initialize TKET backend and device."""
|
95
|
+
try:
|
96
|
+
if self.device_name in ['aer_simulator', 'simulator']:
|
97
|
+
# Use TKET Aer simulator
|
98
|
+
if TKET_EXTENSIONS_AVAILABLE:
|
99
|
+
from pytket.extensions.qiskit import AerBackend
|
100
|
+
self._backend = AerBackend()
|
101
|
+
else:
|
102
|
+
# Fallback to basic simulator
|
103
|
+
logger.warning("TKET extensions not available, using basic simulator")
|
104
|
+
self._backend = None
|
105
|
+
|
106
|
+
elif self.device_name.startswith('H') and '-' in self.device_name:
|
107
|
+
# Quantinuum hardware (H1-1E, H1-2E, H2-1, etc.)
|
108
|
+
if not QUANTINUUM_AVAILABLE:
|
109
|
+
raise ImportError("Quantinuum backend not available. Install with: pip install pytket-quantinuum")
|
110
|
+
|
111
|
+
if not self.api_key:
|
112
|
+
raise ValueError("API key required for Quantinuum hardware")
|
113
|
+
|
114
|
+
self._backend = QuantinuumBackend(
|
115
|
+
device_name=self.machine,
|
116
|
+
api_key=self.api_key
|
117
|
+
)
|
118
|
+
logger.info(f"Initialized Quantinuum backend: {self.machine}")
|
119
|
+
|
120
|
+
elif self.device_name in ['cirq', 'cirq_simulator']:
|
121
|
+
# TKET Cirq integration
|
122
|
+
if TKET_EXTENSIONS_AVAILABLE:
|
123
|
+
self._backend = TKETCirqBackend()
|
124
|
+
else:
|
125
|
+
raise ImportError("TKET Cirq extension not available")
|
126
|
+
|
127
|
+
else:
|
128
|
+
# Try to create generic backend
|
129
|
+
logger.warning(f"Unknown device {self.device_name}, using simulator fallback")
|
130
|
+
self._backend = None
|
131
|
+
|
132
|
+
self.capabilities = {
|
133
|
+
'supports_measurements': True,
|
134
|
+
'supports_parameterized_circuits': True,
|
135
|
+
'supports_classical_control': True,
|
136
|
+
'supports_mid_circuit_measurement': True,
|
137
|
+
'max_qubits': self._get_max_qubits(),
|
138
|
+
'native_gates': self._get_native_gates(),
|
139
|
+
'supports_optimization': True, # TKET's key feature
|
140
|
+
}
|
141
|
+
|
142
|
+
except Exception as e:
|
143
|
+
logger.error(f"Failed to initialize TKET backend: {e}")
|
144
|
+
raise
|
145
|
+
|
146
|
+
def _get_max_qubits(self) -> int:
|
147
|
+
"""Get maximum number of qubits for the device."""
|
148
|
+
if self._backend and hasattr(self._backend, 'device'):
|
149
|
+
try:
|
150
|
+
device_info = self._backend.device
|
151
|
+
if hasattr(device_info, 'n_nodes'):
|
152
|
+
return device_info.n_nodes
|
153
|
+
except:
|
154
|
+
pass
|
155
|
+
|
156
|
+
# Default estimates based on device name
|
157
|
+
if 'H1-1' in self.device_name:
|
158
|
+
return 20 # H1-1E
|
159
|
+
elif 'H1-2' in self.device_name:
|
160
|
+
return 56 # H1-2E
|
161
|
+
elif 'H2' in self.device_name:
|
162
|
+
return 32 # H2-1
|
163
|
+
else:
|
164
|
+
return 32 # Conservative estimate
|
165
|
+
|
166
|
+
def _get_native_gates(self) -> List[str]:
|
167
|
+
"""Get native gates supported by TKET."""
|
168
|
+
# TKET supports a rich set of gates
|
169
|
+
return [
|
170
|
+
'H', 'X', 'Y', 'Z', 'S', 'Sdg', 'T', 'Tdg', 'V', 'Vdg',
|
171
|
+
'Rx', 'Ry', 'Rz', 'R1', 'U1', 'U2', 'U3',
|
172
|
+
'CX', 'CY', 'CZ', 'CH', 'CV', 'CVdg', 'CS', 'CSdg',
|
173
|
+
'CRx', 'CRy', 'CRz', 'CU1', 'CU2', 'CU3',
|
174
|
+
'CCX', 'SWAP', 'CSWAP', 'ISWAP', 'PHASEDX',
|
175
|
+
'ZZMax', 'XXPhase', 'YYPhase', 'ZZPhase'
|
176
|
+
]
|
177
|
+
|
178
|
+
# ========================================================================
|
179
|
+
# Core Circuit Operations
|
180
|
+
# ========================================================================
|
181
|
+
|
182
|
+
def create_circuit(self, n_qubits: int) -> Circuit:
|
183
|
+
"""Create a TKET quantum circuit."""
|
184
|
+
if n_qubits > self._get_max_qubits():
|
185
|
+
logger.warning(f"Requested {n_qubits} qubits exceeds device limit")
|
186
|
+
|
187
|
+
circuit = Circuit(n_qubits)
|
188
|
+
return circuit
|
189
|
+
|
190
|
+
def add_gate(self, circuit: Circuit, gate: str, qubits: Union[int, List[int]],
|
191
|
+
params: Optional[List[float]] = None) -> Circuit:
|
192
|
+
"""Add a quantum gate to the circuit."""
|
193
|
+
if isinstance(qubits, int):
|
194
|
+
qubits = [qubits]
|
195
|
+
|
196
|
+
params = params or []
|
197
|
+
|
198
|
+
# Map gate names to TKET OpTypes
|
199
|
+
gate_mapping = {
|
200
|
+
'i': OpType.noop, # Identity
|
201
|
+
'x': OpType.X, 'y': OpType.Y, 'z': OpType.Z, 'h': OpType.H,
|
202
|
+
's': OpType.S, 'sdg': OpType.Sdg, 't': OpType.T, 'tdg': OpType.Tdg,
|
203
|
+
'rx': OpType.Rx, 'ry': OpType.Ry, 'rz': OpType.Rz,
|
204
|
+
'cnot': OpType.CX, 'cx': OpType.CX, 'cz': OpType.CZ, 'cy': OpType.CY,
|
205
|
+
'ch': OpType.CH, 'swap': OpType.SWAP,
|
206
|
+
'ccnot': OpType.CCX, 'ccx': OpType.CCX, 'toffoli': OpType.CCX,
|
207
|
+
'cswap': OpType.CSWAP, 'fredkin': OpType.CSWAP,
|
208
|
+
'iswap': OpType.ISWAPMax,
|
209
|
+
'u1': OpType.U1, 'u2': OpType.U2, 'u3': OpType.U3,
|
210
|
+
}
|
211
|
+
|
212
|
+
gate_upper = gate.upper()
|
213
|
+
gate_lower = gate.lower()
|
214
|
+
|
215
|
+
# Try exact match first, then case variations
|
216
|
+
op_type = None
|
217
|
+
if hasattr(OpType, gate_upper):
|
218
|
+
op_type = getattr(OpType, gate_upper)
|
219
|
+
elif gate_lower in gate_mapping:
|
220
|
+
op_type = gate_mapping[gate_lower]
|
221
|
+
|
222
|
+
if op_type is None:
|
223
|
+
raise ValueError(f"Gate '{gate}' not supported in TKET backend")
|
224
|
+
|
225
|
+
try:
|
226
|
+
# Convert qubit indices to TKET Qubit objects
|
227
|
+
tket_qubits = [circuit.qubits[q] for q in qubits]
|
228
|
+
|
229
|
+
if len(params) == 0:
|
230
|
+
# Parameter-less gates
|
231
|
+
circuit.add_gate(op_type, tket_qubits)
|
232
|
+
else:
|
233
|
+
# Parameterized gates
|
234
|
+
circuit.add_gate(op_type, tket_qubits, params)
|
235
|
+
|
236
|
+
except Exception as e:
|
237
|
+
logger.error(f"Failed to add gate {gate} to TKET circuit: {e}")
|
238
|
+
raise
|
239
|
+
|
240
|
+
return circuit
|
241
|
+
|
242
|
+
def add_measurement(self, circuit: Circuit, qubits: Union[int, List[int]]) -> Circuit:
|
243
|
+
"""Add measurement instructions to specified qubits."""
|
244
|
+
if isinstance(qubits, int):
|
245
|
+
qubits = [qubits]
|
246
|
+
|
247
|
+
# TKET requires explicit bit allocation for measurements
|
248
|
+
n_bits = len(qubits)
|
249
|
+
if circuit.n_bits < n_bits:
|
250
|
+
circuit.add_c_register("c", n_bits - circuit.n_bits)
|
251
|
+
|
252
|
+
for i, qubit in enumerate(qubits):
|
253
|
+
circuit.Measure(circuit.qubits[qubit], circuit.bits[i])
|
254
|
+
|
255
|
+
return circuit
|
256
|
+
|
257
|
+
def execute_circuit(self, circuit: Circuit, shots: Optional[int] = None) -> Dict[str, Any]:
|
258
|
+
"""Execute quantum circuit and return results."""
|
259
|
+
shots = shots or self.shots
|
260
|
+
|
261
|
+
try:
|
262
|
+
if self._backend is None:
|
263
|
+
# Simple simulation fallback
|
264
|
+
logger.warning("No TKET backend available, using simulation fallback")
|
265
|
+
n_qubits = circuit.n_qubits
|
266
|
+
n_bits = circuit.n_bits or n_qubits
|
267
|
+
|
268
|
+
# Generate random results for fallback
|
269
|
+
counts = {}
|
270
|
+
for _ in range(shots):
|
271
|
+
bitstring = ''.join([str(np.random.randint(0, 2)) for _ in range(n_bits)])
|
272
|
+
counts[bitstring] = counts.get(bitstring, 0) + 1
|
273
|
+
|
274
|
+
return {
|
275
|
+
'counts': counts,
|
276
|
+
'shots': shots,
|
277
|
+
'success': True,
|
278
|
+
'backend': 'tket_fallback',
|
279
|
+
}
|
280
|
+
|
281
|
+
# Compile circuit for backend
|
282
|
+
compiled_circuit = self._backend.get_compiled_circuit(circuit)
|
283
|
+
|
284
|
+
# Execute on backend
|
285
|
+
handle = self._backend.process_circuit(compiled_circuit, n_shots=shots)
|
286
|
+
result = self._backend.get_result(handle)
|
287
|
+
|
288
|
+
# Extract counts
|
289
|
+
counts_dict = {}
|
290
|
+
if hasattr(result, 'get_counts'):
|
291
|
+
counts_dict = result.get_counts()
|
292
|
+
elif hasattr(result, 'counts'):
|
293
|
+
counts_dict = result.counts
|
294
|
+
else:
|
295
|
+
logger.warning("Could not extract counts from TKET result")
|
296
|
+
|
297
|
+
return {
|
298
|
+
'counts': counts_dict,
|
299
|
+
'shots': shots,
|
300
|
+
'success': True,
|
301
|
+
'backend': self._backend.__class__.__name__,
|
302
|
+
'compiled': True,
|
303
|
+
'device': self.device_name,
|
304
|
+
}
|
305
|
+
|
306
|
+
except Exception as e:
|
307
|
+
logger.error(f"TKET circuit execution failed: {e}")
|
308
|
+
return {
|
309
|
+
'counts': {},
|
310
|
+
'shots': shots,
|
311
|
+
'success': False,
|
312
|
+
'error': str(e),
|
313
|
+
'device': self.device_name,
|
314
|
+
}
|
315
|
+
|
316
|
+
# ========================================================================
|
317
|
+
# TKET-Specific Features
|
318
|
+
# ========================================================================
|
319
|
+
|
320
|
+
def optimize_circuit(self, circuit: Circuit, optimization_level: int = 2) -> Circuit:
|
321
|
+
"""Optimize quantum circuit using TKET compiler passes."""
|
322
|
+
try:
|
323
|
+
from pytket.passes import (
|
324
|
+
CliffordSimp,
|
325
|
+
DecomposeBoxes,
|
326
|
+
OptimisePhaseGadgets,
|
327
|
+
RemoveRedundancies,
|
328
|
+
SequencePass,
|
329
|
+
)
|
330
|
+
|
331
|
+
optimized = circuit.copy()
|
332
|
+
|
333
|
+
if optimization_level >= 1:
|
334
|
+
# Basic optimization
|
335
|
+
basic_pass = SequencePass([
|
336
|
+
DecomposeBoxes(),
|
337
|
+
CliffordSimp(),
|
338
|
+
RemoveRedundancies()
|
339
|
+
])
|
340
|
+
basic_pass.apply(optimized)
|
341
|
+
|
342
|
+
if optimization_level >= 2:
|
343
|
+
# Advanced optimization
|
344
|
+
advanced_pass = SequencePass([
|
345
|
+
OptimisePhaseGadgets(),
|
346
|
+
CliffordSimp(),
|
347
|
+
RemoveRedundancies()
|
348
|
+
])
|
349
|
+
advanced_pass.apply(optimized)
|
350
|
+
|
351
|
+
if optimization_level >= 3 and self._backend:
|
352
|
+
# Backend-specific optimization
|
353
|
+
backend_pass = self._backend.default_compilation_pass()
|
354
|
+
backend_pass.apply(optimized)
|
355
|
+
|
356
|
+
logger.info(f"Circuit optimized: {circuit.n_gates} -> {optimized.n_gates} gates")
|
357
|
+
return optimized
|
358
|
+
|
359
|
+
except Exception as e:
|
360
|
+
logger.error(f"Circuit optimization failed: {e}")
|
361
|
+
return circuit
|
362
|
+
|
363
|
+
def create_parameterized_circuit(self, n_qubits: int, n_params: int) -> Tuple[Circuit, List[str]]:
|
364
|
+
"""Create a parameterized quantum circuit for variational algorithms."""
|
365
|
+
from pytket.circuit import fresh_symbol
|
366
|
+
|
367
|
+
circuit = self.create_circuit(n_qubits)
|
368
|
+
|
369
|
+
# Create symbolic parameters
|
370
|
+
symbols = [fresh_symbol(f"theta_{i}") for i in range(n_params)]
|
371
|
+
param_names = [str(symbol) for symbol in symbols]
|
372
|
+
|
373
|
+
# Store parameter info
|
374
|
+
circuit._symbols = symbols
|
375
|
+
circuit._param_names = param_names
|
376
|
+
|
377
|
+
return circuit, param_names
|
378
|
+
|
379
|
+
def bind_parameters(self, circuit: Circuit, param_values: Dict[str, float]) -> Circuit:
|
380
|
+
"""Bind parameter values to parameterized circuit."""
|
381
|
+
try:
|
382
|
+
bound_circuit = circuit.copy()
|
383
|
+
|
384
|
+
# Create symbol substitution map
|
385
|
+
if hasattr(circuit, '_symbols'):
|
386
|
+
symbol_map = {}
|
387
|
+
for symbol, name in zip(circuit._symbols, circuit._param_names):
|
388
|
+
if name in param_values:
|
389
|
+
symbol_map[symbol] = param_values[name]
|
390
|
+
|
391
|
+
# Substitute symbols with values
|
392
|
+
bound_circuit.symbol_substitution(symbol_map)
|
393
|
+
|
394
|
+
return bound_circuit
|
395
|
+
|
396
|
+
except Exception as e:
|
397
|
+
logger.error(f"Parameter binding failed: {e}")
|
398
|
+
return circuit
|
399
|
+
|
400
|
+
def expectation_value(self, circuit: Circuit, observable: Union[str, np.ndarray],
|
401
|
+
shots: Optional[int] = None) -> float:
|
402
|
+
"""Calculate expectation value of observable using TKET."""
|
403
|
+
shots = shots or self.shots
|
404
|
+
|
405
|
+
try:
|
406
|
+
if isinstance(observable, str) and observable.upper() == 'Z':
|
407
|
+
# Measure in Z basis
|
408
|
+
measured_circuit = circuit.copy()
|
409
|
+
measured_circuit.add_c_register("c", 1)
|
410
|
+
measured_circuit.Measure(measured_circuit.qubits[0], measured_circuit.bits[0])
|
411
|
+
|
412
|
+
result = self.execute_circuit(measured_circuit, shots)
|
413
|
+
counts = result['counts']
|
414
|
+
|
415
|
+
# Calculate <Z> expectation
|
416
|
+
expectation = 0.0
|
417
|
+
total_counts = sum(counts.values())
|
418
|
+
|
419
|
+
for bitstring, count in counts.items():
|
420
|
+
prob = count / total_counts
|
421
|
+
if bitstring[0] == '0':
|
422
|
+
expectation += prob
|
423
|
+
else:
|
424
|
+
expectation -= prob
|
425
|
+
|
426
|
+
return expectation
|
427
|
+
else:
|
428
|
+
logger.warning("Only Z observable currently implemented")
|
429
|
+
return 0.0
|
430
|
+
|
431
|
+
except Exception as e:
|
432
|
+
logger.error(f"Expectation value calculation failed: {e}")
|
433
|
+
return 0.0
|
434
|
+
|
435
|
+
# ========================================================================
|
436
|
+
# Backend Information
|
437
|
+
# ========================================================================
|
438
|
+
|
439
|
+
def get_backend_info(self) -> Dict[str, Any]:
|
440
|
+
"""Get information about the TKET backend."""
|
441
|
+
info = {
|
442
|
+
'backend_name': 'tket',
|
443
|
+
'device': self.device_name,
|
444
|
+
'provider': 'Quantinuum/Cambridge Quantum Computing',
|
445
|
+
'shots': self.shots,
|
446
|
+
'capabilities': self.capabilities,
|
447
|
+
}
|
448
|
+
|
449
|
+
if self._backend:
|
450
|
+
info.update({
|
451
|
+
'backend_class': self._backend.__class__.__name__,
|
452
|
+
'supports_compilation': True,
|
453
|
+
'supports_optimization': True,
|
454
|
+
})
|
455
|
+
|
456
|
+
if hasattr(self._backend, 'device'):
|
457
|
+
device_info = self._backend.device
|
458
|
+
info['device_info'] = str(device_info)
|
459
|
+
|
460
|
+
return info
|
461
|
+
|
462
|
+
def get_version_info(self) -> Dict[str, str]:
|
463
|
+
"""Get version information for TKET dependencies."""
|
464
|
+
version_info = {'backend_version': '1.0.0'}
|
465
|
+
|
466
|
+
try:
|
467
|
+
import pytket
|
468
|
+
version_info['pytket'] = pytket.__version__
|
469
|
+
except:
|
470
|
+
pass
|
471
|
+
|
472
|
+
if QUANTINUUM_AVAILABLE:
|
473
|
+
try:
|
474
|
+
import pytket.extensions.quantinuum
|
475
|
+
version_info['pytket_quantinuum'] = getattr(pytket.extensions.quantinuum, '__version__', 'unknown')
|
476
|
+
except:
|
477
|
+
pass
|
478
|
+
|
479
|
+
return version_info
|
480
|
+
|
481
|
+
def is_available(self) -> bool:
|
482
|
+
"""Check if the backend is available and properly configured."""
|
483
|
+
return TKET_AVAILABLE
|
484
|
+
|
485
|
+
def get_circuit_info(self) -> Dict[str, Any]:
|
486
|
+
"""Get information about circuit execution capabilities."""
|
487
|
+
return {
|
488
|
+
'max_qubits': self._get_max_qubits(),
|
489
|
+
'native_gates': self._get_native_gates(),
|
490
|
+
'supports_mid_circuit_measurement': True,
|
491
|
+
'supports_reset': True,
|
492
|
+
'supports_conditional': True,
|
493
|
+
'supports_optimization': True,
|
494
|
+
'supports_compilation': True,
|
495
|
+
}
|
496
|
+
|
497
|
+
def _get_n_qubits(self, circuit: Circuit) -> int:
|
498
|
+
"""Get number of qubits in TKET circuit."""
|
499
|
+
return circuit.n_qubits
|
500
|
+
|
501
|
+
def get_statevector(self, circuit: Circuit) -> np.ndarray:
|
502
|
+
"""Get statevector from TKET circuit."""
|
503
|
+
try:
|
504
|
+
if TKET_EXTENSIONS_AVAILABLE:
|
505
|
+
from pytket.extensions.qiskit import AerStateBackend
|
506
|
+
state_backend = AerStateBackend()
|
507
|
+
compiled_circuit = state_backend.get_compiled_circuit(circuit)
|
508
|
+
handle = state_backend.process_circuit(compiled_circuit)
|
509
|
+
result = state_backend.get_result(handle)
|
510
|
+
|
511
|
+
if hasattr(result, 'get_state'):
|
512
|
+
return result.get_state()
|
513
|
+
|
514
|
+
logger.warning("Statevector not available with current TKET setup")
|
515
|
+
return np.zeros(2**circuit.n_qubits, dtype=complex)
|
516
|
+
|
517
|
+
except Exception as e:
|
518
|
+
logger.error(f"Failed to get statevector: {e}")
|
519
|
+
return np.zeros(2**circuit.n_qubits, dtype=complex)
|