diff --git a/qulacs/CMakeLists.txt b/qulacs/CMakeLists.txt index f4372170..97d765d5 100644 --- a/qulacs/CMakeLists.txt +++ b/qulacs/CMakeLists.txt @@ -1,13 +1,17 @@ cmake_minimum_required(VERSION 3.21) target_sources(qulacs PRIVATE + gate/gate_npair_qubit.cpp gate/gate_one_control_one_target.cpp gate/gate_one_qubit.cpp gate/gate_quantum_matrix.cpp - gate/update_ops_matrix_dense.cpp + gate/gate_two_qubit.cpp + gate/update_ops_dense_matrix.cpp + gate/update_ops_npair_qubit.cpp + gate/update_ops_one_control_one_target.cpp gate/update_ops_one_qubit.cpp - gate/update_ops_one_target_one_control.cpp gate/update_ops_quantum_matrix.cpp + gate/update_ops_two_qubit.cpp state/state_vector.cpp util/random.cpp ) diff --git a/qulacs/all.hpp b/qulacs/all.hpp new file mode 100644 index 00000000..2aa0bc49 --- /dev/null +++ b/qulacs/all.hpp @@ -0,0 +1,11 @@ +#include "gate/constant.hpp" +#include "gate/gate.hpp" +#include "gate/gate_npair_qubit.hpp" +#include "gate/gate_one_control_one_target.hpp" +#include "gate/gate_one_qubit.hpp" +#include "gate/gate_quantum_matrix.hpp" +#include "gate/gate_two_qubit.hpp" +#include "gate/update_ops.hpp" +#include "state/state_vector.hpp" +#include "types.hpp" +#include "util/random.hpp" diff --git a/qulacs/gate/constant.hpp b/qulacs/gate/constant.hpp index 35f1b5ff..db4d02e1 100644 --- a/qulacs/gate/constant.hpp +++ b/qulacs/gate/constant.hpp @@ -27,47 +27,72 @@ KOKKOS_INLINE_FUNCTION double SINPI8() { return 0.382683432365090; } //! identity matrix -matrix_2_2 I_GATE = {1, 0, 0, 1}; +KOKKOS_INLINE_FUNCTION +matrix_2_2 I_GATE() { return {1, 0, 0, 1}; } //! Pauli matrix X -matrix_2_2 X_GATE = {0, 1, 1, 0}; +KOKKOS_INLINE_FUNCTION +matrix_2_2 X_GATE() { return {0, 1, 1, 0}; } //! Pauli matrix Y -matrix_2_2 Y_GATE = {0, Complex(0, -1), Complex(0, 1), 0}; +KOKKOS_INLINE_FUNCTION +matrix_2_2 Y_GATE() { return {0, Complex(0, -1), Complex(0, 1), 0}; } //! Pauli matrix Z -matrix_2_2 Z_GATE = {1, 0, 0, -1}; +KOKKOS_INLINE_FUNCTION +matrix_2_2 Z_GATE() { return {1, 0, 0, -1}; } //! list of Pauli matrix I,X,Y,Z // std::array PAULI_MATRIX = {I_GATE, X_GATE, Y_GATE, Z_GATE}; //! S-gate -matrix_2_2 S_GATE_MATRIX = {1, 0, 0, Complex(0, 1)}; +KOKKOS_INLINE_FUNCTION +matrix_2_2 S_GATE_MATRIX() { return {1, 0, 0, Complex(0, 1)}; } //! Sdag-gate -matrix_2_2 S_DAG_GATE_MATRIX = {1, 0, 0, Complex(0, -1)}; +KOKKOS_INLINE_FUNCTION +matrix_2_2 S_DAG_GATE_MATRIX() { return {1, 0, 0, Complex(0, -1)}; } //! T-gate -matrix_2_2 T_GATE_MATRIX = { - COSPI8() - Complex(0, SINPI8()), 0., 0., COSPI8() + Complex(0, SINPI8()) * SINPI8()}; +KOKKOS_INLINE_FUNCTION +matrix_2_2 T_GATE_MATRIX() { + return {COSPI8() - Complex(0, SINPI8()), 0., 0., COSPI8() + Complex(0, SINPI8()) * SINPI8()}; +} //! Tdag-gate -matrix_2_2 T_DAG_GATE_MATRIX = { - COSPI8() + Complex(0, SINPI8()), 0., 0., COSPI8() - Complex(0, SINPI8())}; +KOKKOS_INLINE_FUNCTION +matrix_2_2 T_DAG_GATE_MATRIX() { + return {COSPI8() + Complex(0, SINPI8()), 0., 0., COSPI8() - Complex(0, SINPI8())}; +} //! Hadamard gate -matrix_2_2 HADAMARD_MATRIX = {INVERSE_SQRT2(), INVERSE_SQRT2(), INVERSE_SQRT2(), -INVERSE_SQRT2()}; +KOKKOS_INLINE_FUNCTION +matrix_2_2 HADAMARD_MATRIX() { + return {INVERSE_SQRT2(), INVERSE_SQRT2(), INVERSE_SQRT2(), -INVERSE_SQRT2()}; +} //! square root of X gate -matrix_2_2 SQRT_X_GATE_MATRIX = { - Complex(0.5, 0.5), Complex(0.5, -0.5), Complex(0.5, -0.5), Complex(0.5, 0.5)}; +KOKKOS_INLINE_FUNCTION +matrix_2_2 SQRT_X_GATE_MATRIX() { + return {Complex(0.5, 0.5), Complex(0.5, -0.5), Complex(0.5, -0.5), Complex(0.5, 0.5)}; +} //! square root of Y gate -matrix_2_2 SQRT_Y_GATE_MATRIX = { - Complex(0.5, 0.5), Complex(-0.5, -0.5), Complex(0.5, 0.5), Complex(0.5, 0.5)}; +KOKKOS_INLINE_FUNCTION +matrix_2_2 SQRT_Y_GATE_MATRIX() { + return {Complex(0.5, 0.5), Complex(-0.5, -0.5), Complex(0.5, 0.5), Complex(0.5, 0.5)}; +} //! square root dagger of X gate -matrix_2_2 SQRT_X_DAG_GATE_MATRIX = { - Complex(0.5, -0.5), Complex(0.5, 0.5), Complex(0.5, 0.5), Complex(0.5, -0.5)}; +KOKKOS_INLINE_FUNCTION +matrix_2_2 SQRT_X_DAG_GATE_MATRIX() { + return {Complex(0.5, -0.5), Complex(0.5, 0.5), Complex(0.5, 0.5), Complex(0.5, -0.5)}; +} //! square root dagger of Y gate -matrix_2_2 SQRT_Y_DAG_GATE_MATRIX = { - Complex(0.5, -0.5), Complex(0.5, -0.5), Complex(-0.5, 0.5), Complex(0.5, -0.5)}; +KOKKOS_INLINE_FUNCTION +matrix_2_2 SQRT_Y_DAG_GATE_MATRIX() { + return {Complex(0.5, -0.5), Complex(0.5, -0.5), Complex(-0.5, 0.5), Complex(0.5, -0.5)}; +} //! Projection to 0 -matrix_2_2 PROJ_0_MATRIX = {1, 0, 0, 0}; +KOKKOS_INLINE_FUNCTION +matrix_2_2 PROJ_0_MATRIX() { return {1, 0, 0, 0}; } //! Projection to 1 -matrix_2_2 PROJ_1_MATRIX = {0, 0, 0, 1}; +KOKKOS_INLINE_FUNCTION +matrix_2_2 PROJ_1_MATRIX() { return {0, 0, 0, 1}; } //! complex values for exp(j * i*pi/4 ) -matrix_2_2 PHASE_90ROT = {1., Complex(0, 1), -1, Complex(0, -1)}; +KOKKOS_INLINE_FUNCTION +matrix_2_2 PHASE_90ROT() { return {1., Complex(0, 1), -1, Complex(0, -1)}; } //! complex values for exp(-j * i*pi/4 ) -matrix_2_2 PHASE_M90ROT = {1., Complex(0, -1), -1, Complex(0, 1)}; +KOKKOS_INLINE_FUNCTION +matrix_2_2 PHASE_M90ROT() { return {1., Complex(0, -1), -1, Complex(0, 1)}; } } // namespace qulacs diff --git a/qulacs/gate/gate.hpp b/qulacs/gate/gate.hpp index 08160a2d..c1cf9025 100644 --- a/qulacs/gate/gate.hpp +++ b/qulacs/gate/gate.hpp @@ -7,5 +7,6 @@ namespace qulacs { class QuantumGate { public: virtual void update_quantum_state(StateVector& state_vector) const = 0; + virtual ~QuantumGate() {} }; } // namespace qulacs diff --git a/qulacs/gate/gate_npair_qubit.cpp b/qulacs/gate/gate_npair_qubit.cpp new file mode 100644 index 00000000..f51d8355 --- /dev/null +++ b/qulacs/gate/gate_npair_qubit.cpp @@ -0,0 +1,9 @@ +#include "gate_npair_qubit.hpp" + +#include "update_ops.hpp" + +namespace qulacs { +void FusedSWAP::update_quantum_state(StateVector& state_vector) const { + fusedswap_gate(this->qubit_index1, this->qubit_index2, this->block_size, state_vector); +} +} // namespace qulacs diff --git a/qulacs/gate/gate_npair_qubit.hpp b/qulacs/gate/gate_npair_qubit.hpp new file mode 100644 index 00000000..567b689e --- /dev/null +++ b/qulacs/gate/gate_npair_qubit.hpp @@ -0,0 +1,24 @@ +#pragma once + +#include + +#include "gate.hpp" + +namespace qulacs { +class FusedSWAP : public QuantumGate { + UINT qubit_index1, qubit_index2, block_size; + +public: + FusedSWAP(UINT _qubit_index1, UINT _qubit_index2, UINT _block_size) + : qubit_index1(_qubit_index1), qubit_index2(_qubit_index2), block_size(_block_size) { + UINT upper_index = std::max(_qubit_index1, _qubit_index2); + UINT lower_index = std::min(_qubit_index1, _qubit_index2); + if (upper_index <= (lower_index + block_size - 1)) { + throw std::runtime_error( + "FusedSwap: upper index must be bigger than lower_index + block_size - 1"); + } + }; + + void update_quantum_state(StateVector& state_vector) const override; +}; +} // namespace qulacs diff --git a/qulacs/gate/gate_two_qubit.cpp b/qulacs/gate/gate_two_qubit.cpp new file mode 100644 index 00000000..9ec3c2f7 --- /dev/null +++ b/qulacs/gate/gate_two_qubit.cpp @@ -0,0 +1,9 @@ +#include "gate_two_qubit.hpp" + +#include "update_ops.hpp" + +namespace qulacs { +void SWAP::update_quantum_state(StateVector& state_vector) const { + swap_gate(this->_target1, this->_target2, state_vector); +} +} // namespace qulacs diff --git a/qulacs/gate/gate_two_qubit.hpp b/qulacs/gate/gate_two_qubit.hpp new file mode 100644 index 00000000..09ee0768 --- /dev/null +++ b/qulacs/gate/gate_two_qubit.hpp @@ -0,0 +1,15 @@ +#pragma once + +#include + +#include "gate.hpp" + +namespace qulacs { +class SWAP : public QuantumGate { + UINT _target1, _target2; + +public: + SWAP(UINT target1, UINT target2) : _target1(target1), _target2(target2){}; + void update_quantum_state(StateVector& state_vector) const override; +}; +} // namespace qulacs diff --git a/qulacs/gate/update_ops.hpp b/qulacs/gate/update_ops.hpp index d368dfbd..cf1e1b05 100644 --- a/qulacs/gate/update_ops.hpp +++ b/qulacs/gate/update_ops.hpp @@ -52,4 +52,20 @@ void single_qubit_dense_matrix_gate(UINT target_qubit_index, StateVector& state); void u_gate(UINT target_qubit_index, const matrix_2_2& matrix, StateVector& state); + +void swap_gate(UINT target1, UINT target2, StateVector& state); + +void fusedswap_gate(UINT target_qubit_index_0, + UINT target_qubit_index_1, + UINT block_size, + StateVector& state); + +void pauli_gate(std::vector target_qubit_index_list, + std::vector pauli_id_index, + StateVector& state); + +void pauli_rotation_gate(std::vector target_qubit_index_list, + std::vector pauli_id_index, + double angle, + StateVector& state); } // namespace qulacs diff --git a/qulacs/gate/update_ops_matrix_dense.cpp b/qulacs/gate/update_ops_dense_matrix.cpp similarity index 100% rename from qulacs/gate/update_ops_matrix_dense.cpp rename to qulacs/gate/update_ops_dense_matrix.cpp diff --git a/qulacs/gate/update_ops_npair_qubit.cpp b/qulacs/gate/update_ops_npair_qubit.cpp new file mode 100644 index 00000000..869b00b7 --- /dev/null +++ b/qulacs/gate/update_ops_npair_qubit.cpp @@ -0,0 +1,37 @@ +#include +#include +#include + +#include "../types.hpp" +#include "update_ops.hpp" + +namespace qulacs { +void fusedswap_gate(UINT target_qubit_index_0, + UINT target_qubit_index_1, + UINT block_size, + StateVector& state) { + UINT n_qubits = state.n_qubits(); + UINT upper_index, lower_index; + upper_index = std::max(target_qubit_index_0, target_qubit_index_1); + lower_index = std::min(target_qubit_index_0, target_qubit_index_1); + if (n_qubits <= (upper_index + block_size - 1)) { + throw std::runtime_error( + "FusedSwap: num of qubits must be bigger than upper_index + block_size - 1"); + } + const UINT mask_block = (1 << block_size) - 1; + auto amplitudes = state.amplitudes_raw(); + const UINT kblk_mask = mask_block << upper_index; + const UINT jblk_mask = mask_block << lower_index; + const UINT else_mask = (1 << n_qubits) - 1 - kblk_mask - jblk_mask; + + Kokkos::parallel_for( + 1 << n_qubits, KOKKOS_LAMBDA(const UINT& i) { + const UINT kblk = (i & kblk_mask) >> upper_index; + const UINT jblk = (i & jblk_mask) >> lower_index; + if (jblk > kblk) { + const UINT index = (i & else_mask) | jblk << upper_index | kblk << lower_index; + Kokkos::Experimental::swap(amplitudes[i], amplitudes[index]); + } + }); +} +} // namespace qulacs diff --git a/qulacs/gate/update_ops_one_target_one_control.cpp b/qulacs/gate/update_ops_one_control_one_target.cpp similarity index 82% rename from qulacs/gate/update_ops_one_target_one_control.cpp rename to qulacs/gate/update_ops_one_control_one_target.cpp index b091c102..d20e84d2 100644 --- a/qulacs/gate/update_ops_one_target_one_control.cpp +++ b/qulacs/gate/update_ops_one_control_one_target.cpp @@ -12,9 +12,11 @@ void cnot_gate(UINT control_qubit_index, UINT target_qubit_index, StateVector& s const UINT control_mask = 1ULL << control_qubit_index; auto [min_qubit_index, max_qubit_index] = Kokkos::minmax(control_qubit_index, target_qubit_index); - const UINT low_mask = (1ULL << min_qubit_index) - 1; - const UINT mid_mask = ((1ULL << (max_qubit_index - 1)) - 1) ^ low_mask; - const UINT high_mask = ~(low_mask | mid_mask); + const UINT min_qubit_mask = 1ULL << min_qubit_index; + const UINT max_qubit_mask = 1ULL << (max_qubit_index - 1); + const UINT low_mask = min_qubit_mask - 1; + const UINT mid_mask = (max_qubit_mask - 1) ^ low_mask; + const UINT high_mask = ~(max_qubit_mask - 1); auto amplitudes = state.amplitudes_raw(); Kokkos::parallel_for( @@ -39,7 +41,7 @@ void cz_gate(UINT control_qubit_index, UINT target_qubit_index, StateVector& sta auto amplitudes = state.amplitudes_raw(); Kokkos::parallel_for( 1ULL << (n_qubits - 2), KOKKOS_LAMBDA(const UINT& it) { - UINT i = ((it & high_mask) << 2) | ((it & mid_mask) << 1) | (it & low_mask) | + UINT i = (it & low_mask) | ((it & mid_mask) << 1) | ((it & high_mask) << 2) | control_mask | target_mask; amplitudes[i] *= -1; }); diff --git a/qulacs/gate/update_ops_one_qubit.cpp b/qulacs/gate/update_ops_one_qubit.cpp index 798a9516..49d001a0 100644 --- a/qulacs/gate/update_ops_one_qubit.cpp +++ b/qulacs/gate/update_ops_one_qubit.cpp @@ -90,27 +90,27 @@ void tdag_gate(UINT target_qubit_index, StateVector& state) { } void sqrtx_gate(UINT target_qubit_index, StateVector& state) { - single_qubit_dense_matrix_gate(target_qubit_index, SQRT_X_GATE_MATRIX, state); + single_qubit_dense_matrix_gate(target_qubit_index, SQRT_X_GATE_MATRIX(), state); } void sqrtxdag_gate(UINT target_qubit_index, StateVector& state) { - single_qubit_dense_matrix_gate(target_qubit_index, SQRT_X_DAG_GATE_MATRIX, state); + single_qubit_dense_matrix_gate(target_qubit_index, SQRT_X_DAG_GATE_MATRIX(), state); } void sqrty_gate(UINT target_qubit_index, StateVector& state) { - single_qubit_dense_matrix_gate(target_qubit_index, SQRT_Y_GATE_MATRIX, state); + single_qubit_dense_matrix_gate(target_qubit_index, SQRT_Y_GATE_MATRIX(), state); } void sqrtydag_gate(UINT target_qubit_index, StateVector& state) { - single_qubit_dense_matrix_gate(target_qubit_index, SQRT_Y_DAG_GATE_MATRIX, state); + single_qubit_dense_matrix_gate(target_qubit_index, SQRT_Y_DAG_GATE_MATRIX(), state); } void p0_gate(UINT target_qubit_index, StateVector& state) { - single_qubit_dense_matrix_gate(target_qubit_index, PROJ_0_MATRIX, state); + single_qubit_dense_matrix_gate(target_qubit_index, PROJ_0_MATRIX(), state); } void p1_gate(UINT target_qubit_index, StateVector& state) { - single_qubit_dense_matrix_gate(target_qubit_index, PROJ_1_MATRIX, state); + single_qubit_dense_matrix_gate(target_qubit_index, PROJ_1_MATRIX(), state); } void rx_gate(UINT target_qubit_index, double angle, StateVector& state) { diff --git a/qulacs/gate/update_ops_two_qubit.cpp b/qulacs/gate/update_ops_two_qubit.cpp new file mode 100644 index 00000000..ac5727f8 --- /dev/null +++ b/qulacs/gate/update_ops_two_qubit.cpp @@ -0,0 +1,30 @@ +#include +#include + +#include "../types.hpp" +#include "constant.hpp" +#include "update_ops.hpp" + +namespace qulacs { +void swap_gate(UINT target0, UINT target1, StateVector& state) { + UINT n_qubits = state.n_qubits(); + UINT mask_0 = 1ULL << target0; + UINT mask_1 = 1ULL << target1; + UINT mask = mask_0 + mask_1; + UINT min_qubit_index = std::min(target0, target1); + UINT max_qubit_index = std::max(target0, target1); + UINT min_qubit_mask = 1ULL << min_qubit_index; + UINT max_qubit_mask = 1ULL << (max_qubit_index - 1); + UINT low_mask = min_qubit_mask - 1; + UINT mid_mask = (max_qubit_mask - 1) ^ low_mask; + UINT high_mask = ~(max_qubit_mask - 1); + auto amplitudes = state.amplitudes_raw(); + Kokkos::parallel_for( + 1ULL << (n_qubits - 2), KOKKOS_LAMBDA(const UINT& it) { + UINT basis_index_0 = + (it & low_mask) + ((it & mid_mask) << 1) + ((it & high_mask) << 2) + mask_0; + UINT basis_index_1 = basis_index_0 ^ mask; + Kokkos::Experimental::swap(amplitudes[basis_index_0], amplitudes[basis_index_1]); + }); +} +} // namespace qulacs diff --git a/tests/gate/gate_test.cpp b/tests/gate/gate_test.cpp index b24419a7..ba14a3ba 100644 --- a/tests/gate/gate_test.cpp +++ b/tests/gate/gate_test.cpp @@ -3,7 +3,11 @@ #include #include #include +#include +#include #include +#include +#include #include #include #include @@ -71,7 +75,6 @@ void run_random_gate_apply(UINT n_qubits, std::function void run_random_gate_apply(UINT n_qubits, std::function matrix_factory) { @@ -81,39 +84,126 @@ void run_random_gate_apply(UINT n_qubits, Eigen::VectorXcd test_state = Eigen::VectorXcd::Zero(dim); for (int repeat = 0; repeat < 10; repeat++) { auto state = StateVector::Haar_random_state(n_qubits); + auto state_cp = state.amplitudes(); for (int i = 0; i < dim; i++) { - test_state[i] = state[i]; + test_state[i] = state_cp[i]; } - const double theta = M_PI * random.uniform(); - const double phi = M_PI * random.uniform(); - const double lambda = M_PI * random.uniform(); - const QuantumGateConstructor gate; - if (typeid(QuantumGateConstructor) == typeid(U1)) { + double theta = M_PI * random.uniform(); + double phi = M_PI * random.uniform(); + double lambda = M_PI * random.uniform(); + if constexpr (std::is_same_v) { theta = 0; phi = 0; - gate = QuantumGateConstructor(lambda); - } else if (typeid(QuantumGateConstructor) == typeid(U2)) { + } else if constexpr (std::is_same_v) { theta = M_PI / 2; - gate = QuantumGateConstructor(phi, lambda); - } else if (typeid(QuantumGateConstructor) == typeid(U3)) { - gate = QuantumGateConstructor(theta, phi, lambda); + } else if constexpr (std::is_same_v) { } else { throw std::runtime_error("Invalid gate type"); } const auto matrix = matrix_factory(theta, phi, lambda); const UINT target = random.int64() % n_qubits; + const U3 gate(target, theta, phi, lambda); gate.update_quantum_state(state); + state_cp = state.amplitudes(); test_state = get_expanded_eigen_matrix_with_identity(target, matrix, n_qubits) * test_state; for (int i = 0; i < dim; i++) { - ASSERT_NEAR(std::abs((CComplex)state[i] - test_state[i]), 0, eps); + ASSERT_NEAR(std::abs((CComplex)state_cp[i] - test_state[i]), 0, eps); + } + } +} + +void run_random_gate_apply_two_qubit(UINT n_qubits) { + const int dim = 1ULL << n_qubits; + Random random; + + Eigen::VectorXcd test_state = Eigen::VectorXcd::Zero(dim); + std::function func_eig; + for (int repeat = 0; repeat < 10; repeat++) { + auto state = StateVector::Haar_random_state(n_qubits); + for (int g = 0; g < 2; g++) { + QuantumGate* gate; + auto state_cp = state.amplitudes(); + for (int i = 0; i < dim; i++) { + test_state[i] = state[i]; + } + + UINT target = random.int64() % n_qubits; + UINT control = random.int64() % n_qubits; + if (target == control) target = (target + 1) % n_qubits; + if (g == 0) { + gate = new CNOT(control, target); + func_eig = get_eigen_matrix_full_qubit_CNOT; + } else { + gate = new CZ(control, target); + func_eig = get_eigen_matrix_full_qubit_CZ; + } + gate->update_quantum_state(state); + state_cp = state.amplitudes(); + + Eigen::MatrixXcd test_mat = func_eig(control, target, n_qubits); + test_state = test_mat * test_state; + + for (int i = 0; i < dim; i++) { + ASSERT_NEAR(std::abs((CComplex)state_cp[i] - test_state[i]), 0, eps); + } + delete gate; + } + } + + func_eig = get_eigen_matrix_full_qubit_SWAP; + for (int repeat = 0; repeat < 10; repeat++) { + QuantumGate* gate; + auto state = StateVector::Haar_random_state(n_qubits); + auto state_cp = state.amplitudes(); + for (int i = 0; i < dim; i++) { + test_state[i] = state_cp[i]; + } + + UINT target = random.int64() % n_qubits; + UINT control = random.int64() % n_qubits; + if (target == control) target = (target + 1) % n_qubits; + gate = new SWAP(control, target); + gate->update_quantum_state(state); + state_cp = state.amplitudes(); + + Eigen::MatrixXcd test_mat = func_eig(control, target, n_qubits); + test_state = test_mat * test_state; + + for (int i = 0; i < dim; i++) { + ASSERT_NEAR(std::abs((CComplex)state_cp[i] - test_state[i]), 0, eps); } + delete gate; + } +} + +void run_random_gate_apply_fused(UINT n_qubits, UINT target0, UINT target1, UINT block_size) { + const UINT dim = 1ULL << n_qubits; + StateVector state_ref = StateVector::Haar_random_state(n_qubits); + StateVector state = state_ref.copy(); + + // update "state_ref" using SWAP gate + for (UINT i = 0; i < block_size; i++) { + QuantumGate* swap_gate; + swap_gate = new SWAP(target0 + i, target1 + i); + swap_gate->update_quantum_state(state_ref); + delete swap_gate; + } + auto state_ref_cp = state_ref.amplitudes(); + + QuantumGate* fused_swap_gate; + fused_swap_gate = new FusedSWAP(target0, target1, block_size); + fused_swap_gate->update_quantum_state(state); + delete fused_swap_gate; + auto state_cp = state.amplitudes(); + + for (UINT i = 0; i < dim; i++) { + ASSERT_NEAR(std::abs((CComplex)state_cp[i] - (CComplex)state_ref_cp[i]), 0, eps); } } -*/ TEST(GateTest, ApplyI) { run_random_gate_apply(5, make_I); } TEST(GateTest, ApplyX) { run_random_gate_apply(5, make_X); } @@ -133,8 +223,20 @@ TEST(GateTest, ApplyP1) { run_random_gate_apply(5, make_P1); } TEST(GateTest, ApplyRX) { run_random_gate_apply(5, make_RX); } TEST(GateTest, ApplyRY) { run_random_gate_apply(5, make_RY); } TEST(GateTest, ApplyRZ) { run_random_gate_apply(5, make_RZ); } -/* TEST(GateTest, ApplyU1) { run_random_gate_apply(5, make_U); } TEST(GateTest, ApplyU2) { run_random_gate_apply(5, make_U); } TEST(GateTest, ApplyU3) { run_random_gate_apply(5, make_U); } -*/ +TEST(GateTest, ApplyTwoQubit) { run_random_gate_apply_two_qubit(5); } +TEST(GateTest, ApplyFused) { + UINT n_qubits = 10; + for (UINT t0 = 0; t0 < n_qubits; t0++) { + for (UINT t1 = 0; t1 < n_qubits; t1++) { + if (t0 == t1) continue; + UINT max_bs = + std::min((t0 < t1) ? (t1 - t0) : (t0 - t1), std::min(n_qubits - t0, n_qubits - t1)); + for (UINT bs = 1; bs <= max_bs; bs++) { + run_random_gate_apply_fused(n_qubits, t0, t1, bs); + } + } + } +} diff --git a/tests/gate/util.hpp b/tests/gate/util.hpp index 20377d76..b5fd139d 100644 --- a/tests/gate/util.hpp +++ b/tests/gate/util.hpp @@ -27,6 +27,53 @@ static Eigen::MatrixXcd get_expanded_eigen_matrix_with_identity( return kronecker_product(kronecker_product(right_identity, one_qubit_matrix), left_identity); } +static Eigen::MatrixXcd get_eigen_matrix_full_qubit_CNOT( + UINT control_qubit_index, UINT target_qubit_index, UINT qubit_count) { + UINT dim = 1ULL << qubit_count; + Eigen::MatrixXcd result = Eigen::MatrixXcd::Zero(dim, dim); + for (UINT ind = 0; ind < dim; ++ind) { + if (ind & (1ULL << control_qubit_index)) { + result(ind, ind ^ (1ULL << target_qubit_index)) = 1; + } else { + result(ind, ind) = 1; + } + } + return result; +} + +static Eigen::MatrixXcd get_eigen_matrix_full_qubit_CZ( + UINT control_qubit_index, UINT target_qubit_index, UINT qubit_count) { + UINT dim = 1ULL << qubit_count; + Eigen::MatrixXcd result = Eigen::MatrixXcd::Zero(dim, dim); + for (UINT ind = 0; ind < dim; ++ind) { + if ((ind & (1ULL << control_qubit_index)) != 0 && + (ind & (1ULL << target_qubit_index)) != 0) { + result(ind, ind) = -1; + } else { + result(ind, ind) = 1; + } + } + return result; +} + +static Eigen::MatrixXcd get_eigen_matrix_full_qubit_SWAP( + UINT target_qubit_index1, UINT target_qubit_index2, UINT qubit_count) { + UINT dim = 1ULL << qubit_count; + Eigen::MatrixXcd result = Eigen::MatrixXcd::Zero(dim, dim); + for (UINT ind = 0; ind < dim; ++ind) { + bool flag1, flag2; + flag1 = (ind & (1ULL << target_qubit_index1)) != 0; + flag2 = (ind & (1ULL << target_qubit_index2)) != 0; + if (flag1 ^ flag2) { + result(ind, ind ^ (1ULL << target_qubit_index1) ^ + (1ULL << target_qubit_index2)) = 1; + } else { + result(ind, ind) = 1; + } + } + return result; +} + #define ASSERT_STATE_NEAR(state, other, eps) \ ASSERT_PRED_FORMAT3(_assert_state_near, state, other, eps) @@ -143,5 +190,4 @@ static Eigen::MatrixXcd make_U(double theta, double phi, double lambda) { std::exp(1i * phi) * std::sin(theta / 2.), std::exp(1i * phi) * std::exp(1i * lambda) * std::cos(theta / 2.)); } - } // namespace qulacs