Skip to content

Commit ad88a79

Browse files
author
Zhuoyang Ye
committed
[Test] Add test for density matrix measurement
1 parent 81a181f commit ad88a79

5 files changed

+265
-15
lines changed

test/density/test_density_measure.py

+53-13
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,64 @@
1+
"""
2+
MIT License
3+
4+
Copyright (c) 2020-present TorchQuantum Authors
5+
6+
Permission is hereby granted, free of charge, to any person obtaining a copy
7+
of this software and associated documentation files (the "Software"), to deal
8+
in the Software without restriction, including without limitation the rights
9+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
copies of the Software, and to permit persons to whom the Software is
11+
furnished to do so, subject to the following conditions:
12+
13+
The above copyright notice and this permission notice shall be included in all
14+
copies or substantial portions of the Software.
15+
16+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+
SOFTWARE.
23+
"""
24+
125
import torchquantum as tq
2-
import numpy as np
326

4-
import qiskit.circuit.library.standard_gates as qiskit_gate
5-
from qiskit.quantum_info import DensityMatrix as qiskitDensity
27+
from torchquantum.plugin import op_history2qiskit
28+
from qiskit import Aer, transpile
29+
import numpy as np
630

7-
from unittest import TestCase
831

32+
def test_measure():
33+
n_shots = 10000
34+
qdev = tq.NoiseDevice(n_wires=3, bsz=1, record_op=True)
35+
qdev.x(wires=2) # type: ignore
36+
qdev.x(wires=1) # type: ignore
37+
qdev.ry(wires=0, params=0.98) # type: ignore
38+
qdev.rx(wires=1, params=1.2) # type: ignore
39+
qdev.cnot(wires=[0, 2]) # type: ignore
940

41+
tq_counts = tq.measure(qdev, n_shots=n_shots)
1042

11-
class density_measure_test(TestCase):
12-
def test_single_qubit_random_layer(self):
13-
return
43+
circ = op_history2qiskit(qdev.n_wires, qdev.op_history)
44+
circ.measure_all()
45+
simulator = Aer.get_backend("aer_simulator_density_matrix")
46+
circ = transpile(circ, simulator)
47+
qiskit_res = simulator.run(circ, shots=n_shots).result()
48+
qiskit_counts = qiskit_res.get_counts()
1449

15-
def test_two_qubit_random_layer(self):
16-
return
50+
for k, v in tq_counts[0].items():
51+
# need to reverse the bitstring because qiskit is in little endian
52+
qiskit_ratio = qiskit_counts.get(k[::-1], 0) / n_shots
53+
tq_ratio = v / n_shots
54+
print(k, qiskit_ratio, tq_ratio)
55+
assert np.isclose(qiskit_ratio, tq_ratio, atol=0.1)
1756

57+
print("tq.measure for density matrix test passed")
1858

19-
def test_three_qubit_random_layer(self):
20-
return
2159

60+
if __name__ == "__main__":
61+
import pdb
2262

23-
def test_mixed_layer(self):
24-
return
63+
pdb.set_trace()
64+
test_measure()

test/density/test_density_op.py

-2
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,6 @@
3535

3636
from random import randrange, uniform
3737

38-
import qiskit.circuit.library as qiskit_library
39-
from qiskit.quantum_info import Operator
4038

4139
RND_TIMES = 100
4240

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
"""
2+
MIT License
3+
4+
Copyright (c) 2020-present TorchQuantum Authors
5+
6+
Permission is hereby granted, free of charge, to any person obtaining a copy
7+
of this software and associated documentation files (the "Software"), to deal
8+
in the Software without restriction, including without limitation the rights
9+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
copies of the Software, and to permit persons to whom the Software is
11+
furnished to do so, subject to the following conditions:
12+
13+
The above copyright notice and this permission notice shall be included in all
14+
copies or substantial portions of the Software.
15+
16+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+
SOFTWARE.
23+
"""
24+
25+
from qiskit import QuantumCircuit
26+
import numpy as np
27+
import random
28+
from qiskit.opflow import StateFn, X, Y, Z, I
29+
30+
import torchquantum as tq
31+
32+
from torchquantum.measurement import expval_joint_analytical_density, expval_joint_sampling_density
33+
from torchquantum.plugin import op_history2qiskit
34+
from torchquantum.util import switch_little_big_endian_matrix
35+
36+
import torch
37+
38+
pauli_str_op_dict = {
39+
"X": X,
40+
"Y": Y,
41+
"Z": Z,
42+
"I": I,
43+
}
44+
45+
46+
def test_expval_observable():
47+
# seed = 0
48+
# random.seed(seed)
49+
# np.random.seed(seed)
50+
# torch.manual_seed(seed)
51+
52+
for k in range(100):
53+
# print(k)
54+
n_wires = random.randint(1, 10)
55+
obs = random.choices(["X", "Y", "Z", "I"], k=n_wires)
56+
random_layer = tq.RandomLayer(n_ops=100, wires=list(range(n_wires)))
57+
qdev = tq.NoiseDevice(n_wires=n_wires, bsz=1, record_op=True)
58+
random_layer(qdev)
59+
60+
expval_tq = expval_joint_analytical_density(qdev, observable="".join(obs))[0].item()
61+
expval_tq_sampling = expval_joint_sampling_density(
62+
qdev, observable="".join(obs), n_shots=100000
63+
)[0].item()
64+
65+
qiskit_circ = op_history2qiskit(qdev.n_wires, qdev.op_history)
66+
operator = pauli_str_op_dict[obs[0]]
67+
for ob in obs[1:]:
68+
# note here the order is reversed because qiskit is in little endian
69+
operator = pauli_str_op_dict[ob] ^ operator
70+
rho = StateFn(qiskit_circ).to_density_matrix()
71+
72+
#print("Rho:")
73+
#print(rho)
74+
75+
rho_evaled = rho
76+
77+
rho_tq = switch_little_big_endian_matrix(
78+
qdev.get_densities_2d().detach().numpy()
79+
)[0]
80+
81+
assert np.allclose(rho_evaled, rho_tq, atol=1e-5)
82+
83+
#print("RHO passed!")
84+
#print("rho_evaled.shape")
85+
#print(rho_evaled.shape)
86+
#print("operator.shape")
87+
#print(operator.to_matrix().shape)
88+
89+
90+
#operator.eval()
91+
expval_qiskit = np.trace(rho_evaled@operator.to_matrix())
92+
#print("TWO")
93+
#print(expval_tq, expval_qiskit)
94+
assert np.isclose(expval_tq, expval_qiskit, atol=1e-1)
95+
if (
96+
n_wires <= 3
97+
): # if too many wires, the stochastic method is not accurate due to limited shots
98+
assert np.isclose(expval_tq_sampling, expval_qiskit, atol=1e-2)
99+
100+
print("expval observable test passed")
101+
102+
103+
def util0():
104+
"""from below we know that the Z ^ I means Z on qubit 1 and I on qubit 0"""
105+
qc = QuantumCircuit(2)
106+
107+
qc.x(0)
108+
109+
operator = Z ^ I
110+
psi = StateFn(qc)
111+
expectation_value = (~psi @ operator @ psi).eval()
112+
print(expectation_value.real)
113+
# result: 1.0, means measurement result is 0, so Z is on qubit 1
114+
115+
operator = I ^ Z
116+
psi = StateFn(qc)
117+
expectation_value = (~psi @ operator @ psi).eval()
118+
print(expectation_value.real)
119+
# result: -1.0 means measurement result is 1, so Z is on qubit 0
120+
121+
operator = I ^ I
122+
psi = StateFn(qc)
123+
expectation_value = (~psi @ operator @ psi).eval()
124+
print(expectation_value.real)
125+
126+
operator = Z ^ Z
127+
psi = StateFn(qc)
128+
expectation_value = (~psi @ operator @ psi).eval()
129+
print(expectation_value.real)
130+
131+
qc = QuantumCircuit(3)
132+
133+
qc.x(0)
134+
135+
operator = I ^ I ^ Z
136+
psi = StateFn(qc)
137+
expectation_value = (~psi @ operator @ psi).eval()
138+
print(expectation_value.real)
139+
140+
141+
if __name__ == "__main__":
142+
#import pdb
143+
144+
#pdb.set_trace()
145+
146+
util0()
147+
#test_expval_observable()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
"""
2+
MIT License
3+
4+
Copyright (c) 2020-present TorchQuantum Authors
5+
6+
Permission is hereby granted, free of charge, to any person obtaining a copy
7+
of this software and associated documentation files (the "Software"), to deal
8+
in the Software without restriction, including without limitation the rights
9+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
copies of the Software, and to permit persons to whom the Software is
11+
furnished to do so, subject to the following conditions:
12+
13+
The above copyright notice and this permission notice shall be included in all
14+
copies or substantial portions of the Software.
15+
16+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+
SOFTWARE.
23+
"""
24+
25+
import torchquantum as tq
26+
from torchquantum.measurement import (
27+
expval_joint_analytical_density,
28+
expval_joint_sampling_grouping_density,
29+
)
30+
31+
import numpy as np
32+
import random
33+
34+
35+
def test_expval_joint_sampling_grouping():
36+
n_obs = 20
37+
n_wires = 4
38+
obs_all = []
39+
for _ in range(n_obs):
40+
obs = random.choices(["X", "Y", "Z", "I"], k=n_wires)
41+
obs_all.append("".join(obs))
42+
obs_all = list(set(obs_all))
43+
44+
random_layer = tq.RandomLayer(n_ops=100, wires=list(range(n_wires)))
45+
qdev = tq.NoiseDevice(n_wires=n_wires, bsz=1, record_op=True)
46+
random_layer(qdev)
47+
48+
expval_ana = {}
49+
for obs in obs_all:
50+
expval_ana[obs] = expval_joint_analytical_density(qdev, observable=obs)[0].item()
51+
52+
expval_sam = expval_joint_sampling_grouping_density(
53+
qdev, observables=obs_all, n_shots_per_group=1000000
54+
)
55+
for obs in obs_all:
56+
# assert
57+
assert np.isclose(expval_ana[obs], expval_sam[obs][0].item(), atol=1e-1)
58+
print(obs, expval_ana[obs], expval_sam[obs][0].item())
59+
60+
61+
if __name__ == "__main__":
62+
test_expval_joint_sampling_grouping()

test/density/test_noise_model.py

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
2+
3+

0 commit comments

Comments
 (0)