Skip to content
This repository has been archived by the owner on Jan 12, 2024. It is now read-only.

Made Width and Depth compatible in current version of the tracer #404

Merged
merged 12 commits into from
Oct 22, 2020
Merged
1 change: 1 addition & 0 deletions src/Simulation/EntryPointDriver.Tests/Tests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -486,6 +486,7 @@ let private resourceSummary =
T 0 0
Depth 0 0
Width 1 1
ExtraWidth 1 1
DmitryVasilevsky marked this conversation as resolved.
Show resolved Hide resolved
DmitryVasilevsky marked this conversation as resolved.
Show resolved Hide resolved
BorrowedWidth 0 0"

[<Fact>]
Expand Down
59 changes: 47 additions & 12 deletions src/Simulation/QCTraceSimulator/DepthCounter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,15 @@ class OperationCallRecord
public double MaxOperationStartTime;
public double ReleasedQubitsAvailableTime;
public double ReturnedQubitsAvailableTime;
public long MaxQubitIdAtStart = -1;
DmitryVasilevsky marked this conversation as resolved.
Show resolved Hide resolved
public QubitTimeMetrics[] InputQubitMetrics;
}

StatisticsCollector<CallGraphEdge> stats;
Stack<OperationCallRecord> operationCallStack;
QubitAvailabilityTimeTracker qubitAvailabilityTime = new QubitAvailabilityTimeTracker(
initialCapacity: 128,
DmitryVasilevsky marked this conversation as resolved.
Show resolved Hide resolved
defaultAvailabilityTime: 0);
DmitryVasilevsky marked this conversation as resolved.
Show resolved Hide resolved

public IStatisticCollectorResults<CallGraphEdge> Results => stats;

Expand All @@ -48,21 +52,27 @@ public class Metrics
{
public const string Depth = "Depth";
public const string StartTimeDifference = "StartTimeDifference";
public const string Width = "Width";
DmitryVasilevsky marked this conversation as resolved.
Show resolved Hide resolved
}

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<QubitTimeMetrics>(qubitsTraceData);
foreach (QubitTimeMetrics metric in qubitsMetrics)
{
qubitAvailabilityTime.MarkQubitIdUsed(metric.QubitId);
}
DmitryVasilevsky marked this conversation as resolved.
Show resolved Hide resolved
}

public void OnBorrow(object[] qubitsTraceData, long newQubitsAllocated)
Expand All @@ -75,11 +85,11 @@ public void OnOperationEnd(object[] returnedQubitsTraceData)
if ( returnedQubitsTraceData != null )
{
QubitTimeMetrics[] qubitsMetrics = Utils.UnboxAs<QubitTimeMetrics>(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),
Expand All @@ -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 );
DmitryVasilevsky marked this conversation as resolved.
Show resolved Hide resolved

stats.AddSample(new CallGraphEdge(opRec.OperationName, callerName,opRec.FunctorSpecialization, caller.FunctorSpecialization), metrics);
}
Expand All @@ -105,33 +116,57 @@ public void OnOperationStart(HashedString name, OperationFunctor functorSpeciali
opRec.FunctorSpecialization = functorSpecialization;
opRec.OperationName = name;
opRec.InputQubitMetrics = Utils.UnboxAs<QubitTimeMetrics>(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;
}
DmitryVasilevsky marked this conversation as resolved.
Show resolved Hide resolved

public void OnPrimitiveOperation(int id, object[] qubitsTraceData, double primitiveOperationDuration)
{
QubitTimeMetrics[] qubitsMetrics = Utils.UnboxAs<QubitTimeMetrics>(qubitsTraceData);
double startTime = QubitsMetricsUtils.MaxQubitAvailableTime(qubitsMetrics);

double startTime = MaxAvailableTime(qubitsMetrics);
foreach (QubitTimeMetrics q in qubitsMetrics )
{
q.RecordQubitUsage(startTime, primitiveOperationDuration);
qubitAvailabilityTime[q.QubitId] = startTime + primitiveOperationDuration;
}
}

public void OnRelease(object[] qubitsTraceData)
{
OperationCallRecord opRec = operationCallStack.Peek();
QubitTimeMetrics[] qubitsMetrics = Utils.UnboxAs<QubitTimeMetrics>(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<QubitTimeMetrics>(qubitsTraceData);
opRec.ReturnedQubitsAvailableTime = Max(opRec.ReturnedQubitsAvailableTime, QubitsMetricsUtils.MaxQubitAvailableTime(qubitsMetrics));
opRec.ReturnedQubitsAvailableTime = Max(opRec.ReturnedQubitsAvailableTime, MaxAvailableTime(qubitsMetrics));
}

/// <summary>
Expand Down
9 changes: 8 additions & 1 deletion src/Simulation/QCTraceSimulator/QCTraceSimulatorCore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,13 @@ public class QCTraceSimulatorCoreConfiguration
/// of the call graph.
/// </summary>
public uint CallStackDepthLimit = uint.MaxValue;

/// <summary>
/// Constrols if depth or width optimization is favored.
DmitryVasilevsky marked this conversation as resolved.
Show resolved Hide resolved
/// 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.
DmitryVasilevsky marked this conversation as resolved.
Show resolved Hide resolved
/// </summary>
public bool OptimizeDepth = false;
}
DmitryVasilevsky marked this conversation as resolved.
Show resolved Hide resolved

/// <summary>
Expand Down Expand Up @@ -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 );
}

Expand Down
63 changes: 63 additions & 0 deletions src/Simulation/QCTraceSimulator/QubitAvailabilityTimeTracker.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
DmitryVasilevsky marked this conversation as resolved.
Show resolved Hide resolved
// Licensed under the MIT License.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;

namespace Microsoft.Quantum.Simulation.QCTraceSimulatorRuntime
{
internal class QubitAvailabilityTimeTracker
DmitryVasilevsky marked this conversation as resolved.
Show resolved Hide resolved
{
private double DefaultAvailabilityTime = 0;
private List<double> QubitAvailableAt;
DmitryVasilevsky marked this conversation as resolved.
Show resolved Hide resolved
private long MaxQubitId = -1;

internal QubitAvailabilityTimeTracker(int initialCapacity, double defaultAvailabilityTime)
{
DefaultAvailabilityTime = defaultAvailabilityTime;
QubitAvailableAt = new List<double>(initialCapacity);
DmitryVasilevsky marked this conversation as resolved.
Show resolved Hide resolved
}

internal double this[long index]
DmitryVasilevsky marked this conversation as resolved.
Show resolved Hide resolved
{
get
{
if (index < QubitAvailableAt.Count)
{
return QubitAvailableAt[(int)index];
}
return DefaultAvailabilityTime;
}
set
{
if (index == QubitAvailableAt.Count)
{
QubitAvailableAt.Add(value);
return;
}
else if (index >= int.MaxValue)
DmitryVasilevsky marked this conversation as resolved.
Show resolved Hide resolved
{
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;
}
}

internal long GetMaxQubitId()
{
return MaxQubitId;
}

internal void MarkQubitIdUsed(long qubitId)
DmitryVasilevsky marked this conversation as resolved.
Show resolved Hide resolved
{
MaxQubitId = System.Math.Max(MaxQubitId, qubitId);
}

}

}
24 changes: 7 additions & 17 deletions src/Simulation/QCTraceSimulator/QubitMetrics.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,24 +10,14 @@ namespace Microsoft.Quantum.Simulation.QCTraceSimulatorRuntime
/// </summary>
public class QubitTimeMetrics
DmitryVasilevsky marked this conversation as resolved.
Show resolved Hide resolved
{
/// <summary>
/// Time when the qubit becomes available
/// </summary>
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)
{
}

/// <param name="timeAt">Beginning of the execution of the primitive operation on the qubit</param>
/// <param name="duration">Duration of the primitive operation</param>
public void RecordQubitUsage(double timeAt, double duration)
{
if (timeAt < AvailableAt)
{
throw new QubitTimeMetricsException();
}
AvailableAt = timeAt + duration;
QubitId = qubitId;
}
}
}
}
44 changes: 0 additions & 44 deletions src/Simulation/QCTraceSimulator/QubitsMetricsUtils.cs

This file was deleted.

4 changes: 2 additions & 2 deletions src/Simulation/QCTraceSimulator/TraceableQubitManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
/// </summary>
public TraceableQubitManager( Func<long,object>[] qubitTraceDataInitializers )
: base(NumQubits, mayExtendCapacity : true, disableBorrowing : false)
public TraceableQubitManager( Func<long,object>[] qubitTraceDataInitializers, bool optimizeDepth )
: base(NumQubits, mayExtendCapacity : true, disableBorrowing : false, encourageReuse: !optimizeDepth)
{
this.qubitTraceDataInitializers = qubitTraceDataInitializers.Clone() as
Func<long, object>[];
Expand Down
Loading