public DepthCounter(IDoubleStatistic[] statisticsToCollect = null, bool optimizeDepth = false) { stats = new StatisticsCollector <CallGraphEdge>( Utils.MethodParametersNames(this, "StatisticsRecord"), statisticsToCollect ?? StatisticsCollector <CallGraphEdge> .DefaultStatistics() ); operationCallStack = new Stack <OperationCallRecord>(); OperationCallRecord opRec = new OperationCallRecord(); opRec.OperationName = CallGraphEdge.CallGraphRootHashed; operationCallStack.Push(opRec); this.optimizeDepth = optimizeDepth; if (optimizeDepth) { qubitStartTimes = new SortedQubitPool(); qubitEndTimes = new SortedQubitPool(); } else { qubitAvailabilityTime = new QubitAvailabilityTimeTracker( initialCapacity: 128, // Reasonable number to preallocate. defaultAvailabilityTime: 0.0); } }
public void OnAllocate(object[] qubitsTraceData) { allocatedQubits += qubitsTraceData.Length; OperationCallRecord opRec = operationCallStack.Peek(); opRec.MaxAllocated = Max(opRec.MaxAllocated, allocatedQubits); }
public void OnOperationEnd(object[] returnedQubitsTraceData) { double maxReturnedQubitsAvailableTime = 0; if (returnedQubitsTraceData != null) { maxReturnedQubitsAvailableTime = MaxAvailableTime(returnedQubitsTraceData.Cast <QubitTimeMetrics>()); } OperationCallRecord opRec = operationCallStack.Pop(); Debug.Assert(operationCallStack.Count != 0, "Operation call stack must never get empty"); double inputQubitsAvailableTime = MaxAvailableTime(opRec.InputQubitMetrics); double operationEndTime = Max( Max(maxReturnedQubitsAvailableTime, opRec.ReturnedQubitsAvailableTime), Max(opRec.ReleasedQubitsAvailableTime, inputQubitsAvailableTime)); OperationCallRecord caller = operationCallStack.Peek(); caller.ReleasedQubitsAvailableTime = Max(opRec.ReleasedQubitsAvailableTime, caller.ReleasedQubitsAvailableTime); caller.ReturnedQubitsAvailableTime = Max(opRec.ReturnedQubitsAvailableTime, caller.ReturnedQubitsAvailableTime); double[] metrics = StatisticsRecord( Depth: operationEndTime - opRec.MaxOperationStartTime, StartTimeDifference: opRec.MaxOperationStartTime - opRec.MinOperationStartTime, Width: maxQubitId - opRec.MaxQubitIdAtStart); stats.AddSample( new CallGraphEdge(opRec.OperationName, caller.OperationName, opRec.FunctorSpecialization, caller.FunctorSpecialization), metrics); }
/// <summary> /// Part of implementation of <see cref="IQCTraceSimulatorListener"/> interface. See the interface documentation for more details. /// </summary> public void OnReturn(object[] qubitsTraceData, long qubitsReleased) { allocatedQubits -= qubitsReleased; OperationCallRecord or = operationCallStack.Peek(); or.CurrentBorrowWidth -= qubitsTraceData.Length - qubitsReleased; }
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)); }
public void OnReturn(object[] qubitsTraceData, long qubitReleased) { OperationCallRecord opRec = operationCallStack.Peek(); opRec.ReturnedQubitsAvailableTime = Max( opRec.ReturnedQubitsAvailableTime, MaxAvailableTime(qubitsTraceData.Cast <QubitTimeMetrics>())); }
public void OnBorrow(object[] qubitsTraceData, long newQubitsAllocated) { allocatedQubits += newQubitsAllocated; OperationCallRecord opRec = operationCallStack.Peek(); opRec.MaxAllocated = Max(opRec.MaxAllocated, allocatedQubits); opRec.CurrentBorrowWidth += qubitsTraceData.Length - newQubitsAllocated; opRec.MaxBorrowed = Max(opRec.MaxBorrowed, opRec.CurrentBorrowWidth); }
public void OnOperationStart(HashedString name, OperationFunctor functorSpecialization, object[] qubitsTraceData) { Debug.Assert(qubitsTraceData != null); OperationCallRecord opRec = new OperationCallRecord(); opRec.FunctorSpecialization = functorSpecialization; opRec.OperationName = name; opRec.InputQubitMetrics = Utils.UnboxAs <QubitTimeMetrics>(qubitsTraceData); opRec.MaxOperationStartTime = QubitsMetricsUtils.MaxQubitAvailableTime(opRec.InputQubitMetrics); opRec.MinOperationStartTime = QubitsMetricsUtils.MinQubitAvailableTime(opRec.InputQubitMetrics); operationCallStack.Push(opRec); }
public DepthCounter(IDoubleStatistic[] statisticsToCollect = null) { stats = new StatisticsCollector <CallGraphEdge>( Utils.MethodParametersNames(this, "StatisticsRecord"), statisticsToCollect ?? StatisticsCollector <CallGraphEdge> .DefaultStatistics() ); operationCallStack = new Stack <OperationCallRecord>(); OperationCallRecord opRec = new OperationCallRecord(); opRec.OperationName = CallGraphEdge.CallGraphRootHashed; operationCallStack.Push(opRec); }
public void OnOperationEnd(object[] returnedQubitsTraceData) { OperationCallRecord or = operationCallStack.Pop(); Debug.Assert(operationCallStack.Count != 0, "Operation call stack is empty. This likely caused by unbalanced OnOperationStart/End"); OperationCallRecord opCaller = operationCallStack.Peek(); opCaller.MaxAllocated = Max(opCaller.MaxAllocated, or.MaxAllocated); double[] statRecord = StatisticsRecord( InputWidth: or.InputWidth, ExtraWidth: or.MaxAllocated - or.QubitsAllocatedAtStart, ReturnWidth: returnedQubitsTraceData.Length, BorrowedWidth: or.MaxBorrowed); CallGraphEdge callGraphEdge = new CallGraphEdge(or.OperationName, opCaller.OperationName, or.Variant, opCaller.Variant); statisticsCollector.AddSample(callGraphEdge, statRecord); }
public void OnRelease(object[] qubitsTraceData) { OperationCallRecord opRec = operationCallStack.Peek(); opRec.ReleasedQubitsAvailableTime = Max( opRec.ReleasedQubitsAvailableTime, MaxAvailableTime(qubitsTraceData.Cast <QubitTimeMetrics>())); if (optimizeDepth) { // Performing width computation and applying reuse heuristics. // First we fix busy time periods for all qubits involved. foreach (QubitTimeMetrics q in qubitsTraceData.Cast <QubitTimeMetrics>()) { // If qubit is not fixed in time we fix it at zero. if (q.StartTime.IsEqualTo(ComplexTime.MinValue)) { q.StartTime = ComplexTime.Zero; } } // Then we sort qubits ascending by the start of busy time. This would result in optimal reuse if: // (1) We were not allowed to move gates in time. // (2) We were sorting all qubits of the cirquit, not just this block. // In reality these statements aren't true, so this is only a heuristic rather than the optimal reuse of qubits. Array.Sort(qubitsTraceData, CompareAsQubitTimeMetricsByStartTime); // Reuse qubits if possible or increase count of qubits used. foreach (QubitTimeMetrics q in qubitsTraceData.Cast <QubitTimeMetrics>()) { // If qubit wasn't used in any gate. We don't allocate it. if (q.EndTime.IsEqualTo(ComplexTime.Zero)) { continue; } // Then we find if we can reuse existing qubits. bool reuseExistingAfterNew = qubitStartTimes.FindBound(q.EndTime, getLowerBound: false, out ComplexTime existingStart); bool reuseNewAfterExising = qubitEndTimes.FindBound(q.StartTime, getLowerBound: true, out ComplexTime existingEnd); if (reuseNewAfterExising && reuseExistingAfterNew) { // If we can do both, see which reuse creates a shorter gap and leave it for reuse. if (ComplexTime.Compare(q.StartTime.Subtract(existingEnd), existingStart.Subtract(q.EndTime)) > 0) { reuseNewAfterExising = false; } else { reuseExistingAfterNew = false; } } if (reuseNewAfterExising) { // If we place new qubit after existing - update end time of existing qubit long idToReuse = qubitEndTimes.Remove(existingEnd); qubitEndTimes.Add(idToReuse, q.EndTime); } else if (reuseExistingAfterNew) { // If we place new qubit before existing - update start time of existing qubit long idToReuse = qubitStartTimes.Remove(existingStart); qubitStartTimes.Add(idToReuse, q.StartTime); } else { // We cannot reuse existing qubits. Use new qubit. long id = maxQubitId; maxQubitId++; qubitStartTimes.Add(id, q.StartTime); qubitEndTimes.Add(id, q.EndTime); } } } }
public void OnRelease(object[] qubitsTraceData) { OperationCallRecord opRec = operationCallStack.Peek(); opRec.ReleasedQubitsAvailableTime = Max( opRec.ReleasedQubitsAvailableTime, MaxAvailableTime(qubitsTraceData.Cast <QubitTimeMetrics>())); if (optimizeDepth) { // Doing width reuse heuristics and width computation. foreach (QubitTimeMetrics q in qubitsTraceData.Cast <QubitTimeMetrics>()) { // If qubit wasn't used in any gate. We don't allocate it. if (q.EndTime.IsEqualTo(ComplexTime.Zero)) { continue; } // If qubit is not fixed in time we fix it at zero. if (q.StartTime.IsEqualTo(ComplexTime.MinValue)) { q.StartTime = ComplexTime.Zero; } // Then we find if we can reuse existing qubits. bool reuseExistingAfterNew = qubitStartTimes.FindBound(q.EndTime, getLowerBound: false, out ComplexTime existingStart); bool reuseNewAfterExising = qubitEndTimes.FindBound(q.StartTime, getLowerBound: true, out ComplexTime existingEnd); if (reuseNewAfterExising && reuseExistingAfterNew) { // If we can do both, see which reuse creates a shorter gap and leave it for reuse. if (ComplexTime.Compare(q.StartTime.Subtract(existingEnd), existingStart.Subtract(q.EndTime)) > 0) { reuseNewAfterExising = false; } else { reuseExistingAfterNew = false; } } if (reuseNewAfterExising) { // If we place new qubit after existing - update end time of existing qubit long idToReuse = qubitEndTimes.Remove(existingEnd); qubitEndTimes.Add(idToReuse, q.EndTime); } else if (reuseExistingAfterNew) { // If we place new qubit before existing - update start time of existing qubit long idToReuse = qubitStartTimes.Remove(existingStart); qubitStartTimes.Add(idToReuse, q.StartTime); } else { // We cannot reuse existing qubits. Use new qubit. long id = maxQubitId; maxQubitId++; qubitStartTimes.Add(id, q.StartTime); qubitEndTimes.Add(id, q.EndTime); } } } }