diff --git a/src/Simulation/Simulators/SparseSimulator/Native/CMakeLists.txt b/src/Simulation/Simulators/SparseSimulator/Native/CMakeLists.txt index 2d790552d71..254484380d4 100644 --- a/src/Simulation/Simulators/SparseSimulator/Native/CMakeLists.txt +++ b/src/Simulation/Simulators/SparseSimulator/Native/CMakeLists.txt @@ -20,3 +20,12 @@ if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") endif() message("Compiler flags: ${CMAKE_CXX_FLAGS_RELEASE}") + +include(CTest) +enable_testing() + +foreach(TEST SparseSimulatorTests CSharpIntegrationTests) + add_executable(${TEST} ${TEST}.cpp TestHelpers.cpp) + target_include_directories(${TEST} PRIVATE ../../../../Qir/Common/Externals/catch2) + add_test(${TEST} ${TEST}) +endforeach() \ No newline at end of file diff --git a/src/Simulation/Simulators/SparseSimulator/Native/CSharpIntegrationTests.cpp b/src/Simulation/Simulators/SparseSimulator/Native/CSharpIntegrationTests.cpp new file mode 100644 index 00000000000..dce4ede6849 --- /dev/null +++ b/src/Simulation/Simulators/SparseSimulator/Native/CSharpIntegrationTests.cpp @@ -0,0 +1,304 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#define CATCH_CONFIG_MAIN +#include + +#include "SparseSimulator.h" +#include "capi.hpp" +#include "capi.cpp" // yes really +#include "factory.hpp" +#include "factory.cpp" +#include "TestHelpers.hpp" + +#include +#include +#include + +using namespace Microsoft::Quantum::SPARSESIMULATOR; +using namespace SparseSimulatorTestHelpers; + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + + +template +void MultiExpReferenceTest( + std::function qubit_prep, + std::function qubit_clear +) { + const qubit_label_type zero(0); + logical_qubit_id* qubits = new logical_qubit_id[3]; + qubits[0] = 0; + qubits[1] = 1; + qubits[2] = 2; + int* Paulis = new int[3]; + for (int intPaulis = 0; intPaulis < 4 * 4 * 4; intPaulis++) { + Paulis[0] = intPaulis % 4; + Paulis[1] = (intPaulis / 4 ) % 4; + Paulis[2] = intPaulis / 16; + + for (double angle = 0.0; angle < M_PI / 2.0; angle += 0.1) { + unsigned sim = init_cpp(32); + qubit_prep(sim); + + std::vector vector_rep(8, 0.0); + for (unsigned i = 0; i < 8; i++) { + vector_rep[i] = getSimulator(sim)->probe(std::bitset<3>(i).to_string()); + } + // New simulator Exp + Exp_cpp(sim, 3, Paulis, angle, qubits); + // Old simulator Exp + std::vector actualPaulis = { (Gates::Basis)Paulis[0],(Gates::Basis)Paulis[1], (Gates::Basis)Paulis[2] }; + apply_exp(vector_rep, actualPaulis, angle, std::vector{ 0, 1, 2 }); + for (unsigned i = 0; i < 8; i++) { + amplitude result = getSimulator(sim)->probe(std::bitset<3>(i).to_string()); + assert_amplitude_equality(vector_rep[i], result); + } + Exp_cpp(sim, 3, Paulis, -angle, qubits); + qubit_clear(sim); + } + } +} + +TEST_CASE("initializationTest") { + unsigned sim = init_cpp(32); +} + +TEST_CASE("AllocationTest") { + unsigned sim = init_cpp(32); + allocateQubit_cpp(sim, 0); + releaseQubit_cpp(sim, 0); +} +TEST_CASE("AllocateRebuildTest") { + unsigned sim = init_cpp(64); + for (int i = 0; i < 1024; i++) { + allocateQubit_cpp(sim, i); + getSimulator(sim)->X(i); + getSimulator(sim)->update_state(); + } + for (int i = 0; i < 1024; i++) { + getSimulator(sim)->X(i); + releaseQubit_cpp(sim, i); + } +} + +TEST_CASE("XTest") { + unsigned sim = init_cpp(32); + allocateQubit_cpp(sim, 0); + X_cpp(sim, 0); + assert_amplitude_equality(getSimulator(sim)->probe("0"), 0.0, 0.0); + assert_amplitude_equality(getSimulator(sim)->probe("1"), 1.0, 0.0); + X_cpp(sim, 0); + releaseQubit_cpp(sim, 0); +} +TEST_CASE("ZTest") { + unsigned sim = init_cpp(32); + allocateQubit_cpp(sim, 0); + Z_cpp(sim, 0); + assert_amplitude_equality(getSimulator(sim)->probe("0"), 1.0, 0.0); + assert_amplitude_equality(getSimulator(sim)->probe("1"), 0.0, 0.0); + Z_cpp(sim, 0); + assert_amplitude_equality(getSimulator(sim)->probe("0"), 1.0, 0.0); + assert_amplitude_equality(getSimulator(sim)->probe("1"), 0.0, 0.0); + X_cpp(sim, 0); + Z_cpp(sim, 0); + assert_amplitude_equality(getSimulator(sim)->probe("0"), 0.0, 0.0); + assert_amplitude_equality(getSimulator(sim)->probe("1"), -1.0, 0.0); + Z_cpp(sim, 0); + assert_amplitude_equality(getSimulator(sim)->probe("0"), 0.0, 0.0); + assert_amplitude_equality(getSimulator(sim)->probe("1"), 1.0, 0.0); + X_cpp(sim, 0); + releaseQubit_cpp(sim, 0); +} +TEST_CASE("HTest") { + unsigned sim = init_cpp(32); + allocateQubit_cpp(sim, 0); + H_cpp(sim, 0); + assert_amplitude_equality(getSimulator(sim)->probe("0"), 1.0 / sqrt(2.0), 0.0); + assert_amplitude_equality(getSimulator(sim)->probe("1"), 1.0 / sqrt(2.0), 0.0); + H_cpp(sim, 0); + assert_amplitude_equality(getSimulator(sim)->probe("0"), 1.0, 0.0); + assert_amplitude_equality(getSimulator(sim)->probe("1"), 0.0, 0.0); + X_cpp(sim, 0); + H_cpp(sim, 0); + assert_amplitude_equality(getSimulator(sim)->probe("0"), 1.0 / sqrt(2.0), 0.0); + assert_amplitude_equality(getSimulator(sim)->probe("1"), -1.0 / sqrt(2.0), 0.0); + H_cpp(sim, 0); + assert_amplitude_equality(getSimulator(sim)->probe("0"), 0.0, 0.0); + assert_amplitude_equality(getSimulator(sim)->probe("1"), 1.0, 0.0); + X_cpp(sim, 0); + releaseQubit_cpp(sim, 0); +} + +TEST_CASE("TGateTest") { + unsigned sim = init_cpp(32); + allocateQubit_cpp(sim, 0); + T_cpp(sim, 0); + assert_amplitude_equality(getSimulator(sim)->probe("0"), 1.0, 0.0); + assert_amplitude_equality(getSimulator(sim)->probe("1"), 0.0, 0.0); + X_cpp(sim, 0); + T_cpp(sim, 0); + assert_amplitude_equality(getSimulator(sim)->probe("0"), 0.0, 0.0); + assert_amplitude_equality(getSimulator(sim)->probe("1"), 1.0 / sqrt(2.0), 1.0 / sqrt(2.0)); + T_cpp(sim, 0); + assert_amplitude_equality(getSimulator(sim)->probe("0"), 0.0, 0.0); + assert_amplitude_equality(getSimulator(sim)->probe("1"), 0.0, 1.0); + T_cpp(sim, 0); + assert_amplitude_equality(getSimulator(sim)->probe("0"), 0.0, 0.0); + assert_amplitude_equality(getSimulator(sim)->probe("1"), -1.0 / sqrt(2.0), 1.0 / sqrt(2.0)); + T_cpp(sim, 0); + assert_amplitude_equality(getSimulator(sim)->probe("0"), 0.0, 0.0); + assert_amplitude_equality(getSimulator(sim)->probe("1"), -1.0, 0.0); + T_cpp(sim, 0); + assert_amplitude_equality(getSimulator(sim)->probe("0"), 0.0, 0.0); + assert_amplitude_equality(getSimulator(sim)->probe("1"), -1.0 / sqrt(2.0), -1.0 / sqrt(2.0)); + T_cpp(sim, 0); + assert_amplitude_equality(getSimulator(sim)->probe("0"), 0.0, 0.0); + assert_amplitude_equality(getSimulator(sim)->probe("1"), 0.0, -1.0); + T_cpp(sim, 0); + assert_amplitude_equality(getSimulator(sim)->probe("0"), 0.0, 0.0); + assert_amplitude_equality(getSimulator(sim)->probe("1"), 1.0 / sqrt(2.0), -1.0 / sqrt(2.0)); + T_cpp(sim, 0); + assert_amplitude_equality(getSimulator(sim)->probe("0"), 0.0, 0.0); + assert_amplitude_equality(getSimulator(sim)->probe("1"), 1.0, 0.0); + X_cpp(sim, 0); + releaseQubit_cpp(sim, 0); +} + +TEST_CASE("HCancellationTest") +{ + int n_qubits = 16; + unsigned sim = init_cpp(n_qubits); + size_t buckets = 0; + for (int i = 0; i < n_qubits; i++) { + allocateQubit_cpp(sim, i); + H_cpp(sim, i); + } + for (int i = n_qubits - 1; i >= 0; i--) { + H_cpp(sim, i); + // If the H do not cancel out, release will fail in an opaque way + releaseQubit_cpp(sim, i); + } +} + +TEST_CASE("HXZCommutationTest") +{ + const int n_qubits = 16; + unsigned sim = init_cpp(n_qubits); + for (int i = 0; i < n_qubits; i++) { + allocateQubit_cpp(sim, i); + H_cpp(sim, i); + } + std::bitset one_state = 0; + for (int i = 0; i < n_qubits - 1; i += 2) { + Z_cpp(sim, i); + X_cpp(sim, i+1); + one_state.set(i); + } + for (int i = n_qubits - 1; i >= 0; i--) { + H_cpp(sim, i); + } + for (std::uint64_t i = 0; i < (std::uint64_t{1} << n_qubits); i++) { + amplitude state = getSimulator(sim)->probe(std::bitset(i).to_string()); + if (i == one_state.to_ulong()) { + assert_amplitude_equality(state, 1.0, 0.0); + } + else { + assert_amplitude_equality(state, 0.0, 0.0); + } + } +} + +TEST_CASE("ResetTest") +{ + const int n_qubits = 16; + unsigned sim = init_cpp(n_qubits); + allocateQubit_cpp(sim, 0); + Reset_cpp(sim, 0); + amplitude state = getSimulator(sim)->probe("0"); + assert_amplitude_equality(state, 1.0, 0.0); + X_cpp(sim, 0); + Reset_cpp(sim, 0); + state = getSimulator(sim)->probe("0"); + // No qubit exists; should have amplitude 0 + assert_amplitude_equality(state, 1.0, 0.0); + allocateQubit_cpp(sim, 1); + X_cpp(sim, 0); + logical_qubit_id* controls = new logical_qubit_id{ 0 }; + MCX_cpp(sim, 1, controls, 1); + Reset_cpp(sim, 0); + state = getSimulator(sim)->probe("00"); + assert_amplitude_equality(state, 0.0, 0.0); + state = getSimulator(sim)->probe("10"); + assert_amplitude_equality(state, 1.0, 0.0); + Reset_cpp(sim, 1); + state = getSimulator(sim)->probe("00"); + assert_amplitude_equality(state, 1.0, 0.0); + state = getSimulator(sim)->probe("10"); + assert_amplitude_equality(state, 0.0, 0.0); + releaseQubit_cpp(sim, 1); + releaseQubit_cpp(sim, 0); +} + +TEST_CASE("MultiExpWithHTest") { + const int num_qubits = 32; + auto qubit_prep = [](unsigned sim ) { + H_cpp(sim, 0); + H_cpp(sim, 1); + H_cpp(sim, 2); + }; + auto qubit_clear = [](unsigned sim) { + H_cpp(sim, 2); + releaseQubit_cpp(sim, 2); + H_cpp(sim, 1); + releaseQubit_cpp(sim, 1); + H_cpp(sim, 0); + releaseQubit_cpp(sim, 0); + }; + MultiExpReferenceTest(qubit_prep, qubit_clear); +} + +TEST_CASE("MultiExpBasisTest") { + const int num_qubits = 32; + auto qubit_prep = [](unsigned sim, int index) { + if ((index & 1) == 0) { X_cpp(sim, 0); } + if ((index & 2) == 0) { X_cpp(sim, 1); } + if ((index & 4) == 0) { X_cpp(sim, 2); } + }; + auto qubit_clear = [](unsigned sim, int index) { + if ((index & 1) == 0) { X_cpp(sim, 0); } + releaseQubit_cpp(sim, 0); + if ((index & 2) == 0) { X_cpp(sim, 1); } + releaseQubit_cpp(sim, 1); + if ((index & 4) == 0) { X_cpp(sim, 2); } + releaseQubit_cpp(sim, 2); + }; + for (int i = 0; i < 8; i++) { + MultiExpReferenceTest([=](unsigned sim) {qubit_prep(sim, i); }, [=](unsigned sim) {qubit_clear(sim, i); }); + } +} + +TEST_CASE("R1Test") { + const int num_qubits = 32; + amplitude result0; + amplitude result1; + for (double angle = 0.0; angle < M_PI / 2.0; angle += 0.1) { + unsigned sim = init_cpp(num_qubits); + H_cpp(sim, 0); + R1_cpp(sim, angle, 0); + result0 = getSimulator(sim)->probe("0"); + result1 = getSimulator(sim)->probe("1"); + assert_amplitude_equality(result0, 1.0 / sqrt(2.0)); + assert_amplitude_equality(result1, amplitude(cos(angle), sin(angle))/sqrt(2.0)); + R1_cpp(sim, -angle, 0); + result0 = getSimulator(sim)->probe("0"); + result1 = getSimulator(sim)->probe("1"); + assert_amplitude_equality(result0, 1.0 / sqrt(2.0)); + assert_amplitude_equality(result1, 1.0 / sqrt(2.0)); + H_cpp(sim, 0); + releaseQubit_cpp(sim, 0); + destroy_cpp(sim); + } +} \ No newline at end of file diff --git a/src/Simulation/Simulators/SparseSimulator/Native/SparseSimulatorTests.cpp b/src/Simulation/Simulators/SparseSimulator/Native/SparseSimulatorTests.cpp new file mode 100644 index 00000000000..88a4c83ee5c --- /dev/null +++ b/src/Simulation/Simulators/SparseSimulator/Native/SparseSimulatorTests.cpp @@ -0,0 +1,766 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#define CATCH_CONFIG_MAIN +#include + +#include "SparseSimulator.h" +#include "TestHelpers.hpp" +#include +#include +#include + +using namespace Microsoft::Quantum::SPARSESIMULATOR; +using namespace SparseSimulatorTestHelpers; + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + + + +template +void MultiExpTest( + std::function qubit_prep, + std::function qubit_clear +) { + for (int intPaulis = 0; intPaulis < 4 * 4 * 4; intPaulis++) { + std::vector Paulis{ + (Gates::Basis)(intPaulis % 4), + (Gates::Basis)((intPaulis / 4) % 4), + (Gates::Basis)(intPaulis / 16) + }; + for (double angle = 0.0; angle < M_PI / 2.0; angle += 0.1) { + SparseSimulator sim = SparseSimulator(num_qubits); + std::vector qubits{ 0,1,2 }; + qubit_prep(sim); + std::vector vector_rep(8, 0.0); + for (unsigned i = 0; i < 8; i++) { + vector_rep[i] = sim.probe(std::bitset<3>(i).to_string()); + } + // New simulator Exp + sim.Exp(Paulis, angle, qubits); + // Old simulator Exp + apply_exp(vector_rep, Paulis, angle, std::vector{ 0, 1, 2 }); + for (unsigned i = 0; i < 8; i++) { + amplitude result = sim.probe(std::bitset<3>(i).to_string()); + assert_amplitude_equality(vector_rep[i], result); + } + sim.Exp(Paulis, -angle, qubits); + qubit_clear(sim); + } + } +} + +// Tests comparisons of bitstrings +TEST_CASE("LabelComparisonTest") { + const logical_qubit_id num_qubits = 1024; + SparseSimulator sim = SparseSimulator(num_qubits); + uint64_t i = 0; + uint64_t j; + uint64_t k = 0; + qubit_label_type label1(0); + qubit_label_type label2(1); + + for (i = 0; i < 500; i++){ + k += i * i * i * i; + uint64_t m = 0; + label1 = qubit_label_type(k); + for (j = 0; j < 500; j++){ + m += j * j * j * j; + label2 = qubit_label_type(m); + REQUIRE((k < m) == (label1 < label2)); + } + } +} +// Tests that the X gate flips the computational basis states +TEST_CASE("XGateTest") { + const logical_qubit_id num_qubits = 32; + SparseSimulator sim = SparseSimulator(num_qubits); + sim.X(0); + assert_amplitude_equality(sim.probe("0"), 0.0, 0.0); + assert_amplitude_equality(sim.probe("1"), 1.0, 0.0); + sim.X(0); + assert_amplitude_equality(sim.probe("0"), 1.0, 0.0); + assert_amplitude_equality(sim.probe("1"), 0.0, 0.0); +} + +// Tests Z on computational basis states +TEST_CASE("ZGateTest") { + const logical_qubit_id num_qubits = 32; + SparseSimulator sim = SparseSimulator(num_qubits); + sim.Z(0); + assert_amplitude_equality(sim.probe("0"), 1.0, 0.0); + assert_amplitude_equality(sim.probe("1"), 0.0, 0.0); + sim.Z(0); + assert_amplitude_equality(sim.probe("0"), 1.0, 0.0); + assert_amplitude_equality(sim.probe("1"), 0.0, 0.0); + sim.X(0); + sim.Z(0); + assert_amplitude_equality(sim.probe("0"), 0.0, 0.0); + assert_amplitude_equality(sim.probe("1"), -1.0, 0.0); + sim.Z(0); + assert_amplitude_equality(sim.probe("0"), 0.0, 0.0); + assert_amplitude_equality(sim.probe("1"), 1.0, 0.0); +} + +// Tests H on computational basis states +TEST_CASE("HGateTest") { + const logical_qubit_id num_qubits = 32; + const qubit_label_type zero(0); + SparseSimulator sim = SparseSimulator(num_qubits); + sim.H(0); + assert_amplitude_equality(sim.probe("0"), 1.0 / sqrt(2.0), 0.0); + assert_amplitude_equality(sim.probe("1"), 1.0 / sqrt(2.0), 0.0); + sim.H(0); + assert_amplitude_equality(sim.probe("0"), 1.0, 0.0); + assert_amplitude_equality(sim.probe("1"), 0.0, 0.0); + sim.X(0); + sim.H(0); + assert_amplitude_equality(sim.probe("0"), 1.0 / sqrt(2.0), 0.0); + assert_amplitude_equality(sim.probe("1"), -1.0 / sqrt(2.0), 0.0); + sim.H(0); + assert_amplitude_equality(sim.probe("0"), 0.0, 0.0); + assert_amplitude_equality(sim.probe("1"), 1.0, 0.0); +} + +// Tests powers of T on computational basis states +TEST_CASE("TGateTest") { + const logical_qubit_id num_qubits = 32; + const qubit_label_type zero(0); + SparseSimulator sim = SparseSimulator(num_qubits); + sim.T(0); + assert_amplitude_equality(sim.probe("0"), 1.0, 0.0); + assert_amplitude_equality(sim.probe("1"), 0.0, 0.0); + sim.X(0); + sim.T(0); + assert_amplitude_equality(sim.probe("0"), 0.0, 0.0); + assert_amplitude_equality(sim.probe("1"), 1.0 / sqrt(2.0), 1.0 / sqrt(2.0)); + sim.T(0); + assert_amplitude_equality(sim.probe("0"), 0.0, 0.0); + assert_amplitude_equality(sim.probe("1"), 0.0, 1.0); + sim.T(0); + assert_amplitude_equality(sim.probe("0"), 0.0, 0.0); + assert_amplitude_equality(sim.probe("1"), -1.0 / sqrt(2.0), 1.0 / sqrt(2.0)); + sim.T(0); + assert_amplitude_equality(sim.probe("0"), 0.0, 0.0); + assert_amplitude_equality(sim.probe("1"), -1.0, 0.0); + sim.T(0); + assert_amplitude_equality(sim.probe("0"), 0.0, 0.0); + assert_amplitude_equality(sim.probe("1"), -1.0 / sqrt(2.0), -1.0 / sqrt(2.0)); + sim.T(0); + assert_amplitude_equality(sim.probe("0"), 0.0, 0.0); + assert_amplitude_equality(sim.probe("1"), 0.0, -1.0); + sim.T(0); + assert_amplitude_equality(sim.probe("0"), 0.0, 0.0); + assert_amplitude_equality(sim.probe("1"), 1.0 / sqrt(2.0), -1.0 / sqrt(2.0)); + sim.T(0); + assert_amplitude_equality(sim.probe("0"), 0.0, 0.0); + assert_amplitude_equality(sim.probe("1"), 1.0, 0.0); +} + +// Tests Rx on computational basis states, for angles between 0 and pi/2 +TEST_CASE("RxGateTest") { + const logical_qubit_id num_qubits = 32; + const qubit_label_type zero(0); + for (double angle = 0.0; angle < M_PI / 2.0; angle += 0.1) { + SparseSimulator sim = SparseSimulator(num_qubits); + sim.R(Gates::Basis::PauliX, angle, 0); + assert_amplitude_equality(sim.probe("0"), cos(angle / 2.0), 0.0); + assert_amplitude_equality(sim.probe("1"), 0.0, -sin(angle / 2.0)); + sim.R(Gates::Basis::PauliX, -angle, 0); + assert_amplitude_equality(sim.probe("0"), 1.0, 0.0); + assert_amplitude_equality(sim.probe("1"), 0.0, 0.0); + sim.X(0); + sim.R(Gates::Basis::PauliX, angle, 0); + assert_amplitude_equality(sim.probe("0"), 0.0, -sin(angle / 2.0)); + assert_amplitude_equality(sim.probe("1"), cos(angle / 2.0), 0.0); + sim.R(Gates::Basis::PauliX, -angle, 0); + assert_amplitude_equality(sim.probe("0"), 0.0, 0.0); + assert_amplitude_equality(sim.probe("1"), 1.0, 0.0); + } +} + +// Tests Ry on computational basis states, for angles between 0 and pi/2 +TEST_CASE("RyGateTest") { + const logical_qubit_id num_qubits = 32; + const qubit_label_type zero(0); + for (double angle = 0.0; angle < M_PI / 2.0; angle += 0.1) { + SparseSimulator sim = SparseSimulator(num_qubits); + sim.R(Gates::Basis::PauliY, angle, 0); + assert_amplitude_equality(sim.probe("0"), cos(angle / 2.0), 0.0); + assert_amplitude_equality(sim.probe("1"), sin(angle / 2.0), 0.0); + sim.R(Gates::Basis::PauliY, -angle, 0); + assert_amplitude_equality(sim.probe("0"), 1.0, 0.0); + assert_amplitude_equality(sim.probe("1"), 0.0, 0.0); + sim.X(0); + sim.R(Gates::Basis::PauliY, angle, 0); + assert_amplitude_equality(sim.probe("0"), -sin(angle / 2.0), 0.0); + assert_amplitude_equality(sim.probe("1"), cos(angle / 2.0), 0.0); + sim.R(Gates::Basis::PauliY, -angle, 0); + assert_amplitude_equality(sim.probe("0"), 0.0, 0.0); + assert_amplitude_equality(sim.probe("1"), 1.0, 0.0); + } +} + +// Tests Rz on computational basis states, for angles between 0 and pi/2 +TEST_CASE("RzGateTest") { + const logical_qubit_id num_qubits = 32; + const qubit_label_type zero(0); + for (double angle = 0.0; angle < M_PI / 2.0; angle += 0.1) { + SparseSimulator sim = SparseSimulator(num_qubits); + logical_qubit_id qubit = 0; + sim.R(Gates::Basis::PauliZ, angle, qubit); + assert_amplitude_equality(sim.probe("0"), 1.0, 0.0); + assert_amplitude_equality(sim.probe("1"), 0.0, 0.0); + sim.R(Gates::Basis::PauliZ, -angle, qubit); + assert_amplitude_equality(sim.probe("0"), 1.0, 0.0); + assert_amplitude_equality(sim.probe("1"), 0.0, 0.0); + sim.X(qubit); + assert_amplitude_equality(sim.probe("0"), 0.0, 0.0); + assert_amplitude_equality(sim.probe("1"), 1.0, 0.0); + sim.R(Gates::Basis::PauliZ, angle, qubit); + assert_amplitude_equality(sim.probe("0"), 0.0, 0.0); + assert_amplitude_equality(sim.probe("1"), cos(angle), sin(angle)); + sim.R(Gates::Basis::PauliZ, -angle, qubit); + assert_amplitude_equality(sim.probe("0"), 0.0, 0.0); + assert_amplitude_equality(sim.probe("1"), 1.0, 0.0); + } +} + +// Tests CNOT on all 2-qubit computational basis stats +TEST_CASE("CNOTGateTest") { + const logical_qubit_id num_qubits = 32; + const qubit_label_type zero(0); + SparseSimulator sim = SparseSimulator(num_qubits); + logical_qubit_id qubits[2]{ 0, 1 }; + sim.MCX({ qubits[0] }, qubits[1]); + assert_amplitude_equality(sim.probe("00"), 1.0, 0.0); + assert_amplitude_equality(sim.probe("01"), 0.0, 0.0); + assert_amplitude_equality(sim.probe("10"), 0.0, 0.0); + assert_amplitude_equality(sim.probe("11"), 0.0, 0.0); + sim.X(qubits[0]); + sim.MCX({ qubits[0] }, qubits[1]); + assert_amplitude_equality(sim.probe("00"), 0.0, 0.0); + assert_amplitude_equality(sim.probe("01"), 0.0, 0.0); + assert_amplitude_equality(sim.probe("10"), 0.0, 0.0); + assert_amplitude_equality(sim.probe("11"), 1.0, 0.0); + sim.MCX({ qubits[0] }, qubits[1]); + assert_amplitude_equality(sim.probe("00"), 0.0, 0.0); + assert_amplitude_equality(sim.probe("01"), 1.0, 0.0); + assert_amplitude_equality(sim.probe("10"), 0.0, 0.0); + assert_amplitude_equality(sim.probe("11"), 0.0, 0.0); + sim.X(qubits[0]); + sim.X(qubits[1]); + sim.MCX({ qubits[0] }, qubits[1]); + assert_amplitude_equality(sim.probe("00"), 0.0, 0.0); + assert_amplitude_equality(sim.probe("01"), 0.0, 0.0); + assert_amplitude_equality(sim.probe("10"), 1.0, 0.0); + assert_amplitude_equality(sim.probe("11"), 0.0, 0.0); +} + + + +// Tests all possible computational basis states +// for some number of controls and one target +TEST_CASE("MCXGateTest") { + const size_t n_qubits = 7; + const qubit_label_type zero(0); + SparseSimulator sim = SparseSimulator(n_qubits); + std::vector qubits(n_qubits); + std::generate(qubits.begin(), qubits.end(), [] { static int i{ 0 }; return i++; }); + std::vector controls(qubits.begin() + 1, qubits.end()); + logical_qubit_id target = qubits[0]; + for (logical_qubit_id i = 0; i < pow(2, n_qubits - 1); i++) { // the bitstring of the controls + sim.MCX(controls, target); + for (logical_qubit_id j = 0; j < pow(2, n_qubits - 1); j++) { // bitstring to test + std::bitset j_bits = j; // a bitset for the string to test, with the target as 0 + j_bits = j_bits << 1; + std::bitset j_odd_bits = j_bits; // same as j, but with the target as 1 + j_odd_bits.set(0); + if (j != i) { // The controls are not in this state, so the amplitude should be 0 + assert_amplitude_equality(sim.probe(j_bits.to_string()), 0.0, 0.0); + assert_amplitude_equality(sim.probe(j_odd_bits.to_string()), 0.0, 0.0); + } + else if (i == pow(2, n_qubits - 1) - 1) { // All controls are 1, so this should flip the output + assert_amplitude_equality(sim.probe(j_bits.to_string()), 0.0, 0.0); + assert_amplitude_equality(sim.probe(j_odd_bits.to_string()), 1.0, 0.0); + } + else { // This is the state of the controls, but they are not all 1, so nothing should have happened + assert_amplitude_equality(sim.probe(j_bits.to_string()), 1.0, 0.0); + assert_amplitude_equality(sim.probe(j_odd_bits.to_string()), 0.0, 0.0); + } + } + // Since MCX^2 = I, this should undo anything previously + sim.MCX(controls, target); + for (logical_qubit_id j = 0; j < pow(2, n_qubits - 1); j++) { + std::bitset j_bits = j; + j_bits = j_bits << 1; + std::bitset j_odd_bits = j_bits; + j_odd_bits.set(0); + if (j != i) { // The controls are not in this state, so the amplitude should be 0 + assert_amplitude_equality(sim.probe(j_bits.to_string()), 0.0, 0.0); + assert_amplitude_equality(sim.probe(j_odd_bits.to_string()), 0.0, 0.0); + } + else { // This is the state of the controls, but the final qubit should be 0 + assert_amplitude_equality(sim.probe(j_bits.to_string()), 1.0, 0.0); + assert_amplitude_equality(sim.probe(j_odd_bits.to_string()), 0.0, 0.0); + } + } + // Update the controls + std::bitset diff = i ^ (i + 1); + for (logical_qubit_id j = 0; j < n_qubits - 1; j++) { + if (diff[j]) sim.X(controls[j]); + } + } +} + + +// Tests a controlled Y +// Same logic as the MCXGateTest +TEST_CASE("MCYGateTest") { + const size_t n_qubits = 7; + const qubit_label_type zero(0); + SparseSimulator sim = SparseSimulator(n_qubits); + std::vector qubits(n_qubits); + std::generate(qubits.begin(), qubits.end(), [] { static int i{ 0 }; return i++; }); + std::vector controls(qubits.begin() + 1, qubits.end()); + logical_qubit_id target = qubits[0]; + for (logical_qubit_id i = 0; i < pow(2, n_qubits - 1); i++) { + sim.MCY(controls, target); + for (logical_qubit_id j = 0; j < pow(2, n_qubits - 1); j++) { + std::bitset j_bits = j; + j_bits = j_bits << 1; + std::bitset j_odd_bits = j_bits; + j_odd_bits.set(0); + if (j != i) { + assert_amplitude_equality(sim.probe(j_bits.to_string()), 0.0, 0.0); + assert_amplitude_equality(sim.probe(j_odd_bits.to_string()), 0.0, 0.0); + } + else if (i == pow(2, n_qubits - 1) - 1) { + assert_amplitude_equality(sim.probe(j_bits.to_string()), 0.0, 0.0); + assert_amplitude_equality(sim.probe(j_odd_bits.to_string()), 0.0, 1.0); + } + else { + assert_amplitude_equality(sim.probe(j_bits.to_string()), 1.0, 0.0); + assert_amplitude_equality(sim.probe(j_odd_bits.to_string()), 0.0, 0.0); + } + } + sim.MCY(controls, target); + for (logical_qubit_id j = 0; j < pow(2, n_qubits - 1); j++) { + std::bitset j_bits = j; + j_bits = j_bits << 1; + std::bitset j_odd_bits = j_bits; + j_odd_bits.set(0); + if (j != i) { + assert_amplitude_equality(sim.probe(j_bits.to_string()), 0.0, 0.0); + assert_amplitude_equality(sim.probe(j_odd_bits.to_string()), 0.0, 0.0); + } + else { + assert_amplitude_equality(sim.probe(j_bits.to_string()), 1.0, 0.0); + assert_amplitude_equality(sim.probe(j_odd_bits.to_string()), 0.0, 0.0); + } + } + std::bitset diff = i ^ (i + 1); + for (logical_qubit_id j = 0; j < n_qubits - 1; j++) { + if (diff[j]) sim.X(controls[j]); + } + } +} +// Tests a controlled Z +// Same logic as the MCXGateTest +TEST_CASE("MCZGateTest") { + const size_t n_qubits = 7; + const qubit_label_type zero(0); + SparseSimulator sim = SparseSimulator(n_qubits); + std::vector qubits(n_qubits); + std::generate(qubits.begin(), qubits.end(), [] { static int i{ 0 }; return i++; }); + std::vector controls(qubits.begin() + 1, qubits.end()); + logical_qubit_id target = qubits[0]; + sim.H(target); + for (logical_qubit_id i = 0; i < pow(2, n_qubits - 1); i++) { + sim.MCZ(controls, target); + for (logical_qubit_id j = 0; j < pow(2, n_qubits - 1); j++) { + std::bitset j_bits = j; + j_bits = j_bits << 1; + std::bitset j_odd_bits = j_bits; + j_odd_bits.set(0); + if (j != i) { + assert_amplitude_equality(sim.probe(j_bits.to_string()), 0.0, 0.0); + assert_amplitude_equality(sim.probe(j_odd_bits.to_string()), 0.0, 0.0); + } + else if (i == pow(2, n_qubits - 1) - 1) { + assert_amplitude_equality(sim.probe(j_bits.to_string()), 1.0/sqrt(2.0), 0.0); + assert_amplitude_equality(sim.probe(j_odd_bits.to_string()), -1.0 / sqrt(2.0), 0.0); + } + else { + assert_amplitude_equality(sim.probe(j_bits.to_string()), 1.0 / sqrt(2.0), 0.0); + assert_amplitude_equality(sim.probe(j_odd_bits.to_string()), 1.0 / sqrt(2.0), 0.0); + } + } + sim.MCZ(controls, target); + for (logical_qubit_id j = 0; j < pow(2, n_qubits - 1); j++) { + std::bitset j_bits = j; + j_bits = j_bits << 1; + std::bitset j_odd_bits = j_bits; + j_odd_bits.set(0); + if (j != i) { + assert_amplitude_equality(sim.probe(j_bits.to_string()), 0.0, 0.0); + assert_amplitude_equality(sim.probe(j_odd_bits.to_string()), 0.0, 0.0); + } + else { + assert_amplitude_equality(sim.probe(j_bits.to_string()), 1.0 / sqrt(2.0), 0.0); + assert_amplitude_equality(sim.probe(j_odd_bits.to_string()), 1.0 / sqrt(2.0), 0.0); + } + } + std::bitset diff = i ^ (i + 1); + for (logical_qubit_id j = 0; j < n_qubits - 1; j++) { + if (diff[j]) sim.X(controls[j]); + } + } +} + + + +// Tests the multi-exp on a uniform superposition +TEST_CASE("MultiExpWithHTest") { + const int num_qubits = 32; + auto qubit_prep = [](SparseSimulator& sim) { + sim.H(0); + sim.H(1); + sim.H(2); + }; + auto qubit_clear = [](SparseSimulator& sim) { + sim.H(2); + sim.release(2); + sim.H(1); + sim.release(1); + sim.H(0); + sim.release(0); + }; + MultiExpTest(qubit_prep, qubit_clear); +} + +// Tests the MultiExp on all computational basis states of 3 qubits +TEST_CASE("MultiExpBasisTest") { + const int num_qubits = 32; + auto qubit_prep = [](SparseSimulator& sim, int index) { + if ((index & 1) == 0) { sim.X(0); } + if ((index & 2) == 0) { sim.X(1); } + if ((index & 4) == 0) { sim.X(2); } + }; + auto qubit_clear = [](SparseSimulator& sim, int index) { + if ((index & 1) == 0) { sim.X(0); } + sim.release(0); + if ((index & 2) == 0) { sim.X(1); } + sim.release(1); + if ((index & 4) == 0) { sim.X(2); } + sim.release(2); + }; + for (int i = 0; i < 8; i++) { + MultiExpTest([=](SparseSimulator& sim) {qubit_prep(sim, i); }, [=](SparseSimulator& sim) {qubit_clear(sim, i); }); + } +} + +// Tests a SWAP gate on all 2-qubit computational basis states +TEST_CASE("SWAPGateTest") { + const logical_qubit_id num_qubits = 32; + const qubit_label_type zero(0); + SparseSimulator sim = SparseSimulator(num_qubits); + std::vector qubits{ 0,1 }; + sim.SWAP({ qubits[0] }, qubits[1]); // 00 -> 00 + assert_amplitude_equality(sim.probe("00"), 1.0, 0.0); + assert_amplitude_equality(sim.probe("01"), 0.0, 0.0); + assert_amplitude_equality(sim.probe("10"), 0.0, 0.0); + assert_amplitude_equality(sim.probe("11"), 0.0, 0.0); + sim.X(qubits[0]); + sim.SWAP( qubits[0] , qubits[1]); // 10 -> 01 + assert_amplitude_equality(sim.probe("00"), 0.0, 0.0); + assert_amplitude_equality(sim.probe("01"), 0.0, 0.0); + assert_amplitude_equality(sim.probe("10"), 1.0, 0.0); + assert_amplitude_equality(sim.probe("11"), 0.0, 0.0); + sim.SWAP( qubits[0] , qubits[1]); // 01 -> 10 + assert_amplitude_equality(sim.probe("0"), 0.0, 0.0); + assert_amplitude_equality(sim.probe("1"), 1.0, 0.0); + assert_amplitude_equality(sim.probe("10"), 0.0, 0.0); + assert_amplitude_equality(sim.probe("11"), 0.0, 0.0); + sim.X(qubits[1]); + sim.SWAP( qubits[0] , qubits[1]); // 11 -> 11 + assert_amplitude_equality(sim.probe("00"), 0.0, 0.0); + assert_amplitude_equality(sim.probe("01"), 0.0, 0.0); + assert_amplitude_equality(sim.probe("10"), 0.0, 0.0); + assert_amplitude_equality(sim.probe("11"), 1.0, 0.0); +} + +// Tests multi-controlled swap on all computational basis states of 4 qubits +// (2 controls, 2 targets) +TEST_CASE("CSWAPGateTest") { + const logical_qubit_id num_qubits = 32; + const qubit_label_type zero(0); + SparseSimulator sim = SparseSimulator(num_qubits); + std::vector target_qubits{ 0,1 }; + std::vector control_qubits{ 2,3 }; + // Lambda to test when controls should cause no swap + auto no_swap_test = [&](std::string controls) { + sim.CSWAP(control_qubits, target_qubits[0], target_qubits[1]); // 00 -> 00 + assert_amplitude_equality(sim.probe(controls+"00"), 1.0, 0.0); + assert_amplitude_equality(sim.probe(controls + "01"), 0.0, 0.0); + assert_amplitude_equality(sim.probe(controls + "10"), 0.0, 0.0); + assert_amplitude_equality(sim.probe(controls + "11"), 0.0, 0.0); + sim.X(target_qubits[0]); + sim.CSWAP(control_qubits, target_qubits[0], target_qubits[1]); // 01 -> 01 + assert_amplitude_equality(sim.probe(controls + "00"), 0.0, 0.0); + assert_amplitude_equality(sim.probe(controls + "01"), 1.0, 0.0); + assert_amplitude_equality(sim.probe(controls + "10"), 0.0, 0.0); + assert_amplitude_equality(sim.probe(controls + "11"), 0.0, 0.0); + sim.X(target_qubits[1]); + sim.CSWAP(control_qubits, target_qubits[0], target_qubits[1]); // 11 -> 11 + assert_amplitude_equality(sim.probe(controls + "00"), 0.0, 0.0); + assert_amplitude_equality(sim.probe(controls + "01"), 0.0, 0.0); + assert_amplitude_equality(sim.probe(controls + "10"), 0.0, 0.0); + assert_amplitude_equality(sim.probe(controls + "11"), 1.0, 0.0); + sim.X(target_qubits[0]); + sim.CSWAP(control_qubits, target_qubits[0], target_qubits[1]); // 10 -> 10 + assert_amplitude_equality(sim.probe(controls + "00"), 0.0, 0.0); + assert_amplitude_equality(sim.probe(controls + "01"), 0.0, 0.0); + assert_amplitude_equality(sim.probe(controls + "10"), 1.0, 0.0); + assert_amplitude_equality(sim.probe(controls + "11"), 0.0, 0.0); + sim.X(target_qubits[1]); + }; + // Controls are 00, no swap + no_swap_test("00"); + sim.X(control_qubits[0]); + // Controls are 01, no swap + no_swap_test("01"); + sim.X(control_qubits[1]); + // Controls are 11, test for swap + sim.CSWAP(control_qubits, target_qubits[0], target_qubits[1]); // 00 -> 00 + assert_amplitude_equality(sim.probe("1100"), 1.0, 0.0); + assert_amplitude_equality(sim.probe("1101"), 0.0, 0.0); + assert_amplitude_equality(sim.probe("1110"), 0.0, 0.0); + assert_amplitude_equality(sim.probe("1111"), 0.0, 0.0); + sim.X(target_qubits[0]); + sim.CSWAP(control_qubits, target_qubits[0], target_qubits[1]); // 10 -> 01 + assert_amplitude_equality(sim.probe("1100"), 0.0, 0.0); + assert_amplitude_equality(sim.probe("1101"), 0.0, 0.0); + assert_amplitude_equality(sim.probe("1110"), 1.0, 0.0); + assert_amplitude_equality(sim.probe("1111"), 0.0, 0.0); + sim.CSWAP(control_qubits, target_qubits[0], target_qubits[1]);// 01 -> 10 + assert_amplitude_equality(sim.probe("1100"), 0.0, 0.0); + assert_amplitude_equality(sim.probe("1101"), 1.0, 0.0); + assert_amplitude_equality(sim.probe("1110"), 0.0, 0.0); + assert_amplitude_equality(sim.probe("1111"), 0.0, 0.0); + sim.X(target_qubits[1]); + sim.CSWAP(control_qubits, target_qubits[0], target_qubits[1]); // 11 -> 11 + assert_amplitude_equality(sim.probe("1100"), 0.0, 0.0); + assert_amplitude_equality(sim.probe("1101"), 0.0, 0.0); + assert_amplitude_equality(sim.probe("1110"), 0.0, 0.0); + assert_amplitude_equality(sim.probe("1111"), 1.0, 0.0); + sim.X(target_qubits[1]); + sim.X(target_qubits[0]); + sim.X(control_qubits[0]); + // Controls are 10, test for no swap + no_swap_test("10"); +} + +// Tests measurement probabilistically +// Based on the expected measurement probabilities for a Pauli Y +// rotation +// It samples a lot of measurements, and based on the +// current variance (of a binomial distrbution): +// - if it's very close to the expected distribution, +// it considers this a success +// - if it's very far from the expected distribution, +// it throws an exception +// - if it's in between, it runs more samples +// While this run-time is undetermined, the threshold +// for an exception shrinks with the number of tests +TEST_CASE("MTest") { + const logical_qubit_id num_qubits = 32; + const qubit_label_type zero(0); + const int n_tests = 5000; + const double log_false_positive_threshold = 0.1; + const double log_false_negative_threshold = 100.0; + for (double angle = 0.0; angle < M_PI / 2.0; angle += 0.1) { + SparseSimulator sim = SparseSimulator(num_qubits); + sim.set_random_seed(12345); + double expected_ratio = sin(angle / 2.0) * sin(angle / 2.0); + double ratio = 0.0; + unsigned long total_tests = 0; + unsigned long ones = 0; + double std_dev = 0.0; + double log_prob = 0.0; + logical_qubit_id qubit = 0; + do { + for (int i = 0; i < n_tests; i++) { + sim.R(Gates::Basis::PauliY, angle, qubit); + if (sim.M(qubit)) { + ones++; + sim.X(qubit); + } + } + total_tests += n_tests; + ratio = (double)ones / (double)total_tests; + double abs_diff = std::abs(expected_ratio - ratio); + // Based on Chernoff bounds + log_prob = abs_diff * abs_diff * expected_ratio * (double)total_tests; + std_dev = sqrt(expected_ratio * (1.0 - expected_ratio)) / (double)total_tests; + // Using variance of the binomial distribution + if (log_false_positive_threshold >= log_prob) { + break; + } + } while (log_false_negative_threshold >= log_prob); + if (log_false_negative_threshold < log_prob) { + throw std::runtime_error("Statistically improbable measurement results"); + } + } + +} + +// Tests an assortment of assertions to both pass and to throw exceptions +TEST_CASE("AssertTest") { + const logical_qubit_id num_qubits = 32; + const qubit_label_type zero(0); + SparseSimulator sim = SparseSimulator(num_qubits); + using namespace Gates; + std::vector basis{ Basis::PauliZ, Basis::PauliZ, Basis::PauliZ }; + std::vector qubits{ 0,1,2 }; + sim.Assert(basis, qubits, false); + sim.update_state(); + // These require forcing the simulator to update the state for it to actually throw the exception + + auto sim_assert = [&](std::vector const& basis, bool val) { + sim.Assert(basis, qubits, val); + sim.update_state(); + }; + REQUIRE_THROWS_AS(sim_assert(basis, true), std::exception); + + basis = { Basis::PauliZ, Basis::PauliZ, Basis::PauliI }; + REQUIRE_THROWS_AS(sim_assert(basis, true), std::exception); + + basis = { Basis::PauliX, Basis::PauliI, Basis::PauliI }; + REQUIRE_THROWS_AS(sim_assert(basis, false), std::exception); + REQUIRE_THROWS_AS(sim_assert(basis, true), std::exception); + + basis = { Basis::PauliY, Basis::PauliI, Basis::PauliI }; + REQUIRE_THROWS_AS(sim_assert(basis, false), std::exception); + REQUIRE_THROWS_AS(sim_assert(basis, true), std::exception); +} + +// Tests an assortment of assertions on GHZ states +TEST_CASE("AssertGHZTest") { + const logical_qubit_id num_qubits = 32; + const qubit_label_type zero(0); + SparseSimulator sim = SparseSimulator(num_qubits); + using namespace Gates; + std::vector basis(3, Basis::PauliX); + std::vector qubits{ 0,1,2 }; + sim.H(0); + sim.MCX({ 0 }, 1); + sim.MCX({ 0 }, 2); + + sim.Assert(basis, qubits, false); + REQUIRE_THROWS_AS(sim.Assert(basis, qubits, true), std::exception); + sim.Z(0); + sim.Assert(basis, qubits, true); + REQUIRE_THROWS_AS(sim.Assert(basis, qubits, false), std::exception); + sim.S(0); + basis = { Basis::PauliY, Basis::PauliY, Basis::PauliY }; + sim.Assert(basis, qubits, false); + REQUIRE_THROWS_AS(sim.Assert(basis, qubits, true), std::exception); + sim.Z(0); + sim.Assert(basis, qubits, true); + REQUIRE_THROWS_AS(sim.Assert(basis, qubits, false), std::exception); + sim.probe("0"); +} + +// Basic test of quantum teleportation +TEST_CASE("TeleportationTest") +{ + const logical_qubit_id num_qubits = 32; + const qubit_label_type zero(0); + for (double test_angle = 0; test_angle < 1.0; test_angle += 0.34) { + SparseSimulator sim = SparseSimulator(num_qubits); + sim.set_random_seed(12345); + std::vector qubits{ 0,1,2 }; + sim.H(qubits[1]); + sim.MCX({ qubits[1] }, qubits[2]); + + sim.R(Gates::Basis::PauliY, test_angle, 0); + + sim.MCX({ qubits[0] }, qubits[1]); + sim.H(qubits[0]); + bool result0 = sim.M(qubits[0]); + bool result1 = sim.M(qubits[1]); + if (result1) { + sim.X(qubits[2]); + sim.X(qubits[1]); + } + if (result0) { + sim.Z(qubits[2]); + sim.X(qubits[0]); + } + + amplitude teleported_qubit_0 = sim.probe("000"); + amplitude teleported_qubit_1 = sim.probe("100"); + REQUIRE((float)cos(test_angle / 2.0) == (float)teleported_qubit_0.real()); + REQUIRE((float)0.0 == (float)teleported_qubit_0.imag()); + REQUIRE((float)sin(test_angle / 2.0) == (float)teleported_qubit_1.real()); + REQUIRE((float)0.0 == (float)teleported_qubit_1.imag()); + } +} + + +// Tests that H gates properly cancel when executed +TEST_CASE("HCancellationTest") { + const int n_qubits = 128; + SparseSimulator sim = SparseSimulator(n_qubits); + sim.set_random_seed(12345); + std::vector qubits(n_qubits); + std::generate(qubits.begin(), qubits.end(), [] { static int i{ 0 }; return i++; }); + size_t buckets = 0; + // Will cause a huge memory problem if there is no cancellation + const int n_samples = 16; + for (int i = 0; i < n_qubits; i += n_samples) { + for (int ii = 0; ii < n_samples; ii++) { + sim.H(qubits[i + ii]); + } + sim.update_state(); + for (int ii = n_samples - 1; ii >= 0; ii--) { + sim.H(qubits[i + ii]); + } + sim.update_state(); + } +} + +// Checks that X and Z gates commute with H +TEST_CASE("HXZCommutationTest") { + const int n_qubits = 16; + SparseSimulator sim = SparseSimulator(n_qubits); + sim.set_random_seed(12345); + std::vector qubits(n_qubits); + std::generate(qubits.begin(), qubits.end(), [] { static int i{ 0 }; return i++; }); + for (int i = 0; i < n_qubits; i++) { + sim.H(qubits[i]); + } + // Here it will actually just commute the X and Z through the H in the queue + // without actually executing anything + std::bitset one_state = 0; + for (int i = 0; i < n_qubits - 1; i += 2) { + sim.Z(qubits[i]); + sim.X(qubits[i + 1]); + one_state.set(i); + } + for (int i = n_qubits - 1; i >= 0; i--) { + sim.H(qubits[i]); + } + for (std::uint64_t i = 0; i < (std::uint64_t{1} << n_qubits); i++) { + amplitude state = sim.probe(std::bitset(i).to_string()); + if (i == one_state.to_ulong()) { + assert_amplitude_equality(state, 1.0, 0.0); + } + else { + assert_amplitude_equality(state, 0.0, 0.0); + } + } +} diff --git a/src/Simulation/Simulators/SparseSimulator/SparseSimulatorTests/TestHelpers.cpp b/src/Simulation/Simulators/SparseSimulator/Native/TestHelpers.cpp similarity index 89% rename from src/Simulation/Simulators/SparseSimulator/SparseSimulatorTests/TestHelpers.cpp rename to src/Simulation/Simulators/SparseSimulator/Native/TestHelpers.cpp index e2d8135a0df..f510d40ec8f 100644 --- a/src/Simulation/Simulators/SparseSimulator/SparseSimulatorTests/TestHelpers.cpp +++ b/src/Simulation/Simulators/SparseSimulator/Native/TestHelpers.cpp @@ -1,20 +1,18 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -#include "pch.h" -#include "CppUnitTest.h" -#include "../Native/SparseSimulator.h" +#include "SparseSimulator.h" #include #include +#include -using namespace Microsoft::VisualStudio::CppUnitTestFramework; using namespace Microsoft::Quantum::SPARSESIMULATOR; #ifndef M_PI #define M_PI 3.14159265358979323846 #endif -#define TEST_TOLERANCE 0.00000001 +#define TEST_TOLERANCE 1.e-10 namespace SparseSimulatorTestHelpers @@ -87,7 +85,6 @@ namespace SparseSimulatorTestHelpers std::size_t mask = make_mask(qs); amplitude phase = std::exp(amplitude(0., -phi)); -#pragma omp parallel for schedule(static) for (std::intptr_t x = 0; x < static_cast(wfn.size()); x++) wfn[x] *= (std::bitset<64>(x & mask).count() % 2 ? phase : std::conj(phase)); } @@ -138,13 +135,7 @@ namespace SparseSimulatorTestHelpers // Assertions for equality of amplitude types inline void assert_double_equality_with_tolerance(double value1, double value2) { - if (value1 > value2) { - value1 = std::max(value1 - TEST_TOLERANCE, value2); - } - else { - value1 = std::min(value1 + TEST_TOLERANCE, value2); - } - Assert::AreEqual(value1, value2); + REQUIRE(value1 == Approx(value2).margin(TEST_TOLERANCE)); } void assert_amplitude_equality(amplitude amp, double real, double imag) { @@ -155,6 +146,4 @@ namespace SparseSimulatorTestHelpers void assert_amplitude_equality(amplitude expected_amp, amplitude actual_amp) { assert_amplitude_equality(actual_amp, expected_amp.real(), expected_amp.imag()); } - - } \ No newline at end of file diff --git a/src/Simulation/Simulators/SparseSimulator/SparseSimulatorTests/TestHelpers.hpp b/src/Simulation/Simulators/SparseSimulator/Native/TestHelpers.hpp similarity index 81% rename from src/Simulation/Simulators/SparseSimulator/SparseSimulatorTests/TestHelpers.hpp rename to src/Simulation/Simulators/SparseSimulator/Native/TestHelpers.hpp index 8eb71c0f7b1..6f2888ff9f5 100644 --- a/src/Simulation/Simulators/SparseSimulator/SparseSimulatorTests/TestHelpers.hpp +++ b/src/Simulation/Simulators/SparseSimulator/Native/TestHelpers.hpp @@ -3,13 +3,10 @@ #pragma once -#include "pch.h" -#include "CppUnitTest.h" -#include "../Native/SparseSimulator.h" +#include "SparseSimulator.h" #include #include -using namespace Microsoft::VisualStudio::CppUnitTestFramework; using namespace Microsoft::Quantum::SPARSESIMULATOR; #ifndef M_PI diff --git a/src/Simulation/Simulators/SparseSimulator/Native/quantum_state.hpp b/src/Simulation/Simulators/SparseSimulator/Native/quantum_state.hpp index c476f1f9361..0832e34332a 100644 --- a/src/Simulation/Simulators/SparseSimulator/Native/quantum_state.hpp +++ b/src/Simulation/Simulators/SparseSimulator/Native/quantum_state.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include "basic_quantum_state.hpp" diff --git a/src/Simulation/Simulators/SparseSimulator/SparseSimulatorTests/CMakeLists.txt b/src/Simulation/Simulators/SparseSimulator/SparseSimulatorTests/CMakeLists.txt deleted file mode 100644 index a208bb9df56..00000000000 --- a/src/Simulation/Simulators/SparseSimulator/SparseSimulatorTests/CMakeLists.txt +++ /dev/null @@ -1,13 +0,0 @@ -cmake_minimum_required(VERSION 3.10) -project(SparseQuantumSimulator) - -set(CMAKE_CXX_STANDARD 17) -set(CMAKE_CXX_STANDARD_REQUIRED ON) -set(CMAKE_CXX_EXTENSIONS OFF) - -include(CTest) -enable_testing() - -set(CMAKE_MACOSX_RPATH 1) -add_executable(SparseSimulatorTests SparseSimulatorTests.cpp TestHelpers.cpp) -add_test(SparseSimulatorTests SparseSimulatorTests) \ No newline at end of file diff --git a/src/Simulation/Simulators/SparseSimulator/SparseSimulatorTests/CSharpIntegrationTests.cpp b/src/Simulation/Simulators/SparseSimulator/SparseSimulatorTests/CSharpIntegrationTests.cpp deleted file mode 100644 index 409e747a77e..00000000000 --- a/src/Simulation/Simulators/SparseSimulator/SparseSimulatorTests/CSharpIntegrationTests.cpp +++ /dev/null @@ -1,307 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include "../Native/SparseSimulator.h" -#include "../Native/capi.hpp" -#include "../Native/capi.cpp" // yes really -#include "../Native/factory.hpp" -#include "../Native/factory.cpp" -#include "TestHelpers.hpp" - -#include "CppUnitTest.h" -#include -#include - -using namespace Microsoft::VisualStudio::CppUnitTestFramework; -using namespace Microsoft::Quantum::SPARSESIMULATOR; -using namespace SparseSimulatorTestHelpers; - -#ifndef M_PI -#define M_PI 3.14159265358979323846 -#endif - -namespace CSharpIntegrationTests -{ - TEST_CLASS(CSharpIntegrationTests) - { - template - void MultiExpReferenceTest( - std::function qubit_prep, - std::function qubit_clear - ) { - const qubit_label_type zero(0); - logical_qubit_id* qubits = new logical_qubit_id[3]; - qubits[0] = 0; - qubits[1] = 1; - qubits[2] = 2; - int* Paulis = new int[3]; - for (int intPaulis = 0; intPaulis < 4 * 4 * 4; intPaulis++) { - Paulis[0] = intPaulis % 4; - Paulis[1] = (intPaulis / 4 ) % 4; - Paulis[2] = intPaulis / 16; - - for (double angle = 0.0; angle < M_PI / 2.0; angle += 0.1) { - unsigned sim = init_cpp(32); - qubit_prep(sim); - - std::vector vector_rep(8, 0.0); - for (unsigned i = 0; i < 8; i++) { - vector_rep[i] = getSimulator(sim)->probe(std::bitset<3>(i).to_string()); - } - // New simulator Exp - Exp_cpp(sim, 3, Paulis, angle, qubits); - // Old simulator Exp - std::vector actualPaulis = { (Gates::Basis)Paulis[0],(Gates::Basis)Paulis[1], (Gates::Basis)Paulis[2] }; - apply_exp(vector_rep, actualPaulis, angle, std::vector{ 0, 1, 2 }); - for (unsigned i = 0; i < 8; i++) { - amplitude result = getSimulator(sim)->probe(std::bitset<3>(i).to_string()); - assert_amplitude_equality(vector_rep[i], result); - } - Exp_cpp(sim, 3, Paulis, -angle, qubits); - qubit_clear(sim); - } - } - } - public: - TEST_METHOD(initializationTest) { - unsigned sim = init_cpp(32); - } - - TEST_METHOD(AllocationTest) { - unsigned sim = init_cpp(32); - allocateQubit_cpp(sim, 0); - releaseQubit_cpp(sim, 0); - } - TEST_METHOD(AllocateRebuildTest) { - unsigned sim = init_cpp(64); - for (int i = 0; i < 1024; i++) { - allocateQubit_cpp(sim, i); - getSimulator(sim)->X(i); - getSimulator(sim)->update_state(); - } - for (int i = 0; i < 1024; i++) { - getSimulator(sim)->X(i); - releaseQubit_cpp(sim, i); - } - } - - TEST_METHOD(XTest) { - unsigned sim = init_cpp(32); - allocateQubit_cpp(sim, 0); - X_cpp(sim, 0); - assert_amplitude_equality(getSimulator(sim)->probe("0"), 0.0, 0.0); - assert_amplitude_equality(getSimulator(sim)->probe("1"), 1.0, 0.0); - X_cpp(sim, 0); - releaseQubit_cpp(sim, 0); - } - TEST_METHOD(ZTest) { - unsigned sim = init_cpp(32); - allocateQubit_cpp(sim, 0); - Z_cpp(sim, 0); - assert_amplitude_equality(getSimulator(sim)->probe("0"), 1.0, 0.0); - assert_amplitude_equality(getSimulator(sim)->probe("1"), 0.0, 0.0); - Z_cpp(sim, 0); - assert_amplitude_equality(getSimulator(sim)->probe("0"), 1.0, 0.0); - assert_amplitude_equality(getSimulator(sim)->probe("1"), 0.0, 0.0); - X_cpp(sim, 0); - Z_cpp(sim, 0); - assert_amplitude_equality(getSimulator(sim)->probe("0"), 0.0, 0.0); - assert_amplitude_equality(getSimulator(sim)->probe("1"), -1.0, 0.0); - Z_cpp(sim, 0); - assert_amplitude_equality(getSimulator(sim)->probe("0"), 0.0, 0.0); - assert_amplitude_equality(getSimulator(sim)->probe("1"), 1.0, 0.0); - X_cpp(sim, 0); - releaseQubit_cpp(sim, 0); - } - TEST_METHOD(HTest) { - unsigned sim = init_cpp(32); - allocateQubit_cpp(sim, 0); - H_cpp(sim, 0); - assert_amplitude_equality(getSimulator(sim)->probe("0"), 1.0 / sqrt(2.0), 0.0); - assert_amplitude_equality(getSimulator(sim)->probe("1"), 1.0 / sqrt(2.0), 0.0); - H_cpp(sim, 0); - assert_amplitude_equality(getSimulator(sim)->probe("0"), 1.0, 0.0); - assert_amplitude_equality(getSimulator(sim)->probe("1"), 0.0, 0.0); - X_cpp(sim, 0); - H_cpp(sim, 0); - assert_amplitude_equality(getSimulator(sim)->probe("0"), 1.0 / sqrt(2.0), 0.0); - assert_amplitude_equality(getSimulator(sim)->probe("1"), -1.0 / sqrt(2.0), 0.0); - H_cpp(sim, 0); - assert_amplitude_equality(getSimulator(sim)->probe("0"), 0.0, 0.0); - assert_amplitude_equality(getSimulator(sim)->probe("1"), 1.0, 0.0); - X_cpp(sim, 0); - releaseQubit_cpp(sim, 0); - } - - TEST_METHOD(TGateTest) { - unsigned sim = init_cpp(32); - allocateQubit_cpp(sim, 0); - T_cpp(sim, 0); - assert_amplitude_equality(getSimulator(sim)->probe("0"), 1.0, 0.0); - assert_amplitude_equality(getSimulator(sim)->probe("1"), 0.0, 0.0); - X_cpp(sim, 0); - T_cpp(sim, 0); - assert_amplitude_equality(getSimulator(sim)->probe("0"), 0.0, 0.0); - assert_amplitude_equality(getSimulator(sim)->probe("1"), 1.0 / sqrt(2.0), 1.0 / sqrt(2.0)); - T_cpp(sim, 0); - assert_amplitude_equality(getSimulator(sim)->probe("0"), 0.0, 0.0); - assert_amplitude_equality(getSimulator(sim)->probe("1"), 0.0, 1.0); - T_cpp(sim, 0); - assert_amplitude_equality(getSimulator(sim)->probe("0"), 0.0, 0.0); - assert_amplitude_equality(getSimulator(sim)->probe("1"), -1.0 / sqrt(2.0), 1.0 / sqrt(2.0)); - T_cpp(sim, 0); - assert_amplitude_equality(getSimulator(sim)->probe("0"), 0.0, 0.0); - assert_amplitude_equality(getSimulator(sim)->probe("1"), -1.0, 0.0); - T_cpp(sim, 0); - assert_amplitude_equality(getSimulator(sim)->probe("0"), 0.0, 0.0); - assert_amplitude_equality(getSimulator(sim)->probe("1"), -1.0 / sqrt(2.0), -1.0 / sqrt(2.0)); - T_cpp(sim, 0); - assert_amplitude_equality(getSimulator(sim)->probe("0"), 0.0, 0.0); - assert_amplitude_equality(getSimulator(sim)->probe("1"), 0.0, -1.0); - T_cpp(sim, 0); - assert_amplitude_equality(getSimulator(sim)->probe("0"), 0.0, 0.0); - assert_amplitude_equality(getSimulator(sim)->probe("1"), 1.0 / sqrt(2.0), -1.0 / sqrt(2.0)); - T_cpp(sim, 0); - assert_amplitude_equality(getSimulator(sim)->probe("0"), 0.0, 0.0); - assert_amplitude_equality(getSimulator(sim)->probe("1"), 1.0, 0.0); - X_cpp(sim, 0); - releaseQubit_cpp(sim, 0); - } - - TEST_METHOD(HCancellationTest) - { - int n_qubits = 16; - unsigned sim = init_cpp(n_qubits); - size_t buckets = 0; - for (int i = 0; i < n_qubits; i++) { - allocateQubit_cpp(sim, i); - H_cpp(sim, i); - } - for (int i = n_qubits - 1; i >= 0; i--) { - H_cpp(sim, i); - // If the H do not cancel out, release will fail in an opaque way - releaseQubit_cpp(sim, i); - } - } - - TEST_METHOD(HXZCommutationTest) - { - const int n_qubits = 16; - unsigned sim = init_cpp(n_qubits); - for (int i = 0; i < n_qubits; i++) { - allocateQubit_cpp(sim, i); - H_cpp(sim, i); - } - std::bitset one_state = 0; - for (int i = 0; i < n_qubits - 1; i += 2) { - Z_cpp(sim, i); - X_cpp(sim, i+1); - one_state.set(i); - } - for (int i = n_qubits - 1; i >= 0; i--) { - H_cpp(sim, i); - } - for (__int64 i = 0; i< pow(2, n_qubits); i++) { - amplitude state = getSimulator(sim)->probe(std::bitset(i).to_string()); - if (i == one_state.to_ulong()) { - assert_amplitude_equality(state, 1.0, 0.0); - } - else { - assert_amplitude_equality(state, 0.0, 0.0); - } - } - } - - TEST_METHOD(ResetTest) - { - const int n_qubits = 16; - unsigned sim = init_cpp(n_qubits); - allocateQubit_cpp(sim, 0); - Reset_cpp(sim, 0); - amplitude state = getSimulator(sim)->probe("0"); - assert_amplitude_equality(state, 1.0, 0.0); - X_cpp(sim, 0); - Reset_cpp(sim, 0); - state = getSimulator(sim)->probe("0"); - // No qubit exists; should have amplitude 0 - assert_amplitude_equality(state, 1.0, 0.0); - allocateQubit_cpp(sim, 1); - X_cpp(sim, 0); - logical_qubit_id* controls = new logical_qubit_id{ 0 }; - MCX_cpp(sim, 1, controls, 1); - Reset_cpp(sim, 0); - state = getSimulator(sim)->probe("00"); - assert_amplitude_equality(state, 0.0, 0.0); - state = getSimulator(sim)->probe("10"); - assert_amplitude_equality(state, 1.0, 0.0); - Reset_cpp(sim, 1); - state = getSimulator(sim)->probe("00"); - assert_amplitude_equality(state, 1.0, 0.0); - state = getSimulator(sim)->probe("10"); - assert_amplitude_equality(state, 0.0, 0.0); - releaseQubit_cpp(sim, 1); - releaseQubit_cpp(sim, 0); - } - - TEST_METHOD(MultiExpWithHTest) { - const int num_qubits = 32; - auto qubit_prep = [](unsigned sim ) { - H_cpp(sim, 0); - H_cpp(sim, 1); - H_cpp(sim, 2); - }; - auto qubit_clear = [](unsigned sim) { - H_cpp(sim, 2); - releaseQubit_cpp(sim, 2); - H_cpp(sim, 1); - releaseQubit_cpp(sim, 1); - H_cpp(sim, 0); - releaseQubit_cpp(sim, 0); - }; - MultiExpReferenceTest(qubit_prep, qubit_clear); - } - - TEST_METHOD(MultiExpBasisTest) { - const int num_qubits = 32; - auto qubit_prep = [](unsigned sim, int index) { - if ((index & 1) == 0) { X_cpp(sim, 0); } - if ((index & 2) == 0) { X_cpp(sim, 1); } - if ((index & 4) == 0) { X_cpp(sim, 2); } - }; - auto qubit_clear = [](unsigned sim, int index) { - if ((index & 1) == 0) { X_cpp(sim, 0); } - releaseQubit_cpp(sim, 0); - if ((index & 2) == 0) { X_cpp(sim, 1); } - releaseQubit_cpp(sim, 1); - if ((index & 4) == 0) { X_cpp(sim, 2); } - releaseQubit_cpp(sim, 2); - }; - for (int i = 0; i < 8; i++) { - MultiExpReferenceTest([=](unsigned sim) {qubit_prep(sim, i); }, [=](unsigned sim) {qubit_clear(sim, i); }); - } - } - - TEST_METHOD(R1Test) { - const int num_qubits = 32; - amplitude result0; - amplitude result1; - for (double angle = 0.0; angle < M_PI / 2.0; angle += 0.1) { - unsigned sim = init_cpp(num_qubits); - H_cpp(sim, 0); - R1_cpp(sim, angle, 0); - result0 = getSimulator(sim)->probe("0"); - result1 = getSimulator(sim)->probe("1"); - assert_amplitude_equality(result0, 1.0 / sqrt(2.0)); - assert_amplitude_equality(result1, amplitude(cos(angle), sin(angle))/sqrt(2.0)); - R1_cpp(sim, -angle, 0); - result0 = getSimulator(sim)->probe("0"); - result1 = getSimulator(sim)->probe("1"); - assert_amplitude_equality(result0, 1.0 / sqrt(2.0)); - assert_amplitude_equality(result1, 1.0 / sqrt(2.0)); - H_cpp(sim, 0); - releaseQubit_cpp(sim, 0); - destroy_cpp(sim); - } - } - }; -} \ No newline at end of file diff --git a/src/Simulation/Simulators/SparseSimulator/SparseSimulatorTests/SparseSimulatorTests.cpp b/src/Simulation/Simulators/SparseSimulator/SparseSimulatorTests/SparseSimulatorTests.cpp deleted file mode 100644 index d3674644420..00000000000 --- a/src/Simulation/Simulators/SparseSimulator/SparseSimulatorTests/SparseSimulatorTests.cpp +++ /dev/null @@ -1,772 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include "pch.h" -#include "CppUnitTest.h" -#include "../Native/SparseSimulator.h" -#include "TestHelpers.hpp" -#include -#include - -using namespace Microsoft::VisualStudio::CppUnitTestFramework; -using namespace Microsoft::Quantum::SPARSESIMULATOR; -using namespace SparseSimulatorTestHelpers; - -#ifndef M_PI -#define M_PI 3.14159265358979323846 -#endif - - - - -namespace SparseSimulatorTests -{ - TEST_CLASS(SparseSimulatorTests) - { - - template - void MultiExpTest( - std::function qubit_prep, - std::function qubit_clear - ) { - for (int intPaulis = 0; intPaulis < 4 * 4 * 4; intPaulis++) { - std::vector Paulis{ - (Gates::Basis)(intPaulis % 4), - (Gates::Basis)((intPaulis / 4) % 4), - (Gates::Basis)(intPaulis / 16) - }; - for (double angle = 0.0; angle < M_PI / 2.0; angle += 0.1) { - SparseSimulator sim = SparseSimulator(num_qubits); - std::vector qubits{ 0,1,2 }; - qubit_prep(sim); - std::vector vector_rep(8, 0.0); - for (unsigned i = 0; i < 8; i++) { - vector_rep[i] = sim.probe(std::bitset<3>(i).to_string()); - } - // New simulator Exp - sim.Exp(Paulis, angle, qubits); - // Old simulator Exp - apply_exp(vector_rep, Paulis, angle, std::vector{ 0, 1, 2 }); - for (unsigned i = 0; i < 8; i++) { - amplitude result = sim.probe(std::bitset<3>(i).to_string()); - assert_amplitude_equality(vector_rep[i], result); - } - sim.Exp(Paulis, -angle, qubits); - qubit_clear(sim); - } - } - } - - public: - // Tests comparisons of bitstrings - TEST_METHOD(LabelComparisonTest) { - const logical_qubit_id num_qubits = 1024; - SparseSimulator sim = SparseSimulator(num_qubits); - uint64_t i = 0; - uint64_t j; - uint64_t k = 0; - qubit_label_type label1(0); - qubit_label_type label2(1); - - for (i = 0; i < 500; i++){ - k += i * i * i * i; - uint64_t m = 0; - label1 = qubit_label_type(k); - for (j = 0; j < 500; j++){ - m += j * j * j * j; - label2 = qubit_label_type(m); - wchar_t message[100]; - swprintf(message, sizeof(message)/sizeof(*message), L"Comparing %llu to %llu\n", k, m); - Assert::AreEqual(k < m, label1 < label2, message); - } - } - } - // Tests that the X gate flips the computational basis states - TEST_METHOD(XGateTest) { - const logical_qubit_id num_qubits = 32; - SparseSimulator sim = SparseSimulator(num_qubits); - sim.X(0); - assert_amplitude_equality(sim.probe("0"), 0.0, 0.0); - assert_amplitude_equality(sim.probe("1"), 1.0, 0.0); - sim.X(0); - assert_amplitude_equality(sim.probe("0"), 1.0, 0.0); - assert_amplitude_equality(sim.probe("1"), 0.0, 0.0); - } - - // Tests Z on computational basis states - TEST_METHOD(ZGateTest) { - const logical_qubit_id num_qubits = 32; - SparseSimulator sim = SparseSimulator(num_qubits); - sim.Z(0); - assert_amplitude_equality(sim.probe("0"), 1.0, 0.0); - assert_amplitude_equality(sim.probe("1"), 0.0, 0.0); - sim.Z(0); - assert_amplitude_equality(sim.probe("0"), 1.0, 0.0); - assert_amplitude_equality(sim.probe("1"), 0.0, 0.0); - sim.X(0); - sim.Z(0); - assert_amplitude_equality(sim.probe("0"), 0.0, 0.0); - assert_amplitude_equality(sim.probe("1"), -1.0, 0.0); - sim.Z(0); - assert_amplitude_equality(sim.probe("0"), 0.0, 0.0); - assert_amplitude_equality(sim.probe("1"), 1.0, 0.0); - } - - // Tests H on computational basis states - TEST_METHOD(HGateTest) { - const logical_qubit_id num_qubits = 32; - const qubit_label_type zero(0); - SparseSimulator sim = SparseSimulator(num_qubits); - sim.H(0); - assert_amplitude_equality(sim.probe("0"), 1.0 / sqrt(2.0), 0.0); - assert_amplitude_equality(sim.probe("1"), 1.0 / sqrt(2.0), 0.0); - sim.H(0); - assert_amplitude_equality(sim.probe("0"), 1.0, 0.0); - assert_amplitude_equality(sim.probe("1"), 0.0, 0.0); - sim.X(0); - sim.H(0); - assert_amplitude_equality(sim.probe("0"), 1.0 / sqrt(2.0), 0.0); - assert_amplitude_equality(sim.probe("1"), -1.0 / sqrt(2.0), 0.0); - sim.H(0); - assert_amplitude_equality(sim.probe("0"), 0.0, 0.0); - assert_amplitude_equality(sim.probe("1"), 1.0, 0.0); - } - - // Tests powers of T on computational basis states - TEST_METHOD(TGateTest) { - const logical_qubit_id num_qubits = 32; - const qubit_label_type zero(0); - SparseSimulator sim = SparseSimulator(num_qubits); - sim.T(0); - assert_amplitude_equality(sim.probe("0"), 1.0, 0.0); - assert_amplitude_equality(sim.probe("1"), 0.0, 0.0); - sim.X(0); - sim.T(0); - assert_amplitude_equality(sim.probe("0"), 0.0, 0.0); - assert_amplitude_equality(sim.probe("1"), 1.0 / sqrt(2.0), 1.0 / sqrt(2.0)); - sim.T(0); - assert_amplitude_equality(sim.probe("0"), 0.0, 0.0); - assert_amplitude_equality(sim.probe("1"), 0.0, 1.0); - sim.T(0); - assert_amplitude_equality(sim.probe("0"), 0.0, 0.0); - assert_amplitude_equality(sim.probe("1"), -1.0 / sqrt(2.0), 1.0 / sqrt(2.0)); - sim.T(0); - assert_amplitude_equality(sim.probe("0"), 0.0, 0.0); - assert_amplitude_equality(sim.probe("1"), -1.0, 0.0); - sim.T(0); - assert_amplitude_equality(sim.probe("0"), 0.0, 0.0); - assert_amplitude_equality(sim.probe("1"), -1.0 / sqrt(2.0), -1.0 / sqrt(2.0)); - sim.T(0); - assert_amplitude_equality(sim.probe("0"), 0.0, 0.0); - assert_amplitude_equality(sim.probe("1"), 0.0, -1.0); - sim.T(0); - assert_amplitude_equality(sim.probe("0"), 0.0, 0.0); - assert_amplitude_equality(sim.probe("1"), 1.0 / sqrt(2.0), -1.0 / sqrt(2.0)); - sim.T(0); - assert_amplitude_equality(sim.probe("0"), 0.0, 0.0); - assert_amplitude_equality(sim.probe("1"), 1.0, 0.0); - } - - // Tests Rx on computational basis states, for angles between 0 and pi/2 - TEST_METHOD(RxGateTest) { - const logical_qubit_id num_qubits = 32; - const qubit_label_type zero(0); - for (double angle = 0.0; angle < M_PI / 2.0; angle += 0.1) { - SparseSimulator sim = SparseSimulator(num_qubits); - sim.R(Gates::Basis::PauliX, angle, 0); - assert_amplitude_equality(sim.probe("0"), cos(angle / 2.0), 0.0); - assert_amplitude_equality(sim.probe("1"), 0.0, -sin(angle / 2.0)); - sim.R(Gates::Basis::PauliX, -angle, 0); - assert_amplitude_equality(sim.probe("0"), 1.0, 0.0); - assert_amplitude_equality(sim.probe("1"), 0.0, 0.0); - sim.X(0); - sim.R(Gates::Basis::PauliX, angle, 0); - assert_amplitude_equality(sim.probe("0"), 0.0, -sin(angle / 2.0)); - assert_amplitude_equality(sim.probe("1"), cos(angle / 2.0), 0.0); - sim.R(Gates::Basis::PauliX, -angle, 0); - assert_amplitude_equality(sim.probe("0"), 0.0, 0.0); - assert_amplitude_equality(sim.probe("1"), 1.0, 0.0); - } - } - - // Tests Ry on computational basis states, for angles between 0 and pi/2 - TEST_METHOD(RyGateTest) { - const logical_qubit_id num_qubits = 32; - const qubit_label_type zero(0); - for (double angle = 0.0; angle < M_PI / 2.0; angle += 0.1) { - SparseSimulator sim = SparseSimulator(num_qubits); - sim.R(Gates::Basis::PauliY, angle, 0); - assert_amplitude_equality(sim.probe("0"), cos(angle / 2.0), 0.0); - assert_amplitude_equality(sim.probe("1"), sin(angle / 2.0), 0.0); - sim.R(Gates::Basis::PauliY, -angle, 0); - assert_amplitude_equality(sim.probe("0"), 1.0, 0.0); - assert_amplitude_equality(sim.probe("1"), 0.0, 0.0); - sim.X(0); - sim.R(Gates::Basis::PauliY, angle, 0); - assert_amplitude_equality(sim.probe("0"), -sin(angle / 2.0), 0.0); - assert_amplitude_equality(sim.probe("1"), cos(angle / 2.0), 0.0); - sim.R(Gates::Basis::PauliY, -angle, 0); - assert_amplitude_equality(sim.probe("0"), 0.0, 0.0); - assert_amplitude_equality(sim.probe("1"), 1.0, 0.0); - } - } - - // Tests Rz on computational basis states, for angles between 0 and pi/2 - TEST_METHOD(RzGateTest) { - const logical_qubit_id num_qubits = 32; - const qubit_label_type zero(0); - for (double angle = 0.0; angle < M_PI / 2.0; angle += 0.1) { - SparseSimulator sim = SparseSimulator(num_qubits); - logical_qubit_id qubit = 0; - sim.R(Gates::Basis::PauliZ, angle, qubit); - assert_amplitude_equality(sim.probe("0"), 1.0, 0.0); - assert_amplitude_equality(sim.probe("1"), 0.0, 0.0); - sim.R(Gates::Basis::PauliZ, -angle, qubit); - assert_amplitude_equality(sim.probe("0"), 1.0, 0.0); - assert_amplitude_equality(sim.probe("1"), 0.0, 0.0); - sim.X(qubit); - assert_amplitude_equality(sim.probe("0"), 0.0, 0.0); - assert_amplitude_equality(sim.probe("1"), 1.0, 0.0); - sim.R(Gates::Basis::PauliZ, angle, qubit); - assert_amplitude_equality(sim.probe("0"), 0.0, 0.0); - assert_amplitude_equality(sim.probe("1"), cos(angle), sin(angle)); - sim.R(Gates::Basis::PauliZ, -angle, qubit); - assert_amplitude_equality(sim.probe("0"), 0.0, 0.0); - assert_amplitude_equality(sim.probe("1"), 1.0, 0.0); - } - } - - // Tests CNOT on all 2-qubit computational basis stats - TEST_METHOD(CNOTGateTest) { - const logical_qubit_id num_qubits = 32; - const qubit_label_type zero(0); - SparseSimulator sim = SparseSimulator(num_qubits); - logical_qubit_id qubits[2]{ 0, 1 }; - sim.MCX({ qubits[0] }, qubits[1]); - assert_amplitude_equality(sim.probe("00"), 1.0, 0.0); - assert_amplitude_equality(sim.probe("01"), 0.0, 0.0); - assert_amplitude_equality(sim.probe("10"), 0.0, 0.0); - assert_amplitude_equality(sim.probe("11"), 0.0, 0.0); - sim.X(qubits[0]); - sim.MCX({ qubits[0] }, qubits[1]); - assert_amplitude_equality(sim.probe("00"), 0.0, 0.0); - assert_amplitude_equality(sim.probe("01"), 0.0, 0.0); - assert_amplitude_equality(sim.probe("10"), 0.0, 0.0); - assert_amplitude_equality(sim.probe("11"), 1.0, 0.0); - sim.MCX({ qubits[0] }, qubits[1]); - assert_amplitude_equality(sim.probe("00"), 0.0, 0.0); - assert_amplitude_equality(sim.probe("01"), 1.0, 0.0); - assert_amplitude_equality(sim.probe("10"), 0.0, 0.0); - assert_amplitude_equality(sim.probe("11"), 0.0, 0.0); - sim.X(qubits[0]); - sim.X(qubits[1]); - sim.MCX({ qubits[0] }, qubits[1]); - assert_amplitude_equality(sim.probe("00"), 0.0, 0.0); - assert_amplitude_equality(sim.probe("01"), 0.0, 0.0); - assert_amplitude_equality(sim.probe("10"), 1.0, 0.0); - assert_amplitude_equality(sim.probe("11"), 0.0, 0.0); - } - - - - // Tests all possible computational basis states - // for some number of controls and one target - TEST_METHOD(MCXGateTest) { - const size_t n_qubits = 7; - const qubit_label_type zero(0); - SparseSimulator sim = SparseSimulator(n_qubits); - std::vector qubits(n_qubits); - std::generate(qubits.begin(), qubits.end(), [] { static int i{ 0 }; return i++; }); - std::vector controls(qubits.begin() + 1, qubits.end()); - logical_qubit_id target = qubits[0]; - for (logical_qubit_id i = 0; i < pow(2, n_qubits - 1); i++) { // the bitstring of the controls - sim.MCX(controls, target); - for (logical_qubit_id j = 0; j < pow(2, n_qubits - 1); j++) { // bitstring to test - std::bitset j_bits = j; // a bitset for the string to test, with the target as 0 - j_bits = j_bits << 1; - std::bitset j_odd_bits = j_bits; // same as j, but with the target as 1 - j_odd_bits.set(0); - if (j != i) { // The controls are not in this state, so the amplitude should be 0 - assert_amplitude_equality(sim.probe(j_bits.to_string()), 0.0, 0.0); - assert_amplitude_equality(sim.probe(j_odd_bits.to_string()), 0.0, 0.0); - } - else if (i == pow(2, n_qubits - 1) - 1) { // All controls are 1, so this should flip the output - assert_amplitude_equality(sim.probe(j_bits.to_string()), 0.0, 0.0); - assert_amplitude_equality(sim.probe(j_odd_bits.to_string()), 1.0, 0.0); - } - else { // This is the state of the controls, but they are not all 1, so nothing should have happened - assert_amplitude_equality(sim.probe(j_bits.to_string()), 1.0, 0.0); - assert_amplitude_equality(sim.probe(j_odd_bits.to_string()), 0.0, 0.0); - } - } - // Since MCX^2 = I, this should undo anything previously - sim.MCX(controls, target); - for (logical_qubit_id j = 0; j < pow(2, n_qubits - 1); j++) { - std::bitset j_bits = j; - j_bits = j_bits << 1; - std::bitset j_odd_bits = j_bits; - j_odd_bits.set(0); - if (j != i) { // The controls are not in this state, so the amplitude should be 0 - assert_amplitude_equality(sim.probe(j_bits.to_string()), 0.0, 0.0); - assert_amplitude_equality(sim.probe(j_odd_bits.to_string()), 0.0, 0.0); - } - else { // This is the state of the controls, but the final qubit should be 0 - assert_amplitude_equality(sim.probe(j_bits.to_string()), 1.0, 0.0); - assert_amplitude_equality(sim.probe(j_odd_bits.to_string()), 0.0, 0.0); - } - } - // Update the controls - std::bitset diff = i ^ (i + 1); - for (logical_qubit_id j = 0; j < n_qubits - 1; j++) { - if (diff[j]) sim.X(controls[j]); - } - } - } - - - // Tests a controlled Y - // Same logic as the MCXGateTest - TEST_METHOD(MCYGateTest) { - const size_t n_qubits = 7; - const qubit_label_type zero(0); - SparseSimulator sim = SparseSimulator(n_qubits); - std::vector qubits(n_qubits); - std::generate(qubits.begin(), qubits.end(), [] { static int i{ 0 }; return i++; }); - std::vector controls(qubits.begin() + 1, qubits.end()); - logical_qubit_id target = qubits[0]; - for (logical_qubit_id i = 0; i < pow(2, n_qubits - 1); i++) { - sim.MCY(controls, target); - for (logical_qubit_id j = 0; j < pow(2, n_qubits - 1); j++) { - std::bitset j_bits = j; - j_bits = j_bits << 1; - std::bitset j_odd_bits = j_bits; - j_odd_bits.set(0); - if (j != i) { - assert_amplitude_equality(sim.probe(j_bits.to_string()), 0.0, 0.0); - assert_amplitude_equality(sim.probe(j_odd_bits.to_string()), 0.0, 0.0); - } - else if (i == pow(2, n_qubits - 1) - 1) { - assert_amplitude_equality(sim.probe(j_bits.to_string()), 0.0, 0.0); - assert_amplitude_equality(sim.probe(j_odd_bits.to_string()), 0.0, 1.0); - } - else { - assert_amplitude_equality(sim.probe(j_bits.to_string()), 1.0, 0.0); - assert_amplitude_equality(sim.probe(j_odd_bits.to_string()), 0.0, 0.0); - } - } - sim.MCY(controls, target); - for (logical_qubit_id j = 0; j < pow(2, n_qubits - 1); j++) { - std::bitset j_bits = j; - j_bits = j_bits << 1; - std::bitset j_odd_bits = j_bits; - j_odd_bits.set(0); - if (j != i) { - assert_amplitude_equality(sim.probe(j_bits.to_string()), 0.0, 0.0); - assert_amplitude_equality(sim.probe(j_odd_bits.to_string()), 0.0, 0.0); - } - else { - assert_amplitude_equality(sim.probe(j_bits.to_string()), 1.0, 0.0); - assert_amplitude_equality(sim.probe(j_odd_bits.to_string()), 0.0, 0.0); - } - } - std::bitset diff = i ^ (i + 1); - for (logical_qubit_id j = 0; j < n_qubits - 1; j++) { - if (diff[j]) sim.X(controls[j]); - } - } - } - // Tests a controlled Z - // Same logic as the MCXGateTest - TEST_METHOD(MCZGateTest) { - const size_t n_qubits = 7; - const qubit_label_type zero(0); - SparseSimulator sim = SparseSimulator(n_qubits); - std::vector qubits(n_qubits); - std::generate(qubits.begin(), qubits.end(), [] { static int i{ 0 }; return i++; }); - std::vector controls(qubits.begin() + 1, qubits.end()); - logical_qubit_id target = qubits[0]; - sim.H(target); - for (logical_qubit_id i = 0; i < pow(2, n_qubits - 1); i++) { - sim.MCZ(controls, target); - for (logical_qubit_id j = 0; j < pow(2, n_qubits - 1); j++) { - std::bitset j_bits = j; - j_bits = j_bits << 1; - std::bitset j_odd_bits = j_bits; - j_odd_bits.set(0); - if (j != i) { - assert_amplitude_equality(sim.probe(j_bits.to_string()), 0.0, 0.0); - assert_amplitude_equality(sim.probe(j_odd_bits.to_string()), 0.0, 0.0); - } - else if (i == pow(2, n_qubits - 1) - 1) { - assert_amplitude_equality(sim.probe(j_bits.to_string()), 1.0/sqrt(2.0), 0.0); - assert_amplitude_equality(sim.probe(j_odd_bits.to_string()), -1.0 / sqrt(2.0), 0.0); - } - else { - assert_amplitude_equality(sim.probe(j_bits.to_string()), 1.0 / sqrt(2.0), 0.0); - assert_amplitude_equality(sim.probe(j_odd_bits.to_string()), 1.0 / sqrt(2.0), 0.0); - } - } - sim.MCZ(controls, target); - for (logical_qubit_id j = 0; j < pow(2, n_qubits - 1); j++) { - std::bitset j_bits = j; - j_bits = j_bits << 1; - std::bitset j_odd_bits = j_bits; - j_odd_bits.set(0); - if (j != i) { - assert_amplitude_equality(sim.probe(j_bits.to_string()), 0.0, 0.0); - assert_amplitude_equality(sim.probe(j_odd_bits.to_string()), 0.0, 0.0); - } - else { - assert_amplitude_equality(sim.probe(j_bits.to_string()), 1.0 / sqrt(2.0), 0.0); - assert_amplitude_equality(sim.probe(j_odd_bits.to_string()), 1.0 / sqrt(2.0), 0.0); - } - } - std::bitset diff = i ^ (i + 1); - for (logical_qubit_id j = 0; j < n_qubits - 1; j++) { - if (diff[j]) sim.X(controls[j]); - } - } - } - - - - // Tests the multi-exp on a uniform superposition - TEST_METHOD(MultiExpWithHTest) { - const int num_qubits = 32; - auto qubit_prep = [](SparseSimulator& sim) { - sim.H(0); - sim.H(1); - sim.H(2); - }; - auto qubit_clear = [](SparseSimulator& sim) { - sim.H(2); - sim.release(2); - sim.H(1); - sim.release(1); - sim.H(0); - sim.release(0); - }; - MultiExpTest(qubit_prep, qubit_clear); - } - - // Tests the MultiExp on all computational basis states of 3 qubits - TEST_METHOD(MultiExpBasisTest) { - const int num_qubits = 32; - auto qubit_prep = [](SparseSimulator& sim, int index) { - if ((index & 1) == 0) { sim.X(0); } - if ((index & 2) == 0) { sim.X(1); } - if ((index & 4) == 0) { sim.X(2); } - }; - auto qubit_clear = [](SparseSimulator& sim, int index) { - if ((index & 1) == 0) { sim.X(0); } - sim.release(0); - if ((index & 2) == 0) { sim.X(1); } - sim.release(1); - if ((index & 4) == 0) { sim.X(2); } - sim.release(2); - }; - for (int i = 0; i < 8; i++) { - MultiExpTest([=](SparseSimulator& sim) {qubit_prep(sim, i); }, [=](SparseSimulator& sim) {qubit_clear(sim, i); }); - } - } - - // Tests a SWAP gate on all 2-qubit computational basis states - TEST_METHOD(SWAPGateTest) { - const logical_qubit_id num_qubits = 32; - const qubit_label_type zero(0); - SparseSimulator sim = SparseSimulator(num_qubits); - std::vector qubits{ 0,1 }; - sim.SWAP({ qubits[0] }, qubits[1]); // 00 -> 00 - assert_amplitude_equality(sim.probe("00"), 1.0, 0.0); - assert_amplitude_equality(sim.probe("01"), 0.0, 0.0); - assert_amplitude_equality(sim.probe("10"), 0.0, 0.0); - assert_amplitude_equality(sim.probe("11"), 0.0, 0.0); - sim.X(qubits[0]); - sim.SWAP( qubits[0] , qubits[1]); // 10 -> 01 - assert_amplitude_equality(sim.probe("00"), 0.0, 0.0); - assert_amplitude_equality(sim.probe("01"), 0.0, 0.0); - assert_amplitude_equality(sim.probe("10"), 1.0, 0.0); - assert_amplitude_equality(sim.probe("11"), 0.0, 0.0); - sim.SWAP( qubits[0] , qubits[1]); // 01 -> 10 - assert_amplitude_equality(sim.probe("0"), 0.0, 0.0); - assert_amplitude_equality(sim.probe("1"), 1.0, 0.0); - assert_amplitude_equality(sim.probe("10"), 0.0, 0.0); - assert_amplitude_equality(sim.probe("11"), 0.0, 0.0); - sim.X(qubits[1]); - sim.SWAP( qubits[0] , qubits[1]); // 11 -> 11 - assert_amplitude_equality(sim.probe("00"), 0.0, 0.0); - assert_amplitude_equality(sim.probe("01"), 0.0, 0.0); - assert_amplitude_equality(sim.probe("10"), 0.0, 0.0); - assert_amplitude_equality(sim.probe("11"), 1.0, 0.0); - } - - // Tests multi-controlled swap on all computational basis states of 4 qubits - // (2 controls, 2 targets) - TEST_METHOD(CSWAPGateTest) { - const logical_qubit_id num_qubits = 32; - const qubit_label_type zero(0); - SparseSimulator sim = SparseSimulator(num_qubits); - std::vector target_qubits{ 0,1 }; - std::vector control_qubits{ 2,3 }; - // Lambda to test when controls should cause no swap - auto no_swap_test = [&](std::string controls) { - sim.CSWAP(control_qubits, target_qubits[0], target_qubits[1]); // 00 -> 00 - assert_amplitude_equality(sim.probe(controls+"00"), 1.0, 0.0); - assert_amplitude_equality(sim.probe(controls + "01"), 0.0, 0.0); - assert_amplitude_equality(sim.probe(controls + "10"), 0.0, 0.0); - assert_amplitude_equality(sim.probe(controls + "11"), 0.0, 0.0); - sim.X(target_qubits[0]); - sim.CSWAP(control_qubits, target_qubits[0], target_qubits[1]); // 01 -> 01 - assert_amplitude_equality(sim.probe(controls + "00"), 0.0, 0.0); - assert_amplitude_equality(sim.probe(controls + "01"), 1.0, 0.0); - assert_amplitude_equality(sim.probe(controls + "10"), 0.0, 0.0); - assert_amplitude_equality(sim.probe(controls + "11"), 0.0, 0.0); - sim.X(target_qubits[1]); - sim.CSWAP(control_qubits, target_qubits[0], target_qubits[1]); // 11 -> 11 - assert_amplitude_equality(sim.probe(controls + "00"), 0.0, 0.0); - assert_amplitude_equality(sim.probe(controls + "01"), 0.0, 0.0); - assert_amplitude_equality(sim.probe(controls + "10"), 0.0, 0.0); - assert_amplitude_equality(sim.probe(controls + "11"), 1.0, 0.0); - sim.X(target_qubits[0]); - sim.CSWAP(control_qubits, target_qubits[0], target_qubits[1]); // 10 -> 10 - assert_amplitude_equality(sim.probe(controls + "00"), 0.0, 0.0); - assert_amplitude_equality(sim.probe(controls + "01"), 0.0, 0.0); - assert_amplitude_equality(sim.probe(controls + "10"), 1.0, 0.0); - assert_amplitude_equality(sim.probe(controls + "11"), 0.0, 0.0); - sim.X(target_qubits[1]); - }; - // Controls are 00, no swap - no_swap_test("00"); - sim.X(control_qubits[0]); - // Controls are 01, no swap - no_swap_test("01"); - sim.X(control_qubits[1]); - // Controls are 11, test for swap - sim.CSWAP(control_qubits, target_qubits[0], target_qubits[1]); // 00 -> 00 - assert_amplitude_equality(sim.probe("1100"), 1.0, 0.0); - assert_amplitude_equality(sim.probe("1101"), 0.0, 0.0); - assert_amplitude_equality(sim.probe("1110"), 0.0, 0.0); - assert_amplitude_equality(sim.probe("1111"), 0.0, 0.0); - sim.X(target_qubits[0]); - sim.CSWAP(control_qubits, target_qubits[0], target_qubits[1]); // 10 -> 01 - assert_amplitude_equality(sim.probe("1100"), 0.0, 0.0); - assert_amplitude_equality(sim.probe("1101"), 0.0, 0.0); - assert_amplitude_equality(sim.probe("1110"), 1.0, 0.0); - assert_amplitude_equality(sim.probe("1111"), 0.0, 0.0); - sim.CSWAP(control_qubits, target_qubits[0], target_qubits[1]);// 01 -> 10 - assert_amplitude_equality(sim.probe("1100"), 0.0, 0.0); - assert_amplitude_equality(sim.probe("1101"), 1.0, 0.0); - assert_amplitude_equality(sim.probe("1110"), 0.0, 0.0); - assert_amplitude_equality(sim.probe("1111"), 0.0, 0.0); - sim.X(target_qubits[1]); - sim.CSWAP(control_qubits, target_qubits[0], target_qubits[1]); // 11 -> 11 - assert_amplitude_equality(sim.probe("1100"), 0.0, 0.0); - assert_amplitude_equality(sim.probe("1101"), 0.0, 0.0); - assert_amplitude_equality(sim.probe("1110"), 0.0, 0.0); - assert_amplitude_equality(sim.probe("1111"), 1.0, 0.0); - sim.X(target_qubits[1]); - sim.X(target_qubits[0]); - sim.X(control_qubits[0]); - // Controls are 10, test for no swap - no_swap_test("10"); - } - - // Tests measurement probabilistically - // Based on the expected measurement probabilities for a Pauli Y - // rotation - // It samples a lot of measurements, and based on the - // current variance (of a binomial distrbution): - // - if it's very close to the expected distribution, - // it considers this a success - // - if it's very far from the expected distribution, - // it throws an exception - // - if it's in between, it runs more samples - // While this run-time is undetermined, the threshold - // for an exception shrinks with the number of tests - TEST_METHOD(MTest) { - const logical_qubit_id num_qubits = 32; - const qubit_label_type zero(0); - const int n_tests = 5000; - const double log_false_positive_threshold = 0.1; - const double log_false_negative_threshold = 100.0; - for (double angle = 0.0; angle < M_PI / 2.0; angle += 0.1) { - SparseSimulator sim = SparseSimulator(num_qubits); - sim.set_random_seed(12345); - double expected_ratio = sin(angle / 2.0) * sin(angle / 2.0); - double ratio = 0.0; - unsigned long total_tests = 0; - unsigned long ones = 0; - double std_dev = 0.0; - double log_prob = 0.0; - logical_qubit_id qubit = 0; - do { - for (int i = 0; i < n_tests; i++) { - sim.R(Gates::Basis::PauliY, angle, qubit); - if (sim.M(qubit)) { - ones++; - sim.X(qubit); - } - } - total_tests += n_tests; - ratio = (double)ones / (double)total_tests; - double abs_diff = abs(expected_ratio - ratio); - // Based on Chernoff bounds - log_prob = abs_diff * abs_diff * expected_ratio * (double)total_tests; - std_dev = sqrt(expected_ratio * (1.0 - expected_ratio)) / (double)total_tests; - // Using variance of the binomial distribution - if (log_false_positive_threshold >= log_prob) { - break; - } - } while (log_false_negative_threshold >= log_prob); - if (log_false_negative_threshold < log_prob) { - throw std::runtime_error("Statistically improbable measurement results"); - } - } - - } - - // Tests an assortment of assertions to both pass and to throw exceptions - TEST_METHOD(AssertTest) { - const logical_qubit_id num_qubits = 32; - const qubit_label_type zero(0); - SparseSimulator sim = SparseSimulator(num_qubits); - using namespace Gates; - std::vector basis{ Basis::PauliZ, Basis::PauliZ, Basis::PauliZ }; - std::vector qubits{ 0,1,2 }; - sim.Assert(basis, qubits, false); - sim.update_state(); - // These require forcing the simulator to update the state for it to actually throw the exception - Assert::ExpectException([&]() {sim.Assert(basis, qubits, true); sim.update_state(); }); - basis = { Basis::PauliZ, Basis::PauliZ, Basis:: PauliI }; - sim.Assert(basis, qubits, false); - Assert::ExpectException([&]() {sim.Assert(basis, qubits, true); sim.update_state(); }); - basis = { Basis::PauliX, Basis::PauliI, Basis::PauliI }; - Assert::ExpectException([&]() {sim.Assert(basis, qubits, false); sim.update_state(); }); - Assert::ExpectException([&]() {sim.Assert(basis, qubits, true); sim.update_state(); }); - basis = { Basis::PauliY, Basis::PauliI, Basis::PauliI }; - Assert::ExpectException([&]() {sim.Assert(basis, qubits, false); sim.update_state(); }); - Assert::ExpectException([&]() {sim.Assert(basis, qubits, true); sim.update_state(); }); - } - - // Tests an assortment of assertions on GHZ states - TEST_METHOD(AssertGHZTest) { - const logical_qubit_id num_qubits = 32; - const qubit_label_type zero(0); - SparseSimulator sim = SparseSimulator(num_qubits); - using namespace Gates; - std::vector basis(3, Basis::PauliX); - std::vector qubits{ 0,1,2 }; - sim.H(0); - sim.MCX({ 0 }, 1); - sim.MCX({ 0 }, 2); - sim.Assert(basis, qubits, false); - Assert::ExpectException([&]() {sim.Assert(basis, qubits, true); }); - sim.Z(0); - sim.Assert(basis, qubits, true); - Assert::ExpectException([&]() {sim.Assert(basis, qubits, false); }); - sim.S(0); - basis = { Basis::PauliY, Basis::PauliY, Basis::PauliY }; - sim.Assert(basis, qubits, false); - Assert::ExpectException([&]() {sim.Assert(basis, qubits, true); }); - sim.Z(0); - sim.Assert(basis, qubits, true); - Assert::ExpectException([&]() {sim.Assert(basis, qubits, false); }); - sim.probe("0"); - } - - // Basic test of quantum teleportation - TEST_METHOD(TeleportationTest) - { - const logical_qubit_id num_qubits = 32; - const qubit_label_type zero(0); - for (double test_angle = 0; test_angle < 1.0; test_angle += 0.34) { - SparseSimulator sim = SparseSimulator(num_qubits); - sim.set_random_seed(12345); - std::vector qubits{ 0,1,2 }; - sim.H(qubits[1]); - sim.MCX({ qubits[1] }, qubits[2]); - - sim.R(Gates::Basis::PauliY, test_angle, 0); - - sim.MCX({ qubits[0] }, qubits[1]); - sim.H(qubits[0]); - bool result0 = sim.M(qubits[0]); - bool result1 = sim.M(qubits[1]); - if (result1) { - sim.X(qubits[2]); - sim.X(qubits[1]); - } - if (result0) { - sim.Z(qubits[2]); - sim.X(qubits[0]); - } - - amplitude teleported_qubit_0 = sim.probe("000"); - amplitude teleported_qubit_1 = sim.probe("100"); - Assert::AreEqual((float)cos(test_angle / 2.0), (float)teleported_qubit_0.real()); - Assert::AreEqual((float)0.0, (float)teleported_qubit_0.imag()); - Assert::AreEqual((float)sin(test_angle / 2.0), (float)teleported_qubit_1.real()); - Assert::AreEqual((float)0.0, (float)teleported_qubit_1.imag()); - } - } - - - // Tests that H gates properly cancel when executed - TEST_METHOD(HCancellationTest) - { - const int n_qubits = 128; - SparseSimulator sim = SparseSimulator(n_qubits); - sim.set_random_seed(12345); - std::vector qubits(n_qubits); - std::generate(qubits.begin(), qubits.end(), [] { static int i{ 0 }; return i++; }); - size_t buckets = 0; - // Will cause a huge memory problem if there is no cancellation - const int n_samples = 16; - for (int i = 0; i < n_qubits; i += n_samples) { - for (int ii = 0; ii < n_samples; ii++) { - sim.H(qubits[i + ii]); - } - sim.update_state(); - for (int ii = n_samples - 1; ii >= 0; ii--) { - sim.H(qubits[i + ii]); - } - sim.update_state(); - } - } - - // Checks that X and Z gates commute with H - TEST_METHOD(HXZCommutationTest) - { - const int n_qubits = 16; - SparseSimulator sim = SparseSimulator(n_qubits); - sim.set_random_seed(12345); - std::vector qubits(n_qubits); - std::generate(qubits.begin(), qubits.end(), [] { static int i{ 0 }; return i++; }); - for (int i = 0; i < n_qubits; i++) { - sim.H(qubits[i]); - } - // Here it will actually just commute the X and Z through the H in the queue - // without actually executing anything - std::bitset one_state = 0; - for (int i = 0; i < n_qubits - 1; i += 2) { - sim.Z(qubits[i]); - sim.X(qubits[i + 1]); - one_state.set(i); - } - for (int i = n_qubits - 1; i >= 0; i--) { - sim.H(qubits[i]); - } - for (__int64 i = 0; i < pow(2, n_qubits); i++) { - amplitude state = sim.probe(std::bitset(i).to_string()); - if (i == one_state.to_ulong()) { - assert_amplitude_equality(state, 1.0, 0.0); - } - else { - assert_amplitude_equality(state, 0.0, 0.0); - } - } - } - - }; - -} diff --git a/src/Simulation/Simulators/SparseSimulator/SparseSimulatorTests/SparseSimulatorTests.vcxproj b/src/Simulation/Simulators/SparseSimulator/SparseSimulatorTests/SparseSimulatorTests.vcxproj deleted file mode 100644 index 4cbcb322744..00000000000 --- a/src/Simulation/Simulators/SparseSimulator/SparseSimulatorTests/SparseSimulatorTests.vcxproj +++ /dev/null @@ -1,179 +0,0 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - Debug - x64 - - - Release - x64 - - - - 16.0 - {E1663845-BBA8-41EA-AC31-50A092EEA728} - Win32Proj - SparseSimulatorTests - 10.0 - NativeUnitTestProject - - - - DynamicLibrary - true - v142 - Unicode - false - - - DynamicLibrary - false - v142 - true - Unicode - false - - - DynamicLibrary - true - v142 - Unicode - false - - - DynamicLibrary - false - v142 - true - Unicode - false - - - - - - - - - - - - - - - - - - - - - true - - - true - - - false - - - false - - - - NotUsing - Level3 - true - $(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories) - WIN32;_DEBUG;%(PreprocessorDefinitions) - true - pch.h - stdcpp17 - - - Windows - $(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories) - - - - - NotUsing - Level3 - true - $(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories) - _DEBUG;%(PreprocessorDefinitions) - true - pch.h - stdcpp17 - - - Windows - $(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories) - - - - - NotUsing - Level3 - true - true - true - $(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories) - WIN32;NDEBUG;%(PreprocessorDefinitions) - true - pch.h - - - Windows - true - true - $(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories) - - - - - NotUsing - Level3 - true - true - true - $(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories) - NDEBUG;%(PreprocessorDefinitions) - true - pch.h - stdcpp17 - - - Windows - true - true - $(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories) - - - - - - Create - Create - Create - Create - - - - - - - - - - - - \ No newline at end of file diff --git a/src/Simulation/Simulators/SparseSimulator/SparseSimulatorTests/SparseSimulatorTests.vcxproj.filters b/src/Simulation/Simulators/SparseSimulator/SparseSimulatorTests/SparseSimulatorTests.vcxproj.filters deleted file mode 100644 index 5947f4294c2..00000000000 --- a/src/Simulation/Simulators/SparseSimulator/SparseSimulatorTests/SparseSimulatorTests.vcxproj.filters +++ /dev/null @@ -1,39 +0,0 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd - - - {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} - rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms - - - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - - - Header Files - - - Source Files - - - \ No newline at end of file diff --git a/src/Simulation/Simulators/SparseSimulator/SparseSimulatorTests/pch.cpp b/src/Simulation/Simulators/SparseSimulator/SparseSimulatorTests/pch.cpp deleted file mode 100644 index 2d641a7e3b9..00000000000 --- a/src/Simulation/Simulators/SparseSimulator/SparseSimulatorTests/pch.cpp +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -// pch.cpp: source file corresponding to the pre-compiled header - -#include "pch.h" - -// When you are using pre-compiled headers, this source file is necessary for compilation to succeed. diff --git a/src/Simulation/Simulators/SparseSimulator/SparseSimulatorTests/pch.h b/src/Simulation/Simulators/SparseSimulator/SparseSimulatorTests/pch.h deleted file mode 100644 index 2cd3c85c965..00000000000 --- a/src/Simulation/Simulators/SparseSimulator/SparseSimulatorTests/pch.h +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -// pch.h: This is a precompiled header file. -// Files listed below are compiled only once, improving build performance for future builds. -// This also affects IntelliSense performance, including code completion and many code browsing features. -// However, files listed here are ALL re-compiled if any one of them is updated between builds. -// Do not add files here that you will be updating frequently as this negates the performance advantage. - -#ifndef PCH_H -#define PCH_H - -// add headers that you want to pre-compile here - - -#endif //PCH_H