Coverage for tests/test_entanglement.py: 28%
94 statements
« prev ^ index » next coverage.py v7.6.12, created at 2025-03-07 14:54 +0000
« prev ^ index » next coverage.py v7.6.12, created at 2025-03-07 14:54 +0000
1from qml_essentials.model import Model
2from qml_essentials.entanglement import Entanglement
4import logging
5import math
6import pytest
8from copy import deepcopy
10logger = logging.getLogger(__name__)
13def get_test_cases():
14 # Results taken from: https://doi.org/10.1002/qute.201900070
16 circuits = [
17 # "No_Entangling",
18 # "Strongly_Entangling",
19 1,
20 7,
21 3,
22 16,
23 8,
24 5,
25 18,
26 17,
27 4,
28 10,
29 19,
30 13,
31 12,
32 14,
33 11,
34 6,
35 2,
36 15,
37 9,
38 ]
40 results_n_layers_1 = [
41 # 0.0000,
42 # 0.8379,
43 0.0000,
44 0.3241,
45 0.3412,
46 0.3439,
47 0.3926,
48 0.4090,
49 0.4385,
50 0.4533,
51 0.4721,
52 0.5362,
53 0.5916,
54 0.6077,
55 0.6486,
56 0.6604,
57 0.7335,
58 0.7781,
59 0.8104,
60 0.8184,
61 1.0000,
62 ]
64 results_n_layers_3 = [
65 0.0000,
66 0.6194,
67 0.5852,
68 0.5859,
69 0.6567,
70 0.7953,
71 0.7130,
72 0.6557,
73 0.6607,
74 0.7865,
75 0.7906,
76 0.8224,
77 0.7838,
78 0.8557,
79 0.8288,
80 0.8721,
81 0.8657,
82 0.8734,
83 1.0000,
84 ]
86 # Circuits [5,7,8,11,12,13,14] are not included in the test cases,
87 # because not implemented in ansaetze.py
89 # Circuit 10 excluded because implementation with current setup not possible
90 skip_indices = [5, 7, 8, 11, 12, 13, 14, 10]
91 skip_indices += [2, 3] # exclude these for now as order is failing
93 return circuits, results_n_layers_1, results_n_layers_3, skip_indices
96@pytest.mark.expensive
97@pytest.mark.unittest
98def test_mw_measure() -> None:
99 circuits, results_n_layers_1, results_n_layers_3, skip_indices = get_test_cases()
101 test_cases = []
102 for circuit_id, res_1l, res_3l in zip(
103 circuits, results_n_layers_1, results_n_layers_3
104 ):
105 if circuit_id in skip_indices:
106 continue
107 if isinstance(circuit_id, int):
108 test_cases.append(
109 {
110 "circuit_type": f"Circuit_{circuit_id}",
111 "n_qubits": 4,
112 "n_layers": 1,
113 "result": res_1l,
114 }
115 )
116 elif isinstance(circuit_id, str):
117 test_cases.append(
118 {
119 "circuit_type": circuit_id,
120 "n_qubits": 4,
121 "n_layers": 1,
122 "result": res_1l,
123 }
124 )
126 tolerance = 0.55 # FIXME: reduce when reason for discrepancy is found
127 ent_caps: list[tuple[str, float]] = []
128 for test_case in test_cases:
129 print(f"--- Running Entanglement test for {test_case['circuit_type']} ---")
130 model = Model(
131 n_qubits=test_case["n_qubits"],
132 n_layers=test_case["n_layers"],
133 circuit_type=test_case["circuit_type"],
134 data_reupload=False,
135 initialization="random",
136 )
138 ent_cap = Entanglement.meyer_wallach(
139 model, n_samples=5000, seed=1000, cache=False
140 )
142 # Save results for later comparison
143 circuit_number = test_case["circuit_type"]
144 if circuit_number.split("_")[1].isdigit():
145 circuit_number = int(circuit_number.split("_")[1])
146 ent_caps.append((circuit_number, ent_cap))
148 difference = abs(ent_cap - test_case["result"])
149 if math.isclose(difference, 0.0, abs_tol=1e-3):
150 error = 0
151 else:
152 error = abs(ent_cap - test_case["result"]) / (test_case["result"])
154 print(
155 f"Entangling-capability: {ent_cap},\t"
156 + f"Expected Result: {test_case['result']},\t"
157 + f"Error: {error}"
158 )
159 assert (
160 error < tolerance
161 ), f"Entangling-capability of circuit {test_case['circuit_type']} is not\
162 {test_case['result']} but {ent_cap} instead.\
163 Deviation {(error * 100):.1f}%>{tolerance * 100}%"
165 references = sorted(
166 [
167 (circuit, ent_result)
168 for circuit, ent_result in zip(circuits, results_n_layers_1)
169 if circuit not in skip_indices
170 ],
171 key=lambda x: x[1],
172 )
174 actuals = sorted(ent_caps, key=lambda x: x[1])
176 print("Expected \t| Actual")
177 for reference, actual in zip(references, actuals):
178 print(f"{reference[0]}, {reference[1]} \t| {actual[0]}, {actual[1]}")
179 assert [circuit for circuit, _ in actuals] == [
180 circuit for circuit, _ in references
181 ], f"Order of circuits does not match: {actuals} != {references}"
184@pytest.mark.smoketest
185def test_no_sampling() -> None:
186 model = Model(
187 n_qubits=2,
188 n_layers=1,
189 circuit_type="Hardware_Efficient",
190 data_reupload=False,
191 initialization="random",
192 )
194 _ = Entanglement.meyer_wallach(model, n_samples=-1, seed=1000, cache=False)
197@pytest.mark.expensive
198@pytest.mark.unittest
199def test_bell_measure() -> None:
200 circuits, results_n_layers_1, results_n_layers_3, skip_indices = get_test_cases()
202 test_cases = []
203 for circuit_id, res_1l in zip(circuits, results_n_layers_1):
204 if circuit_id in skip_indices:
205 continue
206 if isinstance(circuit_id, int):
207 test_cases.append(
208 {
209 "circuit_type": f"Circuit_{circuit_id}",
210 "n_qubits": 4,
211 "n_layers": 1,
212 "result": res_1l,
213 }
214 )
215 elif isinstance(circuit_id, str):
216 test_cases.append(
217 {
218 "circuit_type": circuit_id,
219 "n_qubits": 4,
220 "n_layers": 1,
221 "result": res_1l,
222 }
223 )
225 tolerance = 0.55 # FIXME: reduce when reason for discrepancy is found
226 ent_caps: list[tuple[str, float]] = []
227 for test_case in test_cases:
228 print(f"--- Running Entanglement test for {test_case['circuit_type']} ---")
229 model = Model(
230 n_qubits=test_case["n_qubits"],
231 n_layers=test_case["n_layers"],
232 circuit_type=test_case["circuit_type"],
233 data_reupload=False,
234 initialization="random",
235 )
237 ent_cap = Entanglement.bell_measurements(
238 model, n_samples=5000, seed=1000, cache=False
239 )
241 # Save results for later comparison
242 circuit_number = test_case["circuit_type"]
243 if circuit_number.split("_")[1].isdigit():
244 circuit_number = int(circuit_number.split("_")[1])
245 ent_caps.append((circuit_number, ent_cap))
247 difference = abs(ent_cap - test_case["result"])
248 if math.isclose(difference, 0.0, abs_tol=1e-3):
249 error = 0
250 else:
251 error = abs(ent_cap - test_case["result"]) / (test_case["result"])
253 print(
254 f"Entangling-capability: {ent_cap},\t"
255 + f"Expected Result: {test_case['result']},\t"
256 + f"Error: {error}"
257 )
258 assert (
259 error < tolerance
260 ), f"Entangling-capability of circuit {test_case['circuit_type']} is not\
261 {test_case['result']} but {ent_cap} instead.\
262 Deviation {(error * 100):.1f}%>{tolerance * 100}%"
264 references = sorted(
265 [
266 (circuit, ent_result)
267 for circuit, ent_result in zip(circuits, results_n_layers_1)
268 if circuit not in skip_indices
269 ],
270 key=lambda x: x[1],
271 )
273 actuals = sorted(ent_caps, key=lambda x: x[1])
275 print("Expected \t| Actual")
276 for reference, actual in zip(references, actuals):
277 print(f"{reference[0]}, {reference[1]} \t| {actual[0]}, {actual[1]}")
278 assert [circuit for circuit, _ in actuals] == [
279 circuit for circuit, _ in references
280 ], f"Order of circuits does not match: {actuals} != {references}"
283@pytest.mark.unittest
284def test_entangling_measures() -> None:
285 test_cases = [
286 {"circuit_type": "Circuit_4", "n_qubits": 2, "n_layers": 1},
287 {"circuit_type": "Circuit_4", "n_qubits": 3, "n_layers": 1},
288 {"circuit_type": "Circuit_4", "n_qubits": 4, "n_layers": 1},
289 {"circuit_type": "Circuit_4", "n_qubits": 5, "n_layers": 1},
290 ]
292 for test_case in test_cases:
293 model = Model(
294 n_qubits=test_case["n_qubits"],
295 n_layers=test_case["n_layers"],
296 circuit_type=test_case["circuit_type"],
297 data_reupload=False,
298 initialization="random",
299 )
301 mw_meas = Entanglement.meyer_wallach(
302 deepcopy(model), n_samples=2000, seed=1000, cache=False
303 )
305 bell_meas = Entanglement.bell_measurements(
306 model, n_samples=2000, seed=1000, cache=False
307 )
309 assert math.isclose(mw_meas, bell_meas, abs_tol=1e-5), (
310 f"Meyer-Wallach and Bell-measurement are not the same. Got {mw_meas} "
311 f"and {bell_meas}, respectively."
312 )