Skip to content
This repository was archived by the owner on Jan 12, 2024. It is now read-only.
Merged
2 changes: 2 additions & 0 deletions src/Simulation/Core/Qubit.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down
18 changes: 14 additions & 4 deletions src/Simulation/Simulators.Tests/Circuits/CoreOperations.qs
Original file line number Diff line number Diff line change
Expand Up @@ -540,7 +540,7 @@ namespace Microsoft.Quantum.Simulation.Simulators.Tests.Circuits {
return Foo(3);
}

operation ToffoliUsingQubitCheck () : Unit
operation UsingQubitCheck () : Unit
{
using (q = Qubit())
{
Expand All @@ -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())
{
Expand Down
Original file line number Diff line number Diff line change
@@ -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<ReleasedQubitsAreNotInZeroState>(() => 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<Intrinsic.Allocate>();
var release = sim.Get<Intrinsic.Release>();
var q1 = allocate.Apply(1);
var q1Id = q1[0].Id;
var gate = sim.Get<Intrinsic.X>();
var measure = sim.Get<Intrinsic.M>();
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);



}
}
}
4 changes: 2 additions & 2 deletions src/Simulation/Simulators.Tests/ToffoliSimulatorTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -156,15 +156,15 @@ public async Task ToffoliUsingCheck()
{
var sim = new ToffoliSimulator();

await Assert.ThrowsAsync<ReleasedQubitsAreNotInZeroState>(() => ToffoliUsingQubitCheck.Run(sim));
await Assert.ThrowsAsync<ReleasedQubitsAreNotInZeroState>(() => UsingQubitCheck.Run(sim));
}

[Fact]
public async Task ToffoliBorrowingCheck()
{
var sim = new ToffoliSimulator();

await Assert.ThrowsAsync<ExecutionFailException>(() => ToffoliBorrowingQubitCheck.Run(sim));
await Assert.ThrowsAsync<ExecutionFailException>(() => BorrowingQubitCheck.Run(sim));
}

[Fact]
Expand Down
2 changes: 1 addition & 1 deletion src/Simulation/Simulators/QuantumSimulator/Assert.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down
2 changes: 1 addition & 1 deletion src/Simulation/Simulators/QuantumSimulator/AssertProb.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down
2 changes: 1 addition & 1 deletion src/Simulation/Simulators/QuantumSimulator/Dump.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
};
Expand Down
3 changes: 2 additions & 1 deletion src/Simulation/Simulators/QuantumSimulator/M.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ public QSimM(QuantumSimulator m) : base(m)
public override Func<Qubit, Result> Body => (q) =>
{
Simulator.CheckQubit(q);

//setting qubit as measured to allow for release
q.IsMeasured = true;
return M(Simulator.Id, (uint)q.Id).ToResult();
};
}
Expand Down
7 changes: 5 additions & 2 deletions src/Simulation/Simulators/QuantumSimulator/Measure.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
};
}
Expand Down
34 changes: 34 additions & 0 deletions src/Simulation/Simulators/QuantumSimulator/QuantumSimulator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -89,28 +89,35 @@ static void CheckAngle(double angle)

/// <summary>
/// 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
/// </summary>
void CheckQubit(Qubit q1)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason that the "if (q == null) throw new ArgumentNullException..." in CheckQubitInUse above is not just replace with a call to CheckQubit? Then all the adaptions in CheckQubits would not be needed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am unsure of why it is in the current state. However, before making a change around this, I would let the original author comment on this, as this was as is when I looked at it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you don't here back, please add that change - I will be to blame for any resulting issues. :)

{
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;
}

/// <summary>
/// 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
/// </summary>
bool[] CheckQubits(IQArray<Qubit> 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;
}
}

Expand All @@ -122,6 +129,7 @@ bool[] CheckQubits(IQArray<Qubit> 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
/// </summary>
bool[] CheckQubits(IQArray<Qubit> targets)
{
Expand All @@ -133,16 +141,40 @@ bool[] CheckQubits(IQArray<Qubit> 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;
}

/// <summary>
/// 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
/// </summary>
bool[] CheckAndPreserveQubits(IQArray<Qubit> 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;
}

/// <summary>
/// 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
/// </summary>
bool[] CheckQubits(IQArray<Qubit> ctrls, IQArray<Qubit> targets)
{
bool[] used = CheckQubits(targets);
Expand All @@ -152,6 +184,8 @@ bool[] CheckQubits(IQArray<Qubit> ctrls, IQArray<Qubit> 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;
}
}

Expand Down
5 changes: 3 additions & 2 deletions src/Simulation/Simulators/QuantumSimulator/QubitManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
}
}
}

}
}