diff --git a/src/Qir/Runtime/lib/Simulators/FullstateSimulator.cpp b/src/Qir/Runtime/lib/Simulators/FullstateSimulator.cpp index 74fd47ef266..f6f6db2ea75 100644 --- a/src/Qir/Runtime/lib/Simulators/FullstateSimulator.cpp +++ b/src/Qir/Runtime/lib/Simulators/FullstateSimulator.cpp @@ -23,7 +23,8 @@ #include "FloatUtils.hpp" -#include "QirTypes.hpp" // TODO: Consider removing dependency on this file. +#include "QirTypes.hpp" // TODO: Consider removing dependency on this file. +#include "QirRuntime.hpp" #include "QirRuntimeApi_I.hpp" #include "QSharpSimApi_I.hpp" #include "SimFactory.hpp" @@ -225,11 +226,15 @@ namespace Quantum void ReleaseQubit(Qubit q) override { - typedef void (*TReleaseQubit)(unsigned, unsigned); + typedef bool (*TReleaseQubit)(unsigned, unsigned); static TReleaseQubit releaseQubit = reinterpret_cast(this->GetProc("release")); - releaseQubit(this->simulatorId, GetQubitId(q)); // Release qubit in the simulator. - qubitManager->Release(q); // Release it in the qubit manager. + // Release qubit in the simulator. + if (!releaseQubit(this->simulatorId, GetQubitId(q))) + { + quantum__rt__fail_cstr("Released qubits are entangled."); + } + qubitManager->Release(q); // Release it in the qubit manager. } Result Measure(long numBases, PauliId bases[], long numTargets, Qubit targets[]) override diff --git a/src/Qir/Tests/FullstateSimulator/FullstateSimulatorTests.cpp b/src/Qir/Tests/FullstateSimulator/FullstateSimulatorTests.cpp index a7b87737f8c..50f609d8441 100644 --- a/src/Qir/Tests/FullstateSimulator/FullstateSimulatorTests.cpp +++ b/src/Qir/Tests/FullstateSimulator/FullstateSimulatorTests.cpp @@ -145,6 +145,9 @@ TEST_CASE("Fullstate simulator: ZZ measure", "[fullstate_simulator]") Result rOne = iqa->Measure(2, paulis, 2, q); REQUIRE(Result_One == sim->GetResultValue(rOne)); + // Disentangle for release. + iqa->ControlledX(1, &q[0], q[1]); + sim->ReleaseQubit(q[0]); sim->ReleaseQubit(q[1]); } @@ -311,6 +314,10 @@ TEST_CASE("Fullstate simulator: exponents", "[fullstate_simulator]") // not crashes? consider it passing REQUIRE(true); + // Disentangle for release. + iqa->ControlledExp(2, qs, 3, paulis, &qs[2], -0.17); + iqa->Exp(2, paulis, qs, -0.42); + for (int i = 0; i < n; i++) { sim->ReleaseQubit(qs[i]); @@ -377,6 +384,9 @@ TEST_CASE("Fullstate simulator: get qubit state of Bell state", "[fullstate_simu REQUIRE(1.0 == Approx(norm).epsilon(0.0001)); norm = 0.0; + // Disentangle to prepare for release. + iqa->ControlledX(1, &qs[0], qs[1]); + for (int i = 0; i < n; i++) { sim->ReleaseQubit(qs[i]); diff --git a/src/Simulation/Common/Exceptions/ReleasedQubitsAreEntangled.cs b/src/Simulation/Common/Exceptions/ReleasedQubitsAreEntangled.cs new file mode 100644 index 00000000000..4f379804907 --- /dev/null +++ b/src/Simulation/Common/Exceptions/ReleasedQubitsAreEntangled.cs @@ -0,0 +1,19 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Microsoft.Quantum.Simulation.Simulators.Exceptions +{ + public class ReleasedQubitsAreEntangled : Exception + { + public ReleasedQubitsAreEntangled() + : base("Released qubits are entangled.") + { + } + } +} diff --git a/src/Simulation/Common/Exceptions/ReleasedQubitsAreNotInZeroState.cs b/src/Simulation/Common/Exceptions/ReleasedQubitsAreNotInZeroState.cs index 15ee8255d2d..f0e91e70bd1 100644 --- a/src/Simulation/Common/Exceptions/ReleasedQubitsAreNotInZeroState.cs +++ b/src/Simulation/Common/Exceptions/ReleasedQubitsAreNotInZeroState.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. using System; @@ -9,6 +9,7 @@ namespace Microsoft.Quantum.Simulation.Simulators.Exceptions { + [Obsolete("This class is deprecated and will be removed in a future release.")] public class ReleasedQubitsAreNotInZeroState : Exception { public ReleasedQubitsAreNotInZeroState() diff --git a/src/Simulation/Native/src/simulator/capi.cpp b/src/Simulation/Native/src/simulator/capi.cpp index da95a1754d2..e8dff5d9104 100644 --- a/src/Simulation/Native/src/simulator/capi.cpp +++ b/src/Simulation/Native/src/simulator/capi.cpp @@ -69,9 +69,9 @@ extern "C" Microsoft::Quantum::Simulator::get(id)->allocateQubit(q); } - MICROSOFT_QUANTUM_DECL void release(_In_ unsigned id, _In_ unsigned q) + MICROSOFT_QUANTUM_DECL bool release(_In_ unsigned id, _In_ unsigned q) { - Microsoft::Quantum::Simulator::get(id)->release(q); + return Microsoft::Quantum::Simulator::get(id)->release(q); } MICROSOFT_QUANTUM_DECL unsigned num_qubits(_In_ unsigned id) diff --git a/src/Simulation/Native/src/simulator/capi.hpp b/src/Simulation/Native/src/simulator/capi.hpp index e0d4b25d0c6..87bdf1d4c60 100644 --- a/src/Simulation/Native/src/simulator/capi.hpp +++ b/src/Simulation/Native/src/simulator/capi.hpp @@ -63,14 +63,14 @@ extern "C" MICROSOFT_QUANTUM_DECL bool InjectState( _In_ unsigned sid, _In_ unsigned n, - _In_reads_(n) unsigned* q, // The listed qubits must be unentangled and in state |0> + _In_reads_(n) unsigned* q, // The listed qubits must be disentangled and in state |0> _In_ double* re, // 2^n real parts of the amplitudes of the superposition the listed qubits should be put into _In_ double* im // 2^n imaginary parts of the amplitudes ); // allocate and release MICROSOFT_QUANTUM_DECL void allocateQubit(_In_ unsigned sid, _In_ unsigned qid); // NOLINT - MICROSOFT_QUANTUM_DECL void release(_In_ unsigned sid, _In_ unsigned q); // NOLINT + MICROSOFT_QUANTUM_DECL bool release(_In_ unsigned sid, _In_ unsigned q); // NOLINT MICROSOFT_QUANTUM_DECL unsigned num_qubits(_In_ unsigned sid); // NOLINT // single-qubit gates diff --git a/src/Simulation/Native/src/simulator/kernels.hpp b/src/Simulation/Native/src/simulator/kernels.hpp index 3311bb27f8c..6172a7c0a23 100644 --- a/src/Simulation/Native/src/simulator/kernels.hpp +++ b/src/Simulation/Native/src/simulator/kernels.hpp @@ -144,6 +144,63 @@ bool isclassical( return true; } +template +std::pair is_classical_or_entangled( + std::vector, A> const& wfn, + std::size_t q, + T eps = 100. * std::numeric_limits::epsilon()) +{ + // Iterate through the wave function using offstet and mask to check for both whether the qubit + // is classical (in the |0⟩ or |1⟩ state) or is entangled with any other qubit. By checking states + // where the target qubit is the only difference, the "isclassical" check can speed up the iteration + // which must check the whole vector. If the target qubit has a nonzero probability of being measured + // as |0⟩ (variable "have0") AND nonzero probability of being measured as |1⟩ (variable "have1") then + // we know it is not classical with regard to the computational basis. Further, if the target qubit + // is not classical with regard to the computational basis AND for any two states where the only difference + // in the states is the target qubit (ie: |101⟩ and |100⟩ for qubit 0) those have nonzero probability, then + // the qubit cannot be entangled. + std::size_t offset = 1ull << q; + bool have0 = false; + bool have1 = false; + bool notentangled = false; + + std::size_t maski = ~(offset - 1); + // When iterating below, the entry in the wave function with index `i + j` and the entry with index + // `i + j + offset` represent two states where the only difference is the measured value of the target + // qubit. For example, if the target qubit has id 2 (offset = 4) in wave function of size 4, then for + // i = 0 and j = 0 the two states checked are 0 and 4, or |0000⟩ and |0100⟩. When i = 8 and j = 1, the + // two states are 9 and 13, or |1001⟩ and |1101⟩. +#ifndef _MSC_VER +#pragma omp parallel for schedule(static) reduction(|| : have0, have1, notentangled) + for (std::intptr_t i = 0; i < static_cast(wfn.size()); i += 2 * offset) + for (std::intptr_t j = 0; j < static_cast(offset); ++j) + { + bool has0 = std::norm(wfn[i + j]) >= eps; + bool has1 = std::norm(wfn[i + j + offset]) >= eps; + have0 = have0 || has0; + have1 = have1 || has1; + notentangled = notentangled || (has0 && has1); + } +#else +#pragma omp parallel for schedule(static) reduction(|| : have0, have1, notentangled) + for (std::intptr_t l = 0; l < static_cast(wfn.size()) / 2; l++) + { + std::intptr_t j = l % offset; + std::intptr_t i = ((l & maski) << 1); + bool has0 = std::norm(wfn[i + j]) >= eps; + bool has1 = std::norm(wfn[i + j + offset]) >= eps; + have0 = have0 || has0; + have1 = have1 || has1; + notentangled = notentangled || (has0 && has1); + } +#endif + + // isclassical = true IFF have0 XOR have1 + // isentangled = true IFF have0 AND have1 AND for all pairs of states where only the target qubit + // differs one of those states has zero probability. + return std::make_pair(have0 ^ have1, have0 && have1 && !notentangled); +} + template double jointprobability(std::vector const& wfn, std::vector const& qs, bool val = true) { diff --git a/src/Simulation/Native/src/simulator/local_test.cpp b/src/Simulation/Native/src/simulator/local_test.cpp index d9641b620ad..35d8e0a66ac 100644 --- a/src/Simulation/Native/src/simulator/local_test.cpp +++ b/src/Simulation/Native/src/simulator/local_test.cpp @@ -569,7 +569,7 @@ TEST_CASE("Should fail to inject state if qubits aren't all |0>", "[local_test]" std::vector amplitudes_sub = {{amp, 0.0}, {amp, 0.0}, {amp, 0.0}, {0.0, 0.0}}; - // unentangled but not |0> + // not entangled but not |0> sim.H(qs[1]); REQUIRE_FALSE(sim.InjectState(qs, amplitudes)); REQUIRE_FALSE(sim.InjectState({qs[0], qs[1]}, amplitudes_sub)); @@ -828,6 +828,82 @@ TEST_CASE("test_multicontrol", "[local_test]") } } +TEST_CASE("test_is_classical_or_entangled", "[local_test]") +{ + SimulatorType sim; + auto qbits = sim.allocate(4); + + CHECK(sim.is_classical_or_entangled(qbits[0]).first); + CHECK_FALSE(sim.is_classical_or_entangled(qbits[0]).second); + CHECK(sim.is_classical_or_entangled(qbits[1]).first); + CHECK_FALSE(sim.is_classical_or_entangled(qbits[1]).second); + CHECK(sim.is_classical_or_entangled(qbits[2]).first); + CHECK_FALSE(sim.is_classical_or_entangled(qbits[2]).second); + + sim.X(qbits[0]); + sim.X(qbits[2]); + CHECK(sim.is_classical_or_entangled(qbits[0]).first); + CHECK_FALSE(sim.is_classical_or_entangled(qbits[0]).second); + CHECK(sim.is_classical_or_entangled(qbits[1]).first); + CHECK_FALSE(sim.is_classical_or_entangled(qbits[1]).second); + CHECK(sim.is_classical_or_entangled(qbits[2]).first); + CHECK_FALSE(sim.is_classical_or_entangled(qbits[2]).second); + + sim.X(qbits[0]); + sim.H(qbits[0]); + CHECK_FALSE(sim.is_classical_or_entangled(qbits[0]).first); + CHECK_FALSE(sim.is_classical_or_entangled(qbits[0]).second); + CHECK(sim.is_classical_or_entangled(qbits[1]).first); + CHECK_FALSE(sim.is_classical_or_entangled(qbits[1]).second); + CHECK(sim.is_classical_or_entangled(qbits[2]).first); + CHECK_FALSE(sim.is_classical_or_entangled(qbits[2]).second); + + sim.H(qbits[1]); + CHECK_FALSE(sim.is_classical_or_entangled(qbits[0]).first); + CHECK_FALSE(sim.is_classical_or_entangled(qbits[0]).second); + CHECK_FALSE(sim.is_classical_or_entangled(qbits[1]).first); + CHECK_FALSE(sim.is_classical_or_entangled(qbits[1]).second); + CHECK(sim.is_classical_or_entangled(qbits[2]).first); + CHECK_FALSE(sim.is_classical_or_entangled(qbits[2]).second); + + sim.H(qbits[1]); + sim.CX(qbits[0], qbits[1]); + CHECK_FALSE(sim.is_classical_or_entangled(qbits[0]).first); + CHECK(sim.is_classical_or_entangled(qbits[0]).second); + CHECK_FALSE(sim.is_classical_or_entangled(qbits[1]).first); + CHECK(sim.is_classical_or_entangled(qbits[1]).second); + CHECK(sim.is_classical_or_entangled(qbits[2]).first); + CHECK_FALSE(sim.is_classical_or_entangled(qbits[2]).second); + + sim.CX(qbits[0], qbits[2]); + CHECK_FALSE(sim.is_classical_or_entangled(qbits[0]).first); + CHECK(sim.is_classical_or_entangled(qbits[0]).second); + CHECK_FALSE(sim.is_classical_or_entangled(qbits[1]).first); + CHECK(sim.is_classical_or_entangled(qbits[1]).second); + CHECK_FALSE(sim.is_classical_or_entangled(qbits[2]).first); + CHECK(sim.is_classical_or_entangled(qbits[2]).second); + + sim.CX(qbits[0], qbits[1]); + sim.CX(qbits[0], qbits[2]); + CHECK_FALSE(sim.is_classical_or_entangled(qbits[0]).first); + CHECK_FALSE(sim.is_classical_or_entangled(qbits[0]).second); + CHECK(sim.is_classical_or_entangled(qbits[1]).first); + CHECK_FALSE(sim.is_classical_or_entangled(qbits[1]).second); + CHECK(sim.is_classical_or_entangled(qbits[2]).first); + CHECK_FALSE(sim.is_classical_or_entangled(qbits[2]).second); + + sim.H(qbits[0]); + CHECK(sim.is_classical_or_entangled(qbits[0]).first); + CHECK_FALSE(sim.is_classical_or_entangled(qbits[0]).second); + CHECK(sim.is_classical_or_entangled(qbits[1]).first); + CHECK_FALSE(sim.is_classical_or_entangled(qbits[1]).second); + CHECK(sim.is_classical_or_entangled(qbits[2]).first); + CHECK_FALSE(sim.is_classical_or_entangled(qbits[2]).second); + + sim.release(qbits); + CHECK(sim.num_qubits() == 0); +} + void test_extract_qubits_state_simple(int qubits_number) { SimulatorType sim; diff --git a/src/Simulation/Native/src/simulator/simulator.hpp b/src/Simulation/Native/src/simulator/simulator.hpp index 2a4dc555def..33313f78abe 100644 --- a/src/Simulation/Native/src/simulator/simulator.hpp +++ b/src/Simulation/Native/src/simulator/simulator.hpp @@ -32,7 +32,7 @@ class Simulator : public Microsoft::Quantum::Simulator::SimulatorInterface std::size_t random(std::vector const& d) { - recursive_lock_type l(mutex()); + recursive_lock_type l(getMutex()); std::discrete_distribution dist(d.begin(), d.end()); return dist(psi.rng()); } @@ -40,7 +40,7 @@ class Simulator : public Microsoft::Quantum::Simulator::SimulatorInterface std::size_t random(std::size_t n, double* d) { std::discrete_distribution dist(d, d + n); - recursive_lock_type l(mutex()); + recursive_lock_type l(getMutex()); return dist(psi.rng()); } @@ -52,7 +52,7 @@ class Simulator : public Microsoft::Quantum::Simulator::SimulatorInterface return 0.0; } - recursive_lock_type l(mutex()); + recursive_lock_type l(getMutex()); changebasis(bs, qs, true); double p = psi.jointprobability(qs); changebasis(bs, qs, false); @@ -61,20 +61,26 @@ class Simulator : public Microsoft::Quantum::Simulator::SimulatorInterface bool InjectState(const std::vector& qubits, const std::vector& amplitudes) { - recursive_lock_type l(mutex()); + recursive_lock_type l(getMutex()); return psi.inject_state(qubits, amplitudes); } bool isclassical(logical_qubit_id q) { - recursive_lock_type l(mutex()); + recursive_lock_type l(getMutex()); return psi.isclassical(q); } + std::pair is_classical_or_entangled(logical_qubit_id q) + { + recursive_lock_type l(getMutex()); + return psi.is_classical_or_entangled(q); + } + // allocate and release logical_qubit_id allocate() { - recursive_lock_type l(mutex()); + recursive_lock_type l(getMutex()); return psi.allocate_qubit(); } @@ -82,7 +88,7 @@ class Simulator : public Microsoft::Quantum::Simulator::SimulatorInterface { std::vector qubits; qubits.reserve(n); - recursive_lock_type l(mutex()); + recursive_lock_type l(getMutex()); for (unsigned i = 0; i < n; ++i) { @@ -93,33 +99,31 @@ class Simulator : public Microsoft::Quantum::Simulator::SimulatorInterface void allocateQubit(logical_qubit_id q) { - recursive_lock_type l(mutex()); + recursive_lock_type l(getMutex()); psi.allocate_qubit(q); } void allocateQubit(std::vector const& qubits) { - recursive_lock_type l(mutex()); + recursive_lock_type l(getMutex()); for (auto q : qubits) psi.allocate_qubit(q); } bool release(logical_qubit_id q) { - recursive_lock_type l(mutex()); + recursive_lock_type l(getMutex()); flush(); - bool allok = isclassical(q); - if (allok) - allok = (psi.getvalue(q) == false); - else + std::pair allok = is_classical_or_entangled(q); + if (!allok.first) M(q); psi.release(q); - return allok; + return !allok.second; } bool release(std::vector const& qs) { - recursive_lock_type l(mutex()); + recursive_lock_type l(getMutex()); bool allok = true; for (auto q : qs) allok = release(q) && allok; @@ -131,19 +135,19 @@ class Simulator : public Microsoft::Quantum::Simulator::SimulatorInterface #define GATE1IMPL(OP) \ void OP(logical_qubit_id q) \ { \ - recursive_lock_type l(mutex()); \ + recursive_lock_type l(getMutex()); \ psi.apply(Gates::OP(q)); \ } #define GATE1CIMPL(OP) \ void C##OP(logical_qubit_id c, logical_qubit_id q) \ { \ - recursive_lock_type l(mutex()); \ + recursive_lock_type l(getMutex()); \ psi.apply_controlled(c, Gates::OP(q)); \ } #define GATE1MCIMPL(OP) \ void C##OP(std::vector const& c, logical_qubit_id q) \ { \ - recursive_lock_type l(mutex()); \ + recursive_lock_type l(getMutex()); \ psi.apply_controlled(c, Gates::OP(q)); \ } #define GATE1(OP) GATE1IMPL(OP) GATE1CIMPL(OP) GATE1MCIMPL(OP) @@ -167,19 +171,19 @@ class Simulator : public Microsoft::Quantum::Simulator::SimulatorInterface #define GATE1IMPL(OP) \ void OP(double phi, logical_qubit_id q) \ { \ - recursive_lock_type l(mutex()); \ + recursive_lock_type l(getMutex()); \ psi.apply(Gates::OP(phi, q)); \ } #define GATE1CIMPL(OP) \ void C##OP(double phi, logical_qubit_id c, logical_qubit_id q) \ { \ - recursive_lock_type l(mutex()); \ + recursive_lock_type l(getMutex()); \ psi.apply_controlled(c, Gates::OP(phi, q)); \ } #define GATE1MCIMPL(OP) \ void C##OP(double phi, std::vector const& c, logical_qubit_id q) \ { \ - recursive_lock_type l(mutex()); \ + recursive_lock_type l(getMutex()); \ psi.apply_controlled(c, Gates::OP(phi, q)); \ } #define GATE1(OP) GATE1IMPL(OP) GATE1CIMPL(OP) GATE1MCIMPL(OP) @@ -196,14 +200,14 @@ class Simulator : public Microsoft::Quantum::Simulator::SimulatorInterface // rotations void R(Gates::Basis b, double phi, logical_qubit_id q) { - recursive_lock_type l(mutex()); + recursive_lock_type l(getMutex()); psi.apply(Gates::R(b, phi, q)); } // multi-controlled rotations void CR(Gates::Basis b, double phi, std::vector const& c, logical_qubit_id q) { - recursive_lock_type l(mutex()); + recursive_lock_type l(getMutex()); psi.apply_controlled(c, Gates::R(b, phi, q)); } @@ -219,7 +223,7 @@ class Simulator : public Microsoft::Quantum::Simulator::SimulatorInterface logical_qubit_id somequbit = qs.front(); removeIdentities(bs, qs); - recursive_lock_type l(mutex()); + recursive_lock_type l(getMutex()); if (bs.size() == 0) CR(Gates::PauliI, -2. * phi, cs, somequbit); else if (bs.size() == 1) @@ -230,7 +234,7 @@ class Simulator : public Microsoft::Quantum::Simulator::SimulatorInterface void Exp(std::vector const& bs, double phi, std::vector const& qs) { - recursive_lock_type l(mutex()); + recursive_lock_type l(getMutex()); CExp(bs, phi, std::vector(), qs); } @@ -238,14 +242,14 @@ class Simulator : public Microsoft::Quantum::Simulator::SimulatorInterface bool M(logical_qubit_id q) { - recursive_lock_type l(mutex()); + recursive_lock_type l(getMutex()); return psi.measure(q); } std::vector MultiM(std::vector const& qs) { // ***TODO*** optimized implementation - recursive_lock_type l(mutex()); + recursive_lock_type l(getMutex()); std::vector res; for (auto q : qs) res.push_back(psi.measure(q)); @@ -254,7 +258,7 @@ class Simulator : public Microsoft::Quantum::Simulator::SimulatorInterface bool Measure(std::vector bs, std::vector qs) { - recursive_lock_type l(mutex()); + recursive_lock_type l(getMutex()); removeIdentities(bs, qs); // ***TODO*** optimized kernels changebasis(bs, qs, true); @@ -265,34 +269,34 @@ class Simulator : public Microsoft::Quantum::Simulator::SimulatorInterface void seed(unsigned s) { - recursive_lock_type l(mutex()); + recursive_lock_type l(getMutex()); psi.seed(s); } void reset() { - recursive_lock_type l(mutex()); + recursive_lock_type l(getMutex()); psi.reset(); } unsigned num_qubits() const { - recursive_lock_type l(mutex()); + recursive_lock_type l(getMutex()); return psi.num_qubits(); } void flush() { - recursive_lock_type l(mutex()); + recursive_lock_type l(getMutex()); psi.flush(); } ComplexType const* data() const { - recursive_lock_type l(mutex()); + recursive_lock_type l(getMutex()); return psi.data().data(); } void dump(bool (*callback)(size_t, double, double)) { - recursive_lock_type l(mutex()); + recursive_lock_type l(getMutex()); flush(); auto wfn = psi.data(); @@ -315,7 +319,7 @@ class Simulator : public Microsoft::Quantum::Simulator::SimulatorInterface void dumpIds(void (*callback)(logical_qubit_id)) { - recursive_lock_type l(mutex()); + recursive_lock_type l(getMutex()); flush(); std::vector qubits = psi.get_qubit_ids(); @@ -381,13 +385,13 @@ class Simulator : public Microsoft::Quantum::Simulator::SimulatorInterface assert(std::accumulate(test.begin(), test.end(), 0u) == table_size); #endif - recursive_lock_type l(mutex()); + recursive_lock_type l(getMutex()); psi.permute_basis(qs, table_size, permutation_table, adjoint); } bool subsytemwavefunction(std::vector const& qs, WavefunctionStorage& qubitswfn, double tolerance) { - recursive_lock_type l(mutex()); + recursive_lock_type l(getMutex()); flush(); return psi.subsytemwavefunction(qs, qubitswfn, tolerance); } diff --git a/src/Simulation/Native/src/simulator/simulatorinterface.hpp b/src/Simulation/Native/src/simulator/simulatorinterface.hpp index 6d3bff907b9..697b08b1715 100644 --- a/src/Simulation/Native/src/simulator/simulatorinterface.hpp +++ b/src/Simulation/Native/src/simulator/simulatorinterface.hpp @@ -124,7 +124,7 @@ class SimulatorInterface throw std::runtime_error("this simulator does not support permutation oracle emulation"); }; - recursive_mutex_type& mutex() const + recursive_mutex_type& getMutex() const { return *mutex_ptr; } diff --git a/src/Simulation/Native/src/simulator/wavefunction.hpp b/src/Simulation/Native/src/simulator/wavefunction.hpp index 6cc2d416d05..ec5a5f28cb6 100644 --- a/src/Simulation/Native/src/simulator/wavefunction.hpp +++ b/src/Simulation/Native/src/simulator/wavefunction.hpp @@ -673,6 +673,13 @@ class Wavefunction return kernels::isclassical(wfn_, get_qubit_position(q)); } + /// checks both if the qubit is in classical state or entangled with any other qubits + std::pair is_classical_or_entangled(logical_qubit_id q) const + { + flush(); + return kernels::is_classical_or_entangled(wfn_, get_qubit_position(q)); + } + /// returns the classical value of a qubit (if classical) /// \pre the qubit has to be in a classical state in the computational basis bool getvalue(logical_qubit_id q) const diff --git a/src/Simulation/Simulators.Tests/Circuits/CoreOperations.qs b/src/Simulation/Simulators.Tests/Circuits/CoreOperations.qs index 12d4e4dec65..5eacfcd4122 100644 --- a/src/Simulation/Simulators.Tests/Circuits/CoreOperations.qs +++ b/src/Simulation/Simulators.Tests/Circuits/CoreOperations.qs @@ -546,7 +546,7 @@ namespace Microsoft.Quantum.Simulation.Simulators.Tests.Circuits { using (q = Qubit()) { X(q); - // Should raise an exception + // Should not raise an exception } } @@ -581,7 +581,7 @@ namespace Microsoft.Quantum.Simulation.Simulators.Tests.Circuits { borrowing (q = Qubit()) { X(q); - // Should raise an exception + // Should not raise an exception } } diff --git a/src/Simulation/Simulators.Tests/QuantumSimulatorTests/QubitReleaseTest.cs b/src/Simulation/Simulators.Tests/QuantumSimulatorTests/QubitReleaseTest.cs index 3e2d78ddf7d..f11099d9a51 100644 --- a/src/Simulation/Simulators.Tests/QuantumSimulatorTests/QubitReleaseTest.cs +++ b/src/Simulation/Simulators.Tests/QuantumSimulatorTests/QubitReleaseTest.cs @@ -14,13 +14,13 @@ namespace Microsoft.Quantum.Simulation.Simulators.Tests { public partial class QuantumSimulatorTests { - //test to check that qubit cannot be released if it is not in zero state + //test to check that qubit can be released if it is not entangled [Fact] public async Task ZeroStateQubitReleaseTest() { var sim = new QuantumSimulator(); - await Assert.ThrowsAsync(() => UsingQubitCheck.Run(sim)); + await UsingQubitCheck.Run(sim); } //test to check that qubit can be released if measured @@ -39,7 +39,7 @@ public async Task MeasuredMultipleQubitsReleaseTest() { var sim = new QuantumSimulator(); - await Assert.ThrowsAsync(() => ReleaseMeasureMultipleQubitCheck.Run(sim)); + await Assert.ThrowsAsync(() => ReleaseMeasureMultipleQubitCheck.Run(sim)); } //test to check that qubit that is released and reallocated is in state |0> diff --git a/src/Simulation/Simulators.Tests/ToffoliSimulatorTests.cs b/src/Simulation/Simulators.Tests/ToffoliSimulatorTests.cs index ec72438eb1c..aa27afaf437 100644 --- a/src/Simulation/Simulators.Tests/ToffoliSimulatorTests.cs +++ b/src/Simulation/Simulators.Tests/ToffoliSimulatorTests.cs @@ -165,7 +165,7 @@ public async Task ToffoliUsingCheck() { var sim = new ToffoliSimulator(); - await Assert.ThrowsAsync(() => UsingQubitCheck.Run(sim)); + await UsingQubitCheck.Run(sim); } [Fact] diff --git a/src/Simulation/Simulators/QuantumSimulator/QubitManager.cs b/src/Simulation/Simulators/QuantumSimulator/QubitManager.cs index b1bcc7b09fa..7936726331e 100644 --- a/src/Simulation/Simulators/QuantumSimulator/QubitManager.cs +++ b/src/Simulation/Simulators/QuantumSimulator/QubitManager.cs @@ -56,10 +56,10 @@ protected override void Release(Qubit qubit, bool wasUsedOnlyForBorrowing) base.Release(qubit, wasUsedOnlyForBorrowing); if (qubit != null) { - bool isReleasedQubitZero = ReleaseOne(this.SimulatorId, (uint)qubit.Id); - if (!(isReleasedQubitZero || qubit.IsMeasured) && throwOnReleasingQubitsNotInZeroState) + bool isReleasedQubitEntangled = !ReleaseOne(this.SimulatorId, (uint)qubit.Id); + if (isReleasedQubitEntangled && throwOnReleasingQubitsNotInZeroState) { - throw new ReleasedQubitsAreNotInZeroState(); + throw new ReleasedQubitsAreEntangled(); } } } diff --git a/src/Simulation/Simulators/ToffoliSimulator/ToffoliSimulator.cs b/src/Simulation/Simulators/ToffoliSimulator/ToffoliSimulator.cs index 3f5c95fb88b..d430b3f7bdf 100644 --- a/src/Simulation/Simulators/ToffoliSimulator/ToffoliSimulator.cs +++ b/src/Simulation/Simulators/ToffoliSimulator/ToffoliSimulator.cs @@ -390,10 +390,6 @@ public TSRelease(ToffoliSimulator m) : base(m) /// The qubit to release. public override void Apply(Qubit q) { - if (simulator.State[q.Id]) - { - throw new ReleasedQubitsAreNotInZeroState(); - } manager.Release(q); } @@ -403,17 +399,6 @@ public override void Apply(Qubit q) /// The qubits to release. public override void Apply(IQArray qubits) { - // Note that we need to handle null array pointers (as opposed to empty arrays) - if (qubits != null) - { - foreach (var q in qubits) - { - if (simulator.State[q.Id]) - { - throw new ReleasedQubitsAreNotInZeroState(); - } - } - } manager.Release(qubits); } }