From cdfee099adca3af3a8e992b23a80b74c1f08f82e Mon Sep 17 00:00:00 2001 From: Sasha Date: Sun, 2 Aug 2020 17:27:09 -0700 Subject: [PATCH 1/3] EncourageReuse option in QubitManager. Currently Qubit Manager tries to reuse released qubits as soon as possible and prefers it to allocating fresh unused ones. This is good for some scenarios, however, in other scenarios it may be preferrable to first allocate fresh unused qubits, and only then try to reuse released ones (oldest released first). Both behaviors are now possible via a backwards-compatible optional parameter in QubitManager constructor. --- src/Simulation/Common/QubitManager.cs | 35 +++++++- .../Common/QubitManagerTrackingScope.cs | 4 +- .../Simulators.Tests/QubitManagerTests.cs | 85 +++++++++++++++++++ 3 files changed, 119 insertions(+), 5 deletions(-) diff --git a/src/Simulation/Common/QubitManager.cs b/src/Simulation/Common/QubitManager.cs index bd19bdad338..dae45a0591b 100644 --- a/src/Simulation/Common/QubitManager.cs +++ b/src/Simulation/Common/QubitManager.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; +using Microsoft.Quantum.Intrinsic; using Microsoft.Quantum.Simulation.Core; using Microsoft.Quantum.Simulation.Simulators.Exceptions; @@ -25,11 +26,13 @@ public class QubitManager long AllocatedForBorrowing; // All qubits allocated only for borrowing, will be marked with this number or higher. long[] qubits; // Tracks the allocation state of all qubits. long free; // Points to the first free (unallocated) qubit. + long freeTail; // Points to the last free (unallocated) qubit. Only valid iff (!EncourageReuse). long numAllocatedQubits; // Tracking this for optimization. long numDisabledQubits; // Number of disabled qubits. // Options bool MayExtendCapacity; + bool EncourageReuse; public bool DisableBorrowing { get; } const long MaxQubitCapacity = long.MaxValue - 3; @@ -49,9 +52,10 @@ public class QubitManager /// /// Creates and initializes QubitManager that can handle up to numQubits qubits /// - public QubitManager(long qubitCapacity, bool mayExtendCapacity = false, bool disableBorrowing = false) + public QubitManager(long qubitCapacity, bool mayExtendCapacity = false, bool disableBorrowing = false, bool encourageReuse = true) { MayExtendCapacity = mayExtendCapacity; + EncourageReuse = encourageReuse; DisableBorrowing = disableBorrowing; if (qubitCapacity <= 0) { qubitCapacity = MinQubitCapacity; } @@ -65,6 +69,7 @@ public QubitManager(long qubitCapacity, bool mayExtendCapacity = false, bool dis Debug.Assert(this.qubits[NumQubits - 1] == None); free = 0; + freeTail = NumQubits - 1; numAllocatedQubits = 0; numDisabledQubits = 0; } @@ -121,6 +126,7 @@ private void ExtendQubitArray() if (free == oldNone) { free = oldNumQubits; + freeTail = NumQubits - 1; } else { Debug.Assert(false, "Why do we extend an array, when we still have available slots?"); @@ -300,8 +306,31 @@ protected virtual void ReleaseOneQubit(Qubit qubit, bool usedOnlyForBorrowing) { throw new ArgumentException("Attempt to free qubit that has not been allocated."); } - qubits[qubit.Id] = free; - free = qubit.Id; + if (EncourageReuse) { + qubits[qubit.Id] = free; + free = qubit.Id; + } + else + { + if (MayExtendCapacity) + { + + } + else + { + if (qubits[freeTail] != None) + { + // There were no free qubits at all + free = qubit.Id; + } + else + { + qubits[freeTail] = qubit.Id; + } + } + qubits[qubit.Id] = None; + freeTail = qubit.Id; + } numAllocatedQubits--; Debug.Assert(numAllocatedQubits >= 0); diff --git a/src/Simulation/Common/QubitManagerTrackingScope.cs b/src/Simulation/Common/QubitManagerTrackingScope.cs index 23f654439de..63bf47872b4 100644 --- a/src/Simulation/Common/QubitManagerTrackingScope.cs +++ b/src/Simulation/Common/QubitManagerTrackingScope.cs @@ -44,8 +44,8 @@ public List Locals private Stack operationStack; // Stack of operation calls. private StackFrame curFrame; // Current stack frame - all qubits in current scope are listed here. - public QubitManagerTrackingScope(long qubitCapacity, bool mayExtendCapacity = false, bool disableBorrowing = false) - : base(qubitCapacity, mayExtendCapacity, disableBorrowing) + public QubitManagerTrackingScope(long qubitCapacity, bool mayExtendCapacity = false, bool disableBorrowing = false, bool encourageReuse = true) + : base(qubitCapacity, mayExtendCapacity, disableBorrowing, encourageReuse) { if (!DisableBorrowing) { diff --git a/src/Simulation/Simulators.Tests/QubitManagerTests.cs b/src/Simulation/Simulators.Tests/QubitManagerTests.cs index a5f2123d0f2..2cd522aa225 100644 --- a/src/Simulation/Simulators.Tests/QubitManagerTests.cs +++ b/src/Simulation/Simulators.Tests/QubitManagerTests.cs @@ -152,6 +152,91 @@ public void TestQubitManager() } } + /// + /// Test for QubitManager. + /// + [Fact] + public void TestQubitManagerDiscouragingReuse() + { + { + QubitManager qm = new QubitManager(10, mayExtendCapacity: false, disableBorrowing: false, encourageReuse: false); + + // Test allocation of single qubit + Qubit q1 = qm.Allocate(); + Assert.True(q1.Id == 0); + + // Test allocation of multiple qubits + IQArray qa1 = qm.Allocate(4); + Assert.True(qa1.Length == 4); + Assert.True(qa1[0].Id == 1); + Assert.True(qa1[1].Id == 2); + Assert.True(qa1[2].Id == 3); + Assert.True(qa1[3].Id == 4); + + // Test reuse of deallocated qubits + qm.Release(qa1[1]); + + Qubit q2 = qm.Allocate(); + Assert.True(q2.Id == 5); + + IQArray qa2 = qm.Allocate(3); + Assert.True(qa2.Length == 3); + Assert.True(qa2[0].Id == 6); + Assert.True(qa2[1].Id == 7); + Assert.True(qa2[2].Id == 8); + + qm.Release(qa2); + + Qubit q3 = qm.Allocate(); + Assert.True(q3.Id == 9); + + Qubit q4 = qm.Allocate(); + Assert.True(q4.Id == 2); + + Qubit q5 = qm.Allocate(); + Assert.True(q5.Id == 8); + } + + { + QubitManager qm = new QubitManager(10, mayExtendCapacity: true, disableBorrowing: false, encourageReuse: false); + + // Test allocation of single qubit + Qubit q1 = qm.Allocate(); + Assert.True(q1.Id == 0); + + // Test allocation of multiple qubits + IQArray qa1 = qm.Allocate(4); + Assert.True(qa1.Length == 4); + Assert.True(qa1[0].Id == 1); + Assert.True(qa1[1].Id == 2); + Assert.True(qa1[2].Id == 3); + Assert.True(qa1[3].Id == 4); + + // Test reuse of deallocated qubits + qm.Release(qa1[1]); + + Qubit q2 = qm.Allocate(); + Assert.True(q2.Id == 5); + + IQArray qa2 = qm.Allocate(3); + Assert.True(qa2.Length == 3); + Assert.True(qa2[0].Id == 6); + Assert.True(qa2[1].Id == 7); + Assert.True(qa2[2].Id == 8); + + qm.Release(qa2); + + Qubit q3 = qm.Allocate(); + Assert.True(q3.Id == 9); + + Qubit q4 = qm.Allocate(); + Assert.True(q4.Id == 10); + + Qubit q5 = qm.Allocate(); + Assert.True(q5.Id == 11); + } + } + /// /// Test for QubitManagerTrackingScope. /// From eb0a92d72f307dc28a3eb5c4a8bc49cea46e856c Mon Sep 17 00:00:00 2001 From: Sasha Date: Tue, 4 Aug 2020 14:28:05 -0700 Subject: [PATCH 2/3] Review feedback --- src/Simulation/Common/QubitManager.cs | 14 ++++++++------ src/Simulation/Common/QubitManagerTrackingScope.cs | 6 +++++- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/Simulation/Common/QubitManager.cs b/src/Simulation/Common/QubitManager.cs index dae45a0591b..a70651f5059 100644 --- a/src/Simulation/Common/QubitManager.cs +++ b/src/Simulation/Common/QubitManager.cs @@ -52,7 +52,11 @@ public class QubitManager /// /// Creates and initializes QubitManager that can handle up to numQubits qubits /// - public QubitManager(long qubitCapacity, bool mayExtendCapacity = false, bool disableBorrowing = false, bool encourageReuse = true) + public QubitManager( + long qubitCapacity, + bool mayExtendCapacity = false, + bool disableBorrowing = false, + bool encourageReuse = true) { MayExtendCapacity = mayExtendCapacity; EncourageReuse = encourageReuse; @@ -312,11 +316,9 @@ protected virtual void ReleaseOneQubit(Qubit qubit, bool usedOnlyForBorrowing) } else { - if (MayExtendCapacity) - { - - } - else + // If we are allowed to extend capacity we will never reuse this qubit, + // otherwise we need to add it to the free qubits list. + if (!MayExtendCapacity) { if (qubits[freeTail] != None) { diff --git a/src/Simulation/Common/QubitManagerTrackingScope.cs b/src/Simulation/Common/QubitManagerTrackingScope.cs index 63bf47872b4..a166f98bfa0 100644 --- a/src/Simulation/Common/QubitManagerTrackingScope.cs +++ b/src/Simulation/Common/QubitManagerTrackingScope.cs @@ -44,7 +44,11 @@ public List Locals private Stack operationStack; // Stack of operation calls. private StackFrame curFrame; // Current stack frame - all qubits in current scope are listed here. - public QubitManagerTrackingScope(long qubitCapacity, bool mayExtendCapacity = false, bool disableBorrowing = false, bool encourageReuse = true) + public QubitManagerTrackingScope( + long qubitCapacity, + bool mayExtendCapacity = false, + bool disableBorrowing = false, + bool encourageReuse = true) : base(qubitCapacity, mayExtendCapacity, disableBorrowing, encourageReuse) { if (!DisableBorrowing) From 6d01b323dc1de01ca572d26aba778e8105ffb8c8 Mon Sep 17 00:00:00 2001 From: Sasha Date: Tue, 4 Aug 2020 14:51:07 -0700 Subject: [PATCH 3/3] . --- src/Simulation/Simulators.Tests/QubitManagerTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Simulation/Simulators.Tests/QubitManagerTests.cs b/src/Simulation/Simulators.Tests/QubitManagerTests.cs index 2cd522aa225..8ee972883a8 100644 --- a/src/Simulation/Simulators.Tests/QubitManagerTests.cs +++ b/src/Simulation/Simulators.Tests/QubitManagerTests.cs @@ -158,7 +158,7 @@ public void TestQubitManager() [Fact] public void TestQubitManagerDiscouragingReuse() { - { + { // BLOCK testing mayExtendCapacity:false QubitManager qm = new QubitManager(10, mayExtendCapacity: false, disableBorrowing: false, encourageReuse: false); // Test allocation of single qubit @@ -197,7 +197,7 @@ public void TestQubitManagerDiscouragingReuse() Assert.True(q5.Id == 8); } - { + { // BLOCK testing mayExtendCapacity:true QubitManager qm = new QubitManager(10, mayExtendCapacity: true, disableBorrowing: false, encourageReuse: false); // Test allocation of single qubit