QNLP  v1.0
test_simulator.cpp File Reference

Tests for the simulator interface. More...

#include "catch2/catch.hpp"
#include "Simulator.hpp"
#include "IntelSimulator.cpp"
#include <memory>
Include dependency graph for test_simulator.cpp:

Go to the source code of this file.

Functions

 TEST_CASE ("Intel-QS simulator creation","[simulator]")
 Tests creating a simulator (in this case, the Intel-QS), for a variety of different qubit counts, up to a max limit. More...
 
 TEST_CASE ("Pauli operators")
 Test the Pauli operators. More...
 
 TEST_CASE ("Simulator interface")
 Test the Simulator interface. More...
 
 TEST_CASE ("Measurement of qubits")
 Test the measurement of qubits. More...
 
 TEST_CASE ("Encoding even distribution: Unique Binary Patterns")
 Test the encoding of binary patterns into a superposition of states into an even distribution. More...
 
 TEST_CASE ("Hamming distance check")
 Test the Hamming distance delivers an appropriate distribution quantifying the similarity between a test state and a superposition of states. More...
 

Detailed Description

Tests for the simulator interface.

Author
Lee J. O'Riordan (lee.o.nosp@m.rior.nosp@m.dan@i.nosp@m.chec.nosp@m..ie)
Version
0.1
Date
2019-05-07

Definition in file test_simulator.cpp.

Function Documentation

◆ TEST_CASE() [1/6]

TEST_CASE ( "Intel-QS simulator creation"  ,
""  [simulator] 
)

Tests creating a simulator (in this case, the Intel-QS), for a variety of different qubit counts, up to a max limit.

Definition at line 27 of file test_simulator.cpp.

27  {
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 }
Class definition for IntelSimulator. The purpose of this class is to map the functionality of the und...

References QNLP::IntelSimulator::getNumQubits(), QNLP::IntelSimulator::getQubitRegister(), and ncu_opt_tester::num_qubits.

Here is the call graph for this function:

◆ TEST_CASE() [2/6]

TEST_CASE ( "Pauli operators"  )

Test the Pauli operators.

Definition at line 41 of file test_simulator.cpp.

41  {
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 }
Class definition for IntelSimulator. The purpose of this class is to map the functionality of the und...

References ncu_opt_tester::sim.

◆ TEST_CASE() [3/6]

TEST_CASE ( "Simulator interface"  )

Test the Simulator interface.

Definition at line 95 of file test_simulator.cpp.

95  {
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 }
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

References QNLP::ISimulator::getNumQubits(), QNLP::SimulatorGeneral< DerivedType >::getNumQubits(), and QNLP.tagging.test_tagging::s1.

Here is the call graph for this function:

◆ TEST_CASE() [4/6]

TEST_CASE ( "Measurement of qubits"  )

Test the measurement of qubits.

Definition at line 127 of file test_simulator.cpp.

127  {
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 }
Class definition for IntelSimulator. The purpose of this class is to map the functionality of the und...

References ncu_opt_tester::num_qubits, and ncu_opt_tester::sim.

◆ TEST_CASE() [5/6]

TEST_CASE ( "Encoding even distribution: Unique Binary Patterns"  )

Test the encoding of binary patterns into a superposition of states into an even distribution.

Definition at line 175 of file test_simulator.cpp.

175  : 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 }

References QNLP_Python_MPI::reg_auxiliary, ncu_opt_tester::sim, and ncu_opt_tester::target.

◆ TEST_CASE() [6/6]

TEST_CASE ( "Hamming distance check"  )

Test the Hamming distance delivers an appropriate distribution quantifying the similarity between a test state and a superposition of states.

Definition at line 241 of file test_simulator.cpp.

241  {
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 }
Class definition for IntelSimulator. The purpose of this class is to map the functionality of the und...

References QNLP_Python_MPI::reg_auxiliary, ncu_opt_tester::sim, and ncu_opt_tester::target.