QNLP  v1.0
test_simulator.cpp
Go to the documentation of this file.
1 
11 //#define CATCH_CONFIG_RUNNER
12 //#define CATCH_CONFIG_MAIN
13 
14 #include "catch2/catch.hpp"
15 #include "Simulator.hpp"
16 #include "IntelSimulator.cpp"
17 #include <memory>
18 
19 using namespace QNLP;
20 
21 
22 
27 TEST_CASE("Intel-QS simulator creation","[simulator]"){
28  for(std::size_t num_qubits = 1; num_qubits<16; num_qubits++){
29  DYNAMIC_SECTION("Register creation with " << std::to_string(num_qubits) << " qubits"){
30  IntelSimulator sim_test(num_qubits);
31  REQUIRE(sim_test.getNumQubits() == num_qubits);
32  REQUIRE(sim_test.getQubitRegister().NumQubits() == num_qubits);
33  }
34  }
35 }
36 
41 TEST_CASE("Pauli operators"){
43  auto& reg = sim.getQubitRegister();
44 
45  SECTION("State |0>"){
46  SECTION("Initial state as |0>"){
47  REQUIRE(reg[0] == std::complex<double>(1.,0.) );
48  REQUIRE(reg[1] == std::complex<double>(0.,0.) );
49  }
50  SECTION("Pauli X |0>"){
51  sim.applyGateX(0);
52  REQUIRE(reg[0].real() == std::complex<double>(0.,0.));
53  REQUIRE(reg[1].real() == std::complex<double>(1.,0.));
54  }
55  SECTION("Pauli Y |0>"){
56  sim.applyGateY(0);
57  REQUIRE(reg[0] == std::complex<double>(0.,0.));
58  REQUIRE(reg[1] == std::complex<double>(0.,1.));
59  }
60  SECTION("Pauli Z |0>"){
61  sim.applyGateZ(0);
62  REQUIRE(reg[0] == std::complex<double>(1.,0.) );
63  REQUIRE(reg[1] == std::complex<double>(0.,0.) );
64  }
65  }
66  SECTION("State |1>"){
67  sim.applyGateX(0);
68  SECTION("Pauli X |1>"){
69  sim.applyGateX(0);
70  REQUIRE(reg[0] == std::complex<double>(1.,0.));
71  REQUIRE(reg[1] == std::complex<double>(0.,0.));
72  }
73  SECTION("Pauli Y |1>"){
74  sim.applyGateY(0);
75  REQUIRE(reg[0] == std::complex<double>(0.,-1.));
76  REQUIRE(reg[1] == std::complex<double>(0.,0.));
77  }
78  SECTION("Pauli Z |1>"){
79  sim.applyGateZ(0);
80  REQUIRE(reg[0] == std::complex<double>(0.,0.));
81  REQUIRE(reg[1] == std::complex<double>(-1.,0.));
82  }
83  }
84 }
85 
90 /*
91  * Consider using virtual for non-standard functions, and CRTP for everything else.
92  * Default functions should be as fast as possible, with the others being only
93  * rarely needed, thus performance isn't as critical.
94  */
95 TEST_CASE("Simulator interface"){
96  SECTION("ISimulator virtual base pointer"){
97  ISimulator *s1 = new IntelSimulator(8) ;//createSimulator(8);
98  REQUIRE(s1->getNumQubits() == 8);
99  delete s1;
100  }
101  SECTION("SimulatorGeneral<IntelSimulator> pointer"){
102  SimulatorGeneral<IntelSimulator> *s2 = new IntelSimulator(8) ;//createSimulator(8);
103  REQUIRE(s2->getNumQubits() == 8);
104  delete s2;
105  }
106  SECTION("unique_ptr<ISimulator> to IntelSimulator object"){
107  std::unique_ptr<ISimulator> s3(new IntelSimulator(8));
108  REQUIRE(s3->getNumQubits() == 8);
109  }
110  SECTION("unique_ptr<ISimulator> to IntelSimulator object"){
111  std::unique_ptr<SimulatorGeneral<IntelSimulator> > s4(new IntelSimulator(8));
112  REQUIRE(s4->getNumQubits() == 8);
113  }
114  SECTION("Create multiple simulator objects"){
115  std::unique_ptr<ISimulator> s5(new IntelSimulator(8));
116  std::unique_ptr<SimulatorGeneral<IntelSimulator>> s6(new IntelSimulator(8));
117  REQUIRE(s5 != s6);
118  REQUIRE(s5->getNumQubits() == 8);
119  REQUIRE(s5->getNumQubits() == 8);
120  }
121 }
122 
127 TEST_CASE("Measurement of qubits"){
128  std::size_t num_qubits = 3;
129 
130  SECTION("Measure single qubit"){
131 
133 
134  SECTION("State |0>"){
135  REQUIRE(sim->applyMeasurement(0) == 0 );
136  }
137  SECTION("State |1>"){
138  sim->applyGateX(0);
139  REQUIRE(sim->applyMeasurement(0) == 1 );
140  }
141  }
142 
143  // Only one encoded state so there is a 100% certainty
144  // of the measured output.
145  SECTION("Measure multiple qubits"){
146 
147  std::size_t test_val = 0;
148 
149  std::vector<std::size_t> reg(num_qubits);
150  for(std::size_t i = 0; i < num_qubits; i++){
151  reg[i] = i;
152  }
153  for(std::size_t i = 0; i < num_qubits + 1; i++){
154  DYNAMIC_SECTION("Encoded " << i){
156 
157  for(std::size_t j = 0; j < i; j++){
158  sim->applyGateX(reg[j]);
159 
160  test_val = (test_val << 1) | 1;
161  }
162 
163  REQUIRE(sim->applyMeasurementToRegister(reg) == test_val);
164  }
165  }
166  }
167 }
168 
169 
170 
175 TEST_CASE("Encoding even distribution: Unique Binary Patterns"){
176  SECTION("Check encoding works correctly without throws and with expected distribution"){
177  std::size_t max_num_qubits_mem = 6;
178  std::size_t num_exp = 500;
179 
180  std::size_t result;
181 
182 
183  // Repeat test for varying numbers of qubits
184  for( std::size_t num_qubits_mem = 3; num_qubits_mem < max_num_qubits_mem; num_qubits_mem++){
185  DYNAMIC_SECTION("Encoding " << num_qubits_mem << " qubits"){
186 
187  std::vector<std::size_t> reg_mem(num_qubits_mem);
188  for(std::size_t i = 0; i < num_qubits_mem; i++){
189  reg_mem[i] = i;
190  }
191  std::vector<std::size_t> reg_auxiliary(num_qubits_mem+2);
192  for(std::size_t i = 0; i < num_qubits_mem + 2; i++){
193  reg_auxiliary[i] = i + num_qubits_mem;
194  }
195 
196  std::size_t num_bin_patterns = 4;
197 
198  std::vector<std::size_t> bin_patterns(num_bin_patterns);
199  bin_patterns[0] = pow(2,num_qubits_mem)-1;
200  bin_patterns[1] = 1;
201  bin_patterns[2] = (std::size_t)pow(2,num_qubits_mem)-1 >> (num_qubits_mem / 2);
202  bin_patterns[3] = ((std::size_t)pow(2,num_qubits_mem)-1 >> (num_qubits_mem / 2)) << (num_qubits_mem / 2);
203 
204  std::map<std::size_t, std::size_t> count_bin_pattern;
205  for(std::size_t i = 0; i < num_bin_patterns; i++){
206  count_bin_pattern.insert(std::pair<std::size_t,std::size_t>(bin_patterns[i],0));
207  }
208 
209  SimulatorGeneral<IntelSimulator> *sim = new IntelSimulator(2*num_qubits_mem + 2);
210 
211  for(std::size_t exp = 0; exp < num_exp; exp++){
212 
213  sim->initRegister();
214  sim->encodeBinToSuperpos_unique(reg_mem, reg_auxiliary, bin_patterns,num_qubits_mem);
215  result = sim->applyMeasurementToRegister(reg_mem);
216 
217  // Check measured result is valid
218  CHECK_THAT(bin_patterns, Catch::Matchers::VectorContains(result));
219 
220  // Update distributions of the results
221  count_bin_pattern[result]++;
222  }
223 
224  // Check resulting distribution of results lies within an acceptable limit.
225  // For smaller sample sizes ~500, 30% epsilon value is qualitatively said to be an acceptable limit.
226  Approx target = Approx(num_exp / num_bin_patterns).epsilon(0.30);
227  for(std::map<std::size_t,std::size_t>::iterator it = count_bin_pattern.begin(); it != count_bin_pattern.end(); it++){
228  CHECK( target == (double) it->second);
229  }
230 
231  }
232 
233  }
234  }
235 }
236 
241 TEST_CASE("Hamming distance check"){
242  SECTION("Check encoding works correctly without throws and with expected distribution"){
243  std::size_t max_num_qubits_mem = 6;
244  std::size_t num_exp = 500;
245 
246  std::size_t result;
247 
248  // Repeat test for varying numbers of qubits
249  for( std::size_t num_qubits_mem = 3; num_qubits_mem < max_num_qubits_mem; num_qubits_mem++){
250  DYNAMIC_SECTION("Encoding " << num_qubits_mem << " qubits"){
251 
252  std::vector<std::size_t> reg_mem(num_qubits_mem);
253  for(std::size_t i = 0; i < num_qubits_mem; i++){
254  reg_mem[i] = i;
255  }
256  std::vector<std::size_t> reg_auxiliary(num_qubits_mem+2);
257  for(std::size_t i = 0; i < num_qubits_mem + 2; i++){
258  reg_auxiliary[i] = i + num_qubits_mem;
259  }
260 
261  std::size_t num_bin_patterns = 4;
262 
263  std::vector<std::size_t> bin_patterns(num_bin_patterns);
264  bin_patterns[0] = pow(2,num_qubits_mem)-1;
265  bin_patterns[1] = 1;
266  bin_patterns[2] = (std::size_t)pow(2,num_qubits_mem)-1 >> (num_qubits_mem / 2);
267  bin_patterns[3] = ((std::size_t)pow(2,num_qubits_mem)-1 >> (num_qubits_mem / 2)) << (num_qubits_mem / 2);
268 
269  std::map<std::size_t, std::size_t> count_bin_pattern;
270  for(std::size_t i = 0; i < num_bin_patterns; i++){
271  count_bin_pattern.insert(std::pair<std::size_t,std::size_t>(bin_patterns[i],0));
272  }
273 
274  SimulatorGeneral<IntelSimulator> *sim = new IntelSimulator(2*num_qubits_mem + 2);
275 
276  for(std::size_t exp = 0; exp < num_exp; exp++){
277 
278  sim->initRegister();
279  sim->encodeBinToSuperpos_unique(reg_mem, reg_auxiliary, bin_patterns,num_qubits_mem);
280  result = sim->applyMeasurementToRegister(reg_mem);
281 
282  // Update distributions of the results
283  count_bin_pattern[result]++;
284  }
285 
286  // Check resulting distribution of results lies within an acceptable limit.
287  // For smaller sample sizes ~500, 30% epsilon value is qualitatively said to be an acceptable limit.
288  Approx target = Approx(num_exp / num_bin_patterns).epsilon(0.30);
289  for(std::map<std::size_t,std::size_t>::iterator it = count_bin_pattern.begin(); it != count_bin_pattern.end(); it++){
290  CHECK( target == (double) it->second);
291  }
292 
293  }
294 
295  }
296  }
297 }
298 
299 
QubitRegister< ComplexDP > & getQubitRegister()
Get the Qubit Register object.
Class definition for IntelSimulator. The purpose of this class is to map the functionality of the und...
std::size_t getNumQubits()
Get the number of Qubits.
Definition: Simulator.hpp:413
std::size_t getNumQubits()
Get the number of Qubits.
TEST_CASE("Intel-QS simulator creation","[simulator]")
Tests creating a simulator (in this case, the Intel-QS), for a variety of different qubit counts,...
virtual std::size_t getNumQubits()=0
Get the Number of qubits in the simulator.