/// <summary> /// Find qubit with the specified time in the set. If such qubit is not present, /// find either maximum value less than it or minimum value greater than it. /// This function uses one call to SortedSet.TryGetValue() so it takes log(N) time, where N - number of nodes. /// </summary> /// <param name="requestedTime">Sample time to find</param> /// <param name="getLowerBound">"true" to find maximum value <= requestedTime, "false" to find minimum value >= requestedTime</param> /// <param name="actualTime">Time found in the set</param> /// <returns>"true" if the requested bound was found in the set, "false" otherwise</returns> public bool FindBound(ComplexTime requestedTime, bool getLowerBound, out ComplexTime actualTime) { // We use the following approach: // We call function TryGetValue on a sorted set. If value is found, we return it. // Otherwise TryGetValue must make conclusion that the value is absent. // Any comparison-based algorithm without caching must inspect both // maximum value less than the sample and minimum value greater than the sample. // So they must be among the values seen since reset. // So we just need to harvest them from VisitingComparer. Sample.Time = requestedTime; NodeComparer.ResetForComparison(Sample); if (QubitsSortedByTime.TryGetValue(Sample, out QubitTimeNode foundValue)) { actualTime = foundValue.Time; return(true); } if (getLowerBound) { actualTime = NodeComparer.MaxLowerBound; return(!actualTime.IsEqualTo(ComplexTime.MinValue)); } else { actualTime = NodeComparer.MinUpperBound; return(!actualTime.IsEqualTo(ComplexTime.MaxValue)); } }
/// <summary> /// Compares two complex times. First DepthTime is compared, then TrailingZeroDepthGateCount is compared. /// </summary> /// <returns>0 when a = b, -1 when a< b, and 1 when a > b</returns> internal static int Compare(ComplexTime a, ComplexTime b) { int result = a.DepthTime.CompareTo(b.DepthTime); if (result != 0) { return(result); } return(a.TrailingZeroDepthGateCount.CompareTo(b.TrailingZeroDepthGateCount)); }
/// <summary> /// Subtracts argument ComplexTime from "this" ComplexTime. Returns the result. /// Depth times are assumed to be precise for comparison. /// </summary> /// <param name="time">Time to subtract from this object.</param> /// <returns>Result of subtraction.</returns> internal ComplexTime Subtract(ComplexTime time) { if (DepthTime == time.DepthTime) { return(new ComplexTime(0, TrailingZeroDepthGateCount - time.TrailingZeroDepthGateCount)); } double result = DepthTime - time.DepthTime; if (result <= 0) { // This could happen due to insufficient floating point calculation precision. throw new ArgumentException("Result of ComplexTime subtraction is not positive."); } return(new ComplexTime(result, 0)); }
public void OnPrimitiveOperation(int id, object[] qubitsTraceData, double primitiveOperationDuration) { IEnumerable <QubitTimeMetrics> qubitsMetrics = qubitsTraceData.Cast <QubitTimeMetrics>(); if (optimizeDepth) { // When we optimize for depth we may need to adjust qubit start times in addition to qubit end times. if (qubitsTraceData.Length == 1) { // Single qubit gate always advances end time by operation duration // in case qubit is fixed or not fixed in time. ((QubitTimeMetrics)qubitsTraceData[0]).EndTime = ((QubitTimeMetrics)qubitsTraceData[0]).EndTime.AdvanceBy(primitiveOperationDuration); } else { // Multi-qubit gate fixes all qubits in time and advances end time // First, figure out what time it is. It's max over fixed and not fixed times. ComplexTime maxEndTime = ComplexTime.Zero; foreach (QubitTimeMetrics q in qubitsMetrics) { maxEndTime = ComplexTime.Max(maxEndTime, q.EndTime); } // Now we fix qubits that are not yet fixed by adjusting their start time. // And adjust end time for all qubits involved. foreach (QubitTimeMetrics q in qubitsMetrics) { if (q.StartTime.IsEqualTo(ComplexTime.MinValue)) { q.StartTime = maxEndTime.Subtract(q.EndTime); } q.EndTime = maxEndTime.AdvanceBy(primitiveOperationDuration); } } } else { // When we don't optimize for depth we use max availability time // as gate execution time and then adjust availability time of qubits involved double startTime = MaxAvailableTime(qubitsMetrics); foreach (QubitTimeMetrics q in qubitsMetrics) { qubitAvailabilityTime[q.QubitId] = startTime + primitiveOperationDuration; } } }
/// <summary> /// Add Qubit to the set with specified time. Multiple qubits with the same time can be added. /// Complexity is log(N), where N - number of nodes. /// </summary> /// <param name="qubitId">Id of a qubit to add to the set.</param> /// <param name="time">Time of the qubit.</param> public void Add(long qubitId, ComplexTime time) { QubitTimeNode newNode = new QubitTimeNode(qubitId, time); if (QubitsSortedByTime.Add(newNode)) { // New item was added to the set, we are done. return; } if (!QubitsSortedByTime.TryGetValue(newNode, out QubitTimeNode existingNode)) { // We cannot add, but we cannot find a node with the same value. Is this a floating point glitch? Debug.Assert(false, "Cannot add a value to SortedSet<QubitTimeNode> that isn't in the set."); return; } // Add new node to the linked list as a second element. newNode.NextNode = existingNode.NextNode; existingNode.NextNode = newNode; }
/// <summary> /// Remove one qubit with given time from the set. Complexity is log(N), where N - number of nodes. /// </summary> /// <param name="time">Remove qubit with this time.</param> /// <returns>QubitId if qubit is found. Throws exception if it is not found.</returns> public long Remove(ComplexTime time) { Sample.Time = time; if (!QubitsSortedByTime.TryGetValue(Sample, out QubitTimeNode foundValue)) { throw new ApplicationException("Cannot get qubit that should be present in a qubit pool."); } if (foundValue.NextNode == null) { // Remove only node from the tree. long qubitId = foundValue.QubitId; QubitsSortedByTime.Remove(Sample); return(qubitId); } // Get second node in the list. QubitTimeNode nodeToRemove = foundValue.NextNode; // Remove second node from the list. foundValue.NextNode = nodeToRemove.NextNode; return(nodeToRemove.QubitId); }
/// <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); } } } }
/// <summary> /// Resets minimum and maximum value of nodes seen from the set. /// Also sets sample object, which should not be counted because it is not an object from the set. /// </summary> /// <param name="sample">Sample object that is not considered for minimum and maximum.</param> internal void ResetForComparison(QubitTimeNode sample) { MaxLowerBound = ComplexTime.MinValue; MinUpperBound = ComplexTime.MaxValue; Sample = sample; }
internal QubitTimeNode(long qubitId, ComplexTime time) { QubitId = qubitId; Time = time; NextNode = null; }
/// <summary> /// Finds largest of the two ComplexTime arguments. /// </summary> /// <returns>Largest of the two arguments according to comparison.</returns> internal static ComplexTime Max(ComplexTime a, ComplexTime b) { return(Compare(a, b) > 0 ? a : b); }
/// <summary> /// Returns true if this ComplexTime is the same as the argument. /// </summary> internal bool IsEqualTo(ComplexTime t) { return(Compare(this, t) == 0); }