diff --git a/apps/qsim_base_custatevec.cu b/apps/qsim_base_custatevec.cu index a83f3e46..00b32be9 100644 --- a/apps/qsim_base_custatevec.cu +++ b/apps/qsim_base_custatevec.cu @@ -133,7 +133,7 @@ int main(int argc, char* argv[]) { } Simulator CreateSimulator() const { - return Simulator(custatevec_handle); + return Simulator(cublas_handle, custatevec_handle); } cublasHandle_t cublas_handle; diff --git a/lib/circuit_qsim_parser.h b/lib/circuit_qsim_parser.h index e977f21c..de7bd89d 100644 --- a/lib/circuit_qsim_parser.h +++ b/lib/circuit_qsim_parser.h @@ -167,13 +167,19 @@ class CircuitQsimParser final { IO::errorf("invalid gate in %s in line %u.\n", provider.c_str(), line); } + /** + * Checks formatting for a zero-qubit gate parsed from 'ss'. + * @param ss Input stream containing the gate specification. + */ + static bool ValidateGate(std::stringstream& ss) { + return ss && ss.peek() == std::stringstream::traits_type::eof(); + } + /** * Checks formatting for a single-qubit gate parsed from 'ss'. * @param ss Input stream containing the gate specification. * @param num_qubits Number of qubits, as defined at the start of the file. * @param q0 Index of the affected qubit. - * @param provider Circuit source; only used for error reporting. - * @param line Line number of the parsed gate; only used for error reporting. */ static bool ValidateGate(std::stringstream& ss, unsigned num_qubits, unsigned q0) { @@ -261,7 +267,11 @@ class CircuitQsimParser final { unsigned q0, q1; fp_type phi, theta; - if (gate_name == "id1") { + if (gate_name == "p") { + ss >> phi; + if (!ValidateGate(ss)) return false; + gates.push_back(GateGPh::Create(time, phi)); + } else if (gate_name == "id1") { ss >> q0; if (!ValidateGate(ss, num_qubits, q0)) return false; gates.push_back(GateId1::Create(time, q0)); diff --git a/lib/fuser.h b/lib/fuser.h index 93933975..e4f3c3ba 100644 --- a/lib/fuser.h +++ b/lib/fuser.h @@ -122,6 +122,38 @@ class Fuser { return epochs; } + template + static void FuseZeroQubitGates(const GateSeq0& gate_seq0, + Parent parent, std::size_t first, + std::vector& fused_gates) { + GateFused* fuse_to = nullptr; + + for (std::size_t i = first; i < fused_gates.size(); ++i) { + auto& fgate = fused_gates[i]; + + if (fgate.kind != gate::kMeasurement && fgate.kind != gate::kDecomp + && fgate.parent->controlled_by.size() == 0 + && !fgate.parent->unfusible) { + fuse_to = &fgate; + break; + } + } + + if (fuse_to != nullptr) { + // Fuse zero-qubit gates with the first available fused gate. + for (const auto& g : gate_seq0) { + fuse_to->gates.push_back(parent(g)); + } + } else { + auto g0 = parent(gate_seq0[0]); + fused_gates.push_back({g0->kind, g0->time, {}, g0, {g0}, {}}); + + for (std::size_t i = 1; i < gate_seq0.size(); ++i) { + fused_gates.back().gates.push_back(parent(gate_seq0[i])); + } + } + } + private: static bool AddBoundary(unsigned time, unsigned max_time, std::vector& boundaries) { @@ -144,7 +176,9 @@ inline void CalculateFusedMatrix(FusedGate& gate) { MatrixIdentity(unsigned{1} << gate.qubits.size(), gate.matrix); for (auto pgate : gate.gates) { - if (gate.qubits.size() == pgate->qubits.size()) { + if (pgate->qubits.size() == 0) { + MatrixScalarMultiply(pgate->matrix[0], pgate->matrix[1], gate.matrix); + } else if (gate.qubits.size() == pgate->qubits.size()) { MatrixMultiply(gate.qubits.size(), pgate->matrix, gate.matrix); } else { unsigned mask = 0; diff --git a/lib/fuser_basic.h b/lib/fuser_basic.h index 2fafb14a..6bd329c5 100644 --- a/lib/fuser_basic.h +++ b/lib/fuser_basic.h @@ -161,16 +161,24 @@ class BasicGateFuser final : public Fuser { // Sequence of top level gates the other gates get fused to. std::vector gates_seq; + // Sequence of zero-qubit gates. + std::vector gates_seq0; + // Lattice of gates: qubits "hyperplane" and time direction. std::vector> gates_lat(max_qubit1); // Current unfused gate. auto gate_it = gfirst; + std::size_t last_fused_gate_index = 0; + for (std::size_t l = 0; l < times.size(); ++l) { gates_seq.resize(0); gates_seq.reserve(num_gates); + gates_seq0.resize(0); + gates_seq0.reserve(num_gates); + for (unsigned k = 0; k < max_qubit1; ++k) { gates_lat[k].resize(0); gates_lat[k].reserve(128); @@ -212,6 +220,8 @@ class BasicGateFuser final : public Fuser { gates_lat[gate.qubits[0]].push_back(&gate); gates_lat[gate.qubits[1]].push_back(&gate); gates_seq.push_back(&gate); + } else { + gates_seq0.push_back(&gate); } } @@ -306,7 +316,14 @@ class BasicGateFuser final : public Fuser { gates_fused.push_back(std::move(gate_f)); } + if (gates_seq0.size() != 0) { + Base::FuseZeroQubitGates(gates_seq0, [](const RGate* g) { return g; }, + last_fused_gate_index, gates_fused); + } + if (gate_it == glast) break; + + last_fused_gate_index = gates_fused.size(); } if (fuse_matrix) { diff --git a/lib/fuser_mqubit.h b/lib/fuser_mqubit.h index 8ddb029e..a7ed4cd7 100644 --- a/lib/fuser_mqubit.h +++ b/lib/fuser_mqubit.h @@ -282,6 +282,7 @@ class MultiQubitGateFuser final : public Fuser { unsigned max_fused_size = std::min(unsigned{6}, param.max_fused_size); max_fused_size = std::min(max_fused_size, max_qubit1); + std::size_t last_fused_gate_index = 0; auto gate_it = gfirst; // Iterate over epochs. @@ -432,6 +433,14 @@ class MultiQubitGateFuser final : public Fuser { FuseOrphanedGates(max_fused_size, stat, orphaned_gates, fused_gates); } } + + if (fgates[0].size() != 0) { + Base::FuseZeroQubitGates(fgates[0], + [](const GateF* g) { return g->parent; }, + last_fused_gate_index, fused_gates); + } + + last_fused_gate_index = fused_gates.size(); } if (fuse_matrix) { diff --git a/lib/gates_cirq.h b/lib/gates_cirq.h index 235d92f6..d7679596 100644 --- a/lib/gates_cirq.h +++ b/lib/gates_cirq.h @@ -74,6 +74,7 @@ enum GateKind { kMatrixGate1, // One-qubit matrix gate. kMatrixGate2, // Two-qubit matrix gate. kMatrixGate, // Multi-qubit matrix gate. + kGlobalPhaseGate, kDecomp = gate::kDecomp, kMeasurement = gate::kMeasurement, }; @@ -85,6 +86,31 @@ constexpr double h_double = 0.5; constexpr double pi_double = 3.14159265358979323846264338327950288; constexpr double is2_double = 0.7071067811865475; +// Gates from cirq/ops/global_phase_op.py: + +/** + * The global phase gate. + */ +template +struct GlobalPhaseGate { + static constexpr GateKind kind = kGlobalPhaseGate; + static constexpr char name[] = "GlobalPhaseGate"; + static constexpr unsigned num_qubits = 1; + static constexpr bool symmetric = true; + + static GateCirq Create(unsigned time, fp_type phi) { + return Create(time, std::cos(phi), std::sin(phi)); + } + + static GateCirq Create(unsigned time, fp_type cp, fp_type sp) { + return CreateGate, GlobalPhaseGate>( + time, {}, {cp, sp}, {cp, sp}); + } +}; + +template +using global_phase_operation = GlobalPhaseGate; + // Gates from cirq/ops/identity.py: /** diff --git a/lib/gates_qsim.h b/lib/gates_qsim.h index 9b64cf98..366c4f1f 100644 --- a/lib/gates_qsim.h +++ b/lib/gates_qsim.h @@ -46,8 +46,9 @@ enum GateKind { kGateIS, // iSwap kGateFS, // fSim kGateCP, // control phase - kGateMatrix1, // One-qubit matrix gate. - kGateMatrix2, // Two-qubit matrix gate. + kGateMatrix1, // one-qubit matrix gate + kGateMatrix2, // two-qubit matrix gate + kGateGPh, // global phase gate kDecomp = gate::kDecomp, kMeasurement = gate::kMeasurement, }; @@ -59,6 +60,28 @@ using GateQSim = Gate; constexpr double h_double = 0.5; constexpr double is2_double = 0.7071067811865475; +// Zero-qubit gates: + +/** + * The global phase gate. + */ +template +struct GateGPh { + static constexpr GateKind kind = kGateGPh; + static constexpr char name[] = "p"; + static constexpr unsigned num_qubits = 1; + static constexpr bool symmetric = true; + + static GateQSim Create(unsigned time, fp_type phi) { + return Create(time, std::cos(phi), std::sin(phi)); + } + + static GateQSim Create(unsigned time, fp_type cp, fp_type sp) { + return CreateGate, GateGPh>( + time, {}, {cp, sp}, {cp, sp}); + } +}; + // One-qubit gates: /** diff --git a/lib/matrix.h b/lib/matrix.h index e126a02d..a3c26406 100644 --- a/lib/matrix.h +++ b/lib/matrix.h @@ -172,7 +172,7 @@ inline void MatrixMultiply(unsigned mask1, } /** - * Multiply a matrix by a scalar value. + * Multiply a matrix by a real scalar value. * @c Scalar value. * @m Input matrix to be multiplied. Output matrix. */ @@ -183,6 +183,23 @@ inline void MatrixScalarMultiply(fp_type1 c, Matrix& m) { } } +/** + * Multiply a matrix by a complex scalar value. + * @re Real part of scalar value. + * @im Imaginary part of scalar value. + * @m Input matrix to be multiplied. Output matrix. + */ +template +inline void MatrixScalarMultiply( + fp_type1 re, fp_type1 im, Matrix& m) { + for (unsigned i = 0; i < m.size() / 2; ++i) { + fp_type2 re0 = m[2 * i + 0]; + fp_type2 im0 = m[2 * i + 1]; + m[2 * i + 0] = re * re0 - im * im0; + m[2 * i + 1] = re * im0 + im * re0; + } +} + /** * Daggers a matrix. * @n Number of matrix rows (columns). diff --git a/lib/simulator.h b/lib/simulator.h index d5af3c2a..eff54415 100644 --- a/lib/simulator.h +++ b/lib/simulator.h @@ -41,22 +41,27 @@ class SimulatorBase { uint64_t* ms, uint64_t* xss) { constexpr unsigned hsize = 1 << H; - uint64_t xs[H]; - - xs[0] = uint64_t{1} << (qs[L] + 1); - ms[0] = (uint64_t{1} << qs[L]) - 1; - for (unsigned i = 1; i < H; ++i) { - xs[i] = uint64_t{1} << (qs[L + i] + 1); - ms[i] = ((uint64_t{1} << qs[L + i]) - 1) ^ (xs[i - 1] - 1); - } - ms[H] = ((uint64_t{1} << num_qubits) - 1) ^ (xs[H - 1] - 1); + if (H == 0) { + ms[0] = uint64_t(-1); + xss[0] = 0; + } else { + uint64_t xs[H + 1]; + + xs[0] = uint64_t{1} << (qs[L] + 1); + ms[0] = (uint64_t{1} << qs[L]) - 1; + for (unsigned i = 1; i < H; ++i) { + xs[i] = uint64_t{1} << (qs[L + i] + 1); + ms[i] = ((uint64_t{1} << qs[L + i]) - 1) ^ (xs[i - 1] - 1); + } + ms[H] = ((uint64_t{1} << num_qubits) - 1) ^ (xs[H - 1] - 1); - for (unsigned i = 0; i < hsize; ++i) { - uint64_t a = 0; - for (uint64_t k = 0; k < H; ++k) { - a += xs[k] * ((i >> k) & 1); + for (unsigned i = 0; i < hsize; ++i) { + uint64_t a = 0; + for (uint64_t k = 0; k < H; ++k) { + a += xs[k] * ((i >> k) & 1); + } + xss[i] = a; } - xss[i] = a; } } diff --git a/lib/simulator_avx.h b/lib/simulator_avx.h index 351e0832..97428497 100644 --- a/lib/simulator_avx.h +++ b/lib/simulator_avx.h @@ -51,6 +51,9 @@ class SimulatorAVX final : public SimulatorBase { // Assume qs[0] < qs[1] < qs[2] < ... . switch (qs.size()) { + case 0: + ApplyGateH<0>(qs, matrix, state); + break; case 1: if (qs[0] > 2) { ApplyGateH<1>(qs, matrix, state); @@ -137,6 +140,13 @@ class SimulatorAVX final : public SimulatorBase { } switch (qs.size()) { + case 0: + if (cqs[0] > 2) { + ApplyControlledGateHH<0>(qs, cqs, cvals, matrix, state); + } else { + ApplyControlledGateHL<0>(qs, cqs, cvals, matrix, state); + } + break; case 1: if (qs[0] > 2) { if (cqs[0] > 2) { @@ -322,7 +332,6 @@ class SimulatorAVX final : public SimulatorBase { } private: - #ifdef __BMI2__ template diff --git a/lib/simulator_avx512.h b/lib/simulator_avx512.h index 9e03a867..21a2e9d1 100644 --- a/lib/simulator_avx512.h +++ b/lib/simulator_avx512.h @@ -51,6 +51,9 @@ class SimulatorAVX512 final : public SimulatorBase { // Assume qs[0] < qs[1] < qs[2] < ... . switch (qs.size()) { + case 0: + ApplyGateH<0>(qs, matrix, state); + break; case 1: if (qs[0] > 3) { ApplyGateH<1>(qs, matrix, state); @@ -143,6 +146,13 @@ class SimulatorAVX512 final : public SimulatorBase { } switch (qs.size()) { + case 0: + if (cqs[0] > 3) { + ApplyControlledGateHH<0>(qs, cqs, cvals, matrix, state); + } else { + ApplyControlledGateHL<0>(qs, cqs, cvals, matrix, state); + } + break; case 1: if (qs[0] > 3) { if (cqs[0] > 3) { diff --git a/lib/simulator_basic.h b/lib/simulator_basic.h index 9e136c68..752eeb5d 100644 --- a/lib/simulator_basic.h +++ b/lib/simulator_basic.h @@ -49,6 +49,9 @@ class SimulatorBasic final : public SimulatorBase { // Assume qs[0] < qs[1] < qs[2] < ... . switch (qs.size()) { + case 0: + ApplyGateH<0>(qs, matrix, state); + break; case 1: ApplyGateH<1>(qs, matrix, state); break; @@ -92,6 +95,9 @@ class SimulatorBasic final : public SimulatorBase { } switch (qs.size()) { + case 0: + ApplyControlledGateH<0>(qs, cqs, cvals, matrix, state); + break; case 1: ApplyControlledGateH<1>(qs, cqs, cvals, matrix, state); break; diff --git a/lib/simulator_cuda.h b/lib/simulator_cuda.h index 66bf702a..5743bea8 100644 --- a/lib/simulator_cuda.h +++ b/lib/simulator_cuda.h @@ -71,7 +71,9 @@ class SimulatorCUDA final { const fp_type* matrix, State& state) const { // Assume qs[0] < qs[1] < qs[2] < ... . - if (qs[0] > 4) { + if (qs.size() == 0) { + ApplyGateH<0>(qs, matrix, state); + } else if (qs[0] > 4) { switch (qs.size()) { case 1: ApplyGateH<1>(qs, matrix, state); @@ -142,6 +144,9 @@ class SimulatorCUDA final { if (cqs[0] < 5) { switch (qs.size()) { + case 0: + ApplyControlledGateL<0>(qs, cqs, cvals, matrix, state); + break; case 1: ApplyControlledGateL<1>(qs, cqs, cvals, matrix, state); break; @@ -159,7 +164,9 @@ class SimulatorCUDA final { break; } } else { - if (qs[0] > 4) { + if (qs.size() == 0) { + ApplyControlledGateHH<0>(qs, cqs, cvals, matrix, state); + } else if (qs[0] > 4) { switch (qs.size()) { case 1: ApplyControlledGateHH<1>(qs, cqs, cvals, matrix, state); diff --git a/lib/simulator_custatevec.h b/lib/simulator_custatevec.h index df01d974..40d1902d 100644 --- a/lib/simulator_custatevec.h +++ b/lib/simulator_custatevec.h @@ -19,9 +19,11 @@ #include #include +#include #include #include +#include "io.h" #include "statespace_custatevec.h" #include "util_custatevec.h" @@ -43,8 +45,10 @@ class SimulatorCuStateVec final { static constexpr auto kComputeType = StateSpace::kComputeType; static constexpr auto kMatrixLayout = StateSpace::kMatrixLayout; - explicit SimulatorCuStateVec(const custatevecHandle_t& handle) - : handle_(handle), workspace_(nullptr), workspace_size_(0) {} + explicit SimulatorCuStateVec(const cublasHandle_t& cublas_handle, + const custatevecHandle_t& custatevec_handle) + : cublas_handle_(cublas_handle), custatevec_handle_(custatevec_handle), + workspace_(nullptr), workspace_size_(0) {} ~SimulatorCuStateVec() { ErrorCheck(cudaFree(workspace_)); @@ -58,15 +62,29 @@ class SimulatorCuStateVec final { */ void ApplyGate(const std::vector& qs, const fp_type* matrix, State& state) const { - auto workspace_size = ApplyGateWorkSpaceSize( - state.num_qubits(), qs.size(), 0, matrix); - AllocWorkSpace(workspace_size); - - ErrorCheck(custatevecApplyMatrix( - handle_, state.get(), kStateType, state.num_qubits(), - matrix, kMatrixType, kMatrixLayout, 0, - (int32_t*) qs.data(), qs.size(), nullptr, nullptr, 0, - kComputeType, workspace_, workspace_size)); + if (qs.size() == 0) { + uint64_t size = uint64_t{1} << state.num_qubits(); + + if (StateSpace::is_float) { + cuComplex a = {matrix[0], matrix[1]}; + auto p = (cuComplex*) state.get(); + ErrorCheck(cublasCscal(cublas_handle_, size, &a, p, 1)); + } else { + cuDoubleComplex a = {matrix[0], matrix[1]}; + auto p = (cuDoubleComplex*) state.get(); + ErrorCheck(cublasZscal(cublas_handle_, size, &a, p, 1)); + } + } else { + auto workspace_size = ApplyGateWorkSpaceSize( + state.num_qubits(), qs.size(), 0, matrix); + AllocWorkSpace(workspace_size); + + ErrorCheck(custatevecApplyMatrix( + custatevec_handle_, state.get(), kStateType, + state.num_qubits(), matrix, kMatrixType, kMatrixLayout, 0, + (int32_t*) qs.data(), qs.size(), nullptr, nullptr, 0, + kComputeType, workspace_, workspace_size)); + } } /** @@ -80,23 +98,30 @@ class SimulatorCuStateVec final { void ApplyControlledGate(const std::vector& qs, const std::vector& cqs, uint64_t cmask, const fp_type* matrix, State& state) const { - std::vector control_bits; - control_bits.reserve(cqs.size()); - - for (std::size_t i = 0; i < cqs.size(); ++i) { - control_bits.push_back((cmask >> i) & 1); - } + if (qs.size() == 0) { + IO::errorf( + "error: controlled global phase gate is not implemented %s %d\n", + __FILE__, __LINE__); + exit(1); + } else { + std::vector control_bits; + control_bits.reserve(cqs.size()); + + for (std::size_t i = 0; i < cqs.size(); ++i) { + control_bits.push_back((cmask >> i) & 1); + } - auto workspace_size = ApplyGateWorkSpaceSize( - state.num_qubits(), qs.size(), cqs.size(), matrix); - AllocWorkSpace(workspace_size); + auto workspace_size = ApplyGateWorkSpaceSize( + state.num_qubits(), qs.size(), cqs.size(), matrix); + AllocWorkSpace(workspace_size); - ErrorCheck(custatevecApplyMatrix( - handle_, state.get(), kStateType, state.num_qubits(), - matrix, kMatrixType, kMatrixLayout, 0, - (int32_t*) qs.data(), qs.size(), - (int32_t*) cqs.data(), control_bits.data(), cqs.size(), - kComputeType, workspace_, workspace_size)); + ErrorCheck(custatevecApplyMatrix( + custatevec_handle_, state.get(), kStateType, + state.num_qubits(), matrix, kMatrixType, kMatrixLayout, 0, + (int32_t*) qs.data(), qs.size(), + (int32_t*) cqs.data(), control_bits.data(), cqs.size(), + kComputeType, workspace_, workspace_size)); + } } /** @@ -117,9 +142,9 @@ class SimulatorCuStateVec final { cuDoubleComplex eval; ErrorCheck(custatevecComputeExpectation( - handle_, state.get(), kStateType, state.num_qubits(), - &eval, kExpectType, nullptr, matrix, kMatrixType, - kMatrixLayout, (int32_t*) qs.data(), qs.size(), + custatevec_handle_, state.get(), kStateType, + state.num_qubits(), &eval, kExpectType, nullptr, matrix, + kMatrixType, kMatrixLayout, (int32_t*) qs.data(), qs.size(), kComputeType, workspace_, workspace_size)); return {cuCreal(eval), cuCimag(eval)}; @@ -139,9 +164,9 @@ class SimulatorCuStateVec final { size_t size; ErrorCheck(custatevecApplyMatrixGetWorkspaceSize( - handle_, kStateType, num_qubits, matrix, kMatrixType, - kMatrixLayout, 0, num_targets, num_controls, kComputeType, - &size)); + custatevec_handle_, kStateType, num_qubits, matrix, + kMatrixType, kMatrixLayout, 0, num_targets, num_controls, + kComputeType, &size)); return size; } @@ -151,8 +176,9 @@ class SimulatorCuStateVec final { size_t size; ErrorCheck(custatevecComputeExpectationGetWorkspaceSize( - handle_, kStateType, num_qubits, matrix, kMatrixType, - kMatrixLayout, num_targets, kComputeType, &size)); + custatevec_handle_, kStateType, num_qubits, matrix, + kMatrixType, kMatrixLayout, num_targets, kComputeType, + &size)); return size; } @@ -171,7 +197,8 @@ class SimulatorCuStateVec final { return workspace_; } - const custatevecHandle_t handle_; + const cublasHandle_t cublas_handle_; + const custatevecHandle_t custatevec_handle_; void* workspace_; size_t workspace_size_; diff --git a/lib/simulator_sse.h b/lib/simulator_sse.h index 50279e22..5256c53b 100644 --- a/lib/simulator_sse.h +++ b/lib/simulator_sse.h @@ -51,6 +51,9 @@ class SimulatorSSE final : public SimulatorBase { // Assume qs[0] < qs[1] < qs[2] < ... . switch (qs.size()) { + case 0: + ApplyGateH<0>(qs, matrix, state); + break; case 1: if (qs[0] > 1) { ApplyGateH<1>(qs, matrix, state); @@ -129,6 +132,13 @@ class SimulatorSSE final : public SimulatorBase { } switch (qs.size()) { + case 0: + if (cqs[0] > 1) { + ApplyControlledGateHH<0>(qs, cqs, cvals, matrix, state); + } else { + ApplyControlledGateHL<0>(qs, cqs, cvals, matrix, state); + } + break; case 1: if (qs[0] > 1) { if (cqs[0] > 1) { diff --git a/lib/statespace_custatevec.h b/lib/statespace_custatevec.h index 12d8db55..f2f5de10 100644 --- a/lib/statespace_custatevec.h +++ b/lib/statespace_custatevec.h @@ -59,10 +59,8 @@ class StateSpaceCuStateVec : using State = typename Base::State; using fp_type = typename Base::fp_type; - private: static constexpr auto is_float = std::is_same::value; - public: static constexpr auto kStateType = is_float ? CUDA_C_32F : CUDA_C_64F; static constexpr auto kMatrixType = kStateType; static constexpr auto kExpectType = CUDA_C_64F; diff --git a/pybind_interface/custatevec/pybind_main_custatevec.cpp b/pybind_interface/custatevec/pybind_main_custatevec.cpp index 1e399633..1fe1d70b 100644 --- a/pybind_interface/custatevec/pybind_main_custatevec.cpp +++ b/pybind_interface/custatevec/pybind_main_custatevec.cpp @@ -46,7 +46,7 @@ struct Factory { } Simulator CreateSimulator() const { - return Simulator(custatevec_handle); + return Simulator(cublas_handle, custatevec_handle); } cublasHandle_t cublas_handle; diff --git a/pybind_interface/pybind_main.cpp b/pybind_interface/pybind_main.cpp index 64df5d2a..49f63ece 100644 --- a/pybind_interface/pybind_main.cpp +++ b/pybind_interface/pybind_main.cpp @@ -233,6 +233,9 @@ Cirq::GateCirq create_matrix_gate(const unsigned time, const std::vector& qubits, const std::vector& matrix) { switch (qubits.size()) { + case 0: + // Global phase gate. + return Cirq::GlobalPhaseGate::Create(time, matrix[0], matrix[1]); case 1: return Cirq::MatrixGate1::Create(time, qubits[0], matrix); case 2: diff --git a/qsimcirq_tests/qsimcirq_test.py b/qsimcirq_tests/qsimcirq_test.py index 4aaa040b..de2b6a85 100644 --- a/qsimcirq_tests/qsimcirq_test.py +++ b/qsimcirq_tests/qsimcirq_test.py @@ -1953,3 +1953,35 @@ def test_qsimcirq_identity_expectation_value(): cirqSim = cirq.Simulator() cirq_result = cirqSim.simulate_expectation_values(cirq_circuit, hamiltonian) assert cirq.approx_eq(qsimcirq_result, cirq_result, atol=1e-6) + + +def test_cirq_global_phase_gate(): + qsim_sim = qsimcirq.QSimSimulator() + cirq_sim = cirq.Simulator() + + a, b, c = [cirq.LineQubit(0), cirq.LineQubit(1), cirq.LineQubit(2)] + + circuit = cirq.Circuit( + [ + cirq.Moment( + [ + cirq.H(a), + cirq.H(b), + cirq.H(c), + cirq.global_phase_operation(np.exp(0.4j * np.pi)), + ] + ), + cirq.Moment( + [ + cirq.global_phase_operation(np.exp(0.7j * np.pi)).controlled_by(a), + ] + ), + ] + ) + + cirq_result = cirq_sim.simulate(circuit) + qsim_result = qsim_sim.simulate(circuit) + + assert cirq.approx_eq( + qsim_result.state_vector(), cirq_result.state_vector(), atol=1e-6 + ) diff --git a/tests/circuit_qsim_parser_test.cc b/tests/circuit_qsim_parser_test.cc index b5d97826..36b2cac4 100644 --- a/tests/circuit_qsim_parser_test.cc +++ b/tests/circuit_qsim_parser_test.cc @@ -54,6 +54,7 @@ R"(2 12 cp 0 1 0.5 13 m 0 14 m 1 +15 p 0.3 )"; Circuit> circuit; @@ -61,7 +62,7 @@ R"(2 EXPECT_TRUE(CircuitQsimParser::FromStream(99, provider, ss1, circuit)); EXPECT_EQ(circuit.num_qubits, 2); - EXPECT_EQ(circuit.gates.size(), 21); + EXPECT_EQ(circuit.gates.size(), 22); std::stringstream ss2(valid_circuit); @@ -75,17 +76,20 @@ TEST(CircuitQsimParserTest, ValidCircuitWithControlledGates) { R"(5 0 c 0 1 h 2 1 c 4 3 2 is 0 1 +2 c 2 4 p 0.5 )"; Circuit> circuit; std::stringstream ss(valid_circuit); EXPECT_TRUE(CircuitQsimParser::FromStream(99, provider, ss, circuit)); EXPECT_EQ(circuit.num_qubits, 5); - EXPECT_EQ(circuit.gates.size(), 2); + EXPECT_EQ(circuit.gates.size(), 3); EXPECT_EQ(circuit.gates[0].qubits.size(), 1); EXPECT_EQ(circuit.gates[0].controlled_by.size(), 2); EXPECT_EQ(circuit.gates[1].qubits.size(), 2); EXPECT_EQ(circuit.gates[1].controlled_by.size(), 3); + EXPECT_EQ(circuit.gates[2].qubits.size(), 0); + EXPECT_EQ(circuit.gates[2].controlled_by.size(), 2); } TEST(CircuitQsimParserTest, ValidTimeOrder) { diff --git a/tests/fuser_basic_test.cc b/tests/fuser_basic_test.cc index 1eb85c28..7da2e1d6 100644 --- a/tests/fuser_basic_test.cc +++ b/tests/fuser_basic_test.cc @@ -1166,6 +1166,71 @@ bool TestFusedGates(unsigned num_qubits, } // namespace +TEST(FuserBasicTest, SmallCircuits) { + using Gate = GateQSim; + using Fuser = BasicGateFuser; + + Fuser::Parameter param; + param.verbosity = 0; + + { + unsigned num_qubits = 2; + std::vector circuit = { + GateGPh::Create(0, -1, 0), + }; + + auto fused_gates = Fuser::FuseGates( + param, num_qubits, circuit.begin(), circuit.end(), false); + + EXPECT_TRUE(TestFusedGates(num_qubits, circuit, fused_gates)); + EXPECT_EQ(fused_gates.size(), 1); + } + + { + unsigned num_qubits = 2; + std::vector circuit = { + GateGPh::Create(0, -1, 0), + GateGPh::Create(0, -1, 0), + }; + + auto fused_gates = Fuser::FuseGates( + param, num_qubits, circuit.begin(), circuit.end(), false); + + EXPECT_TRUE(TestFusedGates(num_qubits, circuit, fused_gates)); + EXPECT_EQ(fused_gates.size(), 1); + } + + { + unsigned num_qubits = 2; + std::vector circuit = { + GateCZ::Create(0, 0, 1), + GateGPh::Create(0, -1, 0), + }; + + auto fused_gates = Fuser::FuseGates( + param, num_qubits, circuit.begin(), circuit.end(), false); + + EXPECT_TRUE(TestFusedGates(num_qubits, circuit, fused_gates)); + EXPECT_EQ(fused_gates.size(), 1); + } + + { + unsigned num_qubits = 2; + std::vector circuit = { + GateZ::Create(0, 0).ControlledBy({1}, {1}), + GateCZ::Create(1, 0, 1), + GateGPh::Create(1, -1, 0), + }; + + auto fused_gates = Fuser::FuseGates( + param, num_qubits, circuit.begin(), circuit.end(), false); + + EXPECT_TRUE(TestFusedGates(num_qubits, circuit, fused_gates)); + EXPECT_EQ(fused_gates.size(), 2); + EXPECT_EQ(fused_gates[1].gates[1], &circuit[2]); + } +} + TEST(FuserBasicTest, ValidTimeOrder) { using Gate = GateQSim; using Fuser = BasicGateFuser; @@ -1304,6 +1369,35 @@ TEST(FuserBasicTest, ValidTimeOrder) { EXPECT_EQ(fused_gates.size(), 7); EXPECT_TRUE(TestFusedGates(num_qubits, circuit, fused_gates)); } + + { + unsigned num_qubits = 2; + std::vector circuit = { + GateCZ::Create(1, 0, 1), + GateGPh::Create(2, -1, 0), + }; + + std::vector time_boundary = {1}; + auto fused_gates = Fuser::FuseGates(param, num_qubits, circuit.begin(), + circuit.end(), time_boundary, false); + + EXPECT_EQ(fused_gates.size(), 2); + EXPECT_TRUE(TestFusedGates(num_qubits, circuit, fused_gates)); + } + + { + unsigned num_qubits = 2; + std::vector circuit = { + GateCZ::Create(1, 0, 1), + GateGPh::Create(0, -1, 0), + }; + + auto fused_gates = Fuser::FuseGates( + param, num_qubits, circuit.begin(), circuit.end(), false); + + EXPECT_TRUE(TestFusedGates(num_qubits, circuit, fused_gates)); + EXPECT_EQ(fused_gates.size(), 1); + } } TEST(FuserBasicTest, InvalidTimeOrder) { @@ -1436,6 +1530,20 @@ TEST(FuserBasicTest, InvalidTimeOrder) { EXPECT_EQ(fused_gates.size(), 0); } + + { + unsigned num_qubits = 2; + std::vector circuit = { + GateCZ::Create(1, 0, 1), + GateGPh::Create(0, -1, 0), + }; + + std::vector time_boundary = {1}; + auto fused_gates = Fuser::FuseGates(param, num_qubits, circuit.begin(), + circuit.end(), time_boundary, false); + + EXPECT_EQ(fused_gates.size(), 0); + } } TEST(FuserBasicTest, QubitsOutOfRange) { diff --git a/tests/fuser_mqubit_test.cc b/tests/fuser_mqubit_test.cc index cf49b73b..d6a42dc1 100644 --- a/tests/fuser_mqubit_test.cc +++ b/tests/fuser_mqubit_test.cc @@ -85,7 +85,8 @@ std::vector GenQubits(unsigned num_qubits, std::mt19937& rgen, return qubits; } -constexpr double p1 = 0.2; +constexpr double p0 = 0.02; +constexpr double p1 = 0.18 + p0; constexpr double p2 = 0.6 + p1; constexpr double p3 = 0.08 + p2; constexpr double p4 = 0.05 + p3; @@ -103,7 +104,9 @@ void AddToCircuit(unsigned time, std::vector& circuit) { double r = distr(rgen); - if (r < p1) { + if (r < p0) { + circuit.push_back(CreateDummyGate(time, {})); + } else if (r < p1) { circuit.push_back(CreateDummyGate(time, GenQubits(1, rgen, n, available))); } else if (r < p2) { circuit.push_back(CreateDummyGate(time, GenQubits(2, rgen, n, available))); @@ -342,7 +345,7 @@ TEST(FuserMultiQubitTest, Simulation) { for (unsigned i = 0; i < size; ++i) { gate.matrix.push_back(2 * distr(rgen) - 1); } - }; + } using StateSpace = typename Simulator::StateSpace; @@ -653,6 +656,67 @@ TEST(FuserMultiQubitTest, SmallCircuits) { EXPECT_EQ(fused_gates.size(), 4); } } + + { + unsigned num_qubits = 2; + std::vector circuit = { + CreateDummyGate(0, {}), + }; + + param.max_fused_size = 2; + auto fused_gates = Fuser::FuseGates( + param, num_qubits, circuit.begin(), circuit.end(), false); + + EXPECT_TRUE(TestFusedGates(num_qubits, circuit, fused_gates)); + EXPECT_EQ(fused_gates.size(), 1); + } + + { + unsigned num_qubits = 2; + std::vector circuit = { + CreateDummyGate(0, {}), + CreateDummyGate(0, {}), + }; + + param.max_fused_size = 2; + auto fused_gates = Fuser::FuseGates( + param, num_qubits, circuit.begin(), circuit.end(), false); + + EXPECT_TRUE(TestFusedGates(num_qubits, circuit, fused_gates)); + EXPECT_EQ(fused_gates.size(), 1); + } + + { + unsigned num_qubits = 2; + std::vector circuit = { + CreateDummyGate(0, {0, 1}), + CreateDummyGate(0, {}), + }; + + param.max_fused_size = 2; + auto fused_gates = Fuser::FuseGates( + param, num_qubits, circuit.begin(), circuit.end(), false); + + EXPECT_TRUE(TestFusedGates(num_qubits, circuit, fused_gates)); + EXPECT_EQ(fused_gates.size(), 1); + } + + { + unsigned num_qubits = 2; + std::vector circuit = { + CreateDummyControlledGate(0, {0}, {1}), + CreateDummyGate(1, {0, 1}), + CreateDummyGate(1, {}), + }; + + param.max_fused_size = 2; + auto fused_gates = Fuser::FuseGates( + param, num_qubits, circuit.begin(), circuit.end(), false); + + EXPECT_TRUE(TestFusedGates(num_qubits, circuit, fused_gates)); + EXPECT_EQ(fused_gates.size(), 2); + EXPECT_EQ(fused_gates[1].gates[1], &circuit[2]); + } } TEST(FuserMultiQubitTest, ValidTimeOrder) { @@ -845,6 +909,37 @@ TEST(FuserMultiQubitTest, ValidTimeOrder) { EXPECT_EQ(fused_gates.size(), 3); EXPECT_TRUE(TestFusedGates(num_qubits, circuit, fused_gates)); } + + { + unsigned num_qubits = 2; + std::vector circuit = { + CreateDummyGate(1, {0, 1}), + CreateDummyGate(2, {}), + }; + + param.max_fused_size = 2; + std::vector time_boundary = {1}; + auto fused_gates = Fuser::FuseGates(param, num_qubits, circuit.begin(), + circuit.end(), time_boundary, false); + + EXPECT_TRUE(TestFusedGates(num_qubits, circuit, fused_gates)); + EXPECT_EQ(fused_gates.size(), 2); + } + + { + unsigned num_qubits = 2; + std::vector circuit = { + CreateDummyGate(1, {0, 1}), + CreateDummyGate(0, {}), + }; + + param.max_fused_size = 2; + auto fused_gates = Fuser::FuseGates( + param, num_qubits, circuit.begin(), circuit.end(), false); + + EXPECT_TRUE(TestFusedGates(num_qubits, circuit, fused_gates)); + EXPECT_EQ(fused_gates.size(), 1); + } } TEST(FuserMultiQubitTest, InvalidTimeOrder) { @@ -980,6 +1075,21 @@ TEST(FuserMultiQubitTest, InvalidTimeOrder) { EXPECT_EQ(fused_gates.size(), 0); } + + { + unsigned num_qubits = 2; + std::vector circuit = { + CreateDummyGate(1, {0, 1}), + CreateDummyGate(0, {}), + }; + + param.max_fused_size = 2; + std::vector time_boundary = {1}; + auto fused_gates = Fuser::FuseGates(param, num_qubits, circuit.begin(), + circuit.end(), time_boundary, false); + + EXPECT_EQ(fused_gates.size(), 0); + } } TEST(FuserMultiQubitTest, QubitsOutOfRange) { diff --git a/tests/gates_cirq_testfixture.h b/tests/gates_cirq_testfixture.h index 87656764..39e51873 100644 --- a/tests/gates_cirq_testfixture.h +++ b/tests/gates_cirq_testfixture.h @@ -120,6 +120,8 @@ Circuit> GetCircuit(bool qsim) { circuit.gates.emplace_back(Cirq::rz::Create(18, 1, 0.4)); circuit.gates.emplace_back(Cirq::rx::Create(18, 2, 0.7)); circuit.gates.emplace_back(Cirq::S::Create(18, 3)); + circuit.gates.emplace_back( + Cirq::GlobalPhaseGate::Create(18, -1, 0)); circuit.gates.emplace_back(Cirq::I::Create(19, {0, 1, 2, 3})); @@ -208,22 +210,22 @@ std::vector> expected_results0 = { }; std::vector> expected_results1 = { - {-0.014675243, 0.05654204}, - {-0.0075858636, 0.28545904}, - {0.044140648, 0.053896483}, - {-0.033529136, 0.32497203}, - {-0.13991567, -0.13084067}, - {0.234054, -0.07352882}, - {-0.14253256, -0.022177307}, - {-0.09260284, -0.13516076}, - {-0.061443992, -0.14103678}, - {0.25451535, 0.22917412}, - {-0.34202546, -0.27581766}, - {0.0010748552, 0.1542618}, - {0.07094702, -0.21318978}, - {0.06633715, 0.37584817}, - {0.2312484, 0.09549438}, - {-0.18656375, -0.08693269}, + {0.014675243, -0.05654204}, + {0.0075858636, -0.28545904}, + {-0.044140648, -0.053896483}, + {0.033529136, -0.32497203}, + {0.13991567, 0.13084067}, + {-0.234054, 0.07352882}, + {0.14253256, 0.022177307}, + {0.09260284, 0.13516076}, + {0.061443992, 0.14103678}, + {-0.25451535, -0.22917412}, + {0.34202546, 0.27581766}, + {-0.0010748552, -0.1542618}, + {-0.07094702, 0.21318978}, + {-0.06633715, -0.37584817}, + {-0.2312484, -0.09549438}, + {0.18656375, 0.08693269}, }; /* @@ -340,6 +342,7 @@ def main(): cirq.rz(0.4)(q1), cirq.rx(0.7)(q2), cirq.S(q3), + cirq.global_phase_operation(-1), ]), # The following moment should not be included if qsim is false above. cirq.Moment([ diff --git a/tests/hybrid_custatevec_test.cu b/tests/hybrid_custatevec_test.cu index 82dbeafc..ef637ca9 100644 --- a/tests/hybrid_custatevec_test.cu +++ b/tests/hybrid_custatevec_test.cu @@ -44,7 +44,7 @@ struct Factory { } Simulator CreateSimulator() const { - return Simulator(custatevec_handle); + return Simulator(cublas_handle, custatevec_handle); } cublasHandle_t cublas_handle; diff --git a/tests/matrix_test.cc b/tests/matrix_test.cc index 7a8b2309..1ecf193b 100644 --- a/tests/matrix_test.cc +++ b/tests/matrix_test.cc @@ -161,7 +161,7 @@ TEST(MatrixTest, MatrixMultiply2) { } TEST(MatrixTest, MatrixScalarMultiply) { - Matrix m1 = {1, 2, 3, 4, 5, 6, 7, 8}; + Matrix m1 = {1, 2, 3, 4, 5, 6, 7, 8}; MatrixScalarMultiply(3, m1); @@ -169,6 +169,15 @@ TEST(MatrixTest, MatrixScalarMultiply) { EXPECT_FLOAT_EQ(m1[i], (i + 1) * 3); } + m1 = {1, 2, 3, 4, 5, 6, 7, 8}; + + MatrixScalarMultiply(2, 3, m1); + + for (unsigned i = 0; i < 4; ++i) { + EXPECT_FLOAT_EQ(m1[2 * i], -2 * float(i) - 4); + EXPECT_FLOAT_EQ(m1[2 * i + 1], 10 * i + 7); + } + Matrix m2 = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, @@ -179,6 +188,18 @@ TEST(MatrixTest, MatrixScalarMultiply) { for (unsigned i = 0; i < 32; ++i) { EXPECT_FLOAT_EQ(m2[i], (i + 1) * 3); } + + m2 = { 1, 2, 3, 4, 5, 6, 7, 8, + 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32}; + + MatrixScalarMultiply(2, 3, m2); + + for (unsigned i = 0; i < 16; ++i) { + EXPECT_FLOAT_EQ(m2[2 * i], -2 * float(i) - 4); + EXPECT_FLOAT_EQ(m2[2 * i + 1], 10 * i + 7); + } } TEST(MatrixTest, MatrixDagger) { diff --git a/tests/qtrajectory_custatevec_test.cu b/tests/qtrajectory_custatevec_test.cu index ca7e823b..1001805e 100644 --- a/tests/qtrajectory_custatevec_test.cu +++ b/tests/qtrajectory_custatevec_test.cu @@ -44,7 +44,7 @@ struct Factory { } Simulator CreateSimulator() const { - return Simulator(custatevec_handle); + return Simulator(cublas_handle, custatevec_handle); } cublasHandle_t cublas_handle; diff --git a/tests/simulator_avx512_test.cc b/tests/simulator_avx512_test.cc index fbc9510e..4c1070b4 100644 --- a/tests/simulator_avx512_test.cc +++ b/tests/simulator_avx512_test.cc @@ -84,6 +84,10 @@ TYPED_TEST(SimulatorAVX512Test, ControlledGates) { TestControlledGates(Factory(), false); } +TYPED_TEST(SimulatorAVX512Test, GlobalPhaseGate) { + TestGlobalPhaseGate(Factory()); +} + TYPED_TEST(SimulatorAVX512Test, ExpectationValue1) { TestExpectationValue1(Factory()); } diff --git a/tests/simulator_avx_test.cc b/tests/simulator_avx_test.cc index f5c87a09..6c125e70 100644 --- a/tests/simulator_avx_test.cc +++ b/tests/simulator_avx_test.cc @@ -82,6 +82,10 @@ TYPED_TEST(SimulatorAVXTest, ControlledGates) { TestControlledGates(Factory(), false); } +TYPED_TEST(SimulatorAVXTest, GlobalPhaseGate) { + TestGlobalPhaseGate(Factory()); +} + TYPED_TEST(SimulatorAVXTest, ExpectationValue1) { TestExpectationValue1(Factory()); } diff --git a/tests/simulator_basic_test.cc b/tests/simulator_basic_test.cc index ee4c60b5..46713b3b 100644 --- a/tests/simulator_basic_test.cc +++ b/tests/simulator_basic_test.cc @@ -82,6 +82,10 @@ TYPED_TEST(SimulatorBasicTest, ControlledGates) { TestControlledGates(Factory(), true); } +TYPED_TEST(SimulatorBasicTest, GlobalPhaseGate) { + TestGlobalPhaseGate(Factory()); +} + TYPED_TEST(SimulatorBasicTest, ExpectationValue1) { TestExpectationValue1(Factory()); } diff --git a/tests/simulator_cuda_test.cu b/tests/simulator_cuda_test.cu index baecfd45..dd507da6 100644 --- a/tests/simulator_cuda_test.cu +++ b/tests/simulator_cuda_test.cu @@ -104,6 +104,13 @@ TYPED_TEST(SimulatorCUDATest, ControlledGates) { TestControlledGates(factory, high_precision); } +TYPED_TEST(SimulatorCUDATest, GlobalPhaseGate) { + using Factory = qsim::Factory; + typename Factory::StateSpace::Parameter param; + Factory factory(param); + TestGlobalPhaseGate(factory); +} + TYPED_TEST(SimulatorCUDATest, ExpectationValue1) { using Factory = qsim::Factory; typename Factory::StateSpace::Parameter param; diff --git a/tests/simulator_custatevec_test.cu b/tests/simulator_custatevec_test.cu index 646e0c07..1a2cc245 100644 --- a/tests/simulator_custatevec_test.cu +++ b/tests/simulator_custatevec_test.cu @@ -52,7 +52,7 @@ struct Factory { } Simulator CreateSimulator() const { - return Simulator(custatevec_handle); + return Simulator(cublas_handle, custatevec_handle); } cublasHandle_t cublas_handle; @@ -89,7 +89,11 @@ TYPED_TEST(SimulatorCuStateVecTest, MultiQubitGates) { TYPED_TEST(SimulatorCuStateVecTest, ControlledGates) { bool high_precision = std::is_same::value; - TestControlledGates(qsim::Factory(), high_precision); + TestControlledGates(qsim::Factory(), high_precision, true); +} + +TYPED_TEST(SimulatorCuStateVecTest, GlobalPhaseGate) { + TestGlobalPhaseGate(qsim::Factory()); } TYPED_TEST(SimulatorCuStateVecTest, ExpectationValue1) { diff --git a/tests/simulator_sse_test.cc b/tests/simulator_sse_test.cc index fda6ebfb..4032213b 100644 --- a/tests/simulator_sse_test.cc +++ b/tests/simulator_sse_test.cc @@ -82,6 +82,10 @@ TYPED_TEST(SimulatorSSETest, ControlledGates) { TestControlledGates(Factory(), false); } +TYPED_TEST(SimulatorSSETest, GlobalPhaseGate) { + TestGlobalPhaseGate(Factory()); +} + TYPED_TEST(SimulatorSSETest, ExpectationValue1) { TestExpectationValue1(Factory()); } diff --git a/tests/simulator_testfixture.h b/tests/simulator_testfixture.h index 885afede..2e15c287 100644 --- a/tests/simulator_testfixture.h +++ b/tests/simulator_testfixture.h @@ -222,6 +222,7 @@ void TestApplyGate5(const Factory& factory) { auto gate23 = GateCP::Create(10, 0, 1, 0.7); auto gate24 = GateY2::Create(11, 3); auto gate25 = GateRX::Create(11, 4, 0.3); + auto gate26 = GateGPh::Create(12, -1, 0); GateFused> fgate1{kGateCZ, 2, {0, 1}, &gate11, {&gate1, &gate2, &gate6, &gate7, &gate11, &gate12, &gate13}, {}}; @@ -312,7 +313,7 @@ void TestApplyGate5(const Factory& factory) { } GateFused> fgate5{kGateCP, 10, {0, 1}, &gate23, - {&gate23}, {}}; + {&gate23, &gate26}, {}}; CalculateFusedMatrix(fgate5); ApplyFusedGate(simulator, fgate5, state); @@ -320,17 +321,17 @@ void TestApplyGate5(const Factory& factory) { { auto ampl0 = state_space.GetAmpl(state, 0); - EXPECT_NEAR(std::real(ampl0), -0.00938794, 1e-6); - EXPECT_NEAR(std::imag(ampl0), -0.15174214, 1e-6); + EXPECT_NEAR(std::real(ampl0), 0.00938794, 1e-6); + EXPECT_NEAR(std::imag(ampl0), 0.15174214, 1e-6); auto ampl1 = state_space.GetAmpl(state, 1); - EXPECT_NEAR(std::real(ampl1), 0.18047242, 1e-6); - EXPECT_NEAR(std::imag(ampl1), 0.13247597, 1e-6); + EXPECT_NEAR(std::real(ampl1), -0.18047242, 1e-6); + EXPECT_NEAR(std::imag(ampl1), -0.13247597, 1e-6); auto ampl2 = state_space.GetAmpl(state, 2); - EXPECT_NEAR(std::real(ampl2), 0.03860849, 1e-6); - EXPECT_NEAR(std::imag(ampl2), 0.16120625, 1e-6); + EXPECT_NEAR(std::real(ampl2), -0.03860849, 1e-6); + EXPECT_NEAR(std::imag(ampl2), -0.16120625, 1e-6); auto ampl3 = state_space.GetAmpl(state, 3); - EXPECT_NEAR(std::real(ampl3), 0.00843010, 1e-6); - EXPECT_NEAR(std::imag(ampl3), -0.02001594, 1e-6); + EXPECT_NEAR(std::real(ampl3), -0.00843010, 1e-6); + EXPECT_NEAR(std::imag(ampl3), 0.02001594, 1e-6); } ApplyGate(simulator, gate24, state); @@ -340,17 +341,17 @@ void TestApplyGate5(const Factory& factory) { { auto ampl0 = state_space.GetAmpl(state, 0); - EXPECT_NEAR(std::real(ampl0), 0.05261526, 1e-6); - EXPECT_NEAR(std::imag(ampl0), -0.03246338, 1e-6); + EXPECT_NEAR(std::real(ampl0), -0.05261526, 1e-6); + EXPECT_NEAR(std::imag(ampl0), 0.03246338, 1e-6); auto ampl1 = state_space.GetAmpl(state, 1); - EXPECT_NEAR(std::real(ampl1), 0.02790548, 1e-6); - EXPECT_NEAR(std::imag(ampl1), -0.02198864, 1e-6); + EXPECT_NEAR(std::real(ampl1), -0.02790548, 1e-6); + EXPECT_NEAR(std::imag(ampl1), 0.02198864, 1e-6); auto ampl2 = state_space.GetAmpl(state, 2); - EXPECT_NEAR(std::real(ampl2), 0.10250939, 1e-6); - EXPECT_NEAR(std::imag(ampl2), -0.02654653, 1e-6); + EXPECT_NEAR(std::real(ampl2), -0.10250939, 1e-6); + EXPECT_NEAR(std::imag(ampl2), 0.02654653, 1e-6); auto ampl3 = state_space.GetAmpl(state, 3); - EXPECT_NEAR(std::real(ampl3), -0.03221833, 1e-6); - EXPECT_NEAR(std::imag(ampl3), -0.11284899, 1e-6); + EXPECT_NEAR(std::real(ampl3), 0.03221833, 1e-6); + EXPECT_NEAR(std::imag(ampl3), 0.11284899, 1e-6); } } @@ -1168,8 +1169,7 @@ void TestMultiQubitGates(const Factory& factory) { fp_type inorm = std::sqrt(1.0 / (1 << num_qubits)); unsigned max_gate_qubits2 = std::min(max_gate_qubits, num_qubits); - for (unsigned q = 1; q <= max_gate_qubits2; ++q) { - + for (unsigned q = 0; q <= max_gate_qubits2; ++q) { unsigned size1 = 1 << q; unsigned size2 = size1 * size1; @@ -1211,7 +1211,8 @@ void TestMultiQubitGates(const Factory& factory) { } template -void TestControlledGates(const Factory& factory, bool high_precision) { +void TestControlledGates( + const Factory& factory, bool high_precision, bool custatevec = false) { using Simulator = typename Factory::Simulator; using StateSpace = typename Simulator::StateSpace; using fp_type = typename StateSpace::fp_type; @@ -1269,21 +1270,23 @@ void TestControlledGates(const Factory& factory, bool high_precision) { } if (cmask != (mask & cmask)) continue; + if (custatevec && qubits.size() == 0) continue; unsigned num_available = num_qubits - cqubits.size(); - if (qubits.size() == 0 - || qubits.size() > std::min(max_target_qubits, num_available)) { + if (qubits.size() > std::min(max_target_qubits, num_available)) { continue; } - // Target qubits are consecuitive. - std::size_t i = 1; - for (; i < qubits.size(); ++i) { - if (qubits[i - 1] + 1 != qubits[i]) break; + if (qubits.size() > 1) { + // Target qubits are consecuitive. + std::size_t i = 1; + for (; i < qubits.size(); ++i) { + if (qubits[i - 1] + 1 != qubits[i]) break; + } + if (i < qubits.size()) continue; } - if (i < qubits.size()) continue; - unsigned k = qubits[0]; + unsigned k = qubits.size() > 0 ? qubits[0] : 0; unsigned size1 = 1 << qubits.size(); unsigned size2 = size1 * size1; @@ -1357,6 +1360,57 @@ void TestControlledGates(const Factory& factory, bool high_precision) { } } +template +void TestGlobalPhaseGate(const Factory& factory) { + using Simulator = typename Factory::Simulator; + using StateSpace = typename Simulator::StateSpace; + using fp_type = typename StateSpace::fp_type; + + unsigned max_num_qubits = 8 + std::log2(Simulator::SIMDRegisterSize()); + + StateSpace state_space = factory.CreateStateSpace(); + Simulator simulator = factory.CreateSimulator(); + + fp_type c = 0.8; + fp_type s = 0.5; + + auto gate = GateGPh::Create(0, c, s); + + std::vector vec(state_space.MinSize(max_num_qubits)); + + for (unsigned num_qubits = 1; num_qubits <= max_num_qubits; ++num_qubits) { + uint64_t size = uint64_t{1} << num_qubits; + + fp_type a = 6.0 / size; + + for (uint64_t i = 0; i < size; ++i) { + vec[2 * i] = std::cos(a * i); + vec[2 * i + 1] = std::sin(a * i); + } + + auto state = state_space.Create(num_qubits); + state_space.SetAllZeros(state); + + state_space.Copy(vec.data(), state); + state_space.NormalToInternalOrder(state); + + ApplyGate(simulator, gate, state); + + state_space.InternalToNormalOrder(state); + state_space.Copy(state, vec.data()); + + for (uint64_t i = 0; i < size; ++i) { + fp_type re = std::cos(a * i); + fp_type im = std::sin(a * i); + fp_type expected_re = re * c - im * s; + fp_type expected_im = re * s + im * c; + + EXPECT_NEAR(vec[2 * i], expected_re, 1e-6); + EXPECT_NEAR(vec[2 * i + 1], expected_im, 1e-6); + } + } +} + template void TestExpectationValue1(const Factory& factory) { using Simulator = typename Factory::Simulator; diff --git a/tests/statespace_custatevec_test.cu b/tests/statespace_custatevec_test.cu index 841be8ee..058461f9 100644 --- a/tests/statespace_custatevec_test.cu +++ b/tests/statespace_custatevec_test.cu @@ -51,7 +51,7 @@ struct Factory { } Simulator CreateSimulator() const { - return Simulator(custatevec_handle); + return Simulator(cublas_handle, custatevec_handle); } cublasHandle_t cublas_handle;