|
1 | 1 | import numpy as np |
2 | 2 |
|
3 | | -from src.core.operator_set import LocalOperator, Operator |
| 3 | +from src.core.operator import Operator |
| 4 | +from src.utils.hermitian import hermitian |
4 | 5 |
|
5 | 6 |
|
6 | 7 | class Hamiltonian(Operator): |
| 8 | + """ |
| 9 | + Represents a quantum Hamiltonian. |
| 10 | + The Hamiltonian must be Hermitian to be valid. |
| 11 | + """ |
| 12 | + |
7 | 13 | def __init__(self, matrix: np.ndarray): |
| 14 | + """Initializes a Hamiltonian object which represents a quantum |
| 15 | + Hamiltonian. The Hamtonian must be Hermitian to be valid. |
| 16 | +
|
| 17 | + Parameter matrix: The matrix representation of the Hamiltonian. |
| 18 | + Precondition: matrix is a numpy array. |
| 19 | + """ |
| 20 | + assert isinstance(matrix, np.ndarray), "matrix must be a numpy array" |
| 21 | + assert hermitian(matrix), "matrix must be Hermitian" |
| 22 | + assert ( |
| 23 | + matrix.ndim == 2 and matrix.shape[0] == matrix.shape[1] |
| 24 | + ), "matrix must be a square 2D array" |
8 | 25 | super().__init__(matrix) |
9 | 26 |
|
10 | 27 | def __str__(self): |
| 28 | + """Returns a string representation of the Hamiltonian.""" |
11 | 29 | return f"Hamiltonian(matrix={self.matrix})" |
12 | 30 |
|
13 | 31 | def __repr__(self): |
| 32 | + """Returns a string representation of the Hamiltonian.""" |
14 | 33 | return f"Hamiltonian(matrix={self.matrix})" |
15 | 34 |
|
16 | 35 | def __eq__(self, other): |
| 36 | + """Returns whether the Hamiltonian is equal to another Hamiltonian.""" |
17 | 37 | return np.allclose(self.matrix, other.matrix) |
18 | 38 |
|
19 | 39 | def __ne__(self, other): |
| 40 | + """Returns whether the Hamiltonian is not equal to another Hamiltonian.""" |
20 | 41 | return not np.allclose(self.matrix, other.matrix) |
21 | 42 |
|
22 | 43 | def to_hamiltonian_set(self): |
| 44 | + """Returns a HamiltonianSet containing the Hamiltonian.""" |
| 45 | + from src.core.hamiltonian_set import HamiltonianSet |
| 46 | + |
23 | 47 | return HamiltonianSet([self]) |
24 | 48 |
|
25 | 49 | def to_local_hamiltonian(self, local_dim: int = 2): |
26 | | - lo = self.to_local_operator(local_dim) |
27 | | - return LocalHamiltonian(lo.matrix, lo.sites, lo.local_dim) |
28 | | - |
29 | | - |
30 | | -class LocalHamiltonian(LocalOperator, Hamiltonian): |
31 | | - """ |
32 | | - Local Hamiltonian acting non-trivially on the provided `sites`. |
33 | | -
|
34 | | - ``sites`` must be distinct consecutive integers; see `LocalOperator`. |
35 | | - """ |
36 | | - |
37 | | - def __init__( |
38 | | - self, |
39 | | - matrix: np.ndarray, |
40 | | - sites: list[int], |
41 | | - local_dim: int = 2, |
42 | | - ): |
43 | | - super().__init__(matrix, sites, local_dim) |
44 | | - |
45 | | - def __str__(self): |
46 | | - return ( |
47 | | - "LocalHamiltonian(" |
48 | | - f"sites={self.sites}, " |
49 | | - f"local_dim={self.local_dim}, " |
50 | | - f"shape={self.matrix.shape}" |
51 | | - ")" |
52 | | - ) |
| 50 | + """Returns a LocalHamiltonian representing the Hamiltonian acting on |
| 51 | + the given sites. |
53 | 52 |
|
54 | | - def __repr__(self): |
55 | | - return ( |
56 | | - "LocalHamiltonian(" |
57 | | - f"matrix={self.matrix!r}, " |
58 | | - f"sites={self.sites!r}, " |
59 | | - f"local_dim={self.local_dim}" |
60 | | - ")" |
61 | | - ) |
62 | | - |
63 | | - |
64 | | -class HamiltonianSet: |
65 | | - def __init__(self, hamiltonians: list[Hamiltonian]): |
66 | | - self.hamiltonians = hamiltonians |
67 | | - self.hamiltonian_count = len(hamiltonians) |
68 | | - |
69 | | - def __str__(self): |
70 | | - return f"HamiltonianSet(hamiltonian_count={self.hamiltonian_count})" |
71 | | - |
72 | | - def __repr__(self): |
73 | | - return f"HamiltonianSet(hamiltonians={self.hamiltonians!r})" |
| 53 | + Parameter local_dim: The local dimension of the Hamiltonian. |
| 54 | + Precondition: local_dim is a positive integer. |
| 55 | + """ |
| 56 | + assert isinstance(local_dim, int), "local_dim must be an integer" |
| 57 | + assert local_dim > 0, "local_dim must be a positive integer" |
74 | 58 |
|
| 59 | + from src.core.local_hamiltonian import LocalHamiltonian |
75 | 60 |
|
76 | | -def combined_hamiltonian_matrix( |
77 | | - hamiltonians: list[Hamiltonian], |
78 | | - num_qubits: int, |
79 | | -) -> np.ndarray: |
80 | | - """ |
81 | | - Sum Hamiltonian terms on the full ``num_qubits``-site tensor space. |
82 | | -
|
83 | | - ``LocalHamiltonian`` terms are embedded with identities on the remaining |
84 | | - sites; full-domain ``Hamiltonian`` matrices are added as-is. All terms must |
85 | | - match a common Hilbert-space dimension (``local_dim ** num_qubits`` for |
86 | | - locals, or the matrix size of bare terms). |
87 | | - """ |
88 | | - if not hamiltonians: |
89 | | - raise ValueError("hamiltonians must be a non-empty list") |
90 | | - |
91 | | - target_dim: int | None = None |
92 | | - for h in hamiltonians: |
93 | | - if isinstance(h, LocalHamiltonian): |
94 | | - d = h.local_dim**num_qubits |
95 | | - else: |
96 | | - d = int(np.asarray(h.matrix).shape[0]) |
97 | | - if target_dim is None: |
98 | | - target_dim = d |
99 | | - elif d != target_dim: |
100 | | - raise ValueError( |
101 | | - f"Hamiltonian dimensions disagree: got {d} vs {target_dim}" |
102 | | - ) |
103 | | - |
104 | | - H_tot = np.zeros((target_dim, target_dim), dtype=np.complex128) |
105 | | - for h in hamiltonians: |
106 | | - if isinstance(h, LocalHamiltonian): |
107 | | - ld = h.local_dim |
108 | | - lo, hi = min(h.sites), max(h.sites) |
109 | | - n_before = lo |
110 | | - n_after = num_qubits - 1 - hi |
111 | | - term = np.asarray(h.matrix, dtype=np.complex128) |
112 | | - if n_before > 0: |
113 | | - term = np.kron(np.eye(ld**n_before, dtype=np.complex128), term) |
114 | | - if n_after > 0: |
115 | | - term = np.kron(term, np.eye(ld**n_after, dtype=np.complex128)) |
116 | | - H_tot += term |
117 | | - else: |
118 | | - H_tot += np.asarray(h.matrix, dtype=np.complex128) |
119 | | - return H_tot |
| 61 | + lo = self.to_local_operator(local_dim) |
| 62 | + return LocalHamiltonian(lo.matrix, lo.sites, lo.local_dim) |
0 commit comments