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);
            }
        }
Example #2
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);
        }
Example #4
0
        /// <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;
        }
Example #5
0
        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>()));
        }
Example #7
0
        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);
        }
Example #8
0
        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);
        }
Example #9
0
        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);
        }
Example #10
0
        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);
                    }
                }
            }
        }