From 93bd71e5427c30f8f08f189e58fce14a104aedde Mon Sep 17 00:00:00 2001 From: Dmitry Vasilevsky Date: Mon, 19 Oct 2020 18:49:04 -0700 Subject: [PATCH 1/8] Made Width and Depth compatible in current version of the tracer (v1) --- .../EntryPointDriver.Tests/Tests.fs | 1 + .../QCTraceSimulator/DepthCounter.cs | 61 +++++++++++++--- .../QCTraceSimulator/QCTraceSimulatorCore.cs | 9 ++- .../QCTraceSimulator/QubitMetrics.cs | 24 ++---- .../QCTraceSimulator/QubitsMetricsUtils.cs | 73 ++++++++++++------- .../QCTraceSimulator/TraceableQubitManager.cs | 4 +- .../ResourcesEstimatorTests.cs | 40 +++++++--- .../QCTraceSimulator/QCTraceSimulator.cs | 8 ++ .../QCTraceSimulator/QCTraceSimulatorImpl.cs | 6 +- .../ResourcesEstimator/ResourcesEstimator.cs | 4 - 10 files changed, 152 insertions(+), 78 deletions(-) diff --git a/src/Simulation/EntryPointDriver.Tests/Tests.fs b/src/Simulation/EntryPointDriver.Tests/Tests.fs index 7206cf9f44e..63815f6d816 100644 --- a/src/Simulation/EntryPointDriver.Tests/Tests.fs +++ b/src/Simulation/EntryPointDriver.Tests/Tests.fs @@ -486,6 +486,7 @@ let private resourceSummary = T 0 0 Depth 0 0 Width 1 1 + ExtraWidth 1 1 BorrowedWidth 0 0" [] diff --git a/src/Simulation/QCTraceSimulator/DepthCounter.cs b/src/Simulation/QCTraceSimulator/DepthCounter.cs index db02a56fb8b..45177fc45d6 100644 --- a/src/Simulation/QCTraceSimulator/DepthCounter.cs +++ b/src/Simulation/QCTraceSimulator/DepthCounter.cs @@ -23,11 +23,15 @@ class OperationCallRecord public double MaxOperationStartTime; public double ReleasedQubitsAvailableTime; public double ReturnedQubitsAvailableTime; + public long MaxQubitIdAtStart = -1; public QubitTimeMetrics[] InputQubitMetrics; } StatisticsCollector stats; Stack operationCallStack; + QubitAvailabilityTimeTracker qubitAvailabilityTime = new QubitAvailabilityTimeTracker( + initialCapacity: 128, + defaultAvailabilityTime: 0); public IStatisticCollectorResults Results => stats; @@ -48,21 +52,27 @@ public class Metrics { public const string Depth = "Depth"; public const string StartTimeDifference = "StartTimeDifference"; + public const string Width = "Width"; } - public double[] StatisticsRecord(double Depth, double StartTimeDifference) + public double[] StatisticsRecord(double Depth, double StartTimeDifference, double Width) { - return new double[] { Depth, StartTimeDifference }; + return new double[] { Depth, StartTimeDifference, Width }; } #region IQCTraceSimulatorListener implementation public object NewTracingData(long qubitId) { - return new QubitTimeMetrics(); + return new QubitTimeMetrics(qubitId); } public void OnAllocate(object[] qubitsTraceData) { + QubitTimeMetrics[] qubitsMetrics = Utils.UnboxAs(qubitsTraceData); + foreach (QubitTimeMetrics metric in qubitsMetrics) + { + qubitAvailabilityTime.MarkQubitIdUsed(metric.QubitId); + } } public void OnBorrow(object[] qubitsTraceData, long newQubitsAllocated) @@ -75,11 +85,11 @@ public void OnOperationEnd(object[] returnedQubitsTraceData) if ( returnedQubitsTraceData != null ) { QubitTimeMetrics[] qubitsMetrics = Utils.UnboxAs(returnedQubitsTraceData); - maxReturnedQubitsAvailableTime = QubitsMetricsUtils.MaxQubitAvailableTime(qubitsMetrics); + maxReturnedQubitsAvailableTime = MaxAvailableTime(qubitsMetrics); } OperationCallRecord opRec = operationCallStack.Pop(); Debug.Assert(operationCallStack.Count != 0, "Operation call stack must never get empty"); - double inputQubitsAvailableTime = QubitsMetricsUtils.MaxQubitAvailableTime(opRec.InputQubitMetrics); + double inputQubitsAvailableTime = MaxAvailableTime(opRec.InputQubitMetrics); double operationEndTime = Max( Max(maxReturnedQubitsAvailableTime, opRec.ReturnedQubitsAvailableTime), @@ -93,7 +103,8 @@ public void OnOperationEnd(object[] returnedQubitsTraceData) double[] metrics = StatisticsRecord( Depth : operationEndTime - opRec.MaxOperationStartTime, - StartTimeDifference: opRec.MaxOperationStartTime - opRec.MinOperationStartTime ); + StartTimeDifference: opRec.MaxOperationStartTime - opRec.MinOperationStartTime, + Width: qubitAvailabilityTime.GetMaxQubitId() - opRec.MaxQubitIdAtStart ); stats.AddSample(new CallGraphEdge(opRec.OperationName, callerName,opRec.FunctorSpecialization, caller.FunctorSpecialization), metrics); } @@ -105,18 +116,44 @@ public void OnOperationStart(HashedString name, OperationFunctor functorSpeciali opRec.FunctorSpecialization = functorSpecialization; opRec.OperationName = name; opRec.InputQubitMetrics = Utils.UnboxAs(qubitsTraceData); - opRec.MaxOperationStartTime = QubitsMetricsUtils.MaxQubitAvailableTime(opRec.InputQubitMetrics); - opRec.MinOperationStartTime = QubitsMetricsUtils.MinQubitAvailableTime(opRec.InputQubitMetrics); + opRec.MaxOperationStartTime = MaxAvailableTime(opRec.InputQubitMetrics); + opRec.MinOperationStartTime = MinAvailableTime(opRec.InputQubitMetrics); + opRec.MaxQubitIdAtStart = qubitAvailabilityTime.GetMaxQubitId(); operationCallStack.Push(opRec); } + private double MinAvailableTime(QubitTimeMetrics[] qubitTimeMetrics) + { + Debug.Assert(qubitTimeMetrics != null); + double min = Double.MaxValue; + foreach (QubitTimeMetrics metric in qubitTimeMetrics) + { + min = Min(min, qubitAvailabilityTime[metric.QubitId]); + } + return min != Double.MaxValue ? min : 0; + } + + private double MaxAvailableTime(QubitTimeMetrics[] qubitTimeMetrics) + { + Debug.Assert(qubitTimeMetrics != null); + double max = 0; + foreach (QubitTimeMetrics metric in qubitTimeMetrics) + { + max = Max(max, qubitAvailabilityTime[metric.QubitId]); + } + return max; + } + public void OnPrimitiveOperation(int id, object[] qubitsTraceData, double primitiveOperationDuration) { QubitTimeMetrics[] qubitsMetrics = Utils.UnboxAs(qubitsTraceData); - double startTime = QubitsMetricsUtils.MaxQubitAvailableTime(qubitsMetrics); + + double startTime = MaxAvailableTime(qubitsMetrics); foreach (QubitTimeMetrics q in qubitsMetrics ) { - q.RecordQubitUsage(startTime, primitiveOperationDuration); + qubitAvailabilityTime[q.QubitId] = startTime + primitiveOperationDuration; + // TODO: Remove! This is not needed. If qubit id is in the metrics data, it was already seen by this class. + // this.MaxQubitId = System.Math.Max(this.MaxQubitId, qubitId); } } @@ -124,14 +161,14 @@ public void OnRelease(object[] qubitsTraceData) { OperationCallRecord opRec = operationCallStack.Peek(); QubitTimeMetrics[] qubitsMetrics = Utils.UnboxAs(qubitsTraceData); - opRec.ReleasedQubitsAvailableTime = Max(opRec.ReleasedQubitsAvailableTime, QubitsMetricsUtils.MaxQubitAvailableTime(qubitsMetrics)); + opRec.ReleasedQubitsAvailableTime = Max(opRec.ReleasedQubitsAvailableTime, MaxAvailableTime(qubitsMetrics)); } public void OnReturn(object[] qubitsTraceData, long qubitReleased) { OperationCallRecord opRec = operationCallStack.Peek(); QubitTimeMetrics[] qubitsMetrics = Utils.UnboxAs(qubitsTraceData); - opRec.ReturnedQubitsAvailableTime = Max(opRec.ReturnedQubitsAvailableTime, QubitsMetricsUtils.MaxQubitAvailableTime(qubitsMetrics)); + opRec.ReturnedQubitsAvailableTime = Max(opRec.ReturnedQubitsAvailableTime, MaxAvailableTime(qubitsMetrics)); } /// diff --git a/src/Simulation/QCTraceSimulator/QCTraceSimulatorCore.cs b/src/Simulation/QCTraceSimulator/QCTraceSimulatorCore.cs index 87c56594945..f074746afb3 100644 --- a/src/Simulation/QCTraceSimulator/QCTraceSimulatorCore.cs +++ b/src/Simulation/QCTraceSimulator/QCTraceSimulatorCore.cs @@ -31,6 +31,13 @@ public class QCTraceSimulatorCoreConfiguration /// of the call graph. /// public uint CallStackDepthLimit = uint.MaxValue; + + /// + /// Constrols if depth or width optimization is favored. + /// If set to true, resulting circuit is optimized for depth by discouraging qubit reuse. + /// If set to false, resulting circuit is optimized for width be encouraging qubit reuse. + /// + public bool OptimizeDepth = false; } /// @@ -102,7 +109,7 @@ QCTraceSimulatorCoreConfiguration config tracingDataInQubitsIsNeeded = listenerNeedsTracingData[i] || tracingDataInQubitsIsNeeded; } - qubitManager = (IQubitManager) new TraceableQubitManager(qubitDataInitializers); + qubitManager = (IQubitManager) new TraceableQubitManager(qubitDataInitializers, configuration.OptimizeDepth); callStackDepthLimit = Math.Max( 1, configuration.CallStackDepthLimit ); } diff --git a/src/Simulation/QCTraceSimulator/QubitMetrics.cs b/src/Simulation/QCTraceSimulator/QubitMetrics.cs index 397d9c1003b..0a2a18ba431 100644 --- a/src/Simulation/QCTraceSimulator/QubitMetrics.cs +++ b/src/Simulation/QCTraceSimulator/QubitMetrics.cs @@ -10,24 +10,14 @@ namespace Microsoft.Quantum.Simulation.QCTraceSimulatorRuntime /// public class QubitTimeMetrics { - /// - /// Time when the qubit becomes available - /// - public double AvailableAt { get; private set; } = 0; + // TODO: Qubit Ids are already available in qubits, but DepthCounter doesn't have access to it + // in OnPrimitiveOperation because it's not part of IQCTraceSimulatorListener interface. + // Consider changing architecture to pass qubits rather than metrics in IQCTraceSimulatorListener. + public long QubitId { get; } - public QubitTimeMetrics() + public QubitTimeMetrics(long qubitId) { - } - - /// Beginning of the execution of the primitive operation on the qubit - /// Duration of the primitive operation - public void RecordQubitUsage(double timeAt, double duration) - { - if (timeAt < AvailableAt) - { - throw new QubitTimeMetricsException(); - } - AvailableAt = timeAt + duration; + QubitId = qubitId; } } -} \ No newline at end of file +} diff --git a/src/Simulation/QCTraceSimulator/QubitsMetricsUtils.cs b/src/Simulation/QCTraceSimulator/QubitsMetricsUtils.cs index 4440e6329ce..af90263c978 100644 --- a/src/Simulation/QCTraceSimulator/QubitsMetricsUtils.cs +++ b/src/Simulation/QCTraceSimulator/QubitsMetricsUtils.cs @@ -1,44 +1,63 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; namespace Microsoft.Quantum.Simulation.QCTraceSimulatorRuntime { - - using System; - using System.Diagnostics; - - class QubitsMetricsUtils + internal class QubitAvailabilityTimeTracker { - public static double MaxQubitAvailableTime(QubitTimeMetrics[] qubitMetrics) + private double DefaultAvailabilityTime = 0; + private List QubitAvailableAt; + private long MaxQubitId = -1; + + internal QubitAvailabilityTimeTracker(int initialCapacity, double defaultAvailabilityTime) { - Debug.Assert(qubitMetrics != null); - if (qubitMetrics.Length == 0) + DefaultAvailabilityTime = defaultAvailabilityTime; + QubitAvailableAt = new List(initialCapacity); + } + + internal double this[long index] + { + get { - return 0; // if there are no qubits to depend on, we start at the beginning e.g. 0 + if (index < QubitAvailableAt.Count) + { + return QubitAvailableAt[(int)index]; + } + return DefaultAvailabilityTime; } - - double maxStartTime = 0; - for (int i = 0; i < qubitMetrics.Length; ++i) + set { - maxStartTime = Math.Max(maxStartTime, qubitMetrics[i].AvailableAt); + if (index == QubitAvailableAt.Count) + { + QubitAvailableAt.Add(value); + return; + } + else if (index >= int.MaxValue) + { + throw new IndexOutOfRangeException("Too many qubits to track."); + } + else if (index > QubitAvailableAt.Count) + { + QubitAvailableAt.AddRange(Enumerable.Repeat(DefaultAvailabilityTime, (int)index - QubitAvailableAt.Count + 1)); + } + QubitAvailableAt[(int)index] = value; } - return maxStartTime; } - public static double MinQubitAvailableTime(QubitTimeMetrics[] qubitMetrics) + internal long GetMaxQubitId() { - Debug.Assert(qubitMetrics != null); - if (qubitMetrics.Length == 0) - { - return 0; // if there are no qubits to depend on, we start at the beginning e.g. 0 - } + return MaxQubitId; + } - double minStartTime = double.MaxValue; - for (int i = 0; i < qubitMetrics.Length; ++i) - { - minStartTime = Math.Min(minStartTime, qubitMetrics[i].AvailableAt); - } - return minStartTime; + internal void MarkQubitIdUsed(long qubitId) + { + MaxQubitId = System.Math.Max(MaxQubitId, qubitId); } + } -} \ No newline at end of file + +} diff --git a/src/Simulation/QCTraceSimulator/TraceableQubitManager.cs b/src/Simulation/QCTraceSimulator/TraceableQubitManager.cs index f11d9042056..f386485f527 100644 --- a/src/Simulation/QCTraceSimulator/TraceableQubitManager.cs +++ b/src/Simulation/QCTraceSimulator/TraceableQubitManager.cs @@ -23,8 +23,8 @@ class TraceableQubitManager : QubitManagerTrackingScope /// The qubit manager makes sure that trace data array for qubits /// is initialized with objects created by qubitTraceDataInitializers callbacks /// - public TraceableQubitManager( Func[] qubitTraceDataInitializers ) - : base(NumQubits, mayExtendCapacity : true, disableBorrowing : false) + public TraceableQubitManager( Func[] qubitTraceDataInitializers, bool optimizeDepth ) + : base(NumQubits, mayExtendCapacity : true, disableBorrowing : false, encourageReuse: !optimizeDepth) { this.qubitTraceDataInitializers = qubitTraceDataInitializers.Clone() as Func[]; diff --git a/src/Simulation/Simulators.Tests/ResourcesEstimatorTests.cs b/src/Simulation/Simulators.Tests/ResourcesEstimatorTests.cs index 0a60328484d..a22b270864c 100644 --- a/src/Simulation/Simulators.Tests/ResourcesEstimatorTests.cs +++ b/src/Simulation/Simulators.Tests/ResourcesEstimatorTests.cs @@ -90,7 +90,7 @@ public void ToTSVTest() Assert.NotNull(data); var rows = data.Split('\n'); - Assert.Equal(9, rows.Length); + Assert.Equal(10, rows.Length); var cols = rows[0].Split('\t'); Assert.Equal("Metric", cols[0].Trim()); @@ -120,24 +120,40 @@ public void DepthDifferentQubitsTest() } /// - /// Documents that the width and depth statistics reflect independent lower + /// Documents that the ExtraWidth and Depth statistics reflect independent lower /// bounds for each (two T gates cannot be combined into a circuit of depth - /// one and width one). + /// one and width one). Width on the other hand is compatible with Depth. /// [Fact] public void DepthVersusWidthTest() { - var sim = new ResourcesEstimator(); - + // Operation to execute: // using(q = Qubit()) { T(q); } using(q = Qubit()) { T(q); } (yes, twice) - DepthVersusWidth.Run(sim).Wait(); - var data = sim.Data; + // First run with width optimization + DataTable data = RunDepthVersusWidthTest(false); Assert.Equal(2.0, data.Rows.Find("T")["Sum"]); + Assert.Equal(1.0, data.Rows.Find("ExtraWidth")["Sum"]); Assert.Equal(1.0, data.Rows.Find("Width")["Sum"]); + Assert.Equal(2.0, data.Rows.Find("Depth")["Sum"]); + + // Now run with depth optimization + data = RunDepthVersusWidthTest(true); + Assert.Equal(2.0, data.Rows.Find("T")["Sum"]); + Assert.Equal(1.0, data.Rows.Find("ExtraWidth")["Sum"]); + Assert.Equal(2.0, data.Rows.Find("Width")["Sum"]); Assert.Equal(1.0, data.Rows.Find("Depth")["Sum"]); } + private DataTable RunDepthVersusWidthTest(bool optimizeDepth) { + QCTraceSimulators.QCTraceSimulatorConfiguration config = ResourcesEstimator.RecommendedConfig(); + config.OptimizeDepth = optimizeDepth; + var sim = new ResourcesEstimator(config); + + DepthVersusWidth.Run(sim).Wait(); + return sim.Data; + } + /// /// Verifies that for multiple separately traced operations, the final /// statistics are cumulative. @@ -155,7 +171,7 @@ public void VerifyTracingMultipleOperationsTest() Assert.Equal(1.0, data1.Rows.Find("T")["Sum"]); Assert.Equal(0.0, data1.Rows.Find("R")["Sum"]); Assert.Equal(0.0, data1.Rows.Find("Measure")["Sum"]); - Assert.Equal(2.0, data1.Rows.Find("Width")["Sum"]); + Assert.Equal(2.0, data1.Rows.Find("ExtraWidth")["Sum"]); Operation_2_of_2.Run(sim).Wait(); DataTable data2 = sim.Data; @@ -166,14 +182,14 @@ public void VerifyTracingMultipleOperationsTest() Assert.Equal(1.0 + 0.0, data2.Rows.Find("T")["Sum"]); Assert.Equal(0.0 + 1.0, data2.Rows.Find("R")["Sum"]); Assert.Equal(0.0 + 1.0, data2.Rows.Find("Measure")["Sum"]); - Assert.Equal(2.0 + 3.0, data2.Rows.Find("Width")["Sum"]); - Assert.Equal(System.Math.Max(2.0, 3.0), data2.Rows.Find("Width")["Max"]); + Assert.Equal(2.0 + 3.0, data2.Rows.Find("ExtraWidth")["Sum"]); + Assert.Equal(System.Math.Max(2.0, 3.0), data2.Rows.Find("ExtraWidth")["Max"]); // Run again to confirm two operations isn't the limit! VerySimpleEstimate.Run(sim).Wait(); DataTable data3 = sim.Data; - Assert.Equal(2.0 + 3.0 + 3.0, data3.Rows.Find("Width")["Sum"]); - Assert.Equal(3.0, data3.Rows.Find("Width")["Max"]); + Assert.Equal(2.0 + 3.0 + 3.0, data3.Rows.Find("ExtraWidth")["Sum"]); + Assert.Equal(3.0, data3.Rows.Find("ExtraWidth")["Max"]); } } } diff --git a/src/Simulation/Simulators/QCTraceSimulator/QCTraceSimulator.cs b/src/Simulation/Simulators/QCTraceSimulator/QCTraceSimulator.cs index 9b5ea578132..5957cdfb28f 100644 --- a/src/Simulation/Simulators/QCTraceSimulator/QCTraceSimulator.cs +++ b/src/Simulation/Simulators/QCTraceSimulator/QCTraceSimulator.cs @@ -98,6 +98,14 @@ public class QCTraceSimulatorConfiguration /// public bool UseWidthCounter = false; + /// + /// Constrols if depth or width optimization is favored. + /// If set to true, resulting circuit is optimized for depth by discouraging qubit reuse. + /// If set to false, resulting circuit is optimized for width be encouraging qubit reuse. + /// Optimization is only limited to reuse of qubits after they are released by user code. + /// + public bool OptimizeDepth; + /// /// Specifies the time it takes to execute each gate. /// In other words, specifies the depth of each primitive operation. diff --git a/src/Simulation/Simulators/QCTraceSimulator/QCTraceSimulatorImpl.cs b/src/Simulation/Simulators/QCTraceSimulator/QCTraceSimulatorImpl.cs index c3bd7f2de57..506030a6f3d 100644 --- a/src/Simulation/Simulators/QCTraceSimulator/QCTraceSimulatorImpl.cs +++ b/src/Simulation/Simulators/QCTraceSimulator/QCTraceSimulatorImpl.cs @@ -80,9 +80,9 @@ public QCTraceSimulatorImpl(QCTraceSimulatorConfiguration config) gateTimes[i] = config.TraceGateTimes[(PrimitiveOperationsGroups)i]; } - tCoreConfig = new QCTraceSimulatorCoreConfiguration - { - ThrowOnUnconstrainedMeasurement = configuration.ThrowOnUnconstrainedMeasurement + tCoreConfig = new QCTraceSimulatorCoreConfiguration { + ThrowOnUnconstrainedMeasurement = configuration.ThrowOnUnconstrainedMeasurement, + OptimizeDepth = configuration.OptimizeDepth }; tCoreConfig.CallStackDepthLimit = config.CallStackDepthLimit; diff --git a/src/Simulation/Simulators/ResourcesEstimator/ResourcesEstimator.cs b/src/Simulation/Simulators/ResourcesEstimator/ResourcesEstimator.cs index d08847898cf..952e7a50286 100644 --- a/src/Simulation/Simulators/ResourcesEstimator/ResourcesEstimator.cs +++ b/src/Simulation/Simulators/ResourcesEstimator/ResourcesEstimator.cs @@ -75,10 +75,6 @@ public ResourcesEstimator(QCTraceSimulatorConfiguration config) : base(config) { return null; } - else if (name == MetricsNames.WidthCounter.ExtraWidth) - { - return "Width"; - } else { return name; From cc9b07569b424971dba770fdab0bc2bf52d16b2f Mon Sep 17 00:00:00 2001 From: Dmitry Vasilevsky Date: Mon, 19 Oct 2020 19:09:34 -0700 Subject: [PATCH 2/8] Renamed file to match class, removed unneeded comment --- src/Simulation/QCTraceSimulator/DepthCounter.cs | 2 -- .../{QubitsMetricsUtils.cs => QubitAvailabilityTimeTracker.cs} | 0 2 files changed, 2 deletions(-) rename src/Simulation/QCTraceSimulator/{QubitsMetricsUtils.cs => QubitAvailabilityTimeTracker.cs} (100%) diff --git a/src/Simulation/QCTraceSimulator/DepthCounter.cs b/src/Simulation/QCTraceSimulator/DepthCounter.cs index 45177fc45d6..9cf8d2e580c 100644 --- a/src/Simulation/QCTraceSimulator/DepthCounter.cs +++ b/src/Simulation/QCTraceSimulator/DepthCounter.cs @@ -152,8 +152,6 @@ public void OnPrimitiveOperation(int id, object[] qubitsTraceData, double primit foreach (QubitTimeMetrics q in qubitsMetrics ) { qubitAvailabilityTime[q.QubitId] = startTime + primitiveOperationDuration; - // TODO: Remove! This is not needed. If qubit id is in the metrics data, it was already seen by this class. - // this.MaxQubitId = System.Math.Max(this.MaxQubitId, qubitId); } } diff --git a/src/Simulation/QCTraceSimulator/QubitsMetricsUtils.cs b/src/Simulation/QCTraceSimulator/QubitAvailabilityTimeTracker.cs similarity index 100% rename from src/Simulation/QCTraceSimulator/QubitsMetricsUtils.cs rename to src/Simulation/QCTraceSimulator/QubitAvailabilityTimeTracker.cs From c49d2cce7bf96e5847015eb2cd2269391d27993b Mon Sep 17 00:00:00 2001 From: Dmitry Vasilevsky Date: Wed, 21 Oct 2020 10:31:02 -0700 Subject: [PATCH 3/8] Code review feedback - typos, formatting, literals, etc --- src/Simulation/QCTraceSimulator/DepthCounter.cs | 4 ++-- src/Simulation/QCTraceSimulator/QCTraceSimulatorCore.cs | 2 +- .../QCTraceSimulator/QubitAvailabilityTimeTracker.cs | 4 ++-- src/Simulation/Simulators.Tests/ResourcesEstimatorTests.cs | 7 ++++--- .../Simulators/QCTraceSimulator/QCTraceSimulator.cs | 2 +- 5 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/Simulation/QCTraceSimulator/DepthCounter.cs b/src/Simulation/QCTraceSimulator/DepthCounter.cs index 9cf8d2e580c..55613d29eb1 100644 --- a/src/Simulation/QCTraceSimulator/DepthCounter.cs +++ b/src/Simulation/QCTraceSimulator/DepthCounter.cs @@ -30,8 +30,8 @@ class OperationCallRecord StatisticsCollector stats; Stack operationCallStack; QubitAvailabilityTimeTracker qubitAvailabilityTime = new QubitAvailabilityTimeTracker( - initialCapacity: 128, - defaultAvailabilityTime: 0); + initialCapacity: 128, // Reasonable number to preallocate. + defaultAvailabilityTime: 0.0); public IStatisticCollectorResults Results => stats; diff --git a/src/Simulation/QCTraceSimulator/QCTraceSimulatorCore.cs b/src/Simulation/QCTraceSimulator/QCTraceSimulatorCore.cs index f074746afb3..8e2a4205b35 100644 --- a/src/Simulation/QCTraceSimulator/QCTraceSimulatorCore.cs +++ b/src/Simulation/QCTraceSimulator/QCTraceSimulatorCore.cs @@ -35,7 +35,7 @@ public class QCTraceSimulatorCoreConfiguration /// /// Constrols if depth or width optimization is favored. /// If set to true, resulting circuit is optimized for depth by discouraging qubit reuse. - /// If set to false, resulting circuit is optimized for width be encouraging qubit reuse. + /// If set to false, resulting circuit is optimized for width by encouraging qubit reuse. /// public bool OptimizeDepth = false; } diff --git a/src/Simulation/QCTraceSimulator/QubitAvailabilityTimeTracker.cs b/src/Simulation/QCTraceSimulator/QubitAvailabilityTimeTracker.cs index af90263c978..bca237365c0 100644 --- a/src/Simulation/QCTraceSimulator/QubitAvailabilityTimeTracker.cs +++ b/src/Simulation/QCTraceSimulator/QubitAvailabilityTimeTracker.cs @@ -19,8 +19,8 @@ internal QubitAvailabilityTimeTracker(int initialCapacity, double defaultAvailab QubitAvailableAt = new List(initialCapacity); } - internal double this[long index] - { + internal double this[long index] + { get { if (index < QubitAvailableAt.Count) diff --git a/src/Simulation/Simulators.Tests/ResourcesEstimatorTests.cs b/src/Simulation/Simulators.Tests/ResourcesEstimatorTests.cs index a22b270864c..186c0abda30 100644 --- a/src/Simulation/Simulators.Tests/ResourcesEstimatorTests.cs +++ b/src/Simulation/Simulators.Tests/ResourcesEstimatorTests.cs @@ -131,21 +131,22 @@ public void DepthVersusWidthTest() // using(q = Qubit()) { T(q); } using(q = Qubit()) { T(q); } (yes, twice) // First run with width optimization - DataTable data = RunDepthVersusWidthTest(false); + DataTable data = RunDepthVersusWidthTest(optimizeDepth: false); Assert.Equal(2.0, data.Rows.Find("T")["Sum"]); Assert.Equal(1.0, data.Rows.Find("ExtraWidth")["Sum"]); Assert.Equal(1.0, data.Rows.Find("Width")["Sum"]); Assert.Equal(2.0, data.Rows.Find("Depth")["Sum"]); // Now run with depth optimization - data = RunDepthVersusWidthTest(true); + data = RunDepthVersusWidthTest(optimizeDepth: true); Assert.Equal(2.0, data.Rows.Find("T")["Sum"]); Assert.Equal(1.0, data.Rows.Find("ExtraWidth")["Sum"]); Assert.Equal(2.0, data.Rows.Find("Width")["Sum"]); Assert.Equal(1.0, data.Rows.Find("Depth")["Sum"]); } - private DataTable RunDepthVersusWidthTest(bool optimizeDepth) { + private DataTable RunDepthVersusWidthTest(bool optimizeDepth) + { QCTraceSimulators.QCTraceSimulatorConfiguration config = ResourcesEstimator.RecommendedConfig(); config.OptimizeDepth = optimizeDepth; var sim = new ResourcesEstimator(config); diff --git a/src/Simulation/Simulators/QCTraceSimulator/QCTraceSimulator.cs b/src/Simulation/Simulators/QCTraceSimulator/QCTraceSimulator.cs index 5957cdfb28f..810180eb0a2 100644 --- a/src/Simulation/Simulators/QCTraceSimulator/QCTraceSimulator.cs +++ b/src/Simulation/Simulators/QCTraceSimulator/QCTraceSimulator.cs @@ -104,7 +104,7 @@ public class QCTraceSimulatorConfiguration /// If set to false, resulting circuit is optimized for width be encouraging qubit reuse. /// Optimization is only limited to reuse of qubits after they are released by user code. /// - public bool OptimizeDepth; + public bool OptimizeDepth = false; /// /// Specifies the time it takes to execute each gate. From a3dd323ac0226d4e46a8784e5c76317ddc3814bc Mon Sep 17 00:00:00 2001 From: Dmitry Vasilevsky Date: Wed, 21 Oct 2020 12:16:06 -0700 Subject: [PATCH 4/8] Code review feedback - static class, enumerable instead of materialization --- src/Simulation/QCTraceSimulator/DepthCounter.cs | 7 +++---- .../QCTraceSimulator/QubitAvailabilityTimeTracker.cs | 2 +- src/Simulation/QCTraceSimulator/WidthCounter.cs | 2 +- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/Simulation/QCTraceSimulator/DepthCounter.cs b/src/Simulation/QCTraceSimulator/DepthCounter.cs index df384b5a1c3..0f871859fc9 100644 --- a/src/Simulation/QCTraceSimulator/DepthCounter.cs +++ b/src/Simulation/QCTraceSimulator/DepthCounter.cs @@ -48,7 +48,7 @@ public DepthCounter(IDoubleStatistic[] statisticsToCollect = null) operationCallStack.Push(opRec); } - public class Metrics + public static class Metrics { public const string Depth = "Depth"; public const string StartTimeDifference = "StartTimeDifference"; @@ -68,8 +68,7 @@ public object NewTracingData(long qubitId) public void OnAllocate(object[] qubitsTraceData) { - QubitTimeMetrics[] qubitsMetrics = Utils.UnboxAs(qubitsTraceData); - foreach (QubitTimeMetrics metric in qubitsMetrics) + foreach (QubitTimeMetrics metric in qubitsTraceData.Cast()) { qubitAvailabilityTime.MarkQubitIdUsed(metric.QubitId); } @@ -149,7 +148,7 @@ public void OnPrimitiveOperation(int id, object[] qubitsTraceData, double primit QubitTimeMetrics[] qubitsMetrics = Utils.UnboxAs(qubitsTraceData); double startTime = MaxAvailableTime(qubitsMetrics); - foreach (QubitTimeMetrics q in qubitsMetrics ) + foreach (QubitTimeMetrics q in qubitsMetrics) { qubitAvailabilityTime[q.QubitId] = startTime + primitiveOperationDuration; } diff --git a/src/Simulation/QCTraceSimulator/QubitAvailabilityTimeTracker.cs b/src/Simulation/QCTraceSimulator/QubitAvailabilityTimeTracker.cs index bca237365c0..fb6dbd454f2 100644 --- a/src/Simulation/QCTraceSimulator/QubitAvailabilityTimeTracker.cs +++ b/src/Simulation/QCTraceSimulator/QubitAvailabilityTimeTracker.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using System; using System.Collections.Generic; diff --git a/src/Simulation/QCTraceSimulator/WidthCounter.cs b/src/Simulation/QCTraceSimulator/WidthCounter.cs index 5e7f5f43175..3760c7f0722 100644 --- a/src/Simulation/QCTraceSimulator/WidthCounter.cs +++ b/src/Simulation/QCTraceSimulator/WidthCounter.cs @@ -45,7 +45,7 @@ public WidthCounter( IDoubleStatistic[] statisticsToCollect = null ) AddToCallStack(CallGraphEdge.CallGraphRootHashed, OperationFunctor.Body); } - public class Metrics + public static class Metrics { public const string InputWidth = "InputWidth"; public const string ExtraWidth = "ExtraWidth"; From 99916b6db45335bee11329bec9d643e7241f5239 Mon Sep 17 00:00:00 2001 From: Dmitry Vasilevsky Date: Wed, 21 Oct 2020 13:01:29 -0700 Subject: [PATCH 5/8] Code review feedback - IEnumerable instead of materializing arrays --- .../QCTraceSimulator/DepthCounter.cs | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/Simulation/QCTraceSimulator/DepthCounter.cs b/src/Simulation/QCTraceSimulator/DepthCounter.cs index 0f871859fc9..f948c29448f 100644 --- a/src/Simulation/QCTraceSimulator/DepthCounter.cs +++ b/src/Simulation/QCTraceSimulator/DepthCounter.cs @@ -83,8 +83,7 @@ public void OnOperationEnd(object[] returnedQubitsTraceData) double maxReturnedQubitsAvailableTime = 0; if ( returnedQubitsTraceData != null ) { - QubitTimeMetrics[] qubitsMetrics = Utils.UnboxAs(returnedQubitsTraceData); - maxReturnedQubitsAvailableTime = MaxAvailableTime(qubitsMetrics); + maxReturnedQubitsAvailableTime = MaxAvailableTime(returnedQubitsTraceData.Cast()); } OperationCallRecord opRec = operationCallStack.Pop(); Debug.Assert(operationCallStack.Count != 0, "Operation call stack must never get empty"); @@ -121,7 +120,7 @@ public void OnOperationStart(HashedString name, OperationFunctor functorSpeciali operationCallStack.Push(opRec); } - private double MinAvailableTime(QubitTimeMetrics[] qubitTimeMetrics) + private double MinAvailableTime(IEnumerable qubitTimeMetrics) { Debug.Assert(qubitTimeMetrics != null); double min = Double.MaxValue; @@ -132,7 +131,7 @@ private double MinAvailableTime(QubitTimeMetrics[] qubitTimeMetrics) return min != Double.MaxValue ? min : 0; } - private double MaxAvailableTime(QubitTimeMetrics[] qubitTimeMetrics) + private double MaxAvailableTime(IEnumerable qubitTimeMetrics) { Debug.Assert(qubitTimeMetrics != null); double max = 0; @@ -145,7 +144,7 @@ private double MaxAvailableTime(QubitTimeMetrics[] qubitTimeMetrics) public void OnPrimitiveOperation(int id, object[] qubitsTraceData, double primitiveOperationDuration) { - QubitTimeMetrics[] qubitsMetrics = Utils.UnboxAs(qubitsTraceData); + IEnumerable qubitsMetrics = qubitsTraceData.Cast(); double startTime = MaxAvailableTime(qubitsMetrics); foreach (QubitTimeMetrics q in qubitsMetrics) @@ -157,15 +156,17 @@ public void OnPrimitiveOperation(int id, object[] qubitsTraceData, double primit public void OnRelease(object[] qubitsTraceData) { OperationCallRecord opRec = operationCallStack.Peek(); - QubitTimeMetrics[] qubitsMetrics = Utils.UnboxAs(qubitsTraceData); - opRec.ReleasedQubitsAvailableTime = Max(opRec.ReleasedQubitsAvailableTime, MaxAvailableTime(qubitsMetrics)); + opRec.ReleasedQubitsAvailableTime = Max( + opRec.ReleasedQubitsAvailableTime, + MaxAvailableTime(qubitsTraceData.Cast())); } public void OnReturn(object[] qubitsTraceData, long qubitReleased) { OperationCallRecord opRec = operationCallStack.Peek(); - QubitTimeMetrics[] qubitsMetrics = Utils.UnboxAs(qubitsTraceData); - opRec.ReturnedQubitsAvailableTime = Max(opRec.ReturnedQubitsAvailableTime, MaxAvailableTime(qubitsMetrics)); + opRec.ReturnedQubitsAvailableTime = Max( + opRec.ReturnedQubitsAvailableTime, + MaxAvailableTime(qubitsTraceData.Cast())); } /// From 5a7ee2b85ff5e27c9334e9042bab57c0b9166122 Mon Sep 17 00:00:00 2001 From: Dmitry Vasilevsky Date: Wed, 21 Oct 2020 14:28:01 -0700 Subject: [PATCH 6/8] Code review feedback - ExtraWidth->QubitCount and comments --- .../EntryPointDriver.Tests/Tests.fs | 2 +- .../QubitAvailabilityTimeTracker.cs | 33 ++++++++++++++----- .../ResourcesEstimatorTests.cs | 16 ++++----- .../ResourcesEstimator/ResourcesEstimator.cs | 4 +++ 4 files changed, 37 insertions(+), 18 deletions(-) diff --git a/src/Simulation/EntryPointDriver.Tests/Tests.fs b/src/Simulation/EntryPointDriver.Tests/Tests.fs index 63815f6d816..076887b8ab7 100644 --- a/src/Simulation/EntryPointDriver.Tests/Tests.fs +++ b/src/Simulation/EntryPointDriver.Tests/Tests.fs @@ -486,7 +486,7 @@ let private resourceSummary = T 0 0 Depth 0 0 Width 1 1 - ExtraWidth 1 1 + QubitCount 1 1 BorrowedWidth 0 0" [] diff --git a/src/Simulation/QCTraceSimulator/QubitAvailabilityTimeTracker.cs b/src/Simulation/QCTraceSimulator/QubitAvailabilityTimeTracker.cs index fb6dbd454f2..4ca9cc42289 100644 --- a/src/Simulation/QCTraceSimulator/QubitAvailabilityTimeTracker.cs +++ b/src/Simulation/QCTraceSimulator/QubitAvailabilityTimeTracker.cs @@ -7,10 +7,25 @@ namespace Microsoft.Quantum.Simulation.QCTraceSimulatorRuntime { + /// + /// Tracks time when qubits were last used and therefore, time when qubits become available. + /// Tracking is done by qubit id, which survives during reuse of qubit. + /// internal class QubitAvailabilityTimeTracker { - private double DefaultAvailabilityTime = 0; + /// + /// Availability time of all qubits starts at 0. + /// + private double DefaultAvailabilityTime = 0.0; + + /// + /// This tracks time when a qubit was last used, indexed by qubit id. + /// private List QubitAvailableAt; + + /// + /// Maximum qubit id seen so far. + /// private long MaxQubitId = -1; internal QubitAvailabilityTimeTracker(int initialCapacity, double defaultAvailabilityTime) @@ -19,32 +34,32 @@ internal QubitAvailabilityTimeTracker(int initialCapacity, double defaultAvailab QubitAvailableAt = new List(initialCapacity); } - internal double this[long index] + internal double this[long qubitId] { get { - if (index < QubitAvailableAt.Count) + if (qubitId < QubitAvailableAt.Count) { - return QubitAvailableAt[(int)index]; + return QubitAvailableAt[(int)qubitId]; } return DefaultAvailabilityTime; } set { - if (index == QubitAvailableAt.Count) + if (qubitId == QubitAvailableAt.Count) { QubitAvailableAt.Add(value); return; } - else if (index >= int.MaxValue) + else if (qubitId >= int.MaxValue) { throw new IndexOutOfRangeException("Too many qubits to track."); } - else if (index > QubitAvailableAt.Count) + else if (qubitId > QubitAvailableAt.Count) { - QubitAvailableAt.AddRange(Enumerable.Repeat(DefaultAvailabilityTime, (int)index - QubitAvailableAt.Count + 1)); + QubitAvailableAt.AddRange(Enumerable.Repeat(DefaultAvailabilityTime, (int)qubitId - QubitAvailableAt.Count + 1)); } - QubitAvailableAt[(int)index] = value; + QubitAvailableAt[(int)qubitId] = value; } } diff --git a/src/Simulation/Simulators.Tests/ResourcesEstimatorTests.cs b/src/Simulation/Simulators.Tests/ResourcesEstimatorTests.cs index 186c0abda30..19cec9f9f7d 100644 --- a/src/Simulation/Simulators.Tests/ResourcesEstimatorTests.cs +++ b/src/Simulation/Simulators.Tests/ResourcesEstimatorTests.cs @@ -120,7 +120,7 @@ public void DepthDifferentQubitsTest() } /// - /// Documents that the ExtraWidth and Depth statistics reflect independent lower + /// Documents that the QubitCount and Depth statistics reflect independent lower /// bounds for each (two T gates cannot be combined into a circuit of depth /// one and width one). Width on the other hand is compatible with Depth. /// @@ -133,14 +133,14 @@ public void DepthVersusWidthTest() // First run with width optimization DataTable data = RunDepthVersusWidthTest(optimizeDepth: false); Assert.Equal(2.0, data.Rows.Find("T")["Sum"]); - Assert.Equal(1.0, data.Rows.Find("ExtraWidth")["Sum"]); + Assert.Equal(1.0, data.Rows.Find("QubitCount")["Sum"]); Assert.Equal(1.0, data.Rows.Find("Width")["Sum"]); Assert.Equal(2.0, data.Rows.Find("Depth")["Sum"]); // Now run with depth optimization data = RunDepthVersusWidthTest(optimizeDepth: true); Assert.Equal(2.0, data.Rows.Find("T")["Sum"]); - Assert.Equal(1.0, data.Rows.Find("ExtraWidth")["Sum"]); + Assert.Equal(1.0, data.Rows.Find("QubitCount")["Sum"]); Assert.Equal(2.0, data.Rows.Find("Width")["Sum"]); Assert.Equal(1.0, data.Rows.Find("Depth")["Sum"]); } @@ -172,7 +172,7 @@ public void VerifyTracingMultipleOperationsTest() Assert.Equal(1.0, data1.Rows.Find("T")["Sum"]); Assert.Equal(0.0, data1.Rows.Find("R")["Sum"]); Assert.Equal(0.0, data1.Rows.Find("Measure")["Sum"]); - Assert.Equal(2.0, data1.Rows.Find("ExtraWidth")["Sum"]); + Assert.Equal(2.0, data1.Rows.Find("QubitCount")["Sum"]); Operation_2_of_2.Run(sim).Wait(); DataTable data2 = sim.Data; @@ -183,14 +183,14 @@ public void VerifyTracingMultipleOperationsTest() Assert.Equal(1.0 + 0.0, data2.Rows.Find("T")["Sum"]); Assert.Equal(0.0 + 1.0, data2.Rows.Find("R")["Sum"]); Assert.Equal(0.0 + 1.0, data2.Rows.Find("Measure")["Sum"]); - Assert.Equal(2.0 + 3.0, data2.Rows.Find("ExtraWidth")["Sum"]); - Assert.Equal(System.Math.Max(2.0, 3.0), data2.Rows.Find("ExtraWidth")["Max"]); + Assert.Equal(2.0 + 3.0, data2.Rows.Find("QubitCount")["Sum"]); + Assert.Equal(System.Math.Max(2.0, 3.0), data2.Rows.Find("QubitCount")["Max"]); // Run again to confirm two operations isn't the limit! VerySimpleEstimate.Run(sim).Wait(); DataTable data3 = sim.Data; - Assert.Equal(2.0 + 3.0 + 3.0, data3.Rows.Find("ExtraWidth")["Sum"]); - Assert.Equal(3.0, data3.Rows.Find("ExtraWidth")["Max"]); + Assert.Equal(2.0 + 3.0 + 3.0, data3.Rows.Find("QubitCount")["Sum"]); + Assert.Equal(3.0, data3.Rows.Find("QubitCount")["Max"]); } } } diff --git a/src/Simulation/Simulators/ResourcesEstimator/ResourcesEstimator.cs b/src/Simulation/Simulators/ResourcesEstimator/ResourcesEstimator.cs index 952e7a50286..84fc8ca8f2d 100644 --- a/src/Simulation/Simulators/ResourcesEstimator/ResourcesEstimator.cs +++ b/src/Simulation/Simulators/ResourcesEstimator/ResourcesEstimator.cs @@ -75,6 +75,10 @@ public ResourcesEstimator(QCTraceSimulatorConfiguration config) : base(config) { return null; } + else if (name == MetricsNames.WidthCounter.ExtraWidth) + { + return "QubitCount"; + } else { return name; From 57e064153fd541dfc8543b9427d27f5e4b9101ca Mon Sep 17 00:00:00 2001 From: Dmitry Vasilevsky Date: Wed, 21 Oct 2020 16:03:08 -0700 Subject: [PATCH 7/8] Making OperationCallRecord private --- src/Simulation/QCTraceSimulator/DepthCounter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Simulation/QCTraceSimulator/DepthCounter.cs b/src/Simulation/QCTraceSimulator/DepthCounter.cs index f948c29448f..c169de55d95 100644 --- a/src/Simulation/QCTraceSimulator/DepthCounter.cs +++ b/src/Simulation/QCTraceSimulator/DepthCounter.cs @@ -15,7 +15,7 @@ namespace Microsoft.Quantum.Simulation.QCTraceSimulatorRuntime { public class DepthCounter : IQCTraceSimulatorListener, ICallGraphStatistics { - class OperationCallRecord + private class OperationCallRecord { public HashedString OperationName; public OperationFunctor FunctorSpecialization; From 7bfd9dec028821e937043f09697d789ec0812c96 Mon Sep 17 00:00:00 2001 From: Dmitry Vasilevsky Date: Wed, 21 Oct 2020 17:07:27 -0700 Subject: [PATCH 8/8] Code review feedback - typo --- src/Simulation/QCTraceSimulator/QCTraceSimulatorCore.cs | 2 +- src/Simulation/Simulators/QCTraceSimulator/QCTraceSimulator.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Simulation/QCTraceSimulator/QCTraceSimulatorCore.cs b/src/Simulation/QCTraceSimulator/QCTraceSimulatorCore.cs index 2c7a2ca4d7f..25eaef0f258 100644 --- a/src/Simulation/QCTraceSimulator/QCTraceSimulatorCore.cs +++ b/src/Simulation/QCTraceSimulator/QCTraceSimulatorCore.cs @@ -32,7 +32,7 @@ public class QCTraceSimulatorCoreConfiguration public uint CallStackDepthLimit = uint.MaxValue; /// - /// Constrols if depth or width optimization is favored. + /// Controls if depth or width optimization is favored. /// If set to true, resulting circuit is optimized for depth by discouraging qubit reuse. /// If set to false, resulting circuit is optimized for width by encouraging qubit reuse. /// diff --git a/src/Simulation/Simulators/QCTraceSimulator/QCTraceSimulator.cs b/src/Simulation/Simulators/QCTraceSimulator/QCTraceSimulator.cs index 810180eb0a2..5ba05575fe9 100644 --- a/src/Simulation/Simulators/QCTraceSimulator/QCTraceSimulator.cs +++ b/src/Simulation/Simulators/QCTraceSimulator/QCTraceSimulator.cs @@ -99,7 +99,7 @@ public class QCTraceSimulatorConfiguration public bool UseWidthCounter = false; /// - /// Constrols if depth or width optimization is favored. + /// Controls if depth or width optimization is favored. /// If set to true, resulting circuit is optimized for depth by discouraging qubit reuse. /// If set to false, resulting circuit is optimized for width be encouraging qubit reuse. /// Optimization is only limited to reuse of qubits after they are released by user code.