0
# Linear Algebra Utilities
1
2
The linalg module provides linear algebra methods and functions essential for quantum computing primitives, including matrix decompositions, quantum-specific operations, and mathematical utilities.
3
4
## Matrix Decompositions
5
6
### KAK Decomposition
7
8
```python { .api }
9
def kak_decomposition(unitary: np.ndarray, *, atol: float = 1e-8) -> 'KakDecomposition':
10
"""KAK decomposition of two-qubit unitary matrices.
11
12
Args:
13
unitary: 4x4 unitary matrix to decompose
14
atol: Absolute tolerance for comparisons
15
16
Returns:
17
KAK decomposition result
18
"""
19
20
class KakDecomposition:
21
"""Result of KAK decomposition: U = (A1 ⊗ A0) exp(i(x XX + y YY + z ZZ)) (A3 ⊗ A2)."""
22
23
def __init__(self, global_phase: complex,
24
single_qubit_operations_before: Tuple[np.ndarray, np.ndarray],
25
interaction_coefficients: Tuple[float, float, float],
26
single_qubit_operations_after: Tuple[np.ndarray, np.ndarray]) -> None:
27
"""Initialize KAK decomposition."""
28
29
@property
30
def global_phase(self) -> complex:
31
"""Global phase factor."""
32
33
@property
34
def single_qubit_operations_before(self) -> Tuple[np.ndarray, np.ndarray]:
35
"""Single-qubit operations before interaction."""
36
37
@property
38
def interaction_coefficients(self) -> Tuple[float, float, float]:
39
"""Interaction coefficients (x, y, z) for XX, YY, ZZ terms."""
40
41
@property
42
def single_qubit_operations_after(self) -> Tuple[np.ndarray, np.ndarray]:
43
"""Single-qubit operations after interaction."""
44
45
def __mul__(self, other: 'KakDecomposition') -> 'KakDecomposition':
46
"""Multiply two KAK decompositions."""
47
48
def kak_vector(unitary: np.ndarray, *, atol: float = 1e-8) -> np.ndarray:
49
"""Extract KAK interaction coefficients from two-qubit unitary."""
50
51
def kak_canonicalize_vector(x: float, y: float, z: float, *, atol: float = 1e-8) -> Tuple[float, float, float]:
52
"""Canonicalize KAK vector to preferred coordinate system."""
53
```
54
55
### Single-Qubit Matrix Decomposition
56
57
```python { .api }
58
def axis_angle(matrix: np.ndarray, *, atol: float = 1e-8) -> 'AxisAngleDecomposition':
59
"""Axis-angle decomposition of single-qubit unitary matrix.
60
61
Args:
62
matrix: 2x2 unitary matrix
63
atol: Absolute tolerance
64
65
Returns:
66
Axis-angle decomposition
67
"""
68
69
class AxisAngleDecomposition:
70
"""Axis-angle decomposition of rotation: exp(-i θ/2 n⃗·σ⃗)."""
71
72
def __init__(self, angle: float, axis: np.ndarray, global_phase: complex = 1) -> None:
73
"""Initialize axis-angle decomposition."""
74
75
@property
76
def angle(self) -> float:
77
"""Rotation angle in radians."""
78
79
@property
80
def axis(self) -> np.ndarray:
81
"""Rotation axis as 3D unit vector."""
82
83
@property
84
def global_phase(self) -> complex:
85
"""Global phase factor."""
86
87
def deconstruct_single_qubit_matrix_into_angles(mat: np.ndarray) -> Tuple[float, float, float]:
88
"""Deconstruct single-qubit matrix into ZYZ Euler angles."""
89
```
90
91
### Matrix Factorization
92
93
```python { .api }
94
def kron_factor_4x4_to_2x2s(matrix: np.ndarray, *, atol: float = 1e-8) -> Optional[Tuple[np.ndarray, np.ndarray]]:
95
"""Factor 4x4 matrix into Kronecker product of 2x2 matrices.
96
97
Args:
98
matrix: 4x4 matrix to factor
99
atol: Tolerance for factorization
100
101
Returns:
102
Tuple of 2x2 matrices (A, B) such that matrix ≈ A ⊗ B, or None if not factorizable
103
"""
104
105
def unitary_eig(matrix: np.ndarray, *, atol: float = 1e-8) -> Tuple[np.ndarray, np.ndarray]:
106
"""Eigendecomposition of unitary matrix.
107
108
Args:
109
matrix: Unitary matrix
110
atol: Tolerance for unitarity check
111
112
Returns:
113
Tuple of (eigenvalues, eigenvectors)
114
"""
115
116
def map_eigenvalues(matrix: np.ndarray, func: Callable[[complex], complex], *, atol: float = 1e-8) -> np.ndarray:
117
"""Apply function to eigenvalues of matrix.
118
119
Args:
120
matrix: Input matrix
121
func: Function to apply to each eigenvalue
122
atol: Tolerance for computations
123
124
Returns:
125
Matrix with transformed eigenvalues
126
"""
127
128
def so4_to_magic_su2s(so4_matrix: np.ndarray, *, atol: float = 1e-8) -> Tuple[np.ndarray, np.ndarray]:
129
"""Convert SO(4) matrix to pair of SU(2) matrices using magic basis."""
130
```
131
132
## Matrix Operations
133
134
### Basic Operations
135
136
```python { .api }
137
def dot(*values: np.ndarray) -> np.ndarray:
138
"""Enhanced matrix multiplication for multiple matrices.
139
140
Args:
141
*values: Matrices to multiply in sequence
142
143
Returns:
144
Product of all matrices
145
"""
146
147
def kron(*matrices: np.ndarray) -> np.ndarray:
148
"""Kronecker product of multiple matrices.
149
150
Args:
151
*matrices: Matrices to take Kronecker product of
152
153
Returns:
154
Kronecker product ⊗ᵢ matrices[i]
155
"""
156
157
def kron_with_controls(matrices: Sequence[np.ndarray], controls: Sequence[Sequence[int]]) -> np.ndarray:
158
"""Kronecker product with control structure.
159
160
Args:
161
matrices: Matrices for each subsystem
162
controls: Control qubit specifications
163
164
Returns:
165
Controlled Kronecker product
166
"""
167
168
def block_diag(*blocks: np.ndarray) -> np.ndarray:
169
"""Block diagonal matrix from given blocks.
170
171
Args:
172
*blocks: Diagonal blocks
173
174
Returns:
175
Block diagonal matrix
176
"""
177
```
178
179
### Quantum-Specific Operations
180
181
```python { .api }
182
def apply_matrix_to_slices(target: np.ndarray,
183
matrix: np.ndarray,
184
slices: Sequence[Union[slice, int, Ellipsis]],
185
*, out: Optional[np.ndarray] = None) -> np.ndarray:
186
"""Apply matrix to specified slices of target tensor.
187
188
Args:
189
target: Target tensor to modify
190
matrix: Matrix to apply
191
slices: Slices specifying which parts to operate on
192
out: Output buffer (optional)
193
194
Returns:
195
Modified tensor
196
"""
197
198
def targeted_left_multiply(left_matrix: np.ndarray,
199
right_target: np.ndarray,
200
target_axes: Sequence[int],
201
*, out: Optional[np.ndarray] = None) -> np.ndarray:
202
"""Left-multiply target tensor by matrix on specified axes."""
203
204
def targeted_conjugate_about(tensor: np.ndarray,
205
matrix: np.ndarray,
206
indices: Sequence[int],
207
*, out: Optional[np.ndarray] = None) -> np.ndarray:
208
"""Conjugate tensor by matrix: M† @ tensor @ M on specified indices."""
209
210
def partial_trace(tensor: np.ndarray,
211
keep_indices: Sequence[int],
212
qid_shape: Tuple[int, ...]) -> np.ndarray:
213
"""Compute partial trace of tensor, keeping specified subsystems.
214
215
Args:
216
tensor: Input tensor (state vector or density matrix)
217
keep_indices: Indices of subsystems to keep
218
qid_shape: Shape of each subsystem
219
220
Returns:
221
Partial trace over discarded subsystems
222
"""
223
224
def partial_trace_of_state_vector_as_mixture(state_vector: np.ndarray,
225
keep_indices: Sequence[int],
226
qid_shape: Tuple[int, ...]) -> Tuple[Tuple[float, np.ndarray], ...]:
227
"""Partial trace of state vector as mixture representation."""
228
229
def sub_state_vector(state_vector: np.ndarray,
230
keep_indices: Sequence[int],
231
qid_shape: Tuple[int, ...],
232
*, default: Optional[np.ndarray] = None,
233
atol: float = 1e-8) -> Optional[np.ndarray]:
234
"""Extract sub-state vector for specified subsystems."""
235
```
236
237
### Kronecker Products for Quantum States
238
239
```python { .api }
240
def state_vector_kronecker_product(*factors: np.ndarray) -> np.ndarray:
241
"""Kronecker product of state vectors.
242
243
Args:
244
*factors: State vectors to tensor together
245
246
Returns:
247
Tensor product state vector
248
"""
249
250
def density_matrix_kronecker_product(*factors: np.ndarray) -> np.ndarray:
251
"""Kronecker product of density matrices.
252
253
Args:
254
*factors: Density matrices to tensor together
255
256
Returns:
257
Tensor product density matrix
258
"""
259
```
260
261
## Matrix Properties and Predicates
262
263
### Unitary and Orthogonal Matrices
264
265
```python { .api }
266
def is_unitary(matrix: np.ndarray, *, atol: float = 1e-8) -> bool:
267
"""Check if matrix is unitary (U† U = I).
268
269
Args:
270
matrix: Matrix to test
271
atol: Absolute tolerance
272
273
Returns:
274
True if matrix is unitary
275
"""
276
277
def is_orthogonal(matrix: np.ndarray, *, atol: float = 1e-8) -> bool:
278
"""Check if matrix is orthogonal (O^T O = I)."""
279
280
def is_special_unitary(matrix: np.ndarray, *, atol: float = 1e-8) -> bool:
281
"""Check if matrix is special unitary (unitary with determinant 1)."""
282
283
def is_special_orthogonal(matrix: np.ndarray, *, atol: float = 1e-8) -> bool:
284
"""Check if matrix is special orthogonal (orthogonal with determinant 1)."""
285
```
286
287
### Hermitian and Normal Matrices
288
289
```python { .api }
290
def is_hermitian(matrix: np.ndarray, *, atol: float = 1e-8) -> bool:
291
"""Check if matrix is Hermitian (M† = M).
292
293
Args:
294
matrix: Matrix to test
295
atol: Absolute tolerance
296
297
Returns:
298
True if matrix is Hermitian
299
"""
300
301
def is_normal(matrix: np.ndarray, *, atol: float = 1e-8) -> bool:
302
"""Check if matrix is normal (M M† = M† M)."""
303
304
def is_diagonal(matrix: np.ndarray, *, atol: float = 1e-8) -> bool:
305
"""Check if matrix is diagonal."""
306
```
307
308
### Quantum Channel Properties
309
310
```python { .api }
311
def is_cptp(choi: np.ndarray, *, atol: float = 1e-8) -> bool:
312
"""Check if Choi matrix represents completely positive trace-preserving map.
313
314
Args:
315
choi: Choi matrix representation
316
atol: Tolerance for tests
317
318
Returns:
319
True if map is CPTP
320
"""
321
```
322
323
### Matrix Comparison
324
325
```python { .api }
326
def allclose_up_to_global_phase(a: np.ndarray, b: np.ndarray, *,
327
rtol: float = 1e-5, atol: float = 1e-8) -> bool:
328
"""Check if matrices are equal up to global phase.
329
330
Args:
331
a, b: Matrices to compare
332
rtol: Relative tolerance
333
atol: Absolute tolerance
334
335
Returns:
336
True if matrices are equal up to global phase
337
"""
338
339
def matrix_commutes(m1: np.ndarray, m2: np.ndarray, *, atol: float = 1e-8) -> bool:
340
"""Check if two matrices commute (AB = BA).
341
342
Args:
343
m1, m2: Matrices to test
344
atol: Absolute tolerance
345
346
Returns:
347
True if matrices commute
348
"""
349
```
350
351
## Diagonalization and Eigenvalue Problems
352
353
### Real Symmetric Matrices
354
355
```python { .api }
356
def diagonalize_real_symmetric_matrix(matrix: np.ndarray, *,
357
atol: float = 1e-8) -> Tuple[np.ndarray, np.ndarray]:
358
"""Diagonalize real symmetric matrix.
359
360
Args:
361
matrix: Real symmetric matrix
362
atol: Tolerance for symmetry check
363
364
Returns:
365
Tuple of (eigenvalues, eigenvectors)
366
"""
367
368
def diagonalize_real_symmetric_and_sorted_diagonal_matrices(symmetric_matrix: np.ndarray,
369
diagonal_matrix: np.ndarray, *,
370
atol: float = 1e-8) -> Tuple[np.ndarray, np.ndarray]:
371
"""Simultaneously diagonalize symmetric and diagonal matrices with sorted eigenvalues."""
372
```
373
374
### Bidiagonalization
375
376
```python { .api }
377
def bidiagonalize_real_matrix_pair_with_symmetric_products(mat1: np.ndarray, mat2: np.ndarray, *,
378
atol: float = 1e-8) -> Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]:
379
"""Bidiagonalize pair of real matrices with symmetric products."""
380
381
def bidiagonalize_unitary_with_special_orthogonals(mat: np.ndarray, *,
382
atol: float = 1e-8) -> Tuple[np.ndarray, np.ndarray, np.ndarray]:
383
"""Bidiagonalize unitary matrix using special orthogonal matrices."""
384
```
385
386
## Operator Space Operations
387
388
### Pauli Basis Operations
389
390
```python { .api }
391
PAULI_BASIS: Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]
392
"""Standard Pauli basis matrices [I, X, Y, Z]."""
393
394
def expand_matrix_in_orthogonal_basis(matrix: np.ndarray,
395
basis: Sequence[np.ndarray]) -> np.ndarray:
396
"""Expand matrix in orthogonal basis.
397
398
Args:
399
matrix: Matrix to expand
400
basis: Orthogonal basis matrices
401
402
Returns:
403
Coefficients in the basis expansion
404
"""
405
406
def matrix_from_basis_coefficients(coefficients: Sequence[complex],
407
basis: Sequence[np.ndarray]) -> np.ndarray:
408
"""Construct matrix from basis coefficients.
409
410
Args:
411
coefficients: Expansion coefficients
412
basis: Basis matrices
413
414
Returns:
415
Matrix = Σᵢ coefficients[i] * basis[i]
416
"""
417
418
def kron_bases(*bases: Sequence[np.ndarray]) -> List[np.ndarray]:
419
"""Kronecker product of multiple bases.
420
421
Args:
422
*bases: Bases to take Kronecker product of
423
424
Returns:
425
List of all Kronecker products
426
"""
427
428
def hilbert_schmidt_inner_product(mat1: np.ndarray, mat2: np.ndarray) -> complex:
429
"""Hilbert-Schmidt inner product: ⟨A, B⟩ = tr(A† B).
430
431
Args:
432
mat1, mat2: Matrices for inner product
433
434
Returns:
435
Inner product value
436
"""
437
438
def pow_pauli_combination(pauli_map: Dict[str, float], exponent: float) -> Dict[str, complex]:
439
"""Raise Pauli combination to a power."""
440
```
441
442
## Tolerance and Comparison Functions
443
444
### Near-Zero Checks
445
446
```python { .api }
447
def all_near_zero(a: np.ndarray, *, atol: float = 1e-8) -> bool:
448
"""Check if all elements are near zero.
449
450
Args:
451
a: Array to check
452
atol: Absolute tolerance
453
454
Returns:
455
True if all elements have magnitude < atol
456
"""
457
458
def all_near_zero_mod(a: np.ndarray, period: float, *, atol: float = 1e-8) -> bool:
459
"""Check if all elements are near zero modulo period."""
460
```
461
462
### Utility Functions
463
464
```python { .api }
465
def match_global_phase(a: np.ndarray, b: np.ndarray) -> np.ndarray:
466
"""Multiply b by phase to match global phase of a.
467
468
Args:
469
a: Reference matrix
470
b: Matrix to adjust
471
472
Returns:
473
Phase-adjusted version of b
474
"""
475
476
def slice_for_qubits_equal_to(target_qubit_axes: Sequence[int],
477
little_endian_qureg_value: int = 0,
478
qid_shape: Optional[Sequence[int]] = None) -> Tuple[Union[slice, int], ...]:
479
"""Create slice for computational basis state."""
480
481
def phase_delta(mat: np.ndarray, mat2: np.ndarray) -> float:
482
"""Phase difference between matrices."""
483
484
def reflection_matrix_pow(reflection_matrix: np.ndarray, exponent: float) -> np.ndarray:
485
"""Raise reflection matrix to a power."""
486
487
def to_special(matrix: np.ndarray) -> np.ndarray:
488
"""Convert to special unitary/orthogonal (determinant 1)."""
489
490
def extract_right_diag(left: np.ndarray, middle: np.ndarray, right: np.ndarray) -> np.ndarray:
491
"""Extract right diagonal matrix from decomposition."""
492
```
493
494
## Utility and Validation Functions
495
496
```python { .api }
497
def num_cnots_required(mat: np.ndarray) -> int:
498
"""Minimum number of CNOTs required to implement two-qubit unitary.
499
500
Args:
501
mat: 4x4 unitary matrix
502
503
Returns:
504
Minimum CNOT count (0, 1, 2, or 3)
505
"""
506
507
def can_numpy_support_shape(shape: Sequence[int]) -> bool:
508
"""Check if numpy can support given tensor shape."""
509
510
def transpose_flattened_array(array: np.ndarray,
511
shape: Tuple[int, ...],
512
permutation: Sequence[int]) -> np.ndarray:
513
"""Transpose flattened array according to permutation."""
514
515
def scatter_plot_normalized_kak_interaction_coefficients(mat: np.ndarray) -> None:
516
"""Create scatter plot of normalized KAK interaction coefficients."""
517
518
CONTROL_TAG: str
519
"""Tag for control operations in linear algebra contexts."""
520
```
521
522
## Usage Examples
523
524
### Matrix Decompositions
525
526
```python
527
import cirq
528
import numpy as np
529
530
# KAK decomposition of two-qubit gate
531
print("=== KAK Decomposition ===")
532
# Create a two-qubit unitary (CNOT gate)
533
cnot_matrix = np.array([[1, 0, 0, 0],
534
[0, 1, 0, 0],
535
[0, 0, 0, 1],
536
[0, 0, 1, 0]], dtype=complex)
537
538
kak = cirq.kak_decomposition(cnot_matrix)
539
print(f"Global phase: {kak.global_phase}")
540
print(f"Interaction coefficients: {kak.interaction_coefficients}")
541
print(f"Single-qubit ops before shape: {[m.shape for m in kak.single_qubit_operations_before]}")
542
543
# Verify decomposition
544
reconstructed = kak.global_phase * np.kron(kak.single_qubit_operations_before[1],
545
kak.single_qubit_operations_before[0])
546
# Apply interaction term...
547
print(f"Decomposition valid: {np.allclose(cnot_matrix, reconstructed, atol=1e-10)}")
548
549
# Axis-angle decomposition for single qubit
550
print("\n=== Axis-Angle Decomposition ===")
551
h_matrix = np.array([[1, 1], [1, -1]]) / np.sqrt(2) # Hadamard
552
axis_angle = cirq.axis_angle(h_matrix)
553
print(f"Rotation angle: {axis_angle.angle:.4f} radians")
554
print(f"Rotation axis: {axis_angle.axis}")
555
```
556
557
### Matrix Properties and Validation
558
559
```python
560
import cirq
561
import numpy as np
562
563
print("=== Matrix Properties ===")
564
565
# Test various matrix properties
566
matrices = {
567
'Identity': np.eye(2),
568
'Hadamard': np.array([[1, 1], [1, -1]]) / np.sqrt(2),
569
'Pauli-X': np.array([[0, 1], [1, 0]]),
570
'Random': np.random.randn(2, 2) + 1j * np.random.randn(2, 2)
571
}
572
573
for name, matrix in matrices.items():
574
print(f"\n{name} matrix:")
575
print(f" Unitary: {cirq.is_unitary(matrix)}")
576
print(f" Hermitian: {cirq.is_hermitian(matrix)}")
577
print(f" Normal: {cirq.is_normal(matrix)}")
578
579
if cirq.is_unitary(matrix):
580
print(f" Special unitary: {cirq.is_special_unitary(matrix)}")
581
582
# Test quantum channel properties
583
print("\n=== Quantum Channel Properties ===")
584
# Depolarizing channel Choi matrix
585
p = 0.1
586
depol_choi = (1-p) * np.kron([[1, 0], [0, 0]], np.eye(2)) + p/3 * sum(
587
np.kron(pauli, pauli) for pauli in [[[0, 1], [1, 0]], [[0, -1j], [1j, 0]], [[1, 0], [0, -1]]]
588
)
589
print(f"Depolarizing channel is CPTP: {cirq.is_cptp(depol_choi)}")
590
```
591
592
### Quantum State Operations
593
594
```python
595
import cirq
596
import numpy as np
597
598
print("=== Quantum State Operations ===")
599
600
# Create entangled state
601
bell_state = np.array([1, 0, 0, 1]) / np.sqrt(2) # (|00⟩ + |11⟩)/√2
602
density_matrix = np.outer(bell_state, bell_state)
603
604
# Partial trace to get reduced density matrix of first qubit
605
reduced_dm = cirq.partial_trace(density_matrix, keep_indices=[0], qid_shape=(2, 2))
606
print(f"Reduced density matrix of first qubit:")
607
print(reduced_dm)
608
609
# Check if reduced state is pure
610
eigenvals = np.linalg.eigvals(reduced_dm)
611
purity = np.sum(eigenvals**2)
612
print(f"Purity of reduced state: {purity.real:.4f}")
613
print(f"Is pure state: {np.isclose(purity, 1.0)}")
614
615
# Kronecker products
616
state1 = np.array([1, 0]) # |0⟩
617
state2 = np.array([0, 1]) # |1⟩
618
product_state = cirq.state_vector_kronecker_product(state1, state2)
619
print(f"Product state |01⟩: {product_state}")
620
```
621
622
### Linear Algebra Utilities
623
624
```python
625
import cirq
626
import numpy as np
627
628
print("=== Linear Algebra Utilities ===")
629
630
# Matrix operations
631
A = np.array([[1, 2], [3, 4]])
632
B = np.array([[5, 6], [7, 8]])
633
C = np.array([[1, 0], [0, -1]])
634
635
# Enhanced dot product
636
product = cirq.dot(A, B, C)
637
print(f"A @ B @ C shape: {product.shape}")
638
639
# Kronecker product
640
kron_product = cirq.kron(A, B)
641
print(f"A ⊗ B shape: {kron_product.shape}")
642
643
# Try to factor back
644
factors = cirq.kron_factor_4x4_to_2x2s(kron_product)
645
if factors is not None:
646
A_recovered, B_recovered = factors
647
print(f"Successfully factored: {np.allclose(A, A_recovered) and np.allclose(B, B_recovered)}")
648
649
# Pauli basis expansion
650
pauli_x = np.array([[0, 1], [1, 0]])
651
coeffs = cirq.expand_matrix_in_orthogonal_basis(pauli_x, cirq.PAULI_BASIS)
652
print(f"Pauli-X coefficients in Pauli basis: {coeffs}")
653
654
# Reconstruct from coefficients
655
reconstructed = cirq.matrix_from_basis_coefficients(coeffs, cirq.PAULI_BASIS)
656
print(f"Reconstruction successful: {np.allclose(pauli_x, reconstructed)}")
657
658
# Near-zero checks
659
small_matrix = np.array([[1e-10, 1e-9], [1e-11, 1e-12]])
660
print(f"All elements near zero (atol=1e-8): {cirq.all_near_zero(small_matrix, atol=1e-8)}")
661
```
662
663
This comprehensive linear algebra toolkit provides the mathematical foundation for quantum computing operations, enabling efficient manipulation of quantum states, gates, and channels.