Coverage for tests/test_expressiblity.py: 36%

61 statements  

« 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.expressibility import Expressibility 

3 

4import pennylane.numpy as np 

5import logging 

6import math 

7import pytest 

8 

9logger = logging.getLogger(__name__) 

10 

11 

12def get_test_cases(): 

13 # Results taken from: https://doi.org/10.1002/qute.201900070 

14 

15 circuits = [9, 1, 2, 16, 3, 18, 10, 12, 15, 17, 4, 11, 7, 8, 19, 5, 13, 14, 6] 

16 

17 results_n_layers_1 = [ 

18 0.6773, 

19 0.2999, 

20 0.2860, 

21 0.2602, 

22 0.2396, 

23 0.2340, 

24 0.2286, 

25 0.1984, 

26 0.1892, 

27 0.1359, 

28 0.1343, 

29 0.1312, 

30 0.0977, 

31 0.0858, 

32 0.0809, 

33 0.0602, 

34 0.0516, 

35 0.0144, 

36 0.0043, 

37 ] 

38 

39 results_n_layers_3 = [ 

40 0.0322, 

41 0.2079, 

42 0.0084, 

43 0.0375, 

44 0.0403, 

45 0.0221, 

46 0.1297, 

47 0.0089, 

48 0.1152, 

49 0.0180, 

50 0.0107, 

51 0.0038, 

52 0.0162, 

53 0.0122, 

54 0.0040, 

55 0.0030, 

56 0.0049, 

57 0.0035, 

58 0.0039, 

59 ] 

60 

61 # Circuits [5,7,8,11,12,13,14] are not included in the test cases, 

62 # because not implemented in ansaetze.py 

63 

64 # Circuit 10 excluded because implementation with current setup not possible 

65 skip_indices = [5, 7, 8, 11, 12, 13, 14, 10] 

66 skip_indices += [16, 2, 3] # exclude these for now as order is failing 

67 

68 return circuits, results_n_layers_1, results_n_layers_3, skip_indices 

69 

70 

71@pytest.mark.unittest 

72def test_divergence() -> None: 

73 test_cases = [ 

74 { 

75 "n_qubits": 2, 

76 "n_bins": 10, 

77 "result": 0.000, 

78 }, 

79 ] 

80 

81 for test_case in test_cases: 

82 _, y_haar_a = Expressibility.haar_integral( 

83 n_qubits=test_case["n_qubits"], 

84 n_bins=test_case["n_bins"], 

85 cache=True, 

86 ) 

87 

88 # We also test here the chache functionality 

89 _, y_haar_b = Expressibility.haar_integral( 

90 n_qubits=test_case["n_qubits"], 

91 n_bins=test_case["n_bins"], 

92 cache=False, 

93 ) 

94 

95 # Calculate the mean (over all inputs, if required) 

96 kl_dist = Expressibility.kullback_leibler_divergence(y_haar_a, y_haar_b).mean() 

97 

98 assert math.isclose( 

99 kl_dist.mean(), test_case["result"], abs_tol=1e-3 

100 ), "Distance between two identical haar measures not equal." 

101 

102 

103@pytest.mark.unittest 

104@pytest.mark.expensive 

105def test_expressibility() -> None: 

106 circuits, results_n_layers_1, results_n_layers_3, skip_indices = get_test_cases() 

107 

108 test_cases = [] 

109 for circuit_id, res_1l, res_3l in zip( 

110 circuits, results_n_layers_1, results_n_layers_3 

111 ): 

112 if circuit_id in skip_indices: 

113 continue 

114 test_cases.append( 

115 { 

116 "circuit_type": f"Circuit_{circuit_id}", 

117 "n_qubits": 4, 

118 "n_layers": 1, 

119 "result": res_1l, 

120 } 

121 ) 

122 # test_cases.append({ 

123 # "circuit_type": f"Circuit_{circuit_id}", 

124 # "n_qubits": 4, 

125 # "n_layers": 3, 

126 # "result": res_3l, 

127 # }) 

128 

129 tolerance = 0.35 # FIXME: reduce when reason for discrepancy is found 

130 kl_distances: list[tuple[int, float]] = [] 

131 for test_case in test_cases: 

132 print(f"--- Running Expressibility test for {test_case['circuit_type']} ---") 

133 model = Model( 

134 n_qubits=test_case["n_qubits"], 

135 n_layers=test_case["n_layers"], 

136 circuit_type=test_case["circuit_type"], 

137 initialization_domain=[0, 2 * np.pi], 

138 data_reupload=False, 

139 ) 

140 

141 _, _, z = Expressibility.state_fidelities( 

142 seed=1000, 

143 n_bins=75, 

144 n_samples=5000, 

145 model=model, 

146 scale=False, 

147 ) 

148 

149 _, y_haar = Expressibility.haar_integral( 

150 n_qubits=test_case["n_qubits"], 

151 n_bins=75, 

152 cache=False, 

153 scale=False, 

154 ) 

155 

156 # Calculate the mean (over all inputs, if required) 

157 kl_dist = Expressibility.kullback_leibler_divergence(z, y_haar).mean() 

158 

159 circuit_number = int(test_case["circuit_type"].split("_")[1]) 

160 kl_distances.append((circuit_number, kl_dist.item())) 

161 

162 difference = abs(kl_dist - test_case["result"]) 

163 if math.isclose(difference, 0.0, abs_tol=1e-10): 

164 error = 0 

165 else: 

166 error = abs(kl_dist - test_case["result"]) / (test_case["result"]) 

167 

168 print( 

169 f"KL Divergence: {kl_dist},\t" 

170 + f"Expected Result: {test_case['result']},\t" 

171 + f"Error: {error}" 

172 ) 

173 assert ( 

174 error < tolerance 

175 ), f"Expressibility of circuit {test_case['circuit_type']} is not\ 

176 {test_case['result']} but {kl_dist} instead.\ 

177 Deviation {(error*100):.1f}>{tolerance*100}%" 

178 

179 references = sorted( 

180 [ 

181 (circuit, result) 

182 for circuit, result in zip(circuits, results_n_layers_1) 

183 if circuit not in skip_indices 

184 ], 

185 key=lambda x: x[1], 

186 ) 

187 

188 actuals = sorted(kl_distances, key=lambda x: x[1]) 

189 

190 print("Expected \t| Actual") 

191 for reference, actual in zip(references, actuals): 

192 print(f"{reference[0]}, {reference[1]} \t| {actual[0]}, {actual[1]}") 

193 assert [circuit for circuit, _ in references] == [ 

194 circuit for circuit, _ in actuals 

195 ], f"Order of circuits does not match: {actuals} != {references}" 

196 

197 

198@pytest.mark.unittest 

199@pytest.mark.expensive 

200def test_scaling() -> None: 

201 model = Model( 

202 n_qubits=2, 

203 n_layers=1, 

204 circuit_type="Circuit_1", 

205 ) 

206 

207 _, _, z = Expressibility.state_fidelities( 

208 seed=1000, 

209 n_bins=4, 

210 n_samples=10, 

211 n_input_samples=0, 

212 input_domain=[0, 2 * np.pi], 

213 model=model, 

214 scale=True, 

215 ) 

216 

217 assert z.shape == (8,) 

218 

219 _, y = Expressibility.haar_integral( 

220 n_qubits=model.n_qubits, 

221 n_bins=4, 

222 cache=False, 

223 scale=True, 

224 ) 

225 

226 assert y.shape == (8,)