Skip to content
This repository was archived by the owner on Jan 12, 2024. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions .github/ISSUE_TEMPLATE/bug_report.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@
name: Bug report
about: Create a report to help us improve
title: ''
labels: bug
labels: bug, needs triage
assignees: ''

---

## Describe the bug
Expand All @@ -30,6 +29,5 @@ If applicable, add screenshots to help explain your problem.

- Python Version (if applicable) [e.g. the output of `python --version`]


**Additional context**
Add any other context about the problem here.
3 changes: 1 addition & 2 deletions .github/ISSUE_TEMPLATE/feature_request.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@
name: Feature request
about: Suggest an idea for this project
title: ''
labels: enhancement
labels: enhancement, needs triage
assignees: ''

---

** Please describe what you would like the feature to accomplish.**
Expand Down
1 change: 1 addition & 0 deletions src/Simulation/EntryPointDriver.Tests/Tests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -487,6 +487,7 @@ let private resourceSummary =
T 0 0
Depth 0 0
Width 1 1
QubitCount 1 1
BorrowedWidth 0 0"

[<Fact>]
Expand Down
73 changes: 54 additions & 19 deletions src/Simulation/QCTraceSimulator/DepthCounter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,23 @@ namespace Microsoft.Quantum.Simulation.QCTraceSimulatorRuntime
{
public class DepthCounter : IQCTraceSimulatorListener, ICallGraphStatistics
{
class OperationCallRecord
private class OperationCallRecord
{
public HashedString OperationName;
public OperationFunctor FunctorSpecialization;
public double MinOperationStartTime;
public double MaxOperationStartTime;
public double ReleasedQubitsAvailableTime;
public double ReturnedQubitsAvailableTime;
public long MaxQubitIdAtStart = -1;
public QubitTimeMetrics[] InputQubitMetrics;
}

StatisticsCollector<CallGraphEdge> stats;
Stack<OperationCallRecord> operationCallStack;
QubitAvailabilityTimeTracker qubitAvailabilityTime = new QubitAvailabilityTimeTracker(
initialCapacity: 128, // Reasonable number to preallocate.
defaultAvailabilityTime: 0.0);

public IStatisticCollectorResults<CallGraphEdge> Results => stats;

Expand All @@ -44,25 +48,30 @@ 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";
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)
{
foreach (QubitTimeMetrics metric in qubitsTraceData.Cast<QubitTimeMetrics>())
{
qubitAvailabilityTime.MarkQubitIdUsed(metric.QubitId);
}
}

public void OnBorrow(object[] qubitsTraceData, long newQubitsAllocated)
Expand All @@ -74,12 +83,11 @@ public void OnOperationEnd(object[] returnedQubitsTraceData)
double maxReturnedQubitsAvailableTime = 0;
if ( returnedQubitsTraceData != null )
{
QubitTimeMetrics[] qubitsMetrics = Utils.UnboxAs<QubitTimeMetrics>(returnedQubitsTraceData);
maxReturnedQubitsAvailableTime = QubitsMetricsUtils.MaxQubitAvailableTime(qubitsMetrics);
maxReturnedQubitsAvailableTime = MaxAvailableTime(returnedQubitsTraceData.Cast<QubitTimeMetrics>());
}
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 +101,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);
}
Expand All @@ -105,33 +114,59 @@ 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(IEnumerable<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(IEnumerable<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<QubitTimeMetrics>(qubitsTraceData);
double startTime = QubitsMetricsUtils.MaxQubitAvailableTime(qubitsMetrics);
foreach (QubitTimeMetrics q in qubitsMetrics )
IEnumerable<QubitTimeMetrics> qubitsMetrics = qubitsTraceData.Cast<QubitTimeMetrics>();

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(qubitsTraceData.Cast<QubitTimeMetrics>()));
}

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(qubitsTraceData.Cast<QubitTimeMetrics>()));
}

/// <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 @@ -30,6 +30,13 @@ public class QCTraceSimulatorCoreConfiguration
/// of the call graph.
/// </summary>
public uint CallStackDepthLimit = uint.MaxValue;

/// <summary>
/// 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.
/// </summary>
public bool OptimizeDepth = false;
}

/// <summary>
Expand Down Expand Up @@ -101,7 +108,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
78 changes: 78 additions & 0 deletions src/Simulation/QCTraceSimulator/QubitAvailabilityTimeTracker.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;

namespace Microsoft.Quantum.Simulation.QCTraceSimulatorRuntime
{
/// <summary>
/// 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.
/// </summary>
internal class QubitAvailabilityTimeTracker
{
/// <summary>
/// Availability time of all qubits starts at 0.
/// </summary>
private double DefaultAvailabilityTime = 0.0;

/// <summary>
/// This tracks time when a qubit was last used, indexed by qubit id.
/// </summary>
private List<double> QubitAvailableAt;

/// <summary>
/// Maximum qubit id seen so far.
/// </summary>
private long MaxQubitId = -1;

internal QubitAvailabilityTimeTracker(int initialCapacity, double defaultAvailabilityTime)
{
DefaultAvailabilityTime = defaultAvailabilityTime;
QubitAvailableAt = new List<double>(initialCapacity);
}

internal double this[long qubitId]
{
get
{
if (qubitId < QubitAvailableAt.Count)
{
return QubitAvailableAt[(int)qubitId];
}
return DefaultAvailabilityTime;
}
set
{
if (qubitId == QubitAvailableAt.Count)
{
QubitAvailableAt.Add(value);
return;
}
else if (qubitId >= int.MaxValue)
{
throw new IndexOutOfRangeException("Too many qubits to track.");
}
else if (qubitId > QubitAvailableAt.Count)
{
QubitAvailableAt.AddRange(Enumerable.Repeat(DefaultAvailabilityTime, (int)qubitId - QubitAvailableAt.Count + 1));
}
QubitAvailableAt[(int)qubitId] = value;
}
}

internal long GetMaxQubitId()
{
return MaxQubitId;
}

internal void MarkQubitIdUsed(long qubitId)
{
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 @@ -8,24 +8,14 @@ namespace Microsoft.Quantum.Simulation.QCTraceSimulatorRuntime
/// </summary>
public class QubitTimeMetrics
{
/// <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
2 changes: 1 addition & 1 deletion src/Simulation/QCTraceSimulator/WidthCounter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down
Loading