QNLP  v1.0
IntelSimulator.cpp
Go to the documentation of this file.
1 //##############################################################################
15 //##############################################################################
16 
17 #include "Simulator.hpp"
18 #include "GateWriter.hpp"
19 #include "qureg/qureg.hpp"
20 #include "util/tinymatrix.hpp"
21 #include <cstdlib>
22 #include <iostream>
23 #include <limits>
24 
25 #ifdef ENABLE_MPI
26  #include "mpi.h"
27 #endif
28 
29 namespace QNLP{
30 
35 class IntelSimulator : public SimulatorGeneral<IntelSimulator> {
36  public:
37  using TMDP = qhipster::TinyMatrix<ComplexDP, 2, 2, 32>;
38  using QRDP = QubitRegister<ComplexDP>;
39  using CST = const std::size_t;
40 
47  IntelSimulator(int numQubits, bool useFusion=false) : SimulatorGeneral<IntelSimulator>(),
49  qubitRegister(QubitRegister<ComplexDP> (numQubits, "base", 0)),
50  gates(5), uid( reinterpret_cast<std::size_t>(this) ){
51 
52  //Define Pauli X
53  gates[0](0,0) = ComplexDP(0.,0.); gates[0](0,1) = ComplexDP(1.,0.);
54  gates[0](1,0) = ComplexDP(1.,0.); gates[0](1,1) = ComplexDP(0.,0.);
55 
56  //Define Pauli Y
57  gates[1](0,0) = ComplexDP(0.,0.); gates[1](0,1) = -ComplexDP(0.,1.);
58  gates[1](1,0) = ComplexDP(0.,1.); gates[1](1,1) = ComplexDP(0.,0.);
59 
60  //Define Pauli Z
61  gates[2](0,0) = ComplexDP(1.,0.); gates[2](0,1) = ComplexDP(0.,0.);
62  gates[2](1,0) = ComplexDP(0.,0.); gates[2](1,1) = ComplexDP(-1.,0.);
63 
64  //Define I
65  gates[3](0,0) = ComplexDP(1.,0.); gates[3](0,1) = ComplexDP(0.,0.);
66  gates[3](1,0) = ComplexDP(0.,0.); gates[3](1,1) = ComplexDP(1.,0.);
67 
68  //Define Pauli H
69  double coeff = (1./sqrt(2.));
70  gates[4](0,0) = coeff*ComplexDP(1.,0.); gates[4](0,1) = coeff*ComplexDP(1.,0.);
71  gates[4](1,0) = coeff*ComplexDP(1.,0.); gates[4](1,1) = -coeff*ComplexDP(1.,0.);
72 
73  //Ensure the cache maps are populated before use.
74  this->initCaches();
75 
76  #ifdef ENABLE_MPI //If for some strange reason the MPI environement is not enable through the Base CRTP class, enable using Intel-QS
77  int mpi_is_init;
78  MPI_Initialized(&mpi_is_init);
79  if (! mpi_is_init){ // Attempt init using Intel-QS MPI env
80  int argc_tmp = 0;
81  char** argv_tmp = new char*[argc_tmp];
82 
83  qhipster::mpi::Environment env(argc_tmp, argv_tmp); //we do not expect to pass any params here
84  delete argv_tmp;
85  }
86  MPI_Comm_rank(MPI_COMM_WORLD, &rank);
87  #endif
88 
89  /* Set up random number generator for randomly collapsing qubit to 0 or 1
90  *
91  * Note: a random number will be generated on rank 0 and then broadcasted
92  * to all ranks so that each rank collapses the respective qubit to the
93  * same value.
94  */
95  std::mt19937 mt_(rd());
96  std::uniform_real_distribution<double> dist_(0.0,1.0);
97  mt = mt_;
98  dist = dist_;
99  if(useFusion == true){
100  qubitRegister.TurnOnFusion();
101  std::cerr << "Warning: enabling fusion may cause inconsistent results." << std::endl;
102  }
103  gate_count_1qubit = 0;
104  gate_count_2qubit = 0;
105  }
106 
112 
113  // 1 qubit
121  inline void applyGateU(const TMDP& U, CST qubitIndex, std::string label="U"){
122  #ifndef RESOURCE_ESTIMATE
123  qubitRegister.Apply1QubitGate(qubitIndex, U);
124  #endif
125 
127 
128  #ifdef GATE_LOGGING
129  writer.oneQubitGateCall(label, U.tostr(), qubitIndex);
130  #endif
131  }
132 
138  inline void applyGateI(std::size_t qubitIndex){
139  #ifndef RESOURCE_ESTIMATE
140  applyGateU(getGateI(), qubitIndex, "I");
141  #endif
142 
144 
145  #ifdef GATE_LOGGING
146  writer.oneQubitGateCall("I", getGateI().tostr(), qubitIndex);
147  #endif
148  }
149 
156  inline void applyGatePhaseShift(std::size_t qubit_idx, double angle){
157  //Phase gate is identity with 1,1 index modulated by angle
158  TMDP U(gates[3]);
159  U(1, 1) = ComplexDP(cos(angle), sin(angle));
160 
161  #ifndef RESOURCE_ESTIMATE
162  applyGateU(U, qubit_idx, "Phase:=" + std::to_string(angle));
163  #endif
164 
166 
167  #ifdef GATE_LOGGING
168  writer.oneQubitGateCall("PShift(theta=" + std::to_string(angle) + ")", U.tostr(), qubitIndex);
169  #endif
170 
171  }
172 
173  // 2 qubit
180  inline void applyGateSqrtSwap( std::size_t qubit_idx0, std::size_t qubit_idx1){
181  std::cerr << "NOT YET IMPLEMENTED" << std::endl;
182  std::abort();
183  }
184 
185  // 3 qubit
193  inline void applyGateCCX(std::size_t ctrl_qubit0, std::size_t ctrl_qubit1, std::size_t target_qubit){
194  this->applyGateNCU(this->getGateX(), std::vector<std::size_t> {ctrl_qubit0, ctrl_qubit1}, target_qubit, "X");
195  }
196 
205  inline void applyGateCSwap(std::size_t ctrl_qubit, std::size_t qubit_swap0, std::size_t qubit_swap1){
206  //V = sqrt(X)
207  TMDP V;
208  V(0,0) = {0.5, 0.5};
209  V(0,1) = {0.5, -0.5};
210  V(1,0) = {0.5, -0.5};
211  V(1,1) = {0.5, 0.5};
212 
213  TMDP V_dag;
214  V_dag(0,0) = {0.5, -0.5};
215  V_dag(0,1) = {0.5, 0.5};
216  V_dag(1,0) = {0.5, 0.5};
217  V_dag(1,1) = {0.5, -0.5};
218 
219  applyGateCX(qubit_swap1, qubit_swap0);
220  applyGateCU(V, qubit_swap0, qubit_swap1, "X");
221  applyGateCU(V, ctrl_qubit, qubit_swap1, "X");
222 
223  applyGateCX(ctrl_qubit, qubit_swap0);
224  applyGateCU(V_dag, qubit_swap0, qubit_swap1, "X");
225  applyGateCX(qubit_swap1, qubit_swap0);
226  applyGateCX(ctrl_qubit, qubit_swap0);
227  }
228 
229  //#################################################
230 
236  inline void applyGateX(CST qubitIndex){
237  #ifndef RESOURCE_ESTIMATE
238  qubitRegister.ApplyPauliX(qubitIndex);
239  #endif
240 
242 
243  #ifdef GATE_LOGGING
244  writer.oneQubitGateCall("X", getGateX().tostr(), qubitIndex);
245  #endif
246  }
247 
253  inline void applyGateY(CST qubitIndex){
254  #ifndef RESOURCE_ESTIMATE
255  qubitRegister.ApplyPauliY(qubitIndex);
256  #endif
257 
259 
260  #ifdef GATE_LOGGING
261  writer.oneQubitGateCall("Y", getGateY().tostr(), qubitIndex);
262  #endif
263  }
264 
270  inline void applyGateZ(CST qubitIndex){
271  #ifndef RESOURCE_ESTIMATE
272  qubitRegister.ApplyPauliZ(qubitIndex);
273  #endif
274 
276 
277  #ifdef GATE_LOGGING
278  writer.oneQubitGateCall("Z", getGateZ().tostr(), qubitIndex);
279  #endif
280  }
281 
287  inline void applyGateH(CST qubitIndex){
288  #ifndef RESOURCE_ESTIMATE
289  qubitRegister.ApplyHadamard(qubitIndex);
290  #endif
291 
293 
294  #ifdef GATE_LOGGING
295  writer.oneQubitGateCall("H", getGateH().tostr(), qubitIndex);
296  #endif
297  }
298 
304  inline void applyGateSqrtX(CST qubitIndex){
305  #ifndef RESOURCE_ESTIMATE
306  qubitRegister.ApplyPauliSqrtX(qubitIndex);
307  #endif
308 
310 
311  #ifdef GATE_LOGGING
312  writer.oneQubitGateCall(
313  "\\sqrt[2]{X}",
314  matrixSqrt<decltype(getGateX())>(getGateX()).tostr(),
315  qubitIndex
316  );
317  #endif
318  };
319 
326  inline void applyGateRotX(CST qubitIndex, double angle) {
327  #ifndef RESOURCE_ESTIMATE
328  qubitRegister.ApplyRotationX(qubitIndex, angle);
329  #endif
330 
332 
333  #ifdef GATE_LOGGING
334  writer.oneQubitGateCall(
335  "R_X(\\theta=" + std::to_string(angle) + ")",
336  getGateI().tostr(),
337  qubitIndex
338  );
339  #endif
340  };
341 
348  inline void applyGateRotY(CST qubitIndex, double angle) {
349  #ifndef RESOURCE_ESTIMATE
350  qubitRegister.ApplyRotationY(qubitIndex, angle);
351  #endif
352 
354 
355  #ifdef GATE_LOGGING
356  writer.oneQubitGateCall(
357  "R_Y(\\theta=" + std::to_string(angle) + ")",
358  getGateI().tostr(),
359  qubitIndex
360  );
361  #endif
362  };
363 
370  inline void applyGateRotZ(CST qubitIndex, double angle) {
371  #ifndef RESOURCE_ESTIMATE
372  qubitRegister.ApplyRotationZ(qubitIndex, angle);
373  #endif
374 
376 
377  #ifdef GATE_LOGGING
378  writer.oneQubitGateCall(
379  "R_Z(\\theta=" + std::to_string(angle) + ")",
380  getGateI().tostr(),
381  qubitIndex
382  );
383  #endif
384  };
385 
390  inline TMDP getGateX(){ return gates[0]; }
391 
396  inline TMDP getGateY(){ return gates[1]; }
397 
402  inline TMDP getGateZ(){ return gates[2]; }
403 
408  inline TMDP getGateI(){ return gates[3]; }
409 
414  inline TMDP getGateH(){ return gates[4]; }
415 
424  inline void applyGateCU(const TMDP& U, CST control, CST target, std::string label="U"){
425  #ifndef RESOURCE_ESTIMATE
426  qubitRegister.ApplyControlled1QubitGate(control, target, U);
427  #endif
428 
430 
431  #ifdef GATE_LOGGING
432  writer.twoQubitGateCall( label, U.tostr(), control, target );
433  #endif
434  }
435 
442  inline void applyGateCX(CST control, CST target){
443  #ifndef RESOURCE_ESTIMATE
444  qubitRegister.ApplyCPauliX(control, target);
445  #endif
446 
448 
449  #ifdef GATE_LOGGING
450  writer.twoQubitGateCall( "X", getGateX().tostr(), control, target );
451  #endif
452  }
453 
460  inline void applyGateCY(CST control, CST target){
461  #ifndef RESOURCE_ESTIMATE
462  qubitRegister.ApplyCPauliY(control, target);
463  #endif
464 
466 
467  #ifdef GATE_LOGGING
468  writer.twoQubitGateCall( "Y", getGateY().tostr(), control, target );
469  #endif
470  }
471 
478  inline void applyGateCZ(CST control, CST target){
479  #ifndef RESOURCE_ESTIMATE
480  qubitRegister.ApplyCPauliZ(control, target);
481  #endif
482 
484 
485  #ifdef GATE_LOGGING
486  writer.twoQubitGateCall( "Z", getGateZ().tostr(), control, target );
487  #endif
488  }
489 
496  inline void applyGateCH(CST control, CST target){
497  #ifndef RESOURCE_ESTIMATE
498  qubitRegister.ApplyCHadamard(control, target);
499  #endif
500 
502 
503  #ifdef GATE_LOGGING
504  writer.twoQubitGateCall( "H", getGateH().tostr(), control, target );
505  #endif
506  }
507 
515  inline void applyGateCPhaseShift(double angle, unsigned int control, unsigned int target){
516  TMDP U(gates[3]);
517  U(1, 1) = ComplexDP(cos(angle), sin(angle));
518 
519  #ifndef RESOURCE_ESTIMATE
520  qubitRegister.ApplyControlled1QubitGate(control, target, U);
521  #endif
522 
524 
525  #ifdef GATE_LOGGING
526  writer.twoQubitGateCall( "CPhase", U.tostr(), control, target );
527  #endif
528  }
529 
537  inline void applyGateCRotX(CST control, CST target, const double theta){
538  #ifndef RESOURCE_ESTIMATE
539  qubitRegister.ApplyCRotationX(control, target, theta);
540  #endif
541 
543 
544  #ifdef GATE_LOGGING
545  writer.twoQubitGateCall( "CR_X", getGateI().tostr(), control, target );
546  #endif
547  }
548 
556  inline void applyGateCRotY(CST control, CST target, double theta){
557  #ifndef RESOURCE_ESTIMATE
558  qubitRegister.ApplyCRotationY(control, target, theta);
559  #endif
560 
562 
563  #ifdef GATE_LOGGING
564  writer.twoQubitGateCall( "CR_Y", getGateI().tostr(), control, target );
565  #endif
566  }
567 
575  inline void applyGateCRotZ(CST control, CST target, const double theta){
576  #ifndef RESOURCE_ESTIMATE
577  qubitRegister.ApplyCRotationZ(control, target, theta);
578  #endif
579 
581 
582  #ifdef GATE_LOGGING
583  writer.twoQubitGateCall( "CR_Z", getGateI().tostr(), control, target );
584  #endif
585  }
586 
593  inline void applyGateSwap(CST qubit_idx0, CST qubit_idx1){
594  qubitRegister.ApplySwap(qubit_idx0, qubit_idx1);
595  #ifdef GATE_LOGGING
596  writer.twoQubitGateCall( "SWAP", getGateI().tostr(), qubit_idx0, qubit_idx1 );
597  #endif
598  }
599 
605  inline QubitRegister<ComplexDP>& getQubitRegister() {
606  return this->qubitRegister;
607  }
608 
614  inline const QubitRegister<ComplexDP>& getQubitRegister() const {
615  return this->qubitRegister;
616  };
617 
623  std::size_t getNumQubits() {
624  return numQubits;
625  }
626 
631  void initRegister(){
632  this->qubitRegister.Initialize("base",0);
633  this->initCaches();
634  gate_count_1qubit = 0;
635  gate_count_2qubit = 0;
636  }
637 
642  inline void applyAmplitudeNorm(){
643  this->qubitRegister.Normalize();
644  }
645 
653  bool applyMeasurement(CST target, bool normalize=true){
654  double rand;
655  bool bit_val;
656 
657  #ifdef ENABLE_MPI
658  if(rank == 0){
659  rand = dist(mt);
660  }
661  MPI_Bcast(&rand, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD);
662  MPI_Barrier(MPI_COMM_WORLD);
663  #else
664  rand = dist(mt);
665  #endif
666  collapseQubit(target, (bit_val = ( rand < getStateProbability(target) ) ) );
667  if(normalize){
669  }
670  return bit_val;
671  }
672 
679  void collapseToBasisZ(CST target, bool collapseValue){
680  collapseQubit(target, collapseValue);
682  }
683 
690  inline void PrintStates(std::string x, std::vector<std::size_t> qubits = {}){
691  qubitRegister.Print(x,qubits);
692  }
693 
694  #ifdef GATE_LOGGING
695 
700  GateWriter& getGateWriter(){
701  return writer;
702  }
703  #endif
704 
709  std::pair<std::size_t, std::size_t> getGateCounts(){
710  std::cout << "######### Gate counts #########" << std::endl;
711  std::cout << "1 qubit = " << gate_count_1qubit << std::endl;
712  std::cout << "2 qubit = " << gate_count_2qubit << std::endl;
713  std::cout << "total = " << gate_count_1qubit + gate_count_2qubit << std::endl;
714  std::cout << "###############################" << std::endl;
715  return std::make_pair(gate_count_1qubit, gate_count_2qubit);
716  }
717 
723  inline complex<double> overlap( IntelSimulator &sim){
724  if(sim.uid != this->uid){
725  return qubitRegister.ComputeOverlap(sim.qubitRegister);
726  }
727  else{
728  return std::numeric_limits<double>::quiet_NaN();
729  }
730  }
731 
732  private:
733  //inline static std::size_t suid = 0; //works in C++17.
734  const std::size_t uid;
735 
736  std::size_t numQubits = 0;
738  std::vector<TMDP> gates;
739  #ifdef ENABLE_MPI
740  int rank;
741  #endif
742 
743  std::size_t gate_count_1qubit;
744  std::size_t gate_count_2qubit;
745 
746  std::random_device rd;
747  std::mt19937 mt;
748  std::uniform_real_distribution<double> dist;
749 
750 
751  // Measurement methods
758  inline void collapseQubit(CST target, bool collapseValue){
759  qubitRegister.CollapseQubit(target, collapseValue);
760  }
761 
769  return qubitRegister.GetProbability(target);
770  }
771 
772 };
773 
774 };
775 
void applyGateCRotX(CST control, CST target, const double theta)
Apply the given Controlled Rotation about X-axis to the given qubit.
std::size_t gate_count_2qubit
const std::size_t uid
void applyGateZ(CST qubitIndex)
Apply the Pauli Z gate to the given qubit.
TMDP getGateI()
Get the Identity.
QubitRegister< ComplexDP > & getQubitRegister()
Get the Qubit Register object.
void applyGateY(CST qubitIndex)
Apply the Pauli Y gate to the given qubit.
void applyGateCH(CST control, CST target)
Apply Controlled Hadamard on target qubit.
void applyGatePhaseShift(std::size_t qubit_idx, double angle)
Apply phase shift to given Qubit; [[1 0] [0 exp(i*angle)]].
Class definition for IntelSimulator. The purpose of this class is to map the functionality of the und...
IntelSimulator(int numQubits, bool useFusion=false)
Construct a new Intel Simulator object. The constructor also sets up and initialises the MPI environe...
~IntelSimulator()
Destroy the Intel Simulator object.
QubitRegister< ComplexDP > QRDP
void applyGateCZ(CST control, CST target)
Apply Controlled Pauli-Z on target qubit.
const std::size_t CST
void applyGateNCU(const Mat2x2Type &U, const std::vector< std::size_t > &ctrlIndices, std::size_t target, std::string label)
Apply n-control unitary gate to the given qubit target.
Definition: Simulator.hpp:462
CRTP defined class for simulator implementations.
Definition: Simulator.hpp:69
void applyAmplitudeNorm()
Apply normalization to the amplitudes of each state. This is required after a qubit in a state is col...
void applyGateU(const TMDP &U, CST qubitIndex, std::string label="U")
Apply arbitrary user-defined unitary gate to qubit at qubit_idx.
double getStateProbability(CST target)
Get the probability of the specified qubit being in the state |1>
std::pair< std::size_t, std::size_t > getGateCounts()
Print 1 and 2 qubit gate call counts.
void applyGateCY(CST control, CST target)
Apply Controlled Pauli-Y on target qubit.
void applyGateI(std::size_t qubitIndex)
Apply the Identity gate to the given qubit.
void initRegister()
(Re)Initialise the underlying register of the encapsulated simulator to well-defined state (|0....
void applyGateSqrtSwap(std::size_t qubit_idx0, std::size_t qubit_idx1)
Performs Sqrt SWAP gate between two given qubits (half way SWAP)
std::random_device rd
void applyGateCX(CST control, CST target)
Apply Controlled Pauli-X (CNOT) on target qubit.
void applyGateCU(const TMDP &U, CST control, CST target, std::string label="U")
Apply the given controlled unitary gate on target qubit.
void applyGateCPhaseShift(double angle, unsigned int control, unsigned int target)
Perform controlled phase shift gate.
void applyGateRotZ(CST qubitIndex, double angle)
Apply the given Rotation about Z-axis to the given qubit.
void applyGateSwap(CST qubit_idx0, CST qubit_idx1)
Swap the qubits at the given indices.
qhipster::TinyMatrix< ComplexDP, 2, 2, 32 > TMDP
TMDP getGateX()
Get the Pauli-X gate.
void applyGateCRotY(CST control, CST target, double theta)
Apply the given Controlled Rotation about Y-axis to the given qubit.
void applyGateH(CST qubitIndex)
Apply the Hadamard gate to the given qubit.
void collapseQubit(CST target, bool collapseValue)
Collapses specified qubit in register to the collapseValue without applying normalization.
std::size_t getNumQubits()
Get the number of Qubits.
void PrintStates(std::string x, std::vector< std::size_t > qubits={})
Prints the string x and then for each state of the specified qubits in the superposition,...
bool applyMeasurement(CST target, bool normalize=true)
Apply measurement to a target qubit, randomly collapsing the qubit proportional to the amplitude and ...
std::uniform_real_distribution< double > dist
std::vector< TMDP > gates
void applyGateCCX(std::size_t ctrl_qubit0, std::size_t ctrl_qubit1, std::size_t target_qubit)
Controlled controlled NOT (CCNOT, CCX) gate.
std::size_t gate_count_1qubit
void applyGateRotY(CST qubitIndex, double angle)
Apply the given Rotation about Y-axis to the given qubit.
void applyGateCSwap(std::size_t ctrl_qubit, std::size_t qubit_swap0, std::size_t qubit_swap1)
void applyGateCRotZ(CST control, CST target, const double theta)
Apply the given Controlled Rotation about Z-axis to the given qubit.
complex< double > overlap(IntelSimulator &sim)
Compute overlap between different simulators. Number of qubits must be the same.
TMDP getGateY()
Get the Pauli-Y gate.
void initCaches()
Initialise caches used in NCU operation.
Definition: Simulator.hpp:687
void collapseToBasisZ(CST target, bool collapseValue)
Apply measurement to a target qubit with respect to the Z-basis, collapsing to a specified value (0 o...
void applyGateSqrtX(CST qubitIndex)
Apply the Sqrt{Pauli X} gate to the given qubit.
void applyGateRotX(CST qubitIndex, double angle)
Apply the given Rotation about X-axis to the given qubit.
TMDP getGateZ()
Get the Pauli-Z gate.
Mat2x2Type matrixSqrt(const Mat2x2Type &U)
Calculates the unitary matrix square root (U == VV, where V is returned)
Definition: Simulator.hpp:729
void applyGateX(CST qubitIndex)
Apply the Pauli X gate to the given qubit.
TMDP getGateH()
Get the Hadamard gate.
const QubitRegister< ComplexDP > & getQubitRegister() const
Get the Qubit Register object.