Program Listing for File qureg.hpp

Return to documentation for file (/home/docs/checkouts/readthedocs.org/user_builds/intel-qs/checkouts/docs/include/qureg.hpp)

#pragma once

#include <algorithm>    // for std::swap
#include <cassert>
#include <cmath>
#include <fstream>
#include <functional>
#include <iostream>
#include <limits>
#include <map>
#include <memory>   // for std::swap
#include <numeric>
#include <tuple>
#include <vector>

#ifdef USE_MKL
#include <mkl.h>
#endif

#ifdef _OPENMP
#include <omp.h>
#endif

#include "permute.hpp"
// utility files
#include "utils.hpp"
#include "mpi_utils.hpp"
#include "rng_utils.hpp"
#include "timer.hpp"
#include "gate_counter.hpp"
#include "alignedallocator.hpp"
#include "mpi_env.hpp"
#include "bitops.hpp"
#include "conversion.hpp"
#include "tinymatrix.hpp"



template<typename T>
struct extract_value_type //lets call it extract_value_type
{
    typedef T value_type;
};


template<template<typename> class X, typename T>
struct extract_value_type<X<T>>   //specialization
{
    typedef T value_type;
};


template<class Type>
using TM2x2 = qhipster::TinyMatrix<Type, 2, 2, 32>;


template<class Type>
using TM4x4 = qhipster::TinyMatrix<Type, 4, 4, 32>;

// Qubitregister class declaration

template <class Type = ComplexDP>
class QubitRegister
{
  public:
    using value_type = Type;
    typedef typename extract_value_type<Type>::value_type BaseType;

  // constructors / destructors
    QubitRegister();
    QubitRegister(std::size_t num_qubits, std::string style = "",
                 std::size_t base_index = 0, std::size_t tmp_spacesize_ = 0);
    QubitRegister(const QubitRegister &in);
    QubitRegister(std::size_t num_qubits, Type *state, std::size_t tmp_spacesize_ = 0);
    ~QubitRegister();

  // allocation & initialization
  void AllocateAdditionalQubit();
  void Allocate(std::size_t new_num_qubits, std::size_t tmp_spacesize_);
  void Initialize(std::size_t new_num_qubits, std::size_t tmp_spacesize_);
  // The 'style' of initialization can be:
  // - 'rand': real and imag part of each amplitudes are uniformly random,
  //           using either the **local** or **pool** RNG stream,
  //           then state is normalized.
  // - 'base': state of the computational basis, only a non-zero amplitude.
  // - '++++': the balanced superposition of all computational basis states.
  void Initialize(std::string style, std::size_t base_index);

  // overload [] operator to return the amplitude stored at the local index.
  inline Type& operator[] (std::size_t index) { return state[index]; }
  inline Type& operator[] (std::size_t index) const { return state[index]; }
  // get the amplitude corresponding to a global index (with MPI broadcast).
  Type GetGlobalAmplitude(std::size_t index) const;

  std::size_t LocalSize() const { return local_size_; }
  std::size_t GlobalSize() const { return global_size_; }

  void Resize(std::size_t new_num_amplitudes);
  std::size_t size() const { return global_size_; }
  std::size_t NumQubits() const { return num_qubits; }
  Type *TmpSpace() const { return state + LocalSize(); }
  size_t TmpSize() const {return tmp_spacesize_;}

  // bit manipulation
  inline bool check_bit(std::size_t variable, std::size_t position) const
  {
    std::size_t one = (std::size_t)1, position_long = UL(position);
    return variable & (one << position_long);
  }
  inline std::size_t set_bit(std::size_t variable, std::size_t position) const
  {
    std::size_t one = (std::size_t)1, position_long = UL(position);
    return variable | (one << position_long);
  }
  inline std::size_t clear_bit(std::size_t variable, std::size_t position) const
  {
     return (variable & ~(UL(1) << UL(position)));
  }

  void EnableStatistics();
  void GetStatistics();
  void DisableStatistics();
  void ResetStatistics();

  void Permute(std::vector<std::size_t> permutation_new_vec);


  // Generic gates
  // single qubit gates
  bool Apply1QubitGate_helper(unsigned qubit,  TM2x2<Type> const&m,
                              std::size_t sstate_ind, std::size_t estate_ind);
  void Apply1QubitGate(unsigned qubit, TM2x2<Type> const&m);
  // constrolled gates
  bool ApplyControlled1QubitGate_helper(unsigned control_qubit, unsigned target_qubit,
                                        TM2x2<Type> const&m,
                                        std::size_t sind, std::size_t eind);
  void ApplyControlled1QubitGate(unsigned control_qubit, unsigned target_qubit,
                                 TM2x2<Type> const&m);
  // swap gates
  bool ApplySwap_helper(unsigned qubit1, unsigned qubit2, TM2x2<Type> const&m);
  void ApplySwap(unsigned qubit1, unsigned qubit2);
  void ApplyISwap(unsigned qubit1, unsigned qubit2);
  void Apply4thRootISwap(unsigned qubit1, unsigned qubit2);
  void ApplySqrtISwap(unsigned qubit1, unsigned qubit2);
  void ApplyISwapRotation(unsigned qubit1, unsigned qubit2, TM2x2<Type> const&m);
  void Swap(unsigned b1, unsigned b2);
  // diagonal gates
  void ApplyDiagSimp(unsigned qubit1, unsigned qubit2, TM4x4<Type> const&m);
  void ApplyDiag(unsigned qubit1, unsigned qubit2, TM4x4<Type> const&m);
  void ApplyDiagControl(unsigned qubit1, unsigned qubit2, TM4x4<Type> const&m);
  void ApplyDiagGeneral(unsigned qubit1, unsigned qubit2, TM4x4<Type> const&m);
  // two-qubit gates
  void Apply2QubitGate(unsigned const qubit_high, unsigned const qubit_low, TM4x4<Type> const&m);
  // specialized gates
  void ApplyRotationX(unsigned const qubit, BaseType theta);
  void ApplyRotationY(unsigned const qubit, BaseType theta);
  void ApplyRotationZ(unsigned const qubit, BaseType theta);
  void ApplyPauliX(unsigned const qubit);
  void ApplyPauliY(unsigned const qubit);
  void ApplyPauliZ(unsigned const qubit);
  void ApplyPauliSqrtX(unsigned const qubit);
  void ApplyPauliSqrtY(unsigned const qubit);
  void ApplyPauliSqrtZ(unsigned const qubit);
  void ApplyT(unsigned const qubit);
  void ApplyToffoli(unsigned const qubit1, unsigned const qubit2, unsigned const qubit3);
  void ApplyHadamard(unsigned const qubit);

  void ApplyCRotationX(unsigned const control_qubit, unsigned const target_qubit,
                       BaseType theta);
  void ApplyCRotationY(unsigned const control_qubit, unsigned const target_qubit,
                       BaseType theta);
  void ApplyCRotationZ(unsigned const control_qubit, unsigned const target_qubit,
                       BaseType theta);
  void ApplyCPauliX(unsigned const control_qubit, unsigned const target_qubit);
  void ApplyCPauliY(unsigned const control_qubit, unsigned const target_qubit);
  void ApplyCPauliZ(unsigned const control_qubit, unsigned const target_qubit);
  void ApplyCPauliSqrtZ(unsigned const control_qubit, unsigned const target_qubit);
  void ApplyCHadamard(unsigned const control_qubit, unsigned const target_qubit);

  void ApplyCPhaseRotation(unsigned const qubit, unsigned const qubit2, BaseType theta);

  // fusion
  void TurnOnFusion(unsigned log2llc = 20);
  void TurnOffFusion();
  bool IsFusionEnabled();
  void ApplyFusedGates();

  // gate specialization (experimental)
  void TurnOnSpecialize();
  void TurnOffSpecialize();

  // measurement
  bool GetClassicalValue(unsigned qubit, BaseType tolerance = 1.e-13) const;
  bool IsClassicalBit(unsigned qubit, BaseType tolerance = 1.e-13) const;
  void CollapseQubit(unsigned qubit, bool value);
  BaseType GetProbability(unsigned qubit);

  // expectation values without state update
  BaseType ExpectationValueX(unsigned const qubit, BaseType coeff=1.);
  BaseType ExpectationValueY(unsigned const qubit, BaseType coeff=1.);
  BaseType ExpectationValueZ(unsigned const qubit, BaseType coeff=1.);
  BaseType ExpectationValueXX(unsigned const qubit, unsigned const qubit2,
                              BaseType coeff=1.);
  BaseType ExpectationValueXY(unsigned const qubit, unsigned const qubit2,
                              BaseType coeff=1.);
  BaseType ExpectationValueXZ(unsigned const qubit, unsigned const qubit2,
                              BaseType coeff=1.);
  BaseType ExpectationValueYX(unsigned const qubit, unsigned const qubit2,
                              BaseType coeff=1.);
  BaseType ExpectationValueYY(unsigned const qubit, unsigned const qubit2,
                              BaseType coeff=1.);
  BaseType ExpectationValueYZ(unsigned const qubit, unsigned const qubit2,
                              BaseType coeff=1.);
  BaseType ExpectationValueZX(unsigned const qubit, unsigned const qubit2,
                              BaseType coeff=1.);
  BaseType ExpectationValueZY(unsigned const qubit, unsigned const qubit2,
                              BaseType coeff=1.);
  BaseType ExpectationValueZZ(unsigned const qubit, unsigned const qubit2,
                              BaseType coeff=1.);
  BaseType ExpectationValue(std::vector<unsigned> &qubits,
                            std::vector<unsigned> &observables,
                            BaseType coeff=1.);

  // noisy simulation:
  BaseType GetT1   () {return T_1_;   }
  BaseType GetT2   () {return T_2_;   }
  BaseType GetTphi () {return T_phi_; }
  void SetNoiseTimescales(BaseType T1, BaseType T2);
  void ApplyNoiseGate(const unsigned qubit, const BaseType duration);
//FIXME DELETE  BaseType IncoherentAverageOverAllStatesOfPool (BaseType local_value);


  // Utilities:
  bool operator==(const QubitRegister &rhs);
  BaseType MaxAbsDiff(QubitRegister &x, Type sfactor = Type(1.0, 0.));
  BaseType MaxL2NormDiff(QubitRegister &x);
  void dumpbin(std::string fn);
  double Entropy();
  std::vector<double> GoogleStats();
  void Normalize();
  BaseType ComputeNorm();
  Type ComputeOverlap( QubitRegister<Type> &psi );

  void Print(std::string x, std::vector<std::size_t> qbits = {});

  double HP_Distrpair(unsigned pos, TM2x2<Type> const&m);
  double HP_Distrpair(unsigned control, unsigned qubit, TM2x2<Type> const&m);

  // related to the internal random number generator.
  qhipster::RandomNumberGenerator<BaseType> * GetRngPtr () {return rng_ptr_; }
  void ResetRngPtr () {rng_ptr_=nullptr; }
  void SetRngPtr (qhipster::RandomNumberGenerator<BaseType> * rng_ptr) {rng_ptr_=rng_ptr; }
  void SetSeedRngPtr (std::size_t seed)
  {assert(rng_ptr_); rng_ptr_->SetSeedStreamPtrs(seed); }

  // Members
  std::size_t num_qubits;
  std::vector<Type, qhipster::AlignedAllocator<Type, 256>> state_storage;
  Type *state;
  Permutation *permutation;
  Timer *timer;
  GateCounter *gate_counter;
  std::size_t llc_watermarkbit;
  bool imported_state;
  bool specialize;

  // temporary buffer for fusion
  bool fusion;
  unsigned log2llc;
  std::vector<std::tuple<std::string, TM2x2<Type>, unsigned, unsigned>> fwindow;

  // set option of printing more info.
  static void SetDoPrintExtraInfo( bool value )
  { do_print_extra_info = value; }

 private:
  std::size_t local_size_;
  std::size_t global_size_;
  std::size_t tmp_spacesize_;
  static bool do_print_extra_info;

  qhipster::RandomNumberGenerator<BaseType> * rng_ptr_ = nullptr;
  BaseType T_1_;    // T_1   given in terms of the chosen time unit
  BaseType T_2_;    // T_2   given in terms of the chosen time unit
  BaseType T_phi_;  // T_phi given in terms of the chosen time unit

  private:
    QubitRegister<Type>& operator=(const QubitRegister<Type>& src) { return *this; }
};

template <typename Type>
bool QubitRegister<Type>::do_print_extra_info = false;

template <typename Type>
using BaseType = typename QubitRegister<Type>::BaseType;


//
// Derived class of QubitRegister that allows measurement of qubit gate depth.
//
#include "QubitRegisterMetric.hpp"
//
// Derived class of QubitRegister that automatically implements noise gates.
//
#include "NoisyQureg.hpp"