0
# Devices and Noise Modeling
1
2
The devices module provides classes for modeling quantum hardware, qubits with different topologies, and realistic noise models for NISQ devices. This enables accurate simulation of quantum computations under realistic conditions.
3
4
## Qubit Types and Identifiers
5
6
### Qid - Base Quantum Identifier
7
8
Abstract base class for all quantum identifiers.
9
10
```python { .api }
11
class Qid:
12
"""Abstract base class for quantum identifiers."""
13
14
@property
15
@abc.abstractmethod
16
def dimension(self) -> int:
17
"""Dimension of the quantum system (2 for qubits, >2 for qudits)."""
18
19
def _comparison_key(self) -> Any:
20
"""Key used for comparison and hashing."""
21
22
def __lt__(self, other: 'Qid') -> bool:
23
"""Less than comparison for sorting."""
24
25
def validate_dimension(self, dimension: int) -> int:
26
"""Validate that this qid supports the given dimension."""
27
```
28
29
### GridQubit - 2D Grid Qubits
30
31
Qubits arranged in a 2D grid topology, commonly used for superconducting quantum processors.
32
33
```python { .api }
34
class GridQubit(Qid):
35
"""2D grid-based qubit (dimension 2)."""
36
37
def __init__(self, row: int, col: int) -> None:
38
"""Initialize a grid qubit at (row, col)."""
39
40
@property
41
def row(self) -> int:
42
"""Row coordinate of the qubit."""
43
44
@property
45
def col(self) -> int:
46
"""Column coordinate of the qubit."""
47
48
@property
49
def dimension(self) -> int:
50
"""Always returns 2 for qubits."""
51
52
@classmethod
53
def rect(cls, rows: int, cols: int) -> List['GridQubit']:
54
"""Create a rectangular array of grid qubits."""
55
56
@classmethod
57
def square(cls, length: int) -> List['GridQubit']:
58
"""Create a square array of grid qubits."""
59
60
def neighbors(self, *, diagonal: bool = True) -> Set['GridQubit']:
61
"""Get neighboring grid positions."""
62
63
def is_adjacent(self, other: 'GridQubit') -> bool:
64
"""Check if this qubit is adjacent to another."""
65
66
def __add__(self, other: Tuple[int, int]) -> 'GridQubit':
67
"""Add a (row, col) offset to this qubit."""
68
69
def __sub__(self, other: Tuple[int, int]) -> 'GridQubit':
70
"""Subtract a (row, col) offset from this qubit."""
71
```
72
73
### GridQid - Generalized Grid Qids
74
75
Generalized version of GridQubit supporting arbitrary dimensions.
76
77
```python { .api }
78
class GridQid(Qid):
79
"""Grid-based qubit identifier (generalized dimension)."""
80
81
def __init__(self, row: int, col: int, dimension: int) -> None:
82
"""Initialize a grid qid with specified dimension."""
83
84
@property
85
def row(self) -> int:
86
"""Row coordinate."""
87
88
@property
89
def col(self) -> int:
90
"""Column coordinate."""
91
92
@property
93
def dimension(self) -> int:
94
"""Dimension of the quantum system."""
95
96
def neighbors(self, *, diagonal: bool = True) -> Set['GridQid']:
97
"""Get neighboring grid positions with same dimension."""
98
```
99
100
### LineQubit - 1D Linear Qubits
101
102
Qubits arranged in a linear topology.
103
104
```python { .api }
105
class LineQubit(Qid):
106
"""1D line-based qubit (dimension 2)."""
107
108
def __init__(self, x: int) -> None:
109
"""Initialize a line qubit at position x."""
110
111
@property
112
def x(self) -> int:
113
"""Position along the line."""
114
115
@property
116
def dimension(self) -> int:
117
"""Always returns 2 for qubits."""
118
119
@classmethod
120
def range(cls, *args) -> List['LineQubit']:
121
"""Create a range of line qubits.
122
123
Args:
124
*args: Either range(stop) or range(start, stop) or range(start, stop, step)
125
"""
126
127
def neighbors(self, radius: int = 1) -> Set['LineQubit']:
128
"""Get neighboring positions within given radius."""
129
130
def is_adjacent(self, other: 'LineQubit') -> bool:
131
"""Check if this qubit is adjacent to another."""
132
```
133
134
### LineQid - Generalized Line Qids
135
136
Generalized version of LineQubit supporting arbitrary dimensions.
137
138
```python { .api }
139
class LineQid(Qid):
140
"""Line-based qubit identifier (generalized dimension)."""
141
142
def __init__(self, x: int, dimension: int) -> None:
143
"""Initialize a line qid with specified dimension."""
144
145
@property
146
def x(self) -> int:
147
"""Position along the line."""
148
149
@property
150
def dimension(self) -> int:
151
"""Dimension of the quantum system."""
152
```
153
154
## Device Classes
155
156
### Device - Abstract Base Class
157
158
Base class for modeling quantum devices with hardware constraints.
159
160
```python { .api }
161
class Device:
162
"""Abstract base class for quantum devices."""
163
164
@abc.abstractmethod
165
def qubits(self) -> FrozenSet[Qid]:
166
"""Qubits available on this device."""
167
168
def qubit_set(self) -> FrozenSet[Qid]:
169
"""Alias for qubits()."""
170
171
def decompose_operation(self, operation: 'cirq.Operation') -> 'cirq.OP_TREE':
172
"""Decompose an operation into device-native operations."""
173
174
def validate_operation(self, operation: 'cirq.Operation') -> None:
175
"""Validate that an operation can be performed on this device."""
176
177
def validate_circuit(self, circuit: 'cirq.AbstractCircuit') -> None:
178
"""Validate that a circuit can be run on this device."""
179
180
def validate_moment(self, moment: 'cirq.Moment') -> None:
181
"""Validate that a moment can be performed on this device."""
182
183
def can_add_operation_into_moment(self, operation: 'cirq.Operation', moment: 'cirq.Moment') -> bool:
184
"""Check if an operation can be added to a moment."""
185
186
def duration_of(self, operation: 'cirq.Operation') -> 'cirq.Duration':
187
"""Get the duration of an operation on this device."""
188
```
189
190
### DeviceMetadata
191
192
Metadata describing device capabilities and constraints.
193
194
```python { .api }
195
class DeviceMetadata:
196
"""Metadata describing device capabilities."""
197
198
def __init__(self, qubits: Iterable[Qid],
199
pairs: Iterable[Tuple[Qid, Qid]]) -> None:
200
"""Initialize device metadata."""
201
202
@property
203
def qubits(self) -> FrozenSet[Qid]:
204
"""Set of qubits on the device."""
205
206
@property
207
def qubit_pairs(self) -> FrozenSet[Tuple[Qid, Qid]]:
208
"""Set of two-qubit gate pairs."""
209
210
def neighbors_of(self, qubit: Qid) -> Set[Qid]:
211
"""Get neighboring qubits that can interact with the given qubit."""
212
213
def isolated_qubits(self) -> FrozenSet[Qid]:
214
"""Qubits that cannot interact with any other qubits."""
215
```
216
217
### GridDeviceMetadata
218
219
Specialized metadata for grid-based quantum devices.
220
221
```python { .api }
222
class GridDeviceMetadata(DeviceMetadata):
223
"""Metadata for grid-based quantum devices."""
224
225
def __init__(self, qubit_pairs: Iterable[Tuple[GridQubit, GridQubit]],
226
gateset: Optional['cirq.Gateset'] = None) -> None:
227
"""Initialize grid device metadata."""
228
229
@classmethod
230
def square(cls, length: int, **kwargs) -> 'GridDeviceMetadata':
231
"""Create metadata for a square grid device."""
232
233
@classmethod
234
def rect(cls, rows: int, cols: int, **kwargs) -> 'GridDeviceMetadata':
235
"""Create metadata for a rectangular grid device."""
236
```
237
238
### UNCONSTRAINED_DEVICE
239
240
A device with no constraints that accepts any operation.
241
242
```python { .api }
243
UNCONSTRAINED_DEVICE: Device
244
"""Device with no constraints - accepts any quantum operation."""
245
```
246
247
## Noise Models
248
249
### NoiseModel - Abstract Base Class
250
251
Base class for modeling quantum noise.
252
253
```python { .api }
254
class NoiseModel:
255
"""Abstract base class for noise models."""
256
257
@abc.abstractmethod
258
def noisy_operation(self, operation: 'cirq.Operation') -> 'cirq.OP_TREE':
259
"""Apply noise to an operation, returning noisy operations."""
260
261
def noisy_moment(self, moment: 'cirq.Moment', system_qubits: Sequence[Qid]) -> 'cirq.OP_TREE':
262
"""Apply noise to all operations in a moment."""
263
264
def noisy_moments(self, moments: Sequence['cirq.Moment'], system_qubits: Sequence[Qid]) -> Sequence['cirq.OP_TREE']:
265
"""Apply noise to a sequence of moments."""
266
267
def is_virtual_moment(self, moment: 'cirq.Moment') -> bool:
268
"""Check if a moment should be treated as virtual (no duration)."""
269
```
270
271
### ConstantQubitNoiseModel
272
273
Noise model with constant per-qubit noise rates.
274
275
```python { .api }
276
class ConstantQubitNoiseModel(NoiseModel):
277
"""Noise model with constant per-qubit noise."""
278
279
def __init__(self, qubit_noise_gate: 'cirq.Gate') -> None:
280
"""Initialize with a noise gate applied to each qubit."""
281
282
def noisy_operation(self, operation: 'cirq.Operation') -> 'cirq.OP_TREE':
283
"""Add qubit noise after the operation."""
284
```
285
286
### NoiseModelFromNoiseProperties
287
288
Noise model derived from detailed noise characterization.
289
290
```python { .api }
291
class NoiseModelFromNoiseProperties(NoiseModel):
292
"""Noise model derived from noise properties."""
293
294
def __init__(self, noise_properties: 'NoiseProperties') -> None:
295
"""Initialize from noise properties characterization."""
296
297
def noisy_operation(self, operation: 'cirq.Operation') -> 'cirq.OP_TREE':
298
"""Apply noise based on operation type and noise properties."""
299
```
300
301
### InsertionNoiseModel
302
303
Noise model that inserts noise operations at specified locations.
304
305
```python { .api }
306
class InsertionNoiseModel(NoiseModel):
307
"""Noise model that inserts noise operations."""
308
309
def __init__(self, ops_added: Dict[OpIdentifier, 'cirq.OP_TREE']) -> None:
310
"""Initialize with operations to add for each gate type."""
311
312
def noisy_operation(self, operation: 'cirq.Operation') -> 'cirq.OP_TREE':
313
"""Insert additional noise operations."""
314
```
315
316
### ThermalNoiseModel
317
318
Thermal noise model for quantum operations.
319
320
```python { .api }
321
class ThermalNoiseModel(NoiseModel):
322
"""Thermal noise model for quantum operations."""
323
324
def __init__(self, frozen_pi_hats: Dict['cirq.Qid', np.ndarray],
325
decay_constants: Dict['cirq.Qid', float],
326
gate_times: Dict[type, 'cirq.Duration']) -> None:
327
"""Initialize thermal noise model."""
328
329
def noisy_operation(self, operation: 'cirq.Operation') -> 'cirq.OP_TREE':
330
"""Apply thermal noise during gate operation."""
331
```
332
333
## Noise Properties
334
335
### NoiseProperties
336
337
Properties characterizing noise in quantum operations.
338
339
```python { .api }
340
class NoiseProperties:
341
"""Properties characterizing noise in quantum operations."""
342
343
def __init__(self,
344
gate_times_ns: Dict[type, float],
345
t1_ns: Dict['cirq.Qid', float],
346
t2_ns: Dict['cirq.Qid', float],
347
readout_errors: Dict['cirq.Qid', Tuple[float, float]],
348
gate_pauli_errors: Dict['cirq.Qid', Dict[type, float]]) -> None:
349
"""Initialize noise properties."""
350
351
@property
352
def gate_times_ns(self) -> Dict[type, float]:
353
"""Gate execution times in nanoseconds."""
354
355
@property
356
def t1_ns(self) -> Dict['cirq.Qid', float]:
357
"""T1 coherence times in nanoseconds."""
358
359
@property
360
def t2_ns(self) -> Dict['cirq.Qid', float]:
361
"""T2 dephasing times in nanoseconds."""
362
363
@property
364
def readout_errors(self) -> Dict['cirq.Qid', Tuple[float, float]]:
365
"""Readout error probabilities (p01, p10)."""
366
367
@property
368
def gate_pauli_errors(self) -> Dict['cirq.Qid', Dict[type, float]]:
369
"""Pauli error rates for different gate types."""
370
```
371
372
### SuperconductingQubitsNoiseProperties
373
374
Specialized noise properties for superconducting qubits.
375
376
```python { .api }
377
class SuperconductingQubitsNoiseProperties(NoiseProperties):
378
"""Noise properties for superconducting qubits."""
379
380
def __init__(self,
381
t1_ns: Dict['cirq.Qid', float],
382
tphi_ns: Dict['cirq.Qid', float],
383
readout_errors: Dict['cirq.Qid', Tuple[float, float]],
384
gate_pauli_errors: Dict['cirq.Qid', Dict[type, float]] = None) -> None:
385
"""Initialize superconducting qubit noise properties."""
386
387
@classmethod
388
def from_default_noise_model(cls, qubits: Iterable['cirq.Qid']) -> 'SuperconductingQubitsNoiseProperties':
389
"""Create default noise model for superconducting qubits."""
390
```
391
392
### OpIdentifier
393
394
Identifier for operations in noise models.
395
396
```python { .api }
397
class OpIdentifier:
398
"""Identifier for operations in noise models."""
399
400
def __init__(self, gate_type: type, *qubits: 'cirq.Qid') -> None:
401
"""Initialize operation identifier."""
402
403
@property
404
def gate_type(self) -> type:
405
"""Type of the gate."""
406
407
@property
408
def qubits(self) -> Tuple['cirq.Qid', ...]:
409
"""Qubits the operation acts on."""
410
```
411
412
## Device Topologies
413
414
### NamedTopology
415
416
Abstract base class for named device topologies.
417
418
```python { .api }
419
class NamedTopology:
420
"""Abstract base class for named device topologies."""
421
422
@abc.abstractmethod
423
def nodes(self) -> Set[Any]:
424
"""Nodes in the topology."""
425
426
@abc.abstractmethod
427
def edges(self) -> Set[Tuple[Any, Any]]:
428
"""Edges in the topology."""
429
430
def neighbors_of(self, node: Any) -> Set[Any]:
431
"""Get neighbors of a node."""
432
```
433
434
### LineTopology
435
436
Linear arrangement of qubits.
437
438
```python { .api }
439
class LineTopology(NamedTopology):
440
"""Linear arrangement of qubits."""
441
442
def __init__(self, length: int) -> None:
443
"""Initialize line topology with given length."""
444
445
def nodes(self) -> Set[int]:
446
"""Node indices in the line."""
447
448
def edges(self) -> Set[Tuple[int, int]]:
449
"""Adjacent pairs in the line."""
450
```
451
452
### TiltedSquareLattice
453
454
Tilted square lattice topology.
455
456
```python { .api }
457
class TiltedSquareLattice(NamedTopology):
458
"""Tilted square lattice topology."""
459
460
def __init__(self, rows: int, cols: int) -> None:
461
"""Initialize tilted square lattice."""
462
463
def nodes(self) -> Set[Tuple[int, int]]:
464
"""Grid positions in the lattice."""
465
466
def edges(self) -> Set[Tuple[Tuple[int, int], Tuple[int, int]]]:
467
"""Connected pairs in the lattice."""
468
```
469
470
## Noise Constants and Utilities
471
472
```python { .api }
473
NO_NOISE: NoiseModel
474
"""No-noise model constant."""
475
476
NOISE_MODEL_LIKE = Union[NoiseModel, 'cirq.Gate', Callable[['cirq.Operation'], 'cirq.OP_TREE']]
477
"""Type hint for noise model-like objects."""
478
```
479
480
## Utility Functions
481
482
### Device Layout Visualization
483
484
```python { .api }
485
def draw_gridlike(qubits: Iterable[GridQubit],
486
ax: Optional['matplotlib.axes.Axes'] = None,
487
tilted: bool = True) -> 'matplotlib.axes.Axes':
488
"""Draw grid-like device layout."""
489
```
490
491
### Placement Functions
492
493
```python { .api }
494
def get_placements(topology: NamedTopology,
495
circuit_qubits: Sequence['cirq.Qid']) -> List[Dict['cirq.Qid', Any]]:
496
"""Get valid qubit placements on topology."""
497
498
def is_valid_placement(topology: NamedTopology,
499
qubit_mapping: Dict['cirq.Qid', Any]) -> bool:
500
"""Check if qubit placement is valid."""
501
502
def draw_placements(topology: NamedTopology,
503
qubit_mapping: Dict['cirq.Qid', Any]) -> None:
504
"""Visualize qubit placements."""
505
```
506
507
### Noise Conversion Functions
508
509
```python { .api }
510
def decay_constant_to_xeb_fidelity(decay_constant: float, num_qubits: int) -> float:
511
"""Convert decay constant to XEB fidelity."""
512
513
def decay_constant_to_pauli_error(decay_constant: float) -> float:
514
"""Convert decay constant to Pauli error."""
515
516
def pauli_error_to_decay_constant(pauli_error: float) -> float:
517
"""Convert Pauli error to decay constant."""
518
519
def xeb_fidelity_to_decay_constant(xeb_fidelity: float, num_qubits: int) -> float:
520
"""Convert XEB fidelity to decay constant."""
521
522
def pauli_error_from_t1(t1: float, gate_time: float) -> float:
523
"""Calculate Pauli error from T1 time."""
524
525
def average_error(errors: Iterable[float]) -> float:
526
"""Calculate average error rate."""
527
528
def decoherence_pauli_error(t1: float, t2: float, gate_time: float) -> float:
529
"""Calculate decoherence Pauli error."""
530
```
531
532
## Usage Examples
533
534
### Creating and Using Qubits
535
536
```python
537
import cirq
538
539
# Line qubits for 1D topologies
540
line_qubits = cirq.LineQubit.range(5)
541
print(f"Line qubits: {line_qubits}")
542
543
# Grid qubits for 2D topologies
544
grid_qubits = cirq.GridQubit.rect(3, 3)
545
print(f"Grid qubits: {grid_qubits}")
546
547
# Check adjacency
548
q0, q1 = cirq.GridQubit(0, 0), cirq.GridQubit(0, 1)
549
print(f"Adjacent: {q0.is_adjacent(q1)}")
550
print(f"Neighbors of {q0}: {q0.neighbors()}")
551
```
552
553
### Applying Noise Models
554
555
```python
556
import cirq
557
558
# Create a simple circuit
559
qubits = cirq.LineQubit.range(2)
560
circuit = cirq.Circuit([
561
cirq.H(qubits[0]),
562
cirq.CNOT(qubits[0], qubits[1])
563
])
564
565
# Apply depolarizing noise
566
noise_model = cirq.ConstantQubitNoiseModel(cirq.depolarize(p=0.01))
567
noisy_circuit = circuit.with_noise(noise_model)
568
569
print("Original circuit:")
570
print(circuit)
571
print("\nNoisy circuit:")
572
print(noisy_circuit)
573
```
574
575
### Custom Device Definition
576
577
```python
578
import cirq
579
580
class MyDevice(cirq.Device):
581
def __init__(self, qubits):
582
self._qubits = frozenset(qubits)
583
584
def qubits(self):
585
return self._qubits
586
587
def validate_operation(self, operation):
588
if not set(operation.qubits).issubset(self._qubits):
589
raise ValueError(f"Operation {operation} uses invalid qubits")
590
# Add custom validation logic here
591
592
# Create device and validate circuit
593
qubits = cirq.LineQubit.range(3)
594
device = MyDevice(qubits)
595
circuit = cirq.Circuit(cirq.H(qubits[0]))
596
597
try:
598
device.validate_circuit(circuit)
599
print("Circuit is valid for device")
600
except ValueError as e:
601
print(f"Invalid circuit: {e}")
602
```
603
604
This documentation provides comprehensive coverage of device modeling and noise simulation capabilities in Cirq for realistic NISQ quantum computing.