test_unitaries.cpp
Go to the documentation of this file.
1 
16 #include "catch.hpp"
17 #include "QuEST.h"
18 #include "utilities.hpp"
19 
24 #define PREPARE_TEST(quregVec, quregMatr, refVec, refMatr) \
25  Qureg quregVec = createQureg(NUM_QUBITS, QUEST_ENV); \
26  Qureg quregMatr = createDensityQureg(NUM_QUBITS, QUEST_ENV); \
27  initDebugState(quregVec); \
28  initDebugState(quregMatr); \
29  QVector refVec = toQVector(quregVec); \
30  QMatrix refMatr = toQMatrix(quregMatr);
31 
33 #define CLEANUP_TEST(quregVec, quregMatr) \
34  destroyQureg(quregVec, QUEST_ENV); \
35  destroyQureg(quregMatr, QUEST_ENV);
36 
37 /* allows concise use of Contains in catch's REQUIRE_THROWS_WITH */
38 using Catch::Matchers::Contains;
39 
40 
41 
46 TEST_CASE( "compactUnitary", "[unitaries]" ) {
47 
48  PREPARE_TEST( quregVec, quregMatr, refVec, refMatr );
49 
50  qcomp a = getRandomReal(-1,1) * expI(getRandomReal(0,2*M_PI));
51  qcomp b = sqrt(1-abs(a)*abs(a)) * expI(getRandomReal(0,2*M_PI));
52  Complex alpha = toComplex( a );
53  Complex beta = toComplex( b );
54  QMatrix op = toQMatrix(alpha, beta);
55 
56  SECTION( "correctness" ) {
57 
58  int target = GENERATE( range(0,NUM_QUBITS) );
59 
60  SECTION( "state-vector" ) {
61 
62  compactUnitary(quregVec, target, alpha, beta);
63  applyReferenceOp(refVec, target, op);
64  REQUIRE( areEqual(quregVec, refVec) );
65  }
66  SECTION( "density-matrix" ) {
67 
68  compactUnitary(quregMatr, target, alpha, beta);
69  applyReferenceOp(refMatr, target, op);
70  REQUIRE( areEqual(quregMatr, refMatr, 10*REAL_EPS) );
71  }
72  }
73  SECTION( "input validation" ) {
74 
75  SECTION( "qubit indices" ) {
76 
77  int target = GENERATE( -1, NUM_QUBITS );
78  REQUIRE_THROWS_WITH( compactUnitary(quregVec, target, alpha, beta), Contains("Invalid target") );
79  }
80  SECTION( "unitarity" ) {
81 
82  // unitary when |alpha|^2 + |beta|^2 = 1
83  alpha = {.real=1, .imag=2};
84  beta = {.real=3, .imag=4};
85  REQUIRE_THROWS_WITH( compactUnitary(quregVec, 0, alpha, beta), Contains("unitary") );
86  }
87  }
88  CLEANUP_TEST( quregVec, quregMatr );
89 }
90 
91 
92 
97 TEST_CASE( "controlledCompactUnitary", "[unitaries]" ) {
98 
99  PREPARE_TEST( quregVec, quregMatr, refVec, refMatr );
100 
101  qcomp a = getRandomReal(-1,1) * expI(getRandomReal(0,2*M_PI));
102  qcomp b = sqrt(1-abs(a)*abs(a)) * expI(getRandomReal(0,2*M_PI));
103  Complex alpha = toComplex( a );
104  Complex beta = toComplex( b );
105  QMatrix op = toQMatrix(alpha, beta);
106 
107  SECTION( "correctness" ) {
108 
109  int target = GENERATE( range(0,NUM_QUBITS) );
110  int control = GENERATE_COPY( filter([=](int c){ return c!=target; }, range(0,NUM_QUBITS)) );
111 
112  SECTION( "state-vector" ) {
113 
114  controlledCompactUnitary(quregVec, control, target, alpha, beta);
115  applyReferenceOp(refVec, control, target, op);
116  REQUIRE( areEqual(quregVec, refVec) );
117  }
118  SECTION( "density-matrix" ) {
119 
120  controlledCompactUnitary(quregMatr, control, target, alpha, beta);
121  applyReferenceOp(refMatr, control, target, op);
122  REQUIRE( areEqual(quregMatr, refMatr, 10*REAL_EPS) );
123  }
124  }
125  SECTION( "input validation" ) {
126 
127  SECTION( "control and target collision" ) {
128 
129  int qb = 0;
130  REQUIRE_THROWS_WITH( controlledCompactUnitary(quregVec, qb, qb, alpha, beta), Contains("Control") && Contains("target") );
131  }
132  SECTION( "qubit indices" ) {
133 
134  int qb = GENERATE( -1, NUM_QUBITS );
135  REQUIRE_THROWS_WITH( controlledCompactUnitary(quregVec, qb, 0, alpha, beta), Contains("Invalid control") );
136  REQUIRE_THROWS_WITH( controlledCompactUnitary(quregVec, 0, qb, alpha, beta), Contains("Invalid target") );
137  }
138  SECTION( "unitarity" ) {
139 
140  // unitary when |a|^2 + |b^2 = 1
141  alpha = {.real=1, .imag=2};
142  beta = {.real=3, .imag=4};
143  REQUIRE_THROWS_WITH( controlledCompactUnitary(quregVec, 0, 1, alpha, beta), Contains("unitary") );
144  }
145  }
146  CLEANUP_TEST( quregVec, quregMatr );
147 }
148 
149 
150 
155 TEST_CASE( "controlledMultiQubitUnitary", "[unitaries]" ) {
156 
157  PREPARE_TEST( quregVec, quregMatr, refVec, refMatr );
158 
159  // figure out max-num targs (inclusive) allowed by hardware backend
160  int maxNumTargs = calcLog2(quregVec.numAmpsPerChunk);
161  if (maxNumTargs >= NUM_QUBITS)
162  maxNumTargs = NUM_QUBITS - 1; // make space for control qubit
163 
164  SECTION( "correctness" ) {
165 
166  // generate all possible qubit arrangements
167  int ctrl = GENERATE( range(0,NUM_QUBITS) );
168  int numTargs = GENERATE_COPY( range(1,maxNumTargs+1) );
169  int* targs = GENERATE_COPY( sublists(range(0,NUM_QUBITS), numTargs, ctrl) );
170 
171  // for each qubit arrangement, use a new random unitary
172  QMatrix op = getRandomUnitary(numTargs);
173  ComplexMatrixN matr = createComplexMatrixN(numTargs);
174  toComplexMatrixN(op, matr);
175 
176  SECTION( "state-vector" ) {
177 
178  controlledMultiQubitUnitary(quregVec, ctrl, targs, numTargs, matr);
179  applyReferenceOp(refVec, ctrl, targs, numTargs, op);
180  REQUIRE( areEqual(quregVec, refVec) );
181  }
182  SECTION( "density-matrix" ) {
183 
184  controlledMultiQubitUnitary(quregMatr, ctrl, targs, numTargs, matr);
185  applyReferenceOp(refMatr, ctrl, targs, numTargs, op);
186  REQUIRE( areEqual(quregMatr, refMatr, 10*REAL_EPS) );
187  }
188  destroyComplexMatrixN(matr);
189  }
190  SECTION( "input validation" ) {
191 
192  SECTION( "number of targets" ) {
193 
194  // there cannot be more targets than qubits in register
195  // (numTargs=NUM_QUBITS is caught elsewhere, because that implies ctrl is invalid)
196  int numTargs = GENERATE( -1, 0, NUM_QUBITS+1 );
197  int targs[NUM_QUBITS+1]; // prevents seg-fault if validation doesn't trigger
198  ComplexMatrixN matr = createComplexMatrixN(NUM_QUBITS+1); // prevent seg-fault
199  REQUIRE_THROWS_WITH( controlledMultiQubitUnitary(quregVec, 0, targs, numTargs, matr), Contains("Invalid number of target"));
200  destroyComplexMatrixN(matr);
201  }
202  SECTION( "repetition in targets" ) {
203 
204  int ctrl = 0;
205  int numTargs = 3;
206  int targs[] = {1,2,2};
207  ComplexMatrixN matr = createComplexMatrixN(numTargs); // prevents seg-fault if validation doesn't trigger
208 
209  REQUIRE_THROWS_WITH( controlledMultiQubitUnitary(quregVec, ctrl, targs, numTargs, matr), Contains("target") && Contains("unique"));
210  destroyComplexMatrixN(matr);
211  }
212  SECTION( "control and target collision" ) {
213 
214  int numTargs = 3;
215  int targs[] = {0,1,2};
216  int ctrl = targs[GENERATE_COPY( range(0,numTargs) )];
217  ComplexMatrixN matr = createComplexMatrixN(numTargs); // prevents seg-fault if validation doesn't trigger
218 
219  REQUIRE_THROWS_WITH( controlledMultiQubitUnitary(quregVec, ctrl, targs, numTargs, matr), Contains("Control") && Contains("target"));
220  destroyComplexMatrixN(matr);
221  }
222  SECTION( "qubit indices" ) {
223 
224  int ctrl = 0;
225  int numTargs = 3;
226  int targs[] = {1,2,3};
227  ComplexMatrixN matr = createComplexMatrixN(numTargs); // prevents seg-fault if validation doesn't trigger
228 
229  int inv = GENERATE( -1, NUM_QUBITS );
230  ctrl = inv;
231  REQUIRE_THROWS_WITH( controlledMultiQubitUnitary(quregVec, ctrl, targs, numTargs, matr), Contains("Invalid control") );
232 
233  ctrl = 0; // restore valid ctrl
234  targs[GENERATE_COPY( range(0,numTargs) )] = inv; // make invalid target
235  REQUIRE_THROWS_WITH( controlledMultiQubitUnitary(quregVec, ctrl, targs, numTargs, matr), Contains("Invalid target") );
236 
237  destroyComplexMatrixN(matr);
238  }
239  SECTION( "unitarity" ) {
240 
241  int ctrl = 0;
242  int numTargs = GENERATE_COPY( range(1,maxNumTargs+1) );
243  ComplexMatrixN matr = createComplexMatrixN(numTargs); // initially zero, hence not-unitary
244 
245  int targs[numTargs];
246  for (int i=0; i<numTargs; i++)
247  targs[i] = i+1;
248 
249  REQUIRE_THROWS_WITH( controlledMultiQubitUnitary(quregVec, ctrl, targs, numTargs, matr), Contains("unitary") );
250  destroyComplexMatrixN(matr);
251  }
252  SECTION( "unitary creation" ) {
253 
254  int numTargs = 3;
255  int targs[] = {1,2,3};
256 
257  /* compilers don't auto-initialise to NULL; the below circumstance
258  * only really occurs when 'malloc' returns NULL in createComplexMatrixN,
259  * which actually triggers its own validation. Hence this test is useless
260  * currently.
261  */
262  ComplexMatrixN matr;
263  matr.real = NULL;
264  matr.imag = NULL;
265  REQUIRE_THROWS_WITH( controlledMultiQubitUnitary(quregVec, 0, targs, numTargs, matr), Contains("created") );
266  }
267  SECTION( "unitary dimensions" ) {
268 
269  int ctrl = 0;
270  int targs[2] = {1,2};
272 
273  REQUIRE_THROWS_WITH( controlledMultiQubitUnitary(quregVec, ctrl, targs, 2, matr), Contains("matrix size"));
274  destroyComplexMatrixN(matr);
275  }
276  SECTION( "unitary fits in node" ) {
277 
278  // pretend we have a very limited distributed memory (judged by matr size)
279  quregVec.numAmpsPerChunk = 1;
280  int qb[] = {1,2};
281  ComplexMatrixN matr = createComplexMatrixN(2); // prevents seg-fault if validation doesn't trigger
282  REQUIRE_THROWS_WITH( controlledMultiQubitUnitary(quregVec, 0, qb, 2, matr), Contains("targets too many qubits"));
283  destroyComplexMatrixN(matr);
284  }
285  }
286  CLEANUP_TEST( quregVec, quregMatr );
287 }
288 
289 
290 
295 TEST_CASE( "controlledNot", "[unitaries]" ) {
296 
297  PREPARE_TEST( quregVec, quregMatr, refVec, refMatr );
298  QMatrix op{{0,1},{1,0}};
299 
300  SECTION( "correctness" ) {
301 
302  int target = GENERATE( range(0,NUM_QUBITS) );
303  int control = GENERATE_COPY( filter([=](int c){ return c!=target; }, range(0,NUM_QUBITS)) );
304 
305  SECTION( "state-vector" ) {
306 
307  controlledNot(quregVec, control, target);
308  applyReferenceOp(refVec, control, target, op);
309  REQUIRE( areEqual(quregVec, refVec) );
310  }
311  SECTION( "density-matrix" ) {
312 
313  controlledNot(quregMatr, control, target);
314  applyReferenceOp(refMatr, control, target, op);
315  REQUIRE( areEqual(quregMatr, refMatr) );
316  }
317  }
318  SECTION( "input validation" ) {
319 
320  SECTION( "control and target collision" ) {
321 
322  int qb = GENERATE( range(0,NUM_QUBITS) );
323  REQUIRE_THROWS_WITH( controlledNot(quregVec, qb, qb), Contains("Control") && Contains("target") );
324  }
325  SECTION( "qubit indices" ) {
326 
327  int qb = GENERATE( -1, NUM_QUBITS );
328  REQUIRE_THROWS_WITH( controlledNot(quregVec, qb, 0), Contains("Invalid control") );
329  REQUIRE_THROWS_WITH( controlledNot(quregVec, 0, qb), Contains("Invalid target") );
330  }
331  }
332  CLEANUP_TEST( quregVec, quregMatr );
333 }
334 
335 
336 
341 TEST_CASE( "controlledPauliY", "[unitaries]" ) {
342 
343  PREPARE_TEST( quregVec, quregMatr, refVec, refMatr );
344  QMatrix op{{0,-qcomp(0,1)},{qcomp(0,1),0}};
345 
346  SECTION( "correctness" ) {
347 
348  int target = GENERATE( range(0,NUM_QUBITS) );
349  int control = GENERATE_COPY( filter([=](int c){ return c!=target; }, range(0,NUM_QUBITS)) );
350 
351  SECTION( "state-vector" ) {
352 
353  controlledPauliY(quregVec, control, target);
354  applyReferenceOp(refVec, control, target, op);
355  REQUIRE( areEqual(quregVec, refVec) );
356  }
357  SECTION( "density-matrix" ) {
358 
359  controlledPauliY(quregMatr, control, target);
360  applyReferenceOp(refMatr, control, target, op);
361  REQUIRE( areEqual(quregMatr, refMatr) );
362  }
363  }
364  SECTION( "input validation" ) {
365 
366  SECTION( "control and target collision" ) {
367 
368  int qb = GENERATE( range(0,NUM_QUBITS) );
369  REQUIRE_THROWS_WITH( controlledPauliY(quregVec, qb, qb), Contains("Control") && Contains("target") );
370  }
371  SECTION( "qubit indices" ) {
372 
373  int qb = GENERATE( -1, NUM_QUBITS );
374  REQUIRE_THROWS_WITH( controlledPauliY(quregVec, qb, 0), Contains("Invalid control") );
375  REQUIRE_THROWS_WITH( controlledPauliY(quregVec, 0, qb), Contains("Invalid target") );
376  }
377  }
378  CLEANUP_TEST( quregVec, quregMatr );
379 }
380 
381 
382 
387 TEST_CASE( "controlledPhaseFlip", "[unitaries]" ) {
388 
389  PREPARE_TEST( quregVec, quregMatr, refVec, refMatr );
390  QMatrix op{{1,0},{0,-1}};
391 
392  SECTION( "correctness" ) {
393 
394  int target = GENERATE( range(0,NUM_QUBITS) );
395  int control = GENERATE_COPY( filter([=](int c){ return c!=target; }, range(0,NUM_QUBITS)) );
396 
397  SECTION( "state-vector" ) {
398 
399  controlledPhaseFlip(quregVec, control, target);
400  applyReferenceOp(refVec, control, target, op);
401  REQUIRE( areEqual(quregVec, refVec) );
402  }
403  SECTION( "density-matrix" ) {
404 
405  controlledPhaseFlip(quregMatr, control, target);
406  applyReferenceOp(refMatr, control, target, op);
407  REQUIRE( areEqual(quregMatr, refMatr) );
408  }
409  }
410  SECTION( "input validation" ) {
411 
412  SECTION( "control and target collision" ) {
413 
414  int qb = GENERATE( range(0,NUM_QUBITS) );
415  REQUIRE_THROWS_WITH( controlledPhaseFlip(quregVec, qb, qb), Contains("Control") && Contains("target") );
416  }
417  SECTION( "qubit indices" ) {
418 
419  int qb = GENERATE( -1, NUM_QUBITS );
420  REQUIRE_THROWS_WITH( controlledPhaseFlip(quregVec, qb, 0), Contains("Invalid control") );
421  REQUIRE_THROWS_WITH( controlledPhaseFlip(quregVec, 0, qb), Contains("Invalid target") );
422  }
423  }
424  CLEANUP_TEST( quregVec, quregMatr );
425 }
426 
427 
428 
433 TEST_CASE( "controlledPhaseShift", "[unitaries]" ) {
434 
435  PREPARE_TEST( quregVec, quregMatr, refVec, refMatr );
436  qreal param = getRandomReal(-2*M_PI, 2*M_PI);
437  QMatrix op{{1,0},{0,expI(param)}};
438 
439  SECTION( "correctness" ) {
440 
441  int target = GENERATE( range(0,NUM_QUBITS) );
442  int control = GENERATE_COPY( filter([=](int c){ return c!=target; }, range(0,NUM_QUBITS)) );
443 
444  SECTION( "state-vector" ) {
445 
446  controlledPhaseShift(quregVec, control, target, param);
447  applyReferenceOp(refVec, control, target, op);
448  REQUIRE( areEqual(quregVec, refVec) );
449  }
450  SECTION( "density-matrix" ) {
451 
452  controlledPhaseShift(quregMatr, control, target, param);
453  applyReferenceOp(refMatr, control, target, op);
454  REQUIRE( areEqual(quregMatr, refMatr) );
455  }
456  }
457  SECTION( "input validation" ) {
458 
459  SECTION( "control and target collision" ) {
460 
461  int qb = GENERATE( range(0,NUM_QUBITS) );
462  REQUIRE_THROWS_WITH( controlledPhaseShift(quregVec, qb, qb, param), Contains("Control") && Contains("target") );
463  }
464  SECTION( "qubit indices" ) {
465 
466  int qb = GENERATE( -1, NUM_QUBITS );
467  REQUIRE_THROWS_WITH( controlledPhaseShift(quregVec, qb, 0, param), Contains("Invalid control") );
468  REQUIRE_THROWS_WITH( controlledPhaseShift(quregVec, 0, qb, param), Contains("Invalid target") );
469  }
470  }
471  CLEANUP_TEST( quregVec, quregMatr );
472 }
473 
474 
475 
480 TEST_CASE( "controlledRotateAroundAxis", "[unitaries]" ) {
481 
482  PREPARE_TEST( quregVec, quregMatr, refVec, refMatr );
483 
484  // each test will use a random parameter and axis vector
485  qreal param = getRandomReal(-4*M_PI, 4*M_PI);
486  Vector vec = {.x=getRandomReal(-1,1), .y=getRandomReal(-1,1), .z=getRandomReal(-1,1)};
487 
488  // Rn(a) = cos(a/2)I - i sin(a/2) n . paulivector
489  // (pg 24 of vcpc.univie.ac.at/~ian/hotlist/qc/talks/bloch-sphere-rotations.pdf)
490  qreal c = cos(param/2);
491  qreal s = sin(param/2);
492  qreal m = sqrt(vec.x*vec.x + vec.y*vec.y + vec.z*vec.z);
493  QMatrix op{{c - qcomp(0,1)*vec.z*s/m, -(vec.y + qcomp(0,1)*vec.x)*s/m},
494  {(vec.y - qcomp(0,1)*vec.x)*s/m, c + qcomp(0,1)*vec.z*s/m}};
495 
496  SECTION( "correctness" ) {
497 
498  int target = GENERATE( range(0,NUM_QUBITS) );
499  int control = GENERATE_COPY( filter([=](int c){ return c!=target; }, range(0,NUM_QUBITS)) );
500 
501  SECTION( "state-vector" ) {
502 
503  controlledRotateAroundAxis(quregVec, control, target, param, vec);
504  applyReferenceOp(refVec, control, target, op);
505  REQUIRE( areEqual(quregVec, refVec) );
506  }
507  SECTION( "density-matrix" ) {
508 
509  controlledRotateAroundAxis(quregMatr, control, target, param, vec);
510  applyReferenceOp(refMatr, control, target, op);
511  REQUIRE( areEqual(quregMatr, refMatr, 10*REAL_EPS) );
512  }
513  }
514  SECTION( "input validation" ) {
515 
516  SECTION( "control and target collision" ) {
517 
518  int qb = GENERATE( range(0,NUM_QUBITS) );
519  REQUIRE_THROWS_WITH( controlledRotateAroundAxis(quregVec, qb, qb, param, vec), Contains("Control") && Contains("target") );
520  }
521  SECTION( "qubit indices" ) {
522 
523  int qb = GENERATE( -1, NUM_QUBITS );
524  REQUIRE_THROWS_WITH( controlledRotateAroundAxis(quregVec, qb, 0, param, vec), Contains("Invalid control") );
525  REQUIRE_THROWS_WITH( controlledRotateAroundAxis(quregVec, 0, qb, param, vec), Contains("Invalid target") );
526  }
527  SECTION( "zero rotation axis" ) {
528 
529  vec = {.x=0, .y=0, .z=0};
530  REQUIRE_THROWS_WITH( controlledRotateAroundAxis(quregVec, 0, 1, param, vec), Contains("Invalid axis") && Contains("zero") );
531  }
532  }
533  CLEANUP_TEST( quregVec, quregMatr );
534 }
535 
536 
537 
542 TEST_CASE( "controlledRotateX", "[unitaries]" ) {
543 
544  PREPARE_TEST( quregVec, quregMatr, refVec, refMatr );
545  qreal param = getRandomReal(-4*M_PI, 4*M_PI);
546  QMatrix op{
547  {cos(param/2), -sin(param/2)*qcomp(0,1)},
548  {-sin(param/2)*qcomp(0,1), cos(param/2)}};
549 
550  SECTION( "correctness" ) {
551 
552  int target = GENERATE( range(0,NUM_QUBITS) );
553  int control = GENERATE_COPY( filter([=](int c){ return c!=target; }, range(0,NUM_QUBITS)) );
554 
555  SECTION( "state-vector" ) {
556 
557  controlledRotateX(quregVec, control, target, param);
558  applyReferenceOp(refVec, control, target, op);
559  REQUIRE( areEqual(quregVec, refVec) );
560  }
561  SECTION( "density-matrix" ) {
562 
563  controlledRotateX(quregMatr, control, target, param);
564  applyReferenceOp(refMatr, control, target, op);
565  REQUIRE( areEqual(quregMatr, refMatr) );
566  }
567  }
568  SECTION( "input validation" ) {
569 
570  SECTION( "control and target collision" ) {
571 
572  int qb = GENERATE( range(0,NUM_QUBITS) );
573  REQUIRE_THROWS_WITH( controlledRotateX(quregVec, qb, qb, param), Contains("Control") && Contains("target") );
574  }
575  SECTION( "qubit indices" ) {
576 
577  int qb = GENERATE( -1, NUM_QUBITS );
578  REQUIRE_THROWS_WITH( controlledRotateX(quregVec, qb, 0, param), Contains("Invalid control") );
579  REQUIRE_THROWS_WITH( controlledRotateX(quregVec, 0, qb, param), Contains("Invalid target") );
580  }
581  }
582  CLEANUP_TEST( quregVec, quregMatr );
583 }
584 
585 
586 
591 TEST_CASE( "controlledRotateY", "[unitaries]" ) {
592 
593  PREPARE_TEST( quregVec, quregMatr, refVec, refMatr );
594  qreal param = getRandomReal(-4*M_PI, 4*M_PI);
595  QMatrix op{{cos(param/2), -sin(param/2)},{sin(param/2), cos(param/2)}};
596 
597  SECTION( "correctness" ) {
598 
599  int target = GENERATE( range(0,NUM_QUBITS) );
600  int control = GENERATE_COPY( filter([=](int c){ return c!=target; }, range(0,NUM_QUBITS)) );
601 
602  SECTION( "state-vector" ) {
603 
604  controlledRotateY(quregVec, control, target, param);
605  applyReferenceOp(refVec, control, target, op);
606  REQUIRE( areEqual(quregVec, refVec) );
607  }
608  SECTION( "density-matrix" ) {
609 
610  controlledRotateY(quregMatr, control, target, param);
611  applyReferenceOp(refMatr, control, target, op);
612  REQUIRE( areEqual(quregMatr, refMatr, 2*REAL_EPS) );
613  }
614  }
615  SECTION( "input validation" ) {
616 
617  SECTION( "control and target collision" ) {
618 
619  int qb = GENERATE( range(0,NUM_QUBITS) );
620  REQUIRE_THROWS_WITH( controlledRotateY(quregVec, qb, qb, param), Contains("Control") && Contains("target") );
621  }
622  SECTION( "qubit indices" ) {
623 
624  int qb = GENERATE( -1, NUM_QUBITS );
625  REQUIRE_THROWS_WITH( controlledRotateY(quregVec, qb, 0, param), Contains("Invalid control") );
626  REQUIRE_THROWS_WITH( controlledRotateY(quregVec, 0, qb, param), Contains("Invalid target") );
627  }
628  }
629  CLEANUP_TEST( quregVec, quregMatr );
630 }
631 
632 
633 
638 TEST_CASE( "controlledRotateZ", "[unitaries]" ) {
639 
640  PREPARE_TEST( quregVec, quregMatr, refVec, refMatr );
641  qreal param = getRandomReal(-4*M_PI, 4*M_PI);
642  QMatrix op{{expI(-param/2.),0},{0,expI(param/2.)}};
643 
644  SECTION( "correctness" ) {
645 
646  int target = GENERATE( range(0,NUM_QUBITS) );
647  int control = GENERATE_COPY( filter([=](int c){ return c!=target; }, range(0,NUM_QUBITS)) );
648 
649  SECTION( "state-vector" ) {
650 
651  controlledRotateZ(quregVec, control, target, param);
652  applyReferenceOp(refVec, control, target, op);
653  REQUIRE( areEqual(quregVec, refVec) );
654  }
655  SECTION( "density-matrix" ) {
656 
657  controlledRotateZ(quregMatr, control, target, param);
658  applyReferenceOp(refMatr, control, target, op);
659  REQUIRE( areEqual(quregMatr, refMatr, 10*REAL_EPS) );
660  }
661  }
662  SECTION( "input validation" ) {
663 
664  SECTION( "control and target collision" ) {
665 
666  int qb = GENERATE( range(0,NUM_QUBITS) );
667  REQUIRE_THROWS_WITH( controlledRotateZ(quregVec, qb, qb, param), Contains("Control") && Contains("target") );
668  }
669  SECTION( "qubit indices" ) {
670 
671  int qb = GENERATE( -1, NUM_QUBITS );
672  REQUIRE_THROWS_WITH( controlledRotateZ(quregVec, qb, 0, param), Contains("Invalid control") );
673  REQUIRE_THROWS_WITH( controlledRotateZ(quregVec, 0, qb, param), Contains("Invalid target") );
674  }
675  }
676  CLEANUP_TEST( quregVec, quregMatr );
677 }
678 
679 
680 
685 TEST_CASE( "controlledTwoQubitUnitary", "[unitaries]" ) {
686 
687  PREPARE_TEST( quregVec, quregMatr, refVec, refMatr );
688 
689  // in distributed mode, each node must be able to fit all amps modified by unitary
690  REQUIRE( quregVec.numAmpsPerChunk >= 4 );
691 
692  // every test will use a unique random matrix
693  QMatrix op = getRandomUnitary(2);
694  ComplexMatrix4 matr = toComplexMatrix4(op);
695 
696  SECTION( "correctness" ) {
697 
698  int targ1 = GENERATE( range(0,NUM_QUBITS) );
699  int targ2 = GENERATE_COPY( filter([=](int t){ return t!=targ1; }, range(0,NUM_QUBITS)) );
700  int control = GENERATE_COPY( filter([=](int c){ return c!=targ1 && c!=targ2; }, range(0,NUM_QUBITS)) );
701 
702  SECTION( "state-vector" ) {
703 
704  controlledTwoQubitUnitary(quregVec, control, targ1, targ2, matr);
705  applyReferenceOp(refVec, control, targ1, targ2, op);
706  REQUIRE( areEqual(quregVec, refVec) );
707  }
708  SECTION( "density-matrix" ) {
709 
710  controlledTwoQubitUnitary(quregMatr, control, targ1, targ2, matr);
711  applyReferenceOp(refMatr, control, targ1, targ2, op);
712  REQUIRE( areEqual(quregMatr, refMatr, 10*REAL_EPS) );
713  }
714  }
715  SECTION( "input validation" ) {
716 
717  SECTION( "repetition of targets" ) {
718  int targ = 0;
719  int ctrl = 1;
720  REQUIRE_THROWS_WITH( controlledTwoQubitUnitary(quregVec, ctrl, targ, targ, matr), Contains("target") && Contains("unique") );
721  }
722  SECTION( "control and target collision" ) {
723 
724  int targ1 = 1;
725  int targ2 = 2;
726  int ctrl = GENERATE( 1,2 ); // catch2 bug; can't do GENERATE_COPY( targ1, targ2 )
727  REQUIRE_THROWS_WITH( controlledTwoQubitUnitary(quregVec, ctrl, targ1, targ2, matr), Contains("Control") && Contains("target") );
728  }
729  SECTION( "qubit indices" ) {
730 
731  // valid config
732  int ctrl = 0;
733  int targ1 = 1;
734  int targ2 = 2;
735 
736  int qb = GENERATE( -1, NUM_QUBITS );
737  REQUIRE_THROWS_WITH( controlledTwoQubitUnitary(quregVec, qb, targ1, targ2, matr), Contains("Invalid control") );
738  REQUIRE_THROWS_WITH( controlledTwoQubitUnitary(quregVec, ctrl, qb, targ2, matr), Contains("Invalid target") );
739  REQUIRE_THROWS_WITH( controlledTwoQubitUnitary(quregVec, ctrl, targ1, qb, matr), Contains("Invalid target") );
740  }
741  SECTION( "unitarity" ) {
742 
743  matr.real[0][0] = 0; // break matr unitarity
744  REQUIRE_THROWS_WITH( controlledTwoQubitUnitary(quregVec, 0, 1, 2, matr), Contains("unitary") );
745  }
746  SECTION( "unitary fits in node" ) {
747 
748  // pretend we have a very limited distributed memory
749  quregVec.numAmpsPerChunk = 1;
750  REQUIRE_THROWS_WITH( controlledTwoQubitUnitary(quregVec, 0, 1, 2, matr), Contains("targets too many qubits"));
751  }
752  }
753  CLEANUP_TEST( quregVec, quregMatr );
754 }
755 
756 
757 
762 TEST_CASE( "controlledUnitary", "[unitaries]" ) {
763 
764  PREPARE_TEST( quregVec, quregMatr, refVec, refMatr );
765  QMatrix op = getRandomUnitary(1);
766  ComplexMatrix2 matr = toComplexMatrix2(op);
767 
768  SECTION( "correctness" ) {
769 
770  int target = GENERATE( range(0,NUM_QUBITS) );
771  int control = GENERATE_COPY( filter([=](int c){ return c!=target; }, range(0,NUM_QUBITS)) );
772 
773  SECTION( "state-vector" ) {
774 
775  controlledUnitary(quregVec, control, target, matr);
776  applyReferenceOp(refVec, control, target, op);
777  REQUIRE( areEqual(quregVec, refVec) );
778  }
779  SECTION( "density-matrix" ) {
780 
781  controlledUnitary(quregMatr, control, target, matr);
782  applyReferenceOp(refMatr, control, target, op);
783  REQUIRE( areEqual(quregMatr, refMatr, 10*REAL_EPS) );
784  }
785  }
786  SECTION( "input validation" ) {
787 
788  SECTION( "control and target collision" ) {
789 
790  int qb = GENERATE( range(0,NUM_QUBITS) );
791  REQUIRE_THROWS_WITH( controlledUnitary(quregVec, qb, qb, matr), Contains("Control") && Contains("target") );
792  }
793  SECTION( "qubit indices" ) {
794 
795  int qb = GENERATE( -1, NUM_QUBITS );
796  REQUIRE_THROWS_WITH( controlledUnitary(quregVec, qb, 0, matr), Contains("Invalid control") );
797  REQUIRE_THROWS_WITH( controlledUnitary(quregVec, 0, qb, matr), Contains("Invalid target") );
798  }
799  SECTION( "unitarity" ) {
800 
801  matr.real[0][0] = 0; // break unitarity
802  REQUIRE_THROWS_WITH( controlledUnitary(quregVec, 0, 1, matr), Contains("unitary") );
803  }
804  }
805  CLEANUP_TEST( quregVec, quregMatr );
806 }
807 
808 
809 
814 TEST_CASE( "hadamard", "[unitaries]" ) {
815 
816  PREPARE_TEST( quregVec, quregMatr, refVec, refMatr );
817  qreal a = 1/sqrt(2);
818  QMatrix op{{a,a},{a,-a}};
819 
820  SECTION( "correctness" ) {
821 
822  int target = GENERATE( range(0,NUM_QUBITS) );
823 
824  SECTION( "state-vector ") {
825 
826  hadamard(quregVec, target);
827  applyReferenceOp(refVec, target, op);
828  REQUIRE( areEqual(quregVec, refVec) );
829  }
830  SECTION( "density-matrix" ) {
831 
832  hadamard(quregMatr, target);
833  applyReferenceOp(refMatr, target, op);
834  REQUIRE( areEqual(quregMatr, refMatr, 10*REAL_EPS) );
835  }
836  }
837  SECTION( "input validation" ) {
838 
839  SECTION( "qubit indices" ) {
840 
841  int target = GENERATE( -1, NUM_QUBITS );
842  REQUIRE_THROWS_WITH( hadamard(quregVec, target), Contains("Invalid target") );
843  }
844  }
845  CLEANUP_TEST( quregVec, quregMatr );
846 }
847 
848 
849 
854 TEST_CASE( "multiControlledMultiQubitNot", "[unitaries]" ) {
855 
856  PREPARE_TEST( quregVec, quregMatr, refVec, refMatr );
857 
858  SECTION( "correctness" ) {
859 
860  // try all possible numbers of targets and controls
861  int numTargs = GENERATE_COPY( range(1,NUM_QUBITS) ); // leave space for 1 ctrl
862  int maxNumCtrls = NUM_QUBITS - numTargs;
863  int numCtrls = GENERATE_COPY( range(1,maxNumCtrls+1) );
864 
865  // generate all possible valid qubit arrangements
866  int* targs = GENERATE_COPY( sublists(range(0,NUM_QUBITS), numTargs) );
867  int* ctrls = GENERATE_COPY( sublists(range(0,NUM_QUBITS), numCtrls, targs, numTargs) );
868 
869  // for each qubit arrangement, use a new random unitary
870  QMatrix notOp{{0, 1},{1,0}};
871 
872  SECTION( "state-vector" ) {
873 
874  multiControlledMultiQubitNot(quregVec, ctrls, numCtrls, targs, numTargs);
875  for (int t=0; t<numTargs; t++)
876  applyReferenceOp(refVec, ctrls, numCtrls, targs[t], notOp);
877 
878  REQUIRE( areEqual(quregVec, refVec) );
879  }
880  SECTION( "density-matrix" ) {
881 
882  multiControlledMultiQubitNot(quregMatr, ctrls, numCtrls, targs, numTargs);
883  for (int t=0; t<numTargs; t++)
884  applyReferenceOp(refMatr, ctrls, numCtrls, targs[t], notOp);
885 
886  REQUIRE( areEqual(quregMatr, refMatr) );
887  }
888  }
889  SECTION( "input validation" ) {
890 
891  SECTION( "number of targets" ) {
892 
893  // there cannot be more targets than qubits in register
894  // (numTargs=NUM_QUBITS is caught elsewhere, because that implies ctrls are invalid)
895  int numTargs = GENERATE( -1, 0, NUM_QUBITS+1 );
896  int targs[NUM_QUBITS+1]; // prevents seg-fault if validation doesn't trigger
897  int ctrls[] = {0};
898  REQUIRE_THROWS_WITH( multiControlledMultiQubitNot(quregVec, ctrls, 1, targs, numTargs), Contains("Invalid number of target"));
899  }
900  SECTION( "repetition in targets" ) {
901 
902  int ctrls[] = {0};
903  int numTargs = 3;
904  int targs[] = {1,2,2};
905  REQUIRE_THROWS_WITH( multiControlledMultiQubitNot(quregVec, ctrls, 1, targs, numTargs), Contains("target") && Contains("unique"));
906  }
907  SECTION( "number of controls" ) {
908 
909  int numCtrls = GENERATE( -1, 0, NUM_QUBITS, NUM_QUBITS+1 );
910  int ctrls[NUM_QUBITS+1]; // avoids seg-fault if validation not triggered
911  int targs[1] = {0};
912  REQUIRE_THROWS_WITH( multiControlledMultiQubitNot(quregVec, ctrls, numCtrls, targs, 1), Contains("Invalid number of control"));
913  }
914  SECTION( "repetition in controls" ) {
915 
916  int ctrls[] = {0,1,1};
917  int targs[] = {3};
918  REQUIRE_THROWS_WITH( multiControlledMultiQubitNot(quregVec, ctrls, 3, targs, 1), Contains("control") && Contains("unique"));
919  }
920  SECTION( "control and target collision" ) {
921 
922  int ctrls[] = {0,1,2};
923  int targs[] = {3,1,4};
924  REQUIRE_THROWS_WITH( multiControlledMultiQubitNot(quregVec, ctrls, 3, targs, 3), Contains("Control") && Contains("target") && Contains("disjoint"));
925  }
926  SECTION( "qubit indices" ) {
927 
928  // valid inds
929  int numQb = 2;
930  int qb1[2] = {0,1};
931  int qb2[2] = {2,3};
932 
933  // make qb1 invalid
934  int inv = GENERATE( -1, NUM_QUBITS );
935  qb1[GENERATE_COPY(range(0,numQb))] = inv;
936 
937  REQUIRE_THROWS_WITH( multiControlledMultiQubitNot(quregVec, qb1, numQb, qb2, numQb), Contains("Invalid control") );
938  REQUIRE_THROWS_WITH( multiControlledMultiQubitNot(quregVec, qb2, numQb, qb1, numQb), Contains("Invalid target") );
939  }
940  }
941  CLEANUP_TEST( quregVec, quregMatr );
942 }
943 
944 
945 
950 TEST_CASE( "multiControlledMultiQubitUnitary", "[unitaries]" ) {
951 
952  PREPARE_TEST( quregVec, quregMatr, refVec, refMatr );
953 
954  // figure out max-num targs (inclusive) allowed by hardware backend
955  int maxNumTargs = calcLog2(quregVec.numAmpsPerChunk);
956  if (maxNumTargs >= NUM_QUBITS)
957  maxNumTargs = NUM_QUBITS - 1; // leave room for min-number of control qubits
958 
959  SECTION( "correctness" ) {
960 
961  // try all possible numbers of targets and controls
962  int numTargs = GENERATE_COPY( range(1,maxNumTargs+1) );
963  int maxNumCtrls = NUM_QUBITS - numTargs;
964  int numCtrls = GENERATE_COPY( range(1,maxNumCtrls+1) );
965 
966  // generate all possible valid qubit arrangements
967  int* targs = GENERATE_COPY( sublists(range(0,NUM_QUBITS), numTargs) );
968  int* ctrls = GENERATE_COPY( sublists(range(0,NUM_QUBITS), numCtrls, targs, numTargs) );
969 
970  // for each qubit arrangement, use a new random unitary
971  QMatrix op = getRandomUnitary(numTargs);
972  ComplexMatrixN matr = createComplexMatrixN(numTargs);
973  toComplexMatrixN(op, matr);
974 
975  SECTION( "state-vector" ) {
976 
977  multiControlledMultiQubitUnitary(quregVec, ctrls, numCtrls, targs, numTargs, matr);
978  applyReferenceOp(refVec, ctrls, numCtrls, targs, numTargs, op);
979  REQUIRE( areEqual(quregVec, refVec) );
980  }
981  SECTION( "density-matrix" ) {
982 
983  multiControlledMultiQubitUnitary(quregMatr, ctrls, numCtrls, targs, numTargs, matr);
984  applyReferenceOp(refMatr, ctrls, numCtrls, targs, numTargs, op);
985  REQUIRE( areEqual(quregMatr, refMatr, 10*REAL_EPS) );
986  }
987  destroyComplexMatrixN(matr);
988  }
989  SECTION( "input validation" ) {
990 
991  SECTION( "number of targets" ) {
992 
993  // there cannot be more targets than qubits in register
994  // (numTargs=NUM_QUBITS is caught elsewhere, because that implies ctrls are invalid)
995  int numTargs = GENERATE( -1, 0, NUM_QUBITS+1 );
996  int targs[NUM_QUBITS+1]; // prevents seg-fault if validation doesn't trigger
997  int ctrls[] = {0};
998  ComplexMatrixN matr = createComplexMatrixN(NUM_QUBITS+1); // prevent seg-fault
999  toComplexMatrixN(getRandomUnitary(NUM_QUBITS+1), matr); // ensure unitary
1000 
1001  REQUIRE_THROWS_WITH( multiControlledMultiQubitUnitary(quregVec, ctrls, 1, targs, numTargs, matr), Contains("Invalid number of target"));
1002  destroyComplexMatrixN(matr);
1003  }
1004  SECTION( "repetition in targets" ) {
1005 
1006  int ctrls[] = {0};
1007  int numTargs = 3;
1008  int targs[] = {1,2,2};
1009  ComplexMatrixN matr = createComplexMatrixN(numTargs); // prevents seg-fault if validation doesn't trigger
1010  toComplexMatrixN(getRandomUnitary(numTargs), matr); // ensure unitary
1011 
1012  REQUIRE_THROWS_WITH( multiControlledMultiQubitUnitary(quregVec, ctrls, 1, targs, numTargs, matr), Contains("target") && Contains("unique"));
1013  destroyComplexMatrixN(matr);
1014  }
1015  SECTION( "number of controls" ) {
1016 
1017  int numCtrls = GENERATE( -1, 0, NUM_QUBITS, NUM_QUBITS+1 );
1018  int ctrls[NUM_QUBITS+1]; // avoids seg-fault if validation not triggered
1019  int targs[1] = {0};
1021  toComplexMatrixN(getRandomUnitary(1), matr); // ensure unitary
1022 
1023  REQUIRE_THROWS_WITH( multiControlledMultiQubitUnitary(quregVec, ctrls, numCtrls, targs, 1, matr), Contains("Invalid number of control"));
1024  destroyComplexMatrixN(matr);
1025  }
1026  SECTION( "repetition in controls" ) {
1027 
1028  int ctrls[] = {0,1,1};
1029  int targs[] = {3};
1031  toComplexMatrixN(getRandomUnitary(1), matr); // ensure unitary
1032 
1033  REQUIRE_THROWS_WITH( multiControlledMultiQubitUnitary(quregVec, ctrls, 3, targs, 1, matr), Contains("control") && Contains("unique"));
1034  destroyComplexMatrixN(matr);
1035  }
1036  SECTION( "control and target collision" ) {
1037 
1038  int ctrls[] = {0,1,2};
1039  int targs[] = {3,1,4};
1041  toComplexMatrixN(getRandomUnitary(3), matr); // ensure unitary
1042 
1043  REQUIRE_THROWS_WITH( multiControlledMultiQubitUnitary(quregVec, ctrls, 3, targs, 3, matr), Contains("Control") && Contains("target") && Contains("disjoint"));
1044  destroyComplexMatrixN(matr);
1045  }
1046  SECTION( "qubit indices" ) {
1047 
1048  // valid inds
1049  int numQb = 2;
1050  int qb1[2] = {0,1};
1051  int qb2[2] = {2,3};
1052  ComplexMatrixN matr = createComplexMatrixN(numQb);
1053  toComplexMatrixN(getRandomUnitary(numQb), matr); // ensure unitary
1054 
1055  // make qb1 invalid
1056  int inv = GENERATE( -1, NUM_QUBITS );
1057  qb1[GENERATE_COPY(range(0,numQb))] = inv;
1058 
1059  REQUIRE_THROWS_WITH( multiControlledMultiQubitUnitary(quregVec, qb1, numQb, qb2, numQb, matr), Contains("Invalid control") );
1060  REQUIRE_THROWS_WITH( multiControlledMultiQubitUnitary(quregVec, qb2, numQb, qb1, numQb, matr), Contains("Invalid target") );
1061  destroyComplexMatrixN(matr);
1062  }
1063  SECTION( "unitarity" ) {
1064 
1065  int ctrls[1] = {0};
1066  int numTargs = GENERATE_COPY( range(1,maxNumTargs+1) );
1067  int targs[numTargs];
1068  for (int i=0; i<numTargs; i++)
1069  targs[i] = i+1;
1070 
1071  ComplexMatrixN matr = createComplexMatrixN(numTargs); // initially zero, hence not-unitary
1072  REQUIRE_THROWS_WITH( multiControlledMultiQubitUnitary(quregVec, ctrls, 1, targs, numTargs, matr), Contains("unitary") );
1073  destroyComplexMatrixN(matr);
1074  }
1075  SECTION( "unitary creation" ) {
1076 
1077  int ctrls[1] = {0};
1078  int targs[3] = {1,2,3};
1079 
1080  /* compilers don't auto-initialise to NULL; the below circumstance
1081  * only really occurs when 'malloc' returns NULL in createComplexMatrixN,
1082  * which actually triggers its own validation. Hence this test is useless
1083  * currently.
1084  */
1085  ComplexMatrixN matr;
1086  matr.real = NULL;
1087  matr.imag = NULL;
1088  REQUIRE_THROWS_WITH( multiControlledMultiQubitUnitary(quregVec, ctrls, 1, targs, 3, matr), Contains("created") );
1089  }
1090  SECTION( "unitary dimensions" ) {
1091 
1092  int ctrls[1] = {0};
1093  int targs[2] = {1,2};
1094  ComplexMatrixN matr = createComplexMatrixN(3); // intentionally wrong size
1095  toComplexMatrixN(getRandomUnitary(3), matr); // ensure unitary
1096 
1097  REQUIRE_THROWS_WITH( multiControlledMultiQubitUnitary(quregVec, ctrls, 1, targs, 2, matr), Contains("matrix size"));
1098  destroyComplexMatrixN(matr);
1099  }
1100  SECTION( "unitary fits in node" ) {
1101 
1102  // pretend we have a very limited distributed memory (judged by matr size)
1103  quregVec.numAmpsPerChunk = 1;
1104  int ctrls[1] = {0};
1105  int targs[2] = {1,2};
1107  toComplexMatrixN(getRandomUnitary(2), matr); // ensure unitary
1108 
1109  REQUIRE_THROWS_WITH( multiControlledMultiQubitUnitary(quregVec, ctrls, 1, targs, 2, matr), Contains("targets too many qubits"));
1110  destroyComplexMatrixN(matr);
1111  }
1112  }
1113  CLEANUP_TEST( quregVec, quregMatr );
1114 }
1115 
1116 
1117 
1122 TEST_CASE( "multiControlledMultiRotatePauli", "[unitaries]" ) {
1123 
1124  PREPARE_TEST( quregVec, quregMatr, refVec, refMatr );
1125  qreal param = getRandomReal(-4*M_PI, 4*M_PI);
1126 
1127  SECTION( "correctness" ) {
1128 
1129  // try all possible numbers of targets and controls
1130  int numTargs = GENERATE_COPY( range(1,NUM_QUBITS) ); // leave space for min 1 control qubit
1131  int maxNumCtrls = NUM_QUBITS - numTargs;
1132  int numCtrls = GENERATE_COPY( range(1,maxNumCtrls+1) );
1133 
1134  // generate all possible valid qubit arrangements
1135  int* targs = GENERATE_COPY( sublists(range(0,NUM_QUBITS), numTargs) );
1136  int* ctrls = GENERATE_COPY( sublists(range(0,NUM_QUBITS), numCtrls, targs, numTargs) );
1137 
1138  /* it's too expensive to try ALL Pauli sequences, via
1139  * pauliOpType* paulis = GENERATE_COPY( pauliseqs(numTargs) );.
1140  * Furthermore, take(10, pauliseqs(numTargs)) will try the same pauli codes.
1141  * Hence, we instead opt to randomly generate pauliseqs
1142  */
1143  pauliOpType paulis[numTargs];
1144  for (int i=0; i<numTargs; i++)
1145  paulis[i] = (pauliOpType) getRandomInt(0,4);
1146 
1147  // exclude identities from reference matrix exp (they apply unwanted global phase)
1148  int refTargs[numTargs];
1149  int numRefTargs = 0;
1150 
1151  QMatrix xMatr{{0,1},{1,0}};
1152  QMatrix yMatr{{0,-qcomp(0,1)},{qcomp(0,1),0}};
1153  QMatrix zMatr{{1,0},{0,-1}};
1154 
1155  // build correct reference matrix by pauli-matrix exponentiation...
1156  QMatrix pauliProd{{1}};
1157  for (int i=0; i<numTargs; i++) {
1158  QMatrix fac;
1159  if (paulis[i] == PAULI_I) continue; // exclude I-targets from ref list
1160  if (paulis[i] == PAULI_X) fac = xMatr;
1161  if (paulis[i] == PAULI_Y) fac = yMatr;
1162  if (paulis[i] == PAULI_Z) fac = zMatr;
1163  pauliProd = getKroneckerProduct(fac, pauliProd);
1164 
1165  // include this target in ref list
1166  refTargs[numRefTargs++] = targs[i];
1167  }
1168 
1169  // produces exp(-i param/2 pauliProd), unless pauliProd = I
1170  QMatrix op;
1171  if (numRefTargs > 0)
1172  op = getExponentialOfPauliMatrix(param, pauliProd);
1173 
1174  SECTION( "state-vector" ) {
1175 
1176  multiControlledMultiRotatePauli(quregVec, ctrls, numCtrls, targs, paulis, numTargs, param);
1177  if (numRefTargs > 0)
1178  applyReferenceOp(refVec, ctrls, numCtrls, refTargs, numRefTargs, op);
1179  REQUIRE( areEqual(quregVec, refVec) );
1180  }
1181  SECTION( "density-matrix" ) {
1182 
1183  multiControlledMultiRotatePauli(quregMatr, ctrls, numCtrls, targs, paulis, numTargs, param);
1184  if (numRefTargs > 0)
1185  applyReferenceOp(refMatr, ctrls, numCtrls, refTargs, numRefTargs, op);
1186  REQUIRE( areEqual(quregMatr, refMatr, 10*REAL_EPS) );
1187  }
1188  }
1189  SECTION( "input validation" ) {
1190 
1191  // test all validation on both state-vector and density-matrix.
1192  // want GENERATE_COPY( quregVec, quregMatr ), but too lazy to patch
1193  // using github.com/catchorg/Catch2/issues/1809
1194  Qureg regs[] = {quregVec, quregMatr};
1195  Qureg qureg = regs[GENERATE(0,1)];
1196 
1197  // over-sized array to prevent seg-fault in case of validation fail below
1198  pauliOpType paulis[NUM_QUBITS+1];
1199  for (int q=0; q<NUM_QUBITS+1; q++)
1200  paulis[q] = PAULI_I;
1201 
1202  SECTION( "pauli codes" ) {
1203 
1204  int numCtrls = 1;
1205  int ctrls[] = {3};
1206  int numTargs = 3;
1207  int targs[3] = {0, 1, 2};
1208 
1209  // make a single Pauli invalid
1210  paulis[GENERATE_COPY(range(0,numTargs))] = (pauliOpType) GENERATE( -1, 4 );
1211 
1212  REQUIRE_THROWS_WITH( multiControlledMultiRotatePauli(qureg, ctrls, numCtrls, targs, paulis, numTargs, param), Contains("Invalid Pauli code"));
1213  }
1214  SECTION( "number of targets" ) {
1215 
1216  // there cannot be more targets than qubits in register
1217  // (numTargs=NUM_QUBITS is caught elsewhere, because that implies ctrls are invalid)
1218  int numTargs = GENERATE( -1, 0, NUM_QUBITS+1 );
1219  int numCtrls = 1;
1220  int targs[NUM_QUBITS+1]; // prevents seg-fault if validation doesn't trigger
1221  int ctrls[] = {0};
1222 
1223  REQUIRE_THROWS_WITH( multiControlledMultiRotatePauli(qureg, ctrls, numCtrls, targs, paulis, numTargs, param), Contains("Invalid number of target") );
1224  }
1225  SECTION( "repetition in targets" ) {
1226 
1227  int numCtrls = 1;
1228  int numTargs = 3;
1229  int ctrls[] = {0};
1230  int targs[] = {1,2,2};
1231 
1232  REQUIRE_THROWS_WITH( multiControlledMultiRotatePauli(qureg, ctrls, numCtrls, targs, paulis, numTargs, param), Contains("target") && Contains("unique"));
1233  }
1234  SECTION( "number of controls" ) {
1235 
1236  int numCtrls = GENERATE( -1, 0, NUM_QUBITS, NUM_QUBITS+1 );
1237  int numTargs = 1;
1238  int ctrls[NUM_QUBITS+1]; // avoids seg-fault if validation not triggered
1239  int targs[1] = {0};
1240 
1241  REQUIRE_THROWS_WITH( multiControlledMultiRotatePauli(qureg, ctrls, numCtrls, targs, paulis, numTargs, param), Contains("Invalid number of control"));
1242  }
1243  SECTION( "repetition in controls" ) {
1244 
1245  int numCtrls = 3;
1246  int numTargs = 1;
1247  int ctrls[] = {0,1,1};
1248  int targs[] = {3};
1249 
1250  REQUIRE_THROWS_WITH( multiControlledMultiRotatePauli(qureg, ctrls, numCtrls, targs, paulis, numTargs, param), Contains("control") && Contains("unique"));
1251  }
1252  SECTION( "control and target collision" ) {
1253 
1254  int numCtrls = 3;
1255  int numTargs = 3;
1256  int ctrls[] = {0,1,2};
1257  int targs[] = {3,1,4};
1258 
1259  REQUIRE_THROWS_WITH( multiControlledMultiRotatePauli(qureg, ctrls, numCtrls, targs, paulis, numTargs, param), Contains("Control") && Contains("target") && Contains("disjoint"));
1260  }
1261  SECTION( "qubit indices" ) {
1262 
1263  // valid inds
1264  int numQb = 2;
1265  int qb1[2] = {0,1};
1266  int qb2[2] = {2,3};
1267 
1268  // make qb1 invalid
1269  int inv = GENERATE( -1, NUM_QUBITS );
1270  qb1[GENERATE_COPY(range(0,numQb))] = inv;
1271 
1272  REQUIRE_THROWS_WITH( multiControlledMultiRotatePauli(qureg, qb1, numQb, qb2, paulis, numQb, param), Contains("Invalid control") );
1273  REQUIRE_THROWS_WITH( multiControlledMultiRotatePauli(qureg, qb2, numQb, qb1, paulis, numQb, param), Contains("Invalid target") );
1274  }
1275  }
1276  CLEANUP_TEST( quregVec, quregMatr );
1277 }
1278 
1279 
1280 
1285 TEST_CASE( "multiControlledMultiRotateZ", "[unitaries]" ) {
1286 
1287  PREPARE_TEST( quregVec, quregMatr, refVec, refMatr );
1288  qreal param = getRandomReal(-4*M_PI, 4*M_PI);
1289 
1290  SECTION( "correctness" ) {
1291 
1292  // try all possible numbers of targets and controls
1293  int numTargs = GENERATE_COPY( range(1,NUM_QUBITS) ); // leave space for min 1 control qubit
1294  int maxNumCtrls = NUM_QUBITS - numTargs;
1295  int numCtrls = GENERATE_COPY( range(1,maxNumCtrls+1) );
1296 
1297  // generate all possible valid qubit arrangements
1298  int* targs = GENERATE_COPY( sublists(range(0,NUM_QUBITS), numTargs) );
1299  int* ctrls = GENERATE_COPY( sublists(range(0,NUM_QUBITS), numCtrls, targs, numTargs) );
1300 
1301  // build correct reference matrix by diagonal-matrix exponentiation...
1302  QMatrix zMatr{{1,0},{0,-1}};
1303  QMatrix zProd = zMatr;
1304  for (int t=0; t<numTargs-1; t++)
1305  zProd = getKroneckerProduct(zMatr, zProd); // Z . Z ... Z
1306 
1307  // exp( -i param/2 Z . Z ... Z)
1308  QMatrix op = getExponentialOfDiagonalMatrix(qcomp(0, -param/2) * zProd);
1309 
1310  SECTION( "state-vector" ) {
1311 
1312  multiControlledMultiRotateZ(quregVec, ctrls, numCtrls, targs, numTargs, param);
1313  applyReferenceOp(refVec, ctrls, numCtrls, targs, numTargs, op);
1314  REQUIRE( areEqual(quregVec, refVec) );
1315  }
1316  SECTION( "density-matrix" ) {
1317 
1318  multiControlledMultiRotateZ(quregMatr, ctrls, numCtrls, targs, numTargs, param);
1319  applyReferenceOp(refMatr, ctrls, numCtrls, targs, numTargs, op);
1320  REQUIRE( areEqual(quregMatr, refMatr, 2*REAL_EPS) );
1321  }
1322  }
1323  SECTION( "input validation" ) {
1324 
1325  // test all validation on both state-vector and density-matrix.
1326  // want GENERATE_COPY( quregVec, quregMatr ), but too lazy to patch
1327  // using github.com/catchorg/Catch2/issues/1809
1328  Qureg regs[] = {quregVec, quregMatr};
1329  Qureg qureg = regs[GENERATE(0,1)];
1330 
1331  SECTION( "number of targets" ) {
1332 
1333  // there cannot be more targets than qubits in register
1334  // (numTargs=NUM_QUBITS is caught elsewhere, because that implies ctrls are invalid)
1335  int numTargs = GENERATE( -1, 0, NUM_QUBITS+1 );
1336  int numCtrls = 1;
1337  int targs[NUM_QUBITS+1]; // prevents seg-fault if validation doesn't trigger
1338  int ctrls[] = {0};
1339 
1340  REQUIRE_THROWS_WITH( multiControlledMultiRotateZ(qureg, ctrls, numCtrls, targs, numTargs, param), Contains("Invalid number of target") );
1341  }
1342  SECTION( "repetition in targets" ) {
1343 
1344  int numCtrls = 1;
1345  int numTargs = 3;
1346  int ctrls[] = {0};
1347  int targs[] = {1,2,2};
1348 
1349  REQUIRE_THROWS_WITH( multiControlledMultiRotateZ(qureg, ctrls, numCtrls, targs, numTargs, param), Contains("target") && Contains("unique"));
1350  }
1351  SECTION( "number of controls" ) {
1352 
1353  int numCtrls = GENERATE( -1, 0, NUM_QUBITS, NUM_QUBITS+1 );
1354  int numTargs = 1;
1355  int ctrls[NUM_QUBITS+1]; // avoids seg-fault if validation not triggered
1356  int targs[1] = {0};
1357 
1358  REQUIRE_THROWS_WITH( multiControlledMultiRotateZ(qureg, ctrls, numCtrls, targs, numTargs, param), Contains("Invalid number of control"));
1359  }
1360  SECTION( "repetition in controls" ) {
1361 
1362  int numCtrls = 3;
1363  int numTargs = 1;
1364  int ctrls[] = {0,1,1};
1365  int targs[] = {3};
1366 
1367  REQUIRE_THROWS_WITH( multiControlledMultiRotateZ(qureg, ctrls, numCtrls, targs, numTargs, param), Contains("control") && Contains("unique"));
1368  }
1369  SECTION( "control and target collision" ) {
1370 
1371  int numCtrls = 3;
1372  int numTargs = 3;
1373  int ctrls[] = {0,1,2};
1374  int targs[] = {3,1,4};
1375 
1376  REQUIRE_THROWS_WITH( multiControlledMultiRotateZ(qureg, ctrls, numCtrls, targs, numTargs, param), Contains("Control") && Contains("target") && Contains("disjoint"));
1377  }
1378  SECTION( "qubit indices" ) {
1379 
1380  // valid inds
1381  int numQb = 2;
1382  int qb1[2] = {0,1};
1383  int qb2[2] = {2,3};
1384 
1385  // make qb1 invalid
1386  int inv = GENERATE( -1, NUM_QUBITS );
1387  qb1[GENERATE_COPY(range(0,numQb))] = inv;
1388 
1389  REQUIRE_THROWS_WITH( multiControlledMultiRotateZ(qureg, qb1, numQb, qb2, numQb, param), Contains("Invalid control") );
1390  REQUIRE_THROWS_WITH( multiControlledMultiRotateZ(qureg, qb2, numQb, qb1, numQb, param), Contains("Invalid target") );
1391  }
1392  }
1393  CLEANUP_TEST( quregVec, quregMatr );
1394 }
1395 
1396 
1397 
1402 TEST_CASE( "multiControlledPhaseFlip", "[unitaries]" ) {
1403 
1404  PREPARE_TEST( quregVec, quregMatr, refVec, refMatr );
1405 
1406  // acts on the final control qubit
1407  QMatrix op{{1,0},{0,-1}};
1408 
1409  SECTION( "correctness" ) {
1410 
1411  // generate ALL valid qubit arrangements
1412  int numCtrls = GENERATE( range(1,NUM_QUBITS) ); // numCtrls=NUM_QUBITS stopped by overzealous validation
1413  int* ctrls = GENERATE_COPY( sublists(range(0,NUM_QUBITS), numCtrls) );
1414 
1415  SECTION( "state-vector" ) {
1416 
1417  multiControlledPhaseFlip(quregVec, ctrls, numCtrls);
1418  applyReferenceOp(refVec, ctrls, numCtrls-1, ctrls[numCtrls-1], op);
1419  REQUIRE( areEqual(quregVec, refVec) );
1420  }
1421  SECTION( "density-matrix" ) {
1422 
1423  multiControlledPhaseFlip(quregMatr, ctrls, numCtrls);
1424  applyReferenceOp(refMatr, ctrls, numCtrls-1, ctrls[numCtrls-1], op);
1425  REQUIRE( areEqual(quregMatr, refMatr) );
1426  }
1427  }
1428  SECTION( "input validation" ) {
1429 
1430  SECTION( "number of controls" ) {
1431 
1432  int numCtrls = GENERATE( -1, 0, NUM_QUBITS+1 );
1433  int ctrls[NUM_QUBITS+1]; // avoids seg-fault if validation not triggered
1434  REQUIRE_THROWS_WITH( multiControlledPhaseFlip(quregVec, ctrls, numCtrls), Contains("Invalid number of qubits"));
1435  }
1436  SECTION( "repetition of controls" ) {
1437 
1438  int numCtrls = 3;
1439  int ctrls[] = {0,1,1};
1440  REQUIRE_THROWS_WITH( multiControlledPhaseFlip(quregVec, ctrls, numCtrls), Contains("qubits must be unique"));
1441  }
1442  SECTION( "qubit indices" ) {
1443 
1444  int numCtrls = 3;
1445  int ctrls[] = { 1, 2, GENERATE( -1, NUM_QUBITS ) };
1446  REQUIRE_THROWS_WITH( multiControlledPhaseFlip(quregVec, ctrls, numCtrls), Contains("Invalid qubit") );
1447  }
1448  }
1449  CLEANUP_TEST( quregVec, quregMatr );
1450 }
1451 
1452 
1453 
1458 TEST_CASE( "multiControlledPhaseShift", "[unitaries]" ) {
1459 
1460  PREPARE_TEST( quregVec, quregMatr, refVec, refMatr );
1461  qreal param = getRandomReal(-2*M_PI, 2*M_PI);
1462  QMatrix op{{1,0},{0,expI(param)}};
1463 
1464  SECTION( "correctness" ) {
1465 
1466  // generate ALL valid qubit arrangements
1467  int numCtrls = GENERATE( range(1,NUM_QUBITS) ); // numCtrls=NUM_QUBITS stopped by overzealous validation
1468  int* ctrls = GENERATE_COPY( sublists(range(0,NUM_QUBITS), numCtrls) );
1469 
1470  SECTION( "state-vector" ) {
1471 
1472  multiControlledPhaseShift(quregVec, ctrls, numCtrls, param);
1473  applyReferenceOp(refVec, ctrls, numCtrls-1, ctrls[numCtrls-1], op);
1474  REQUIRE( areEqual(quregVec, refVec) );
1475  }
1476  SECTION( "density-matrix" ) {
1477 
1478  multiControlledPhaseShift(quregMatr, ctrls, numCtrls, param);
1479  applyReferenceOp(refMatr, ctrls, numCtrls-1, ctrls[numCtrls-1], op);
1480  REQUIRE( areEqual(quregMatr, refMatr) );
1481  }
1482  }
1483  SECTION( "input validation" ) {
1484 
1485  SECTION( "number of controls" ) {
1486 
1487  int numCtrls = GENERATE( -1, 0, NUM_QUBITS+1 );
1488  int ctrls[NUM_QUBITS+1]; // avoids seg-fault if validation not triggered
1489  REQUIRE_THROWS_WITH( multiControlledPhaseShift(quregVec, ctrls, numCtrls, param), Contains("Invalid number of qubits"));
1490  }
1491  SECTION( "repetition of controls" ) {
1492 
1493  int numCtrls = 3;
1494  int ctrls[] = {0,1,1};
1495  REQUIRE_THROWS_WITH( multiControlledPhaseShift(quregVec, ctrls, numCtrls, param), Contains("qubits must be unique"));
1496  }
1497  SECTION( "qubit indices" ) {
1498 
1499  int numCtrls = 3;
1500  int ctrls[] = { 1, 2, GENERATE( -1, NUM_QUBITS ) };
1501  REQUIRE_THROWS_WITH( multiControlledPhaseShift(quregVec, ctrls, numCtrls, param), Contains("Invalid qubit") );
1502  }
1503  }
1504  CLEANUP_TEST( quregVec, quregMatr );
1505 }
1506 
1507 
1508 
1513 TEST_CASE( "multiControlledTwoQubitUnitary", "[unitaries]" ) {
1514 
1515  PREPARE_TEST( quregVec, quregMatr, refVec, refMatr );
1516 
1517  // in distributed mode, each node must be able to fit all amps modified by unitary
1518  REQUIRE( quregVec.numAmpsPerChunk >= 4 );
1519 
1520  // every test will use a unique random matrix
1521  QMatrix op = getRandomUnitary(2);
1522  ComplexMatrix4 matr = toComplexMatrix4(op);
1523 
1524  SECTION( "correctness" ) {
1525 
1526  // generate ALL valid qubit arrangements
1527  int targ1 = GENERATE( range(0,NUM_QUBITS) );
1528  int targ2 = GENERATE_COPY( filter([=](int t){ return t!=targ1; }, range(0,NUM_QUBITS)) );
1529  int targs[] = {targ1, targ2};
1530  int numCtrls = GENERATE( range(1,NUM_QUBITS-1) ); // leave room for 2 targets (upper bound is exclusive)
1531  int* ctrls = GENERATE_COPY( sublists(range(0,NUM_QUBITS), numCtrls, targs, 2) );
1532 
1533  SECTION( "state-vector" ) {
1534 
1535  multiControlledTwoQubitUnitary(quregVec, ctrls, numCtrls, targ1, targ2, matr);
1536  applyReferenceOp(refVec, ctrls, numCtrls, targ1, targ2, op);
1537  REQUIRE( areEqual(quregVec, refVec) );
1538  }
1539  SECTION( "density-matrix" ) {
1540 
1541  multiControlledTwoQubitUnitary(quregMatr, ctrls, numCtrls, targ1, targ2, matr);
1542  applyReferenceOp(refMatr, ctrls, numCtrls, targ1, targ2, op);
1543  REQUIRE( areEqual(quregMatr, refMatr, 10*REAL_EPS) );
1544  }
1545  }
1546  SECTION( "input validation" ) {
1547 
1548  SECTION( "number of controls" ) {
1549 
1550  // numCtrls=(NUM_QUBITS-1) is ok since requires ctrl qubit inds are invalid
1551  int numCtrls = GENERATE( -1, 0, NUM_QUBITS, NUM_QUBITS+1 );
1552  int ctrls[NUM_QUBITS+1]; // avoids seg-fault if validation not triggered
1553  REQUIRE_THROWS_WITH( multiControlledTwoQubitUnitary(quregVec, ctrls, numCtrls, 0, 1, matr), Contains("Invalid number of control"));
1554  }
1555  SECTION( "repetition of controls" ) {
1556 
1557  int numCtrls = 3;
1558  int ctrls[] = {0,1,1};
1559  int targ1 = 2;
1560  int targ2 = 3;
1561  REQUIRE_THROWS_WITH( multiControlledTwoQubitUnitary(quregVec, ctrls, numCtrls, targ1, targ2, matr), Contains("control") && Contains("unique"));;
1562  }
1563  SECTION( "repetition of targets" ) {
1564 
1565  int numCtrls = 3;
1566  int ctrls[] = {0,1,2};
1567  int targ1 = 3;
1568  int targ2 = targ1;
1569  REQUIRE_THROWS_WITH( multiControlledTwoQubitUnitary(quregVec, ctrls, numCtrls, targ1, targ2, matr), Contains("target") && Contains("unique"));
1570  }
1571  SECTION( "control and target collision" ) {
1572 
1573  int numCtrls = 3;
1574  int ctrls[] = {0,1,2};
1575  int targ1 = 3;
1576  int targ2 = ctrls[GENERATE_COPY( range(0,numCtrls) )];
1577  REQUIRE_THROWS_WITH( multiControlledTwoQubitUnitary(quregVec, ctrls, numCtrls, targ1, targ2, matr), Contains("Control") && Contains("target") );
1578  }
1579  SECTION( "qubit indices" ) {
1580 
1581  // valid indices
1582  int targ1 = 0;
1583  int targ2 = 1;
1584  int numCtrls = 3;
1585  int ctrls[] = { 2, 3, 4 };
1586 
1587  int inv = GENERATE( -1, NUM_QUBITS );
1588  REQUIRE_THROWS_WITH( multiControlledTwoQubitUnitary(quregVec, ctrls, numCtrls, inv, targ2, matr), Contains("Invalid target") );
1589  REQUIRE_THROWS_WITH( multiControlledTwoQubitUnitary(quregVec, ctrls, numCtrls, targ1, inv, matr), Contains("Invalid target") );
1590 
1591  ctrls[numCtrls-1] = inv; // make ctrls invalid
1592  REQUIRE_THROWS_WITH( multiControlledTwoQubitUnitary(quregVec, ctrls, numCtrls, targ1, targ2, matr), Contains("Invalid control") );
1593  }
1594  SECTION( "unitarity " ) {
1595 
1596  int ctrls[1] = {0};
1597  matr.real[0][0] = 0; // break unitarity
1598  REQUIRE_THROWS_WITH( multiControlledTwoQubitUnitary(quregVec, ctrls, 1, 1, 2, matr), Contains("unitary") );
1599  }
1600  SECTION( "unitary fits in node" ) {
1601 
1602  // pretend we have a very limited distributed memory
1603  quregVec.numAmpsPerChunk = 1;
1604  int ctrls[1] = {0};
1605  REQUIRE_THROWS_WITH( multiControlledTwoQubitUnitary(quregVec, ctrls, 1, 1, 2, matr), Contains("targets too many qubits"));
1606  }
1607  }
1608  CLEANUP_TEST( quregVec, quregMatr );
1609 }
1610 
1611 
1612 
1617 TEST_CASE( "multiControlledUnitary", "[unitaries]" ) {
1618 
1619  PREPARE_TEST( quregVec, quregMatr, refVec, refMatr );
1620 
1621  // every test will use a unique random matrix
1622  QMatrix op = getRandomUnitary(1);
1623  ComplexMatrix2 matr = toComplexMatrix2(op);
1624 
1625  SECTION( "correctness" ) {
1626 
1627  int target = GENERATE( range(0,NUM_QUBITS) );
1628  int numCtrls = GENERATE( range(1,NUM_QUBITS) ); // leave space for one target (exclusive upper bound)
1629  int* ctrls = GENERATE_COPY( sublists(range(0,NUM_QUBITS), numCtrls, target) );
1630 
1631  SECTION( "state-vector" ) {
1632 
1633  multiControlledUnitary(quregVec, ctrls, numCtrls, target, matr);
1634  applyReferenceOp(refVec, ctrls, numCtrls, target, op);
1635  REQUIRE( areEqual(quregVec, refVec) );
1636  }
1637  SECTION( "density-matrix" ) {
1638 
1639  multiControlledUnitary(quregMatr, ctrls, numCtrls, target, matr);
1640  applyReferenceOp(refMatr, ctrls, numCtrls, target, op);
1641  REQUIRE( areEqual(quregMatr, refMatr, 10*REAL_EPS) );
1642  }
1643  }
1644  SECTION( "input validation" ) {
1645 
1646  SECTION( "number of controls" ) {
1647 
1648  int numCtrls = GENERATE( -1, 0, NUM_QUBITS, NUM_QUBITS+1 );
1649  int ctrls[NUM_QUBITS+1]; // avoids seg-fault if validation not triggered
1650  REQUIRE_THROWS_WITH( multiControlledUnitary(quregVec, ctrls, numCtrls, 0, matr), Contains("Invalid number of control"));
1651  }
1652  SECTION( "repetition of controls" ) {
1653 
1654  int ctrls[] = {0,1,1};
1655  REQUIRE_THROWS_WITH( multiControlledUnitary(quregVec, ctrls, 3, 2, matr), Contains("control") && Contains("unique"));
1656  }
1657  SECTION( "control and target collision" ) {
1658 
1659  int ctrls[] = {0,1,2};
1660  int targ = ctrls[GENERATE( range(0,3) )];
1661  REQUIRE_THROWS_WITH( multiControlledUnitary(quregVec, ctrls, 3, targ, matr), Contains("Control") && Contains("target") );
1662  }
1663  SECTION( "qubit indices" ) {
1664 
1665  int ctrls[] = { 1, 2, GENERATE( -1, NUM_QUBITS ) };
1666  REQUIRE_THROWS_WITH( multiControlledUnitary(quregVec, ctrls, 3, 0, matr), Contains("Invalid control") );
1667 
1668  ctrls[2] = 3; // make ctrls valid
1669  int targ = GENERATE( -1, NUM_QUBITS );
1670  REQUIRE_THROWS_WITH( multiControlledUnitary(quregVec, ctrls, 3, targ, matr), Contains("Invalid target") );
1671  }
1672  SECTION( "unitarity" ) {
1673 
1674  matr.real[0][0] = 0; // break matr unitarity
1675  int ctrls[] = {0};
1676  REQUIRE_THROWS_WITH( multiControlledUnitary(quregVec, ctrls, 1, 1, matr), Contains("unitary") );
1677  }
1678  }
1679  CLEANUP_TEST( quregVec, quregMatr );
1680 }
1681 
1682 
1683 
1688 TEST_CASE( "multiQubitNot", "[unitaries]" ) {
1689 
1690  PREPARE_TEST( quregVec, quregMatr, refVec, refMatr );
1691 
1692  SECTION( "correctness" ) {
1693 
1694  // try all possible numbers of targets and controls
1695  int numTargs = GENERATE_COPY( range(1,NUM_QUBITS+1) ); // leave space for 1 ctrl
1696 
1697  // generate all possible valid qubit arrangements
1698  int* targs = GENERATE_COPY( sublists(range(0,NUM_QUBITS), numTargs) );
1699 
1700  // for each qubit arrangement, use a new random unitary
1701  QMatrix notOp{{0, 1},{1,0}};
1702 
1703  SECTION( "state-vector" ) {
1704 
1705  multiQubitNot(quregVec, targs, numTargs);
1706  for (int t=0; t<numTargs; t++)
1707  applyReferenceOp(refVec, targs[t], notOp);
1708 
1709  REQUIRE( areEqual(quregVec, refVec) );
1710  }
1711  SECTION( "density-matrix" ) {
1712 
1713  multiQubitNot(quregMatr, targs, numTargs);
1714  for (int t=0; t<numTargs; t++)
1715  applyReferenceOp(refMatr, targs[t], notOp);
1716 
1717  REQUIRE( areEqual(quregMatr, refMatr) );
1718  }
1719  }
1720  SECTION( "input validation" ) {
1721 
1722  SECTION( "number of targets" ) {
1723 
1724  // there cannot be more targets than qubits in register
1725  int numTargs = GENERATE( -1, 0, NUM_QUBITS+1 );
1726  int targs[NUM_QUBITS+1];
1727  REQUIRE_THROWS_WITH( multiQubitNot(quregVec, targs, numTargs), Contains("Invalid number of target"));
1728  }
1729  SECTION( "repetition in targets" ) {
1730 
1731  int numTargs = 3;
1732  int targs[] = {1,2,2};
1733  REQUIRE_THROWS_WITH( multiQubitNot(quregVec, targs, numTargs), Contains("target") && Contains("unique"));
1734  }
1735  SECTION( "target indices" ) {
1736 
1737  // valid inds
1738  int numQb = 5;
1739  int qubits[] = {0,1,2,3,4};
1740 
1741  // make one index invalid
1742  qubits[GENERATE_COPY(range(0,numQb))] = GENERATE( -1, NUM_QUBITS );
1743  REQUIRE_THROWS_WITH( multiQubitNot(quregVec, qubits, numQb), Contains("Invalid target") );
1744  }
1745  }
1746  CLEANUP_TEST( quregVec, quregMatr );
1747 }
1748 
1749 
1750 
1755 TEST_CASE( "multiQubitUnitary", "[unitaries]" ) {
1756 
1757  PREPARE_TEST( quregVec, quregMatr, refVec, refMatr );
1758 
1759  // figure out max-num (inclusive) targs allowed by hardware backend
1760  int maxNumTargs = calcLog2(quregVec.numAmpsPerChunk);
1761 
1762  SECTION( "correctness" ) {
1763 
1764  // generate all possible qubit arrangements
1765  int numTargs = GENERATE_COPY( range(1,maxNumTargs+1) ); // inclusive upper bound
1766  int* targs = GENERATE_COPY( sublists(range(0,NUM_QUBITS), numTargs) );
1767 
1768  // for each qubit arrangement, use a new random unitary
1769  QMatrix op = getRandomUnitary(numTargs);
1770  ComplexMatrixN matr = createComplexMatrixN(numTargs);
1771  toComplexMatrixN(op, matr);
1772 
1773  SECTION( "state-vector" ) {
1774 
1775  multiQubitUnitary(quregVec, targs, numTargs, matr);
1776  applyReferenceOp(refVec, targs, numTargs, op);
1777  REQUIRE( areEqual(quregVec, refVec) );
1778  }
1779  SECTION( "density-matrix" ) {
1780 
1781  multiQubitUnitary(quregMatr, targs, numTargs, matr);
1782  applyReferenceOp(refMatr, targs, numTargs, op);
1783  REQUIRE( areEqual(quregMatr, refMatr, 10*REAL_EPS) );
1784  }
1785  destroyComplexMatrixN(matr);
1786  }
1787  SECTION( "input validation" ) {
1788 
1789  SECTION( "number of targets" ) {
1790 
1791  // there cannot be more targets than qubits in register
1792  int numTargs = GENERATE( -1, 0, NUM_QUBITS+1 );
1793  int targs[NUM_QUBITS+1]; // prevents seg-fault if validation doesn't trigger
1794  ComplexMatrixN matr = createComplexMatrixN(NUM_QUBITS+1); // prevent seg-fault
1795 
1796  REQUIRE_THROWS_WITH( multiQubitUnitary(quregVec, targs, numTargs, matr), Contains("Invalid number of target"));
1797  destroyComplexMatrixN(matr);
1798  }
1799  SECTION( "repetition in targets" ) {
1800 
1801  int numTargs = 3;
1802  int targs[] = {1,2,2};
1803  ComplexMatrixN matr = createComplexMatrixN(numTargs); // prevents seg-fault if validation doesn't trigger
1804 
1805  REQUIRE_THROWS_WITH( multiQubitUnitary(quregVec, targs, numTargs, matr), Contains("target") && Contains("unique"));
1806  destroyComplexMatrixN(matr);
1807  }
1808  SECTION( "qubit indices" ) {
1809 
1810  int numTargs = 3;
1811  int targs[] = {1,2,3};
1812  ComplexMatrixN matr = createComplexMatrixN(numTargs); // prevents seg-fault if validation doesn't trigger
1813 
1814  int inv = GENERATE( -1, NUM_QUBITS );
1815  targs[GENERATE_COPY( range(0,numTargs) )] = inv; // make invalid target
1816  REQUIRE_THROWS_WITH( multiQubitUnitary(quregVec, targs, numTargs, matr), Contains("Invalid target") );
1817 
1818  destroyComplexMatrixN(matr);
1819  }
1820  SECTION( "unitarity" ) {
1821 
1822  int numTargs = GENERATE_COPY( range(1,maxNumTargs) );
1823  int targs[numTargs];
1824  for (int i=0; i<numTargs; i++)
1825  targs[i] = i+1;
1826 
1827  ComplexMatrixN matr = createComplexMatrixN(numTargs); // initially zero, hence not-unitary
1828 
1829  REQUIRE_THROWS_WITH( multiQubitUnitary(quregVec, targs, numTargs, matr), Contains("unitary") );
1830  destroyComplexMatrixN(matr);
1831  }
1832  SECTION( "unitary creation" ) {
1833 
1834  int numTargs = 3;
1835  int targs[] = {1,2,3};
1836 
1837  /* compilers don't auto-initialise to NULL; the below circumstance
1838  * only really occurs when 'malloc' returns NULL in createComplexMatrixN,
1839  * which actually triggers its own validation. Hence this test is useless
1840  * currently.
1841  */
1842  ComplexMatrixN matr;
1843  matr.real = NULL;
1844  matr.imag = NULL;
1845  REQUIRE_THROWS_WITH( multiQubitUnitary(quregVec, targs, numTargs, matr), Contains("created") );
1846  }
1847  SECTION( "unitary dimensions" ) {
1848 
1849  int targs[2] = {1,2};
1850  ComplexMatrixN matr = createComplexMatrixN(3); // intentionally wrong size
1851 
1852  REQUIRE_THROWS_WITH( multiQubitUnitary(quregVec, targs, 2, matr), Contains("matrix size"));
1853  destroyComplexMatrixN(matr);
1854  }
1855  SECTION( "unitary fits in node" ) {
1856 
1857  // pretend we have a very limited distributed memory (judged by matr size)
1858  quregVec.numAmpsPerChunk = 1;
1859  int qb[] = {1,2};
1860  ComplexMatrixN matr = createComplexMatrixN(2); // prevents seg-fault if validation doesn't trigger
1861  REQUIRE_THROWS_WITH( multiQubitUnitary(quregVec, qb, 2, matr), Contains("targets too many qubits"));
1862  destroyComplexMatrixN(matr);
1863  }
1864  }
1865  CLEANUP_TEST( quregVec, quregMatr );
1866 }
1867 
1868 
1869 
1874 TEST_CASE( "multiRotatePauli", "[unitaries]" ) {
1875 
1876  PREPARE_TEST( quregVec, quregMatr, refVec, refMatr );
1877  qreal param = getRandomReal(-4*M_PI, 4*M_PI);
1878 
1879  SECTION( "correctness" ) {
1880 
1881  int numTargs = GENERATE( range(1,NUM_QUBITS+1) );
1882  int* targs = GENERATE_COPY( sublists(range(0,NUM_QUBITS), numTargs) );
1883 
1884  /* it's too expensive to try ALL Pauli sequences, via
1885  * pauliOpType* paulis = GENERATE_COPY( pauliseqs(numTargs) );.
1886  * Furthermore, take(10, pauliseqs(numTargs)) will try the same pauli codes.
1887  * Hence, we instead opt to repeatedlyrandomly generate pauliseqs
1888  */
1889  GENERATE( range(0,10) ); // gen 10 random pauli-codes for every targs
1890  pauliOpType paulis[numTargs];
1891  for (int i=0; i<numTargs; i++)
1892  paulis[i] = (pauliOpType) getRandomInt(0,4);
1893 
1894  // exclude identities from reference matrix exp (they apply unwanted global phase)
1895  int refTargs[numTargs];
1896  int numRefTargs = 0;
1897 
1898  QMatrix xMatr{{0,1},{1,0}};
1899  QMatrix yMatr{{0,-qcomp(0,1)},{qcomp(0,1),0}};
1900  QMatrix zMatr{{1,0},{0,-1}};
1901 
1902  // build correct reference matrix by pauli-matrix exponentiation...
1903  QMatrix pauliProd{{1}};
1904  for (int i=0; i<numTargs; i++) {
1905  QMatrix fac;
1906  if (paulis[i] == PAULI_I) continue; // exclude I-targets from ref list
1907  if (paulis[i] == PAULI_X) fac = xMatr;
1908  if (paulis[i] == PAULI_Y) fac = yMatr;
1909  if (paulis[i] == PAULI_Z) fac = zMatr;
1910  pauliProd = getKroneckerProduct(fac, pauliProd);
1911 
1912  // include this target in ref list
1913  refTargs[numRefTargs++] = targs[i];
1914  }
1915 
1916  // produces exp(-i param/2 pauliProd), unless pauliProd = I
1917  QMatrix op;
1918  if (numRefTargs > 0)
1919  op = getExponentialOfPauliMatrix(param, pauliProd);
1920 
1921  SECTION( "state-vector" ) {
1922 
1923  multiRotatePauli(quregVec, targs, paulis, numTargs, param);
1924  if (numRefTargs > 0)
1925  applyReferenceOp(refVec, refTargs, numRefTargs, op);
1926  REQUIRE( areEqual(quregVec, refVec) );
1927  }
1928  SECTION( "density-matrix" ) {
1929 
1930  multiRotatePauli(quregMatr, targs, paulis, numTargs, param);
1931  if (numRefTargs > 0)
1932  applyReferenceOp(refMatr, refTargs, numRefTargs, op);
1933  REQUIRE( areEqual(quregMatr, refMatr, 10*REAL_EPS) );
1934  }
1935  }
1936  SECTION( "input validation" ) {
1937 
1938  SECTION( "number of targets" ) {
1939 
1940  int numTargs = GENERATE( -1, 0, NUM_QUBITS+1 );
1941  int targs[NUM_QUBITS+1]; // prevent seg-fault if validation isn't triggered
1942  pauliOpType paulis[NUM_QUBITS+1] = {PAULI_I};
1943  REQUIRE_THROWS_WITH( multiRotatePauli(quregVec, targs, paulis, numTargs, param), Contains("Invalid number of target"));
1944  }
1945  SECTION( "repetition of targets" ) {
1946 
1947  int numTargs = 3;
1948  int targs[3] = {0, 1, 1};
1949  pauliOpType paulis[3] = {PAULI_I};
1950  REQUIRE_THROWS_WITH( multiRotatePauli(quregVec, targs, paulis, numTargs, param), Contains("target") && Contains("unique"));
1951  }
1952  SECTION( "qubit indices" ) {
1953 
1954  int numTargs = 3;
1955  int targs[3] = {0, 1, 2};
1956  targs[GENERATE_COPY(range(0,numTargs))] = GENERATE( -1, NUM_QUBITS );
1957  pauliOpType paulis[3] = {PAULI_I};
1958  REQUIRE_THROWS_WITH( multiRotatePauli(quregVec, targs, paulis, numTargs, param), Contains("Invalid target"));
1959  }
1960  SECTION( "pauli codes" ) {
1961  int numTargs = 3;
1962  int targs[3] = {0, 1, 2};
1963  pauliOpType paulis[3] = {PAULI_I, PAULI_I, PAULI_I};
1964  paulis[GENERATE_COPY(range(0,numTargs))] = (pauliOpType) GENERATE( -1, 4 );
1965  REQUIRE_THROWS_WITH( multiRotatePauli(quregVec, targs, paulis, numTargs, param), Contains("Invalid Pauli code"));
1966  }
1967  }
1968  CLEANUP_TEST( quregVec, quregMatr );
1969 }
1970 
1971 
1972 
1977 TEST_CASE( "multiRotateZ", "[unitaries]" ) {
1978 
1979  PREPARE_TEST( quregVec, quregMatr, refVec, refMatr );
1980  qreal param = getRandomReal(-4*M_PI, 4*M_PI);
1981 
1982  SECTION( "correctness" ) {
1983 
1984  int numTargs = GENERATE( range(1,NUM_QUBITS+1) );
1985  int* targs = GENERATE_COPY( sublists(range(0,NUM_QUBITS), numTargs) );
1986 
1987  // build correct reference matrix by diagonal-matrix exponentiation...
1988  QMatrix zMatr{{1,0},{0,-1}};
1989  QMatrix zProd = zMatr;
1990  for (int t=0; t<numTargs-1; t++)
1991  zProd = getKroneckerProduct(zMatr, zProd); // Z . Z ... Z
1992 
1993  // (-i param/2) Z . I . Z ...
1994  QMatrix expArg = qcomp(0, -param/2) *
1995  getFullOperatorMatrix(NULL, 0, targs, numTargs, zProd, NUM_QUBITS);
1996 
1997  // exp( -i param/2 Z . I . Z ...)
1999 
2000  // all qubits to specify full operator matrix on reference structures
2001  int allQubits[NUM_QUBITS];
2002  for (int i=0; i<NUM_QUBITS; i++)
2003  allQubits[i] = i;
2004 
2005  SECTION( "state-vector" ) {
2006 
2007  multiRotateZ(quregVec, targs, numTargs, param);
2008  applyReferenceOp(refVec, allQubits, NUM_QUBITS, op);
2009  REQUIRE( areEqual(quregVec, refVec) );
2010  }
2011  SECTION( "density-matrix" ) {
2012 
2013  multiRotateZ(quregMatr, targs, numTargs, param);
2014  applyReferenceOp(refMatr, allQubits, NUM_QUBITS, op);
2015  REQUIRE( areEqual(quregMatr, refMatr, 2*REAL_EPS) );
2016  }
2017  }
2018  SECTION( "input validation" ) {
2019 
2020  SECTION( "number of targets" ) {
2021 
2022  int numTargs = GENERATE( -1, 0, NUM_QUBITS+1 );
2023  int targs[NUM_QUBITS+1]; // prevent seg-fault if validation isn't triggered
2024  REQUIRE_THROWS_WITH( multiRotateZ(quregVec, targs, numTargs, param), Contains("Invalid number of target"));
2025 
2026  }
2027  SECTION( "repetition of targets" ) {
2028 
2029  int numTargs = 3;
2030  int targs[3] = {0, 1, 1};
2031  REQUIRE_THROWS_WITH( multiRotateZ(quregVec, targs, numTargs, param), Contains("target") && Contains("unique"));
2032  }
2033  SECTION( "qubit indices" ) {
2034 
2035  int numTargs = 3;
2036  int targs[3] = {0, 1, 2};
2037  targs[GENERATE_COPY(range(0,numTargs))] = GENERATE( -1, NUM_QUBITS );
2038  REQUIRE_THROWS_WITH( multiRotateZ(quregVec, targs, numTargs, param), Contains("Invalid target"));
2039  }
2040  }
2041  CLEANUP_TEST( quregVec, quregMatr );
2042 }
2043 
2044 
2045 
2050 TEST_CASE( "multiStateControlledUnitary", "[unitaries]" ) {
2051 
2052  PREPARE_TEST( quregVec, quregMatr, refVec, refMatr );
2053 
2054  // every test will use a unique random matrix
2055  QMatrix op = getRandomUnitary(1);
2056  ComplexMatrix2 matr = toComplexMatrix2(op);
2057 
2058  // the zero-conditioned control qubits can be effected by notting before/after ctrls
2059  QMatrix notOp{{0,1},{1,0}};
2060 
2061  SECTION( "correctness" ) {
2062 
2063  int target = GENERATE( range(0,NUM_QUBITS) );
2064  int numCtrls = GENERATE( range(1,NUM_QUBITS) ); // leave space for one target (exclusive upper bound)
2065  int* ctrls = GENERATE_COPY( sublists(range(0,NUM_QUBITS), numCtrls, target) );
2066  int* ctrlState = GENERATE_COPY( bitsets(numCtrls) );
2067 
2068  SECTION( "state-vector" ) {
2069 
2070  multiStateControlledUnitary(quregVec, ctrls, ctrlState, numCtrls, target, matr);
2071 
2072  // simulate controlled-state by notting before & after controls
2073  for (int i=0; i<numCtrls; i++)
2074  if (ctrlState[i] == 0)
2075  applyReferenceOp(refVec, ctrls[i], notOp);
2076  applyReferenceOp(refVec, ctrls, numCtrls, target, op);
2077  for (int i=0; i<numCtrls; i++)
2078  if (ctrlState[i] == 0)
2079  applyReferenceOp(refVec, ctrls[i], notOp);
2080 
2081  REQUIRE( areEqual(quregVec, refVec) );
2082  }
2083  SECTION( "density-matrix" ) {
2084 
2085  multiStateControlledUnitary(quregMatr, ctrls, ctrlState, numCtrls, target, matr);
2086 
2087  // simulate controlled-state by notting before & after controls
2088  for (int i=0; i<numCtrls; i++)
2089  if (ctrlState[i] == 0)
2090  applyReferenceOp(refMatr, ctrls[i], notOp);
2091  applyReferenceOp(refMatr, ctrls, numCtrls, target, op);
2092  for (int i=0; i<numCtrls; i++)
2093  if (ctrlState[i] == 0)
2094  applyReferenceOp(refMatr, ctrls[i], notOp);
2095 
2096  REQUIRE( areEqual(quregMatr, refMatr, 10*REAL_EPS) );
2097  }
2098  }
2099  SECTION( "input validation" ) {
2100 
2101  SECTION( "number of controls" ) {
2102 
2103  int numCtrls = GENERATE( -1, 0, NUM_QUBITS, NUM_QUBITS+1 );
2104  int ctrls[NUM_QUBITS+1];
2105  int ctrlState[NUM_QUBITS+1] = {0};
2106  REQUIRE_THROWS_WITH( multiStateControlledUnitary(quregVec, ctrls, ctrlState, numCtrls, 0, matr), Contains("Invalid number of control"));
2107  }
2108  SECTION( "repetition of controls" ) {
2109 
2110  int ctrls[] = {0,1,1};
2111  int ctrlState[] = {0, 1, 0};
2112  REQUIRE_THROWS_WITH( multiStateControlledUnitary(quregVec, ctrls, ctrlState, 3, 2, matr), Contains("control") && Contains("unique"));
2113  }
2114  SECTION( "control and target collision" ) {
2115 
2116  int ctrls[] = {0,1,2};
2117  int ctrlState[] = {0, 1, 0};
2118  int targ = ctrls[GENERATE( range(0,3) )];
2119  REQUIRE_THROWS_WITH( multiStateControlledUnitary(quregVec, ctrls, ctrlState, 3, targ, matr), Contains("Control") && Contains("target") );
2120  }
2121  SECTION( "qubit indices" ) {
2122 
2123  int ctrls[] = { 1, 2, GENERATE( -1, NUM_QUBITS ) };
2124  int ctrlState[] = {0, 1, 0};
2125  REQUIRE_THROWS_WITH( multiStateControlledUnitary(quregVec, ctrls, ctrlState, 3, 0, matr), Contains("Invalid control") );
2126 
2127  ctrls[2] = 3; // make ctrls valid
2128  int targ = GENERATE( -1, NUM_QUBITS );
2129  REQUIRE_THROWS_WITH( multiStateControlledUnitary(quregVec, ctrls, ctrlState, 3, targ, matr), Contains("Invalid target") );
2130  }
2131  SECTION( "unitarity" ) {
2132 
2133  matr.real[0][0] = 0; // break matr unitarity
2134  int ctrls[] = {0};
2135  int ctrlState[1] = {0};
2136  REQUIRE_THROWS_WITH( multiStateControlledUnitary(quregVec, ctrls, ctrlState, 1, 1, matr), Contains("unitary") );
2137  }
2138  SECTION( "control state bits" ) {
2139 
2140  // valid qubits
2141  int ctrls[] = {0, 1, 2};
2142  int ctrlState[] = {0, 0, 0};
2143  int targ = 3;
2144 
2145  // make invalid
2146  ctrlState[2] = GENERATE(-1, 2);
2147  REQUIRE_THROWS_WITH( multiStateControlledUnitary(quregVec, ctrls, ctrlState, 3, targ, matr), Contains("state") );
2148  }
2149  }
2150  CLEANUP_TEST( quregVec, quregMatr );
2151 }
2152 
2153 
2154 
2159 TEST_CASE( "pauliX", "[unitaries]" ) {
2160 
2161  PREPARE_TEST( quregVec, quregMatr, refVec, refMatr );
2162  QMatrix op{{0,1},{1,0}};
2163 
2164  SECTION( "correctness" ) {
2165 
2166  int target = GENERATE( range(0,NUM_QUBITS) );
2167 
2168  SECTION( "state-vector" ) {
2169 
2170  pauliX(quregVec, target);
2171  applyReferenceOp(refVec, target, op);
2172  REQUIRE( areEqual(quregVec, refVec) );
2173  }
2174  SECTION( "density-matrix correctness" ) {
2175 
2176  pauliX(quregMatr, target);
2177  applyReferenceOp(refMatr, target, op);
2178  REQUIRE( areEqual(quregMatr, refMatr) );
2179  }
2180  }
2181  SECTION( "input validation" ) {
2182 
2183  SECTION( "qubit indices" ) {
2184 
2185  int target = GENERATE( -1, NUM_QUBITS );
2186  REQUIRE_THROWS_WITH( pauliX(quregVec, target), Contains("Invalid target") );
2187  }
2188  }
2189  CLEANUP_TEST( quregVec, quregMatr );
2190 }
2191 
2192 
2193 
2198 TEST_CASE( "pauliY", "[unitaries]" ) {
2199 
2200  PREPARE_TEST( quregVec, quregMatr, refVec, refMatr );
2201  QMatrix op{{0,-qcomp(0,1)},{qcomp(0,1),0}};
2202 
2203  SECTION( "correctness" ) {
2204 
2205  int target = GENERATE( range(0,NUM_QUBITS) );
2206 
2207  SECTION( "state-vector" ) {
2208 
2209  pauliY(quregVec, target);
2210  applyReferenceOp(refVec, target, op);
2211  REQUIRE( areEqual(quregVec, refVec) );
2212  }
2213  SECTION( "density-matrix correctness" ) {
2214 
2215  pauliY(quregMatr, target);
2216  applyReferenceOp(refMatr, target, op);
2217  REQUIRE( areEqual(quregMatr, refMatr) );
2218  }
2219  }
2220  SECTION( "input validation" ) {
2221 
2222  SECTION( "qubit indices" ) {
2223 
2224  int target = GENERATE( -1, NUM_QUBITS );
2225  REQUIRE_THROWS_WITH( pauliY(quregVec, target), Contains("Invalid target") );
2226  }
2227  }
2228  CLEANUP_TEST( quregVec, quregMatr );
2229 }
2230 
2231 
2232 
2237 TEST_CASE( "pauliZ", "[unitaries]" ) {
2238 
2239  PREPARE_TEST( quregVec, quregMatr, refVec, refMatr );
2240  QMatrix op{{1,0},{0,-1}};
2241 
2242  SECTION( "correctness" ) {
2243 
2244  int target = GENERATE( range(0,NUM_QUBITS) );
2245 
2246  SECTION( "state-vector" ) {
2247 
2248  pauliZ(quregVec, target);
2249  applyReferenceOp(refVec, target, op);
2250  REQUIRE( areEqual(quregVec, refVec) );
2251  }
2252  SECTION( "density-matrix correctness" ) {
2253 
2254  pauliZ(quregMatr, target);
2255  applyReferenceOp(refMatr, target, op);
2256  REQUIRE( areEqual(quregMatr, refMatr) );
2257  }
2258  }
2259  SECTION( "input validation" ) {
2260 
2261  SECTION( "qubit indices" ) {
2262 
2263  int target = GENERATE( -1, NUM_QUBITS );
2264  REQUIRE_THROWS_WITH( pauliZ(quregVec, target), Contains("Invalid target") );
2265  }
2266  }
2267  CLEANUP_TEST( quregVec, quregMatr );
2268 }
2269 
2270 
2271 
2276 TEST_CASE( "phaseShift", "[unitaries]" ) {
2277 
2278  PREPARE_TEST( quregVec, quregMatr, refVec, refMatr );
2279  qreal param = getRandomReal(-2*M_PI, 2*M_PI);
2280  QMatrix op{{1,0},{0,expI(param)}};
2281 
2282  SECTION( "correctness" ) {
2283 
2284  int target = GENERATE( range(0,NUM_QUBITS) );
2285 
2286  SECTION( "state-vector ") {
2287 
2288  phaseShift(quregVec, target, param);
2289  applyReferenceOp(refVec, target, op);
2290  REQUIRE( areEqual(quregVec, refVec) );
2291  }
2292  SECTION( "density-matrix" ) {
2293 
2294  phaseShift(quregMatr, target, param);
2295  applyReferenceOp(refMatr, target, op);
2296  REQUIRE( areEqual(quregMatr, refMatr) );
2297  }
2298  }
2299  SECTION( "input validation" ) {
2300 
2301  SECTION( "qubit indices" ) {
2302 
2303  int target = GENERATE( -1, NUM_QUBITS );
2304  REQUIRE_THROWS_WITH( phaseShift(quregVec, target, param), Contains("Invalid target") );
2305  }
2306  }
2307  CLEANUP_TEST( quregVec, quregMatr );
2308 }
2309 
2310 
2311 
2316 TEST_CASE( "rotateAroundAxis", "[unitaries]" ) {
2317 
2318  PREPARE_TEST( quregVec, quregMatr, refVec, refMatr );
2319 
2320  // each test will use a random parameter and axis vector
2321  qreal param = getRandomReal(-4*M_PI, 4*M_PI);
2322  Vector vec = {.x=getRandomReal(-1,1), .y=getRandomReal(-1,1), .z=getRandomReal(-1,1)};
2323 
2324  // Rn(a) = cos(a/2)I - i sin(a/2) n . paulivector
2325  // (pg 24 of vcpc.univie.ac.at/~ian/hotlist/qc/talks/bloch-sphere-rotations.pdf)
2326  qreal c = cos(param/2);
2327  qreal s = sin(param/2);
2328  qreal m = sqrt(vec.x*vec.x + vec.y*vec.y + vec.z*vec.z);
2329  QMatrix op{{c - qcomp(0,1)*vec.z*s/m, -(vec.y + qcomp(0,1)*vec.x)*s/m},
2330  {(vec.y - qcomp(0,1)*vec.x)*s/m, c + qcomp(0,1)*vec.z*s/m}};
2331 
2332  SECTION( "correctness" ) {
2333 
2334  int target = GENERATE( range(0,NUM_QUBITS) );
2335 
2336  SECTION( "state-vector ") {
2337 
2338  rotateAroundAxis(quregVec, target, param, vec);
2339  applyReferenceOp(refVec, target, op);
2340  REQUIRE( areEqual(quregVec, refVec) );
2341  }
2342  SECTION( "density-matrix" ) {
2343 
2344  rotateAroundAxis(quregMatr, target, param, vec);
2345  applyReferenceOp(refMatr, target, op);
2346  REQUIRE( areEqual(quregMatr, refMatr, 10*REAL_EPS) );
2347  }
2348  }
2349  SECTION( "input validation" ) {
2350 
2351  SECTION( "qubit indices" ) {
2352 
2353  int target = GENERATE( -1, NUM_QUBITS );
2354  REQUIRE_THROWS_WITH( rotateAroundAxis(quregVec, target, param, vec), Contains("Invalid target") );
2355  }
2356  SECTION( "zero rotation axis" ) {
2357 
2358  int target = 0;
2359  vec = {.x=0, .y=0, .z=0};
2360  REQUIRE_THROWS_WITH( rotateAroundAxis(quregVec, target, param, vec), Contains("Invalid axis") && Contains("zero") );
2361  }
2362  }
2363  CLEANUP_TEST( quregVec, quregMatr );
2364 }
2365 
2366 
2367 
2372 TEST_CASE( "rotateX", "[unitaries]" ) {
2373 
2374  PREPARE_TEST( quregVec, quregMatr, refVec, refMatr );
2375  qreal param = getRandomReal(-4*M_PI, 4*M_PI);
2376  QMatrix op{
2377  {cos(param/2), - sin(param/2)*qcomp(0,1)},
2378  {- sin(param/2)*qcomp(0,1), cos(param/2)}};
2379 
2380  SECTION( "correctness" ) {
2381 
2382  int target = GENERATE( range(0,NUM_QUBITS) );
2383 
2384  SECTION( "state-vector ") {
2385 
2386  rotateX(quregVec, target, param);
2387  applyReferenceOp(refVec, target, op);
2388  REQUIRE( areEqual(quregVec, refVec) );
2389  }
2390  SECTION( "density-matrix" ) {
2391 
2392  rotateX(quregMatr, target, param);
2393  applyReferenceOp(refMatr, target, op);
2394  REQUIRE( areEqual(quregMatr, refMatr) );
2395  }
2396  }
2397  SECTION( "input validation" ) {
2398 
2399  SECTION( "qubit indices" ) {
2400 
2401  int target = GENERATE( -1, NUM_QUBITS );
2402  REQUIRE_THROWS_WITH( rotateX(quregVec, target, param), Contains("Invalid target") );
2403  }
2404  }
2405  CLEANUP_TEST( quregVec, quregMatr );
2406 }
2407 
2408 
2409 
2414 TEST_CASE( "rotateY", "[unitaries]" ) {
2415 
2416  PREPARE_TEST( quregVec, quregMatr, refVec, refMatr );
2417  qreal param = getRandomReal(-4*M_PI, 4*M_PI);
2418  QMatrix op{{cos(param/2), -sin(param/2)},{sin(param/2), cos(param/2)}};
2419 
2420  SECTION( "correctness" ) {
2421 
2422  int target = GENERATE( range(0,NUM_QUBITS) );
2423 
2424  SECTION( "state-vector ") {
2425 
2426  rotateY(quregVec, target, param);
2427  applyReferenceOp(refVec, target, op);
2428  REQUIRE( areEqual(quregVec, refVec) );
2429  }
2430  SECTION( "density-matrix" ) {
2431 
2432  rotateY(quregMatr, target, param);
2433  applyReferenceOp(refMatr, target, op);
2434  REQUIRE( areEqual(quregMatr, refMatr, 2*REAL_EPS) );
2435  }
2436  }
2437  SECTION( "input validation" ) {
2438 
2439  SECTION( "qubit indices" ) {
2440 
2441  int target = GENERATE( -1, NUM_QUBITS );
2442  REQUIRE_THROWS_WITH( rotateY(quregVec, target, param), Contains("Invalid target") );
2443  }
2444  }
2445  CLEANUP_TEST( quregVec, quregMatr );
2446 }
2447 
2448 
2449 
2454 TEST_CASE( "rotateZ", "[unitaries]" ) {
2455 
2456  PREPARE_TEST( quregVec, quregMatr, refVec, refMatr );
2457  qreal param = getRandomReal(-4*M_PI, 4*M_PI);
2458  QMatrix op{{expI(-param/2.),0},{0,expI(param/2.)}};
2459 
2460  SECTION( "correctness" ) {
2461 
2462  int target = GENERATE( range(0,NUM_QUBITS) );
2463 
2464  SECTION( "state-vector ") {
2465 
2466  rotateZ(quregVec, target, param);
2467  applyReferenceOp(refVec, target, op);
2468  REQUIRE( areEqual(quregVec, refVec) );
2469  }
2470  SECTION( "density-matrix" ) {
2471 
2472  rotateZ(quregMatr, target, param);
2473  applyReferenceOp(refMatr, target, op);
2474  REQUIRE( areEqual(quregMatr, refMatr) );
2475  }
2476  }
2477  SECTION( "input validation" ) {
2478 
2479  SECTION( "qubit indices" ) {
2480 
2481  int target = GENERATE( -1, NUM_QUBITS );
2482  REQUIRE_THROWS_WITH( rotateZ(quregVec, target, param), Contains("Invalid target") );
2483  }
2484  }
2485  CLEANUP_TEST( quregVec, quregMatr );
2486 }
2487 
2488 
2489 
2494 TEST_CASE( "sGate", "[unitaries]" ) {
2495 
2496  PREPARE_TEST( quregVec, quregMatr, refVec, refMatr );
2497  QMatrix op{{1,0},{0,qcomp(0,1)}};
2498 
2499  SECTION( "correctness" ) {
2500 
2501  int target = GENERATE( range(0,NUM_QUBITS) );
2502 
2503  SECTION( "state-vector ") {
2504 
2505  sGate(quregVec, target);
2506  applyReferenceOp(refVec, target, op);
2507  REQUIRE( areEqual(quregVec, refVec) );
2508  }
2509  SECTION( "density-matrix" ) {
2510 
2511  sGate(quregMatr, target);
2512  applyReferenceOp(refMatr, target, op);
2513  REQUIRE( areEqual(quregMatr, refMatr) );
2514  }
2515  }
2516  SECTION( "input validation" ) {
2517 
2518  SECTION( "qubit indices" ) {
2519 
2520  int target = GENERATE( -1, NUM_QUBITS );
2521  REQUIRE_THROWS_WITH( sGate(quregVec, target), Contains("Invalid target") );
2522  }
2523  }
2524  CLEANUP_TEST( quregVec, quregMatr );
2525 }
2526 
2527 
2528 
2533 TEST_CASE( "sqrtSwapGate", "[unitaries]" ) {
2534 
2535  PREPARE_TEST( quregVec, quregMatr, refVec, refMatr );
2536  qcomp a = qcomp(.5, .5);
2537  qcomp b = qcomp(.5, -.5);
2538  QMatrix op{{1,0,0,0},{0,a,b,0},{0,b,a,0},{0,0,0,1}};
2539 
2540  SECTION( "correctness" ) {
2541 
2542  int targ1 = GENERATE( range(0,NUM_QUBITS) );
2543  int targ2 = GENERATE_COPY( filter([=](int t){ return t!=targ1; }, range(0,NUM_QUBITS)) );
2544  int targs[] = {targ1, targ2};
2545 
2546  SECTION( "state-vector" ) {
2547 
2548  sqrtSwapGate(quregVec, targ1, targ2);
2549  applyReferenceOp(refVec, targs, 2, op);
2550  REQUIRE( areEqual(quregVec, refVec) );
2551  }
2552  SECTION( "density-matrix" ) {
2553 
2554  sqrtSwapGate(quregMatr, targ1, targ2);
2555  applyReferenceOp(refMatr, targs, 2, op);
2556  REQUIRE( areEqual(quregMatr, refMatr) );
2557  }
2558  }
2559  SECTION( "input validation" ) {
2560 
2561  SECTION( "qubit indices" ) {
2562 
2563  int targ1 = GENERATE( -1, NUM_QUBITS );
2564  int targ2 = 0;
2565  REQUIRE_THROWS_WITH( sqrtSwapGate(quregVec, targ1, targ2), Contains("Invalid target") );
2566  REQUIRE_THROWS_WITH( sqrtSwapGate(quregVec, targ2, targ1), Contains("Invalid target") );
2567  }
2568  SECTION( "repetition of targets" ) {
2569 
2570  int qb = 0;
2571  REQUIRE_THROWS_WITH( sqrtSwapGate(quregVec, qb, qb), Contains("target") && Contains("unique") );
2572  }
2573  }
2574  CLEANUP_TEST( quregVec, quregMatr );
2575 }
2576 
2577 
2578 
2583 TEST_CASE( "swapGate", "[unitaries]" ) {
2584 
2585  PREPARE_TEST( quregVec, quregMatr, refVec, refMatr );
2586  QMatrix op{{1,0,0,0},{0,0,1,0},{0,1,0,0},{0,0,0,1}};
2587 
2588  SECTION( "correctness" ) {
2589 
2590  int targ1 = GENERATE( range(0,NUM_QUBITS) );
2591  int targ2 = GENERATE_COPY( filter([=](int t){ return t!=targ1; }, range(0,NUM_QUBITS)) );
2592  int targs[] = {targ1, targ2};
2593 
2594  SECTION( "state-vector" ) {
2595 
2596  swapGate(quregVec, targ1, targ2);
2597  applyReferenceOp(refVec, targs, 2, op);
2598  REQUIRE( areEqual(quregVec, refVec) );
2599  }
2600  SECTION( "density-matrix" ) {
2601 
2602  swapGate(quregMatr, targ1, targ2);
2603  applyReferenceOp(refMatr, targs, 2, op);
2604  REQUIRE( areEqual(quregMatr, refMatr) );
2605  }
2606  }
2607  SECTION( "input validation" ) {
2608 
2609  SECTION( "qubit indices" ) {
2610 
2611  int targ1 = GENERATE( -1, NUM_QUBITS );
2612  int targ2 = 0;
2613  REQUIRE_THROWS_WITH( swapGate(quregVec, targ1, targ2), Contains("Invalid target") );
2614  REQUIRE_THROWS_WITH( swapGate(quregVec, targ2, targ1), Contains("Invalid target") );
2615  }
2616  SECTION( "repetition of targets" ) {
2617 
2618  int qb = 0;
2619  REQUIRE_THROWS_WITH( swapGate(quregVec, qb, qb), Contains("target") && Contains("unique") );
2620  }
2621  }
2622  CLEANUP_TEST( quregVec, quregMatr );
2623 }
2624 
2625 
2626 
2631 TEST_CASE( "tGate", "[unitaries]" ) {
2632 
2633  PREPARE_TEST( quregVec, quregMatr, refVec, refMatr );
2634  QMatrix op{{1,0},{0,expI(M_PI/4.)}};
2635 
2636  SECTION( "correctness" ) {
2637 
2638  int target = GENERATE( range(0,NUM_QUBITS) );
2639 
2640  SECTION( "state-vector ") {
2641 
2642  tGate(quregVec, target);
2643  applyReferenceOp(refVec, target, op);
2644  REQUIRE( areEqual(quregVec, refVec) );
2645  }
2646  SECTION( "density-matrix" ) {
2647 
2648  tGate(quregMatr, target);
2649  applyReferenceOp(refMatr, target, op);
2650  REQUIRE( areEqual(quregMatr, refMatr) );
2651  }
2652  }
2653  SECTION( "input validation" ) {
2654 
2655  SECTION( "qubit indices" ) {
2656 
2657  int target = GENERATE( -1, NUM_QUBITS );
2658  REQUIRE_THROWS_WITH( tGate(quregVec, target), Contains("Invalid target") );
2659  }
2660  }
2661  CLEANUP_TEST( quregVec, quregMatr );
2662 }
2663 
2664 
2665 
2670 TEST_CASE( "twoQubitUnitary", "[unitaries]" ) {
2671 
2672  PREPARE_TEST( quregVec, quregMatr, refVec, refMatr );
2673 
2674  // in distributed mode, each node must be able to fit all amps modified by unitary
2675  REQUIRE( quregVec.numAmpsPerChunk >= 4 );
2676 
2677  // every test will use a unique random matrix
2678  QMatrix op = getRandomUnitary(2);
2679  ComplexMatrix4 matr = toComplexMatrix4(op);
2680 
2681  SECTION( "correctness" ) {
2682 
2683  int targ1 = GENERATE( range(0,NUM_QUBITS) );
2684  int targ2 = GENERATE_COPY( filter([=](int t){ return t!=targ1; }, range(0,NUM_QUBITS)) );
2685  int targs[] = {targ1, targ2};
2686 
2687  SECTION( "state-vector" ) {
2688 
2689  twoQubitUnitary(quregVec, targ1, targ2, matr);
2690  applyReferenceOp(refVec, targs, 2, op);
2691  REQUIRE( areEqual(quregVec, refVec) );
2692  }
2693  SECTION( "density-matrix" ) {
2694 
2695  twoQubitUnitary(quregMatr, targ1, targ2, matr);
2696  applyReferenceOp(refMatr, targs, 2, op);
2697  REQUIRE( areEqual(quregMatr, refMatr, 10*REAL_EPS) );
2698  }
2699  }
2700  SECTION( "input validation" ) {
2701 
2702  SECTION( "qubit indices" ) {
2703 
2704  int targ1 = GENERATE( -1, NUM_QUBITS );
2705  int targ2 = 0;
2706  REQUIRE_THROWS_WITH( twoQubitUnitary(quregVec, targ1, targ2, matr), Contains("Invalid target") );
2707  REQUIRE_THROWS_WITH( twoQubitUnitary(quregVec, targ2, targ1, matr), Contains("Invalid target") );
2708  }
2709  SECTION( "repetition of targets" ) {
2710 
2711  int qb = 0;
2712  REQUIRE_THROWS_WITH( twoQubitUnitary(quregVec, qb, qb, matr), Contains("target") && Contains("unique") );
2713  }
2714  SECTION( "unitarity" ) {
2715 
2716  matr.real[0][0] = 0; // break matr unitarity
2717  REQUIRE_THROWS_WITH( twoQubitUnitary(quregVec, 0, 1, matr), Contains("unitary") );
2718  }
2719  SECTION( "unitary fits in node" ) {
2720 
2721  // pretend we have a very limited distributed memory
2722  quregVec.numAmpsPerChunk = 1;
2723  REQUIRE_THROWS_WITH( twoQubitUnitary(quregVec, 0, 1, matr), Contains("targets too many qubits"));
2724  }
2725  }
2726  CLEANUP_TEST( quregVec, quregMatr );
2727 }
2728 
2729 
2730 
2735 TEST_CASE( "unitary", "[unitaries]" ) {
2736 
2737  PREPARE_TEST( quregVec, quregMatr, refVec, refMatr );
2738 
2739  // every test will use a unique random matrix
2740  QMatrix op = getRandomUnitary(1);
2741  ComplexMatrix2 matr = toComplexMatrix2(op);
2742 
2743  SECTION( "correctness" ) {
2744 
2745  int target = GENERATE( range(0,NUM_QUBITS) );
2746 
2747  SECTION( "state-vector" ) {
2748 
2749  unitary(quregVec, target, matr);
2750  applyReferenceOp(refVec, target, op);
2751  REQUIRE( areEqual(quregVec, refVec) );
2752  }
2753  SECTION( "density-matrix" ) {
2754 
2755  unitary(quregMatr, target, matr);
2756  applyReferenceOp(refMatr, target, op);
2757  REQUIRE( areEqual(quregMatr, refMatr, 10*REAL_EPS) );
2758  }
2759  }
2760  SECTION( "input validation" ) {
2761 
2762  SECTION( "qubit indices" ) {
2763 
2764  int target = GENERATE( -1, NUM_QUBITS );
2765  REQUIRE_THROWS_WITH( unitary(quregVec, target, matr), Contains("Invalid target") );
2766  }
2767  SECTION( "unitarity" ) {
2768 
2769  matr.real[0][0] = 0; // break matr unitarity
2770  REQUIRE_THROWS_WITH( unitary(quregVec, 0, matr), Contains("unitary") );
2771  }
2772  }
2773  CLEANUP_TEST( quregVec, quregMatr );
2774 }
void controlledRotateZ(Qureg qureg, int controlQubit, int targetQubit, qreal angle)
Applies a controlled rotation by a given angle around the Z-axis of the Bloch-sphere.
Definition: QuEST.c:244
Represents a 3-vector of real numbers.
Definition: QuEST.h:198
pauliOpType
Codes for specifying Pauli operators.
Definition: QuEST.h:96
QMatrix getFullOperatorMatrix(int *ctrls, int numCtrls, int *targs, int numTargs, QMatrix op, int numQubits)
Takes a 2^numTargs-by-2^numTargs matrix op and a returns a 2^numQubits-by-2^numQubits matrix where op...
Definition: utilities.cpp:304
void twoQubitUnitary(Qureg qureg, int targetQubit1, int targetQubit2, ComplexMatrix4 u)
Apply a general two-qubit unitary (including a global phase factor).
Definition: QuEST.c:256
void controlledRotateX(Qureg qureg, int controlQubit, int targetQubit, qreal angle)
Applies a controlled rotation by a given angle around the X-axis of the Bloch-sphere.
Definition: QuEST.c:220
qreal real[4][4]
Definition: QuEST.h:177
#define CLEANUP_TEST(quregVec, quregMatr)
Destroys the data structures made by PREPARE_TEST.
@ PAULI_Z
Definition: QuEST.h:96
void rotateAroundAxis(Qureg qureg, int rotQubit, qreal angle, Vector axis)
Rotate a single qubit by a given angle around a given Vector on the Bloch-sphere.
Definition: QuEST.c:601
QVector getKroneckerProduct(QVector b, QVector a)
Returns b (otimes) a.
Definition: utilities.cpp:143
void destroyComplexMatrixN(ComplexMatrixN m)
Destroy a ComplexMatrixN instance created with createComplexMatrixN()
Definition: QuEST.c:1369
@ PAULI_I
Definition: QuEST.h:96
void multiControlledMultiQubitNot(Qureg qureg, int *ctrls, int numCtrls, int *targs, int numTargs)
Apply a NOT (or Pauli X) gate with multiple control and target qubits.
Definition: QuEST.c:549
int getRandomInt(int min, int max)
Returns a random integer between min (inclusive) and max (exclusive), from the uniform distribution.
Definition: utilities.cpp:526
TEST_CASE("compactUnitary", "[unitaries]")
ComplexMatrixN createComplexMatrixN(int numQubits)
Allocate dynamic memory for a square complex matrix of any size, which can be passed to functions lik...
Definition: QuEST.c:1348
void controlledRotateAroundAxis(Qureg qureg, int controlQubit, int targetQubit, qreal angle, Vector axis)
Applies a controlled rotation by a given angle around a given vector on the Bloch-sphere.
Definition: QuEST.c:614
qreal z
Definition: QuEST.h:200
void multiControlledTwoQubitUnitary(Qureg qureg, int *controlQubits, int numControlQubits, int targetQubit1, int targetQubit2, ComplexMatrix4 u)
Apply a general multi-controlled two-qubit unitary (including a global phase factor).
Definition: QuEST.c:282
#define NUM_QUBITS
The default number of qubits in the registers created for unit testing (both statevectors and density...
Definition: utilities.hpp:36
QMatrix getRandomUnitary(int numQb)
Returns a uniformly random (under Haar) 2^numQb-by-2^numQb unitary matrix.
Definition: utilities.cpp:530
qreal getRandomReal(qreal min, qreal max)
Returns a random real between min (inclusive) and max (exclusive), from the uniform distribution.
Definition: utilities.cpp:421
void unitary(Qureg qureg, int targetQubit, ComplexMatrix2 u)
Apply a general single-qubit unitary (including a global phase factor).
Definition: QuEST.c:348
void sGate(Qureg qureg, int targetQubit)
Apply the single-qubit S gate.
Definition: QuEST.c:465
Represents a 4x4 matrix of complex numbers.
Definition: QuEST.h:175
void rotateY(Qureg qureg, int targetQubit, qreal angle)
Rotate a single qubit by a given angle around the Y-axis of the Bloch-sphere.
Definition: QuEST.c:198
void multiControlledPhaseShift(Qureg qureg, int *controlQubits, int numControlQubits, qreal angle)
Introduce a phase factor on state of the passed qubits.
Definition: QuEST.c:510
Represents a general 2^N by 2^N matrix of complex numbers.
Definition: QuEST.h:186
#define qreal
void multiRotatePauli(Qureg qureg, int *targetQubits, enum pauliOpType *targetPaulis, int numTargets, qreal angle)
Apply a multi-qubit multi-Pauli rotation, also known as a Pauli gadget, on a selected number of qubit...
Definition: QuEST.c:685
QMatrix toQMatrix(ComplexMatrix2 src)
Returns a copy of the given 2-by-2 matrix.
Definition: utilities.cpp:1044
unsigned int calcLog2(long unsigned int num)
returns log2 of numbers which must be gauranteed to be 2^n
void multiControlledPhaseFlip(Qureg qureg, int *controlQubits, int numControlQubits)
Apply the multiple-qubit controlled phase flip gate, also known as the multiple-qubit controlled paul...
Definition: QuEST.c:587
void multiQubitUnitary(Qureg qureg, int *targs, int numTargs, ComplexMatrixN u)
Apply a general multi-qubit unitary (including a global phase factor) with any number of target qubit...
Definition: QuEST.c:296
#define PREPARE_TEST(quregVec, quregMatr, refVec, refMatr)
Prepares the needed data structures for unit testing unitaries.
void multiControlledMultiRotateZ(Qureg qureg, int *controlQubits, int numControls, int *targetQubits, int numTargets, qreal angle)
Apply a multi-controlled multi-target Z rotation, also known as a controlled phase gadget.
Definition: QuEST.c:668
@ PAULI_X
Definition: QuEST.h:96
void controlledPauliY(Qureg qureg, int controlQubit, int targetQubit)
Apply the controlled pauliY (single control, single target) gate, also known as the c-Y and c-sigma-Y...
Definition: QuEST.c:563
QMatrix getExponentialOfPauliMatrix(qreal angle, QMatrix a)
Returns the matrix exponential of a kronecker product of pauli matrices (or of any involutory matrice...
Definition: utilities.cpp:216
void multiControlledMultiQubitUnitary(Qureg qureg, int *ctrls, int numCtrls, int *targs, int numTargs, ComplexMatrixN u)
Apply a general multi-controlled multi-qubit unitary (including a global phase factor).
Definition: QuEST.c:330
void toComplexMatrixN(QMatrix qm, ComplexMatrixN cm)
Initialises cm with the values of qm.
Definition: utilities.cpp:1033
void rotateX(Qureg qureg, int targetQubit, qreal angle)
Rotate a single qubit by a given angle around the X-axis of the Bloch-sphere.
Definition: QuEST.c:187
void multiControlledMultiRotatePauli(Qureg qureg, int *controlQubits, int numControls, int *targetQubits, enum pauliOpType *targetPaulis, int numTargets, qreal angle)
Apply a multi-controlled multi-target multi-Pauli rotation, also known as a controlled Pauli gadget.
Definition: QuEST.c:705
qreal y
Definition: QuEST.h:200
qcomp expI(qreal phase)
Returns the unit-norm complex number exp(i*phase).
Definition: utilities.cpp:417
qreal x
Definition: QuEST.h:200
void pauliZ(Qureg qureg, int targetQubit)
Apply the single-qubit Pauli-Z (also known as the Z, sigma-Z or phase-flip) gate.
Definition: QuEST.c:454
void swapGate(Qureg qureg, int qb1, int qb2)
Performs a SWAP gate between qubit1 and qubit2.
Definition: QuEST.c:627
#define qcomp
ComplexMatrix4 toComplexMatrix4(QMatrix qm)
Returns a ComplexMatrix4 copy of QMatix qm.
Definition: utilities.cpp:1027
#define toComplex(scalar)
void controlledPhaseFlip(Qureg qureg, int idQubit1, int idQubit2)
Apply the (two-qubit) controlled phase flip gate, also known as the controlled pauliZ gate.
Definition: QuEST.c:575
@ PAULI_Y
Definition: QuEST.h:96
void controlledMultiQubitUnitary(Qureg qureg, int ctrl, int *targs, int numTargs, ComplexMatrixN u)
Apply a general controlled multi-qubit unitary (including a global phase factor).
Definition: QuEST.c:313
void multiQubitNot(Qureg qureg, int *targs, int numTargs)
Apply a NOT (or Pauli X) gate with multiple target qubits, which has the same effect as (but is much ...
Definition: QuEST.c:536
qreal ** real
Definition: QuEST.h:189
void compactUnitary(Qureg qureg, int targetQubit, Complex alpha, Complex beta)
Apply a single-qubit unitary parameterised by two given complex scalars.
Definition: QuEST.c:404
void pauliY(Qureg qureg, int targetQubit)
Apply the single-qubit Pauli-Y (also known as the Y or sigma-Y) gate.
Definition: QuEST.c:443
Represents a system of qubits.
Definition: QuEST.h:322
void controlledPhaseShift(Qureg qureg, int idQubit1, int idQubit2, qreal angle)
Introduce a phase factor on state of qubits idQubit1 and idQubit2.
Definition: QuEST.c:498
void controlledUnitary(Qureg qureg, int controlQubit, int targetQubit, ComplexMatrix2 u)
Apply a general controlled unitary (single control, single target), which can include a global phase ...
Definition: QuEST.c:360
qreal ** imag
Definition: QuEST.h:190
void phaseShift(Qureg qureg, int targetQubit, qreal angle)
Shift the phase between and of a single qubit by a given angle.
Definition: QuEST.c:487
void controlledNot(Qureg qureg, int controlQubit, int targetQubit)
Apply the controlled not (single control, single target) gate, also known as the c-X,...
Definition: QuEST.c:524
qreal real[2][2]
Definition: QuEST.h:139
void pauliX(Qureg qureg, int targetQubit)
Apply the single-qubit Pauli-X (also known as the X, sigma-X, NOT or bit-flip) gate.
Definition: QuEST.c:432
std::vector< std::vector< qcomp > > QMatrix
A complex square matrix.
Definition: utilities.hpp:49
void controlledTwoQubitUnitary(Qureg qureg, int controlQubit, int targetQubit1, int targetQubit2, ComplexMatrix4 u)
Apply a general controlled two-qubit unitary (including a global phase factor).
Definition: QuEST.c:269
ComplexMatrix2 toComplexMatrix2(QMatrix qm)
Returns a ComplexMatrix2 copy of QMatix qm.
Definition: utilities.cpp:1021
Catch::Generators::GeneratorWrapper< int * > sublists(int *list, int len, int sublen)
Returns a Catch2 generator of every length-sublen sublist of length-len list, in increasing lexograph...
Definition: utilities.cpp:1488
qreal real
Definition: QuEST.h:105
void tGate(Qureg qureg, int targetQubit)
Apply the single-qubit T gate.
Definition: QuEST.c:476
void multiRotateZ(Qureg qureg, int *qubits, int numQubits, qreal angle)
Apply a multi-qubit Z rotation, also known as a phase gadget, on a selected number of qubits.
Definition: QuEST.c:652
QMatrix getExponentialOfDiagonalMatrix(QMatrix a)
Returns the matrix exponential of a diagonal, square, complex matrix.
Definition: utilities.cpp:197
void hadamard(Qureg qureg, int targetQubit)
Apply the single-qubit Hadamard gate.
Definition: QuEST.c:176
Represents one complex number.
Definition: QuEST.h:103
void rotateZ(Qureg qureg, int targetQubit, qreal angle)
Rotate a single qubit by a given angle around the Z-axis of the Bloch-sphere (also known as a phase s...
Definition: QuEST.c:209
void multiControlledUnitary(Qureg qureg, int *controlQubits, int numControlQubits, int targetQubit, ComplexMatrix2 u)
Apply a general multiple-control single-target unitary, which can include a global phase factor.
Definition: QuEST.c:373
bool areEqual(QVector a, QVector b)
Returns true if the absolute value of the difference between every amplitude in vectors a and b is le...
Definition: utilities.cpp:398
void applyReferenceOp(QVector &state, int *ctrls, int numCtrls, int *targs, int numTargs, QMatrix op)
Modifies the state-vector state to be the result of applying the multi-target operator matrix op,...
Definition: utilities.cpp:728
#define M_PI
Definition: QuEST_common.c:41
Catch::Generators::GeneratorWrapper< int * > bitsets(int numBits)
Returns a Catch2 generator of every numBits-length bit-set, in increasing lexographic order,...
Definition: utilities.cpp:1557
void controlledCompactUnitary(Qureg qureg, int controlQubit, int targetQubit, Complex alpha, Complex beta)
Apply a controlled unitary (single control, single target) parameterised by two given complex scalars...
Definition: QuEST.c:417
void multiStateControlledUnitary(Qureg qureg, int *controlQubits, int *controlState, int numControlQubits, int targetQubit, ComplexMatrix2 u)
Apply a general single-qubit unitary with multiple control qubits, conditioned upon a specific bit se...
Definition: QuEST.c:388
void sqrtSwapGate(Qureg qureg, int qb1, int qb2)
Performs a sqrt SWAP gate between qubit1 and qubit2.
Definition: QuEST.c:639
Represents a 2x2 matrix of complex numbers.
Definition: QuEST.h:137
void controlledRotateY(Qureg qureg, int controlQubit, int targetQubit, qreal angle)
Applies a controlled rotation by a given angle around the Y-axis of the Bloch-sphere.
Definition: QuEST.c:232