diff --git a/src/Simulation/Core/Qubit.cs b/src/Simulation/Core/Qubit.cs index 61f912fd5b6..ec1e0d40125 100644 --- a/src/Simulation/Core/Qubit.cs +++ b/src/Simulation/Core/Qubit.cs @@ -32,6 +32,8 @@ public Qubit(int id) this.Id = id; } + public bool IsMeasured { get; set; } = false; + public int Id { get; private set; } [DebuggerBrowsable(DebuggerBrowsableState.Never)] diff --git a/src/Simulation/Simulators.Tests/Circuits/CoreOperations.qs b/src/Simulation/Simulators.Tests/Circuits/CoreOperations.qs index 78e9e2fc0af..75898a27657 100644 --- a/src/Simulation/Simulators.Tests/Circuits/CoreOperations.qs +++ b/src/Simulation/Simulators.Tests/Circuits/CoreOperations.qs @@ -540,7 +540,7 @@ namespace Microsoft.Quantum.Simulation.Simulators.Tests.Circuits { return Foo(3); } - operation ToffoliUsingQubitCheck () : Unit + operation UsingQubitCheck () : Unit { using (q = Qubit()) { @@ -549,15 +549,25 @@ namespace Microsoft.Quantum.Simulation.Simulators.Tests.Circuits { } } - operation ToffoliBorrowingQubitCheck () : Unit + operation ReleaseMeasuredQubitCheck () : Unit { using (q = Qubit()) { - ToffoliBorrower(); + X(q); + let r = M(q); + // Should not raise an exception + } + } + + operation BorrowingQubitCheck () : Unit + { + using (q = Qubit()) + { + QubitBorrower(); } } - operation ToffoliBorrower() : Unit + operation QubitBorrower() : Unit { borrowing (q = Qubit()) { diff --git a/src/Simulation/Simulators.Tests/QuantumSimulatorTests/QubitReleaseTest.cs b/src/Simulation/Simulators.Tests/QuantumSimulatorTests/QubitReleaseTest.cs new file mode 100644 index 00000000000..a267a719274 --- /dev/null +++ b/src/Simulation/Simulators.Tests/QuantumSimulatorTests/QubitReleaseTest.cs @@ -0,0 +1,64 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.Quantum.Simulation.Core; +using Microsoft.Quantum.Simulation.Simulators.Exceptions; +using Microsoft.Quantum.Simulation.Simulators.Tests.Circuits; +using Xunit; + +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 + [Fact] + public async Task ZeroStateQubitReleaseTest() + { + var sim = new QuantumSimulator(); + + await Assert.ThrowsAsync(() => UsingQubitCheck.Run(sim)); + } + + //test to check that qubit can be released if measured + [Fact] + public async Task MeasuredQubitReleaseTest() + { + var sim = new QuantumSimulator(); + + //should not throw an exception, as Measured qubits are allowed to be released, and the release aspect is handled in the C++ code + await ReleaseMeasuredQubitCheck.Run(sim); + } + + //test to check that qubit that is released and reallocated is in state |0> + [Fact] + public async Task ReallocateQubitInGroundStateTest() + { + var sim = new QuantumSimulator(); + var allocate = sim.Get(); + var release = sim.Get(); + var q1 = allocate.Apply(1); + var q1Id = q1[0].Id; + var gate = sim.Get(); + var measure = sim.Get(); + gate.Apply(q1[0]); + var result1 = measure.Apply(q1[0]); + //Check X operation + Assert.Equal(result1, Result.One); + release.Apply(q1[0]); + var q2 = allocate.Apply(1); + var q2Id = q2[0].Id; + //Assert reallocated qubit has the same id as the one released + Assert.Equal(q1Id, q2Id); + var result2 = measure.Apply(q2[0]); + //Assert reallocated qubit has is initialized in state |0> + Assert.Equal(result2, Result.Zero); + + + + } + } +} diff --git a/src/Simulation/Simulators.Tests/ToffoliSimulatorTests.cs b/src/Simulation/Simulators.Tests/ToffoliSimulatorTests.cs index a3638cb332a..9e805f8d22c 100644 --- a/src/Simulation/Simulators.Tests/ToffoliSimulatorTests.cs +++ b/src/Simulation/Simulators.Tests/ToffoliSimulatorTests.cs @@ -156,7 +156,7 @@ public async Task ToffoliUsingCheck() { var sim = new ToffoliSimulator(); - await Assert.ThrowsAsync(() => ToffoliUsingQubitCheck.Run(sim)); + await Assert.ThrowsAsync(() => UsingQubitCheck.Run(sim)); } [Fact] @@ -164,7 +164,7 @@ public async Task ToffoliBorrowingCheck() { var sim = new ToffoliSimulator(); - await Assert.ThrowsAsync(() => ToffoliBorrowingQubitCheck.Run(sim)); + await Assert.ThrowsAsync(() => BorrowingQubitCheck.Run(sim)); } [Fact] diff --git a/src/Simulation/Simulators/QuantumSimulator/Assert.cs b/src/Simulation/Simulators/QuantumSimulator/Assert.cs index 50f198d01c7..d0936509470 100644 --- a/src/Simulation/Simulators/QuantumSimulator/Assert.cs +++ b/src/Simulation/Simulators/QuantumSimulator/Assert.cs @@ -28,7 +28,7 @@ public QSimAssert(QuantumSimulator m) : base(m) { var (paulis, qubits, result, msg) = _args; - this.Simulator.CheckQubits(qubits); + this.Simulator.CheckAndPreserveQubits(qubits); if (paulis.Length != qubits.Length) { diff --git a/src/Simulation/Simulators/QuantumSimulator/AssertProb.cs b/src/Simulation/Simulators/QuantumSimulator/AssertProb.cs index cec9be07f3a..8f93eea3b31 100644 --- a/src/Simulation/Simulators/QuantumSimulator/AssertProb.cs +++ b/src/Simulation/Simulators/QuantumSimulator/AssertProb.cs @@ -29,7 +29,7 @@ public QSimAssertProb(QuantumSimulator m) : base(m) { var (paulis, qubits, result, expectedPr, msg, tol) = _args; - Simulator.CheckQubits(qubits); + Simulator.CheckAndPreserveQubits(qubits); if (paulis.Length != qubits.Length) { diff --git a/src/Simulation/Simulators/QuantumSimulator/Dump.cs b/src/Simulation/Simulators/QuantumSimulator/Dump.cs index ce83a813525..5845114baf4 100644 --- a/src/Simulation/Simulators/QuantumSimulator/Dump.cs +++ b/src/Simulation/Simulators/QuantumSimulator/Dump.cs @@ -96,7 +96,7 @@ public QSimDumpRegister(QuantumSimulator m) : base(m) var (location, qubits) = __in; if (location == null) { throw new ArgumentNullException(nameof(location)); } - Simulator.CheckQubits(qubits); + Simulator.CheckAndPreserveQubits(qubits); return Simulator.Dump(location, qubits); }; diff --git a/src/Simulation/Simulators/QuantumSimulator/M.cs b/src/Simulation/Simulators/QuantumSimulator/M.cs index 3ea3717fd63..6f2766ea333 100644 --- a/src/Simulation/Simulators/QuantumSimulator/M.cs +++ b/src/Simulation/Simulators/QuantumSimulator/M.cs @@ -27,7 +27,8 @@ public QSimM(QuantumSimulator m) : base(m) public override Func Body => (q) => { Simulator.CheckQubit(q); - + //setting qubit as measured to allow for release + q.IsMeasured = true; return M(Simulator.Id, (uint)q.Id).ToResult(); }; } diff --git a/src/Simulation/Simulators/QuantumSimulator/Measure.cs b/src/Simulation/Simulators/QuantumSimulator/Measure.cs index 853b61ac8a5..40661c9b4d9 100644 --- a/src/Simulation/Simulators/QuantumSimulator/Measure.cs +++ b/src/Simulation/Simulators/QuantumSimulator/Measure.cs @@ -27,12 +27,15 @@ public QSimMeasure(QuantumSimulator m) : base(m) var (paulis, qubits) = _args; Simulator.CheckQubits(qubits); - if (paulis.Length != qubits.Length) { throw new InvalidOperationException($"Both input arrays for {this.GetType().Name} (paulis,qubits), must be of same size"); } - + foreach (Qubit q in qubits) + { + //setting qubit as measured to allow for release + q.IsMeasured = true; + } return Measure(Simulator.Id, (uint)paulis.Length, paulis.ToArray(), qubits.GetIds()).ToResult(); }; } diff --git a/src/Simulation/Simulators/QuantumSimulator/QuantumSimulator.cs b/src/Simulation/Simulators/QuantumSimulator/QuantumSimulator.cs index 478f511b756..e073a4ac39b 100644 --- a/src/Simulation/Simulators/QuantumSimulator/QuantumSimulator.cs +++ b/src/Simulation/Simulators/QuantumSimulator/QuantumSimulator.cs @@ -89,28 +89,35 @@ static void CheckAngle(double angle) /// /// Makes sure the target qubit of an operation is valid. In particular it checks that the qubit instance is not null. + /// Also sets the isMeasured flag to false for each qubit /// void CheckQubit(Qubit q1) { if (q1 == null) throw new ArgumentNullException(nameof(q1), "Trying to perform a primitive operation on a null Qubit"); + //setting qubit as not measured to not allow release in case of gate operation on qubit + q1.IsMeasured = false; } /// /// Makes sure all qubits are valid as parameter of an intrinsic quantum operation. In particular it checks that /// - none of the qubits are null /// - there are no duplicated qubits + /// Also sets the isMeasured flag to false for each qubit /// bool[] CheckQubits(IQArray ctrls, Qubit q1) { bool[] used = new bool[((QSimQubitManager)QubitManager).MaxId]; CheckQubitInUse(q1, used); + q1.IsMeasured = false; if (ctrls != null && ctrls.Length > 0) { foreach (var q in ctrls) { CheckQubitInUse(q, used); + //setting qubit as not measured to not allow release in case of gate operation on qubit + q.IsMeasured = false; } } @@ -122,6 +129,7 @@ bool[] CheckQubits(IQArray ctrls, Qubit q1) /// Makes sure all qubits are valid as parameter of an intrinsic quantum operation. In particular it checks that /// - none of the qubits are null /// - there are no duplicated qubits + /// Also sets the isMeasured flag to false for each qubit /// bool[] CheckQubits(IQArray targets) { @@ -133,16 +141,40 @@ bool[] CheckQubits(IQArray targets) foreach (var q in targets) { CheckQubitInUse(q, used); + //setting qubit as not measured to not allow release in case of gate operation on qubit + q.IsMeasured = false; } return used; } /// + /// Intended to be used with simulator functions like Dump, Assert, AssertProb /// Makes sure all qubits are valid as parameter of an intrinsic quantum operation. In particular it checks that /// - none of the qubits are null /// - there are no duplicated qubits /// + bool[] CheckAndPreserveQubits(IQArray targets) + { + if (targets == null) throw new ArgumentNullException(nameof(targets), "Trying to perform an intrinsic operation on a null Qubit array."); + if (targets.Length == 0) throw new ArgumentNullException(nameof(targets), "Trying to perform an intrinsic operation on an empty Qubit array."); + + bool[] used = new bool[((QSimQubitManager)QubitManager).MaxId]; + + foreach (var q in targets) + { + CheckQubitInUse(q, used); + } + + return used; + } + + /// + /// Makes sure all qubits are valid as parameter of an intrinsic quantum operation. In particular it checks that + /// - none of the qubits are null + /// - there are no duplicated qubits + /// Also sets the isMeasured flag to false for each qubit + /// bool[] CheckQubits(IQArray ctrls, IQArray targets) { bool[] used = CheckQubits(targets); @@ -152,6 +184,8 @@ bool[] CheckQubits(IQArray ctrls, IQArray targets) foreach (var q in ctrls) { CheckQubitInUse(q, used); + //setting qubit as not measured to not allow release in case of gate operation on qubit + q.IsMeasured = false; } } diff --git a/src/Simulation/Simulators/QuantumSimulator/QubitManager.cs b/src/Simulation/Simulators/QuantumSimulator/QubitManager.cs index 58ff574fc34..7b45f3f9ba4 100644 --- a/src/Simulation/Simulators/QuantumSimulator/QubitManager.cs +++ b/src/Simulation/Simulators/QuantumSimulator/QubitManager.cs @@ -63,13 +63,14 @@ protected override void ReleaseOneQubit(Qubit qubit, bool usedOnlyForBorrowing) base.ReleaseOneQubit(qubit, usedOnlyForBorrowing); if (qubit != null) { - bool areAllReleasedQubitsZero = ReleaseOne(this.SimulatorId, (uint)qubit.Id); - if (!areAllReleasedQubitsZero && throwOnReleasingQubitsNotInZeroState) + bool isReleasedQubitZero = ReleaseOne(this.SimulatorId, (uint)qubit.Id); + if (!(isReleasedQubitZero || qubit.IsMeasured) && throwOnReleasingQubitsNotInZeroState) { throw new ReleasedQubitsAreNotInZeroState(); } } } } + } }