|
14 | 14 | import torchquantum.operator as op
|
15 | 15 | from copy import deepcopy
|
16 | 16 | import matplotlib.pyplot as plt
|
17 |
| -from .measurements import gen_bitstrings |
18 |
| -from .measurements import find_observable_groups |
| 17 | +from torchquantum.measurement import gen_bitstrings |
| 18 | +from torchquantum.measurement import find_observable_groups |
19 | 19 |
|
20 | 20 | __all__ = [
|
21 | 21 | "expval_joint_sampling_grouping_density",
|
@@ -181,76 +181,70 @@ def expval_joint_sampling_density(
|
181 | 181 |
|
182 | 182 | return torch.tensor(expval_all, dtype=F_DTYPE)
|
183 | 183 |
|
| 184 | + |
184 | 185 | def expval_joint_analytical_density(
|
185 | 186 | noisedev: tq.NoiseDevice,
|
186 | 187 | observable: str,
|
187 | 188 | n_shots=1024
|
188 | 189 | ):
|
189 | 190 | """
|
190 |
| - Compute the expectation value of a joint observable from sampling |
191 |
| - the measurement bistring |
192 |
| - Args: |
193 |
| - qdev: the quantum device |
194 |
| - observable: the joint observable, on the qubit 0, 1, 2, 3, etc in this order |
195 |
| - Returns: |
196 |
| - the expectation value |
197 |
| - Examples: |
198 |
| - >>> import torchquantum as tq |
199 |
| - >>> import torchquantum.functional as tqf |
200 |
| - >>> x = tq.NoiseDevice(n_wires=2) |
201 |
| - >>> tqf.hadamard(x, wires=0) |
202 |
| - >>> tqf.x(x, wires=1) |
203 |
| - >>> tqf.cnot(x, wires=[0, 1]) |
204 |
| - >>> print(expval_joint_sampling(x, 'II', n_shots=8192)) |
205 |
| - tensor([[0.9997]]) |
206 |
| - >>> print(expval_joint_sampling(x, 'XX', n_shots=8192)) |
207 |
| - tensor([[0.9991]]) |
208 |
| - >>> print(expval_joint_sampling(x, 'ZZ', n_shots=8192)) |
209 |
| - tensor([[-0.9980]]) |
210 |
| - """ |
211 |
| - # rotation to the desired basis |
212 |
| - n_wires = noisedev.n_wires |
213 |
| - paulix = op.op_name_dict["paulix"] |
214 |
| - pauliy = op.op_name_dict["pauliy"] |
215 |
| - pauliz = op.op_name_dict["pauliz"] |
216 |
| - iden = op.op_name_dict["i"] |
| 191 | + Compute the expectation value of a joint observable in analytical way, assuming the |
| 192 | + density matrix is available. |
| 193 | + Args: |
| 194 | + qdev: the quantum device |
| 195 | + observable: the joint observable, on the qubit 0, 1, 2, 3, etc in this order |
| 196 | + Returns: |
| 197 | + the expectation value |
| 198 | + Examples: |
| 199 | + >>> import torchquantum as tq |
| 200 | + >>> import torchquantum.functional as tqf |
| 201 | + >>> x = tq.QuantumDevice(n_wires=2) |
| 202 | + >>> tqf.hadamard(x, wires=0) |
| 203 | + >>> tqf.x(x, wires=1) |
| 204 | + >>> tqf.cnot(x, wires=[0, 1]) |
| 205 | + >>> print(expval_joint_analytical(x, 'II')) |
| 206 | + tensor([[1.0000]]) |
| 207 | + >>> print(expval_joint_analytical(x, 'XX')) |
| 208 | + tensor([[1.0000]]) |
| 209 | + >>> print(expval_joint_analytical(x, 'ZZ')) |
| 210 | + tensor([[-1.0000]]) |
| 211 | + """ |
| 212 | + # compute the hamiltonian matrix |
| 213 | + paulix = mat_dict["paulix"] |
| 214 | + pauliy = mat_dict["pauliy"] |
| 215 | + pauliz = mat_dict["pauliz"] |
| 216 | + iden = mat_dict["i"] |
217 | 217 | pauli_dict = {"X": paulix, "Y": pauliy, "Z": pauliz, "I": iden}
|
218 | 218 |
|
219 |
| - noisedev_clone = tq.NoiseDevice(n_wires=noisedev.n_wires, bsz=noisedev.bsz, device=noisedev.device) |
220 |
| - noisedev_clone.clone_densities(noisedev.densities) |
221 |
| - |
222 | 219 | observable = observable.upper()
|
223 |
| - for wire in range(n_wires): |
224 |
| - for rotation in pauli_dict[observable[wire]]().diagonalizing_gates(): |
225 |
| - rotation(noisedev_clone, wires=wire) |
| 220 | + assert len(observable) == noisedev.n_wires |
| 221 | + densities = noisedev.get_densities_2d() |
226 | 222 |
|
227 |
| - mask = np.ones(len(observable), dtype=bool) |
228 |
| - mask[np.array([*observable]) == "I"] = False |
| 223 | + hamiltonian = pauli_dict[observable[0]].to(densities.device) |
| 224 | + for op in observable[1:]: |
| 225 | + hamiltonian = torch.kron(hamiltonian, pauli_dict[op].to(densities.device)) |
229 | 226 |
|
230 |
| - expval_all = [] |
231 |
| - # measure |
232 |
| - distributions = measure_density(noisedev_clone, n_shots=n_shots) |
233 |
| - for distri in distributions: |
234 |
| - n_eigen_one = 0 |
235 |
| - n_eigen_minus_one = 0 |
236 |
| - for bitstring, n_count in distri.items(): |
237 |
| - if np.dot(list(map(lambda x: eval(x), [*bitstring])), mask).sum() % 2 == 0: |
238 |
| - n_eigen_one += n_count |
239 |
| - else: |
240 |
| - n_eigen_minus_one += n_count |
| 227 | + batch_size = densities.shape[0] |
| 228 | + expanded_hamiltonian = hamiltonian.unsqueeze(0).expand(batch_size, *hamiltonian.shape) |
241 | 229 |
|
242 |
| - expval = n_eigen_one / n_shots + (-1) * n_eigen_minus_one / n_shots |
243 |
| - expval_all.append(expval) |
| 230 | + product = torch.bmm(expanded_hamiltonian, densities) |
244 | 231 |
|
245 |
| - return torch.tensor(expval_all, dtype=F_DTYPE) |
| 232 | + # Extract the diagonal elements from each matrix in the batch |
| 233 | + diagonals = torch.diagonal(product, dim1=-2, dim2=-1) |
| 234 | + |
| 235 | + # Sum the diagonal elements to get the trace for each batch |
| 236 | + trace = torch.sum(diagonals, dim=-1).real |
| 237 | + |
| 238 | + # Should use expectation= Tr(observable \times density matrix) |
| 239 | + return trace |
246 | 240 |
|
247 | 241 |
|
248 | 242 | def expval_density(
|
249 | 243 | noisedev: tq.NoiseDevice,
|
250 | 244 | wires: Union[int, List[int]],
|
251 | 245 | observables: Union[op.Observable, List[op.Observable]],
|
252 | 246 | ):
|
253 |
| - all_dims = np.arange(noisedev.n_wires+1) |
| 247 | + all_dims = np.arange(noisedev.n_wires + 1) |
254 | 248 | if isinstance(wires, int):
|
255 | 249 | wires = [wires]
|
256 | 250 | observables = [observables]
|
@@ -314,15 +308,22 @@ def set_v_c_reg_mapping(self, mapping):
|
314 | 308 | qdev = tq.NoiseDevice(n_wires=2, bsz=5, device="cpu", record_op=True) # use device='cuda' for GPU
|
315 | 309 | qdev.h(wires=0)
|
316 | 310 | qdev.cnot(wires=[0, 1])
|
317 |
| - tqf.h(qdev, wires=1) |
318 |
| - tqf.x(qdev, wires=1) |
319 |
| - op = tq.RX(has_params=True, trainable=True, init_params=0.5) |
320 |
| - op(qdev, wires=0) |
| 311 | + #tqf.h(qdev, wires=1) |
| 312 | + #tqf.x(qdev, wires=1) |
| 313 | + #tqf.y(qdev, wires=1) |
| 314 | + #tqf.cnot(qdev,wires=[0, 1]) |
| 315 | + # op = tq.RX(has_params=True, trainable=True, init_params=0.5) |
| 316 | + # op(qdev, wires=0) |
321 | 317 |
|
322 | 318 | # measure the state on z basis
|
323 | 319 | print(tq.measure_density(qdev, n_shots=1024))
|
324 | 320 |
|
325 | 321 | # obtain the expval on a observable
|
326 |
| - expval = expval_joint_sampling_density(qdev, 'II', 100000) |
327 |
| - # expval_ana = expval_joint_analytical(qdev, 'II') |
| 322 | + expval = expval_joint_sampling_density(qdev, 'XZ', 100000) |
| 323 | + |
| 324 | + print("expval") |
328 | 325 | print(expval)
|
| 326 | + |
| 327 | + expval_ana = expval_joint_analytical_density(qdev, 'XZ') |
| 328 | + print("expval_ana") |
| 329 | + print(expval_ana) |
0 commit comments