/// <summary>
            /// Compares Time field of two nodes. Also updates MaxLowerBound and MinUpperBound.
            /// </summary>
            /// <returns>Result of comparison of Time field of QubitTimeNode</returns>
            public int Compare(QubitTimeNode a, QubitTimeNode b)
            {
                // Note that comparison of a and b nodes to Sample is "by reference" in this function.
                int result = ComplexTime.Compare(a.Time, b.Time);

                if (result > 0)
                {
                    if (a == Sample && b != Sample)
                    {
                        MaxLowerBound = ComplexTime.Max(MaxLowerBound, b.Time);
                    }
                    else if (a != Sample && b == Sample)
                    {
                        MinUpperBound = ComplexTime.Min(MinUpperBound, a.Time);
                    }
                }
                else if (result < 0)
                {
                    if (a == Sample && b != Sample)
                    {
                        MinUpperBound = ComplexTime.Min(MinUpperBound, b.Time);
                    }
                    else if (a != Sample && b == Sample)
                    {
                        MaxLowerBound = ComplexTime.Max(MaxLowerBound, a.Time);
                    }
                }
                else
                {
                    // We found the value if one argument is the sample and the other is not.
                    if ((a == Sample) != (b == Sample))
                    {
                        MaxLowerBound = b.Time;
                        MinUpperBound = b.Time;
                    }
                }
                return(result);
            }
 /// <summary>
 /// Compare objects assuming they are QubitTimeMetrics. Compare by StartTime.
 /// </summary>
 private static int CompareAsQubitTimeMetricsByStartTime(object x, object y)
 {
     return(ComplexTime.Compare(((QubitTimeMetrics)x).StartTime, ((QubitTimeMetrics)y).StartTime));
 }
        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);
                    }
                }
            }
        }