diff --git a/src/Simulation/Common/Utils.cs b/src/Simulation/Common/Utils.cs index 752c4048dcd..4be748429c7 100644 --- a/src/Simulation/Common/Utils.cs +++ b/src/Simulation/Common/Utils.cs @@ -2,12 +2,14 @@ // Licensed under the MIT License. using Microsoft.Quantum.Simulation.Core; +using System; using System.Collections.Generic; using System.Diagnostics; +using System.Linq; namespace Microsoft.Quantum.Simulation.Common { - public class CommonUtils + public static class CommonUtils { /// /// Removes PauliI terms from observable and corresponding qubits from qubits. @@ -25,7 +27,7 @@ public static void PruneObservable(IQArray observable, IQArray qub /// /// Returns IEnumerable<T> that contains sub-sequence of [i], such that [i] is not equal to . /// - public static IEnumerable PrunedSequence(IQArray sequence, U value, IQArray sequenceToPrune ) + public static IEnumerable PrunedSequence(IQArray sequence, U value, IQArray sequenceToPrune) { for (uint i = 0; i < sequence.Length; ++i) { @@ -63,5 +65,65 @@ public static (long, long) Reduce(long numerator, long denominatorPower) return (numNew, denomPowerNew); } + + /// + /// Takes an array of doubles as + /// input, and returns a randomly-selected index into the array + /// as an `Int`. The probability of selecting a specific index + /// is proportional to the value of the array element at that index. + /// Array elements that are equal to zero are ignored and their indices + /// are never returned.If any array element is less than zero, or if + /// no array element is greater than zero, then the operation fails. + /// As a source of randomness uses a number uniformly distributed between 0 and 1. + /// Used for Quantum.Intrinsic.Random + /// + /// Number between Zero and one, uniformly distributed + public static long SampleDistribution(IQArray unnormalizedDistribution, double uniformZeroOneSample) + { + if (unnormalizedDistribution.Any(prob => prob < 0.0)) + { + throw new ExecutionFailException("Random expects array of non-negative doubles."); + } + + var total = unnormalizedDistribution.Sum(); + if (total == 0) + { + throw new ExecutionFailException("Random expects array of non-negative doubles with positive sum."); + } + + var sample = uniformZeroOneSample * total; + + return unnormalizedDistribution + // Get the unnormalized CDF of the distribution. + .SelectAggregates((double acc, double x) => acc + x) + // Look for the first index at which the CDF is bigger + // than the random sample of 𝑈(0, 1) that we were given + // as a parameter. + .Select((cumulativeProb, idx) => (cumulativeProb, idx)) + .Where(item => item.cumulativeProb >= sample) + // Cast that index to long, and default to returning + // the last item. + .Select( + item => (long)item.idx + ) + .DefaultIfEmpty( + unnormalizedDistribution.Length - 1 + ) + .First(); + } + + internal static IEnumerable SelectAggregates( + this IEnumerable source, + Func aggregate, + TResult initial = default + ) + { + var acc = initial; + foreach (var element in source) + { + acc = aggregate(acc, element); + yield return acc; + } + } } } diff --git a/src/Simulation/Simulators/QCTraceSimulator/InterfaceUtils.cs b/src/Simulation/Simulators/QCTraceSimulator/InterfaceUtils.cs index f3beba01330..b8a4c79c1d8 100644 --- a/src/Simulation/Simulators/QCTraceSimulator/InterfaceUtils.cs +++ b/src/Simulation/Simulators/QCTraceSimulator/InterfaceUtils.cs @@ -74,19 +74,5 @@ public static partial class Extensions { return InterfaceType(t, typeof(IControllable<>)); } - - internal static IEnumerable SelectAggregates( - this IEnumerable source, - Func aggregate, - TResult initial = default - ) - { - var acc = initial; - foreach (var element in source) - { - acc = aggregate(acc, element); - yield return acc; - } - } } } \ No newline at end of file diff --git a/src/Simulation/Simulators/QCTraceSimulator/QCTraceSimulator.Primitive.random.cs b/src/Simulation/Simulators/QCTraceSimulator/QCTraceSimulator.Primitive.random.cs index 1fea9dbfeea..2a5578ed962 100644 --- a/src/Simulation/Simulators/QCTraceSimulator/QCTraceSimulator.Primitive.random.cs +++ b/src/Simulation/Simulators/QCTraceSimulator/QCTraceSimulator.Primitive.random.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. +using Microsoft.Quantum.Simulation.Common; using Microsoft.Quantum.Simulation.Core; using System; @@ -18,7 +19,7 @@ public TracerRandom(QCTraceSimulatorImpl m) : base(m) public override Func, Int64> Body => (p) => { - return SimulatorsUtils.SampleDistribution(p, core.random.NextDouble()); + return CommonUtils.SampleDistribution(p, core.random.NextDouble()); }; } } diff --git a/src/Simulation/Simulators/QCTraceSimulator/Utils.cs b/src/Simulation/Simulators/QCTraceSimulator/Utils.cs index 9f991e7ed0c..ddeac1ab574 100644 --- a/src/Simulation/Simulators/QCTraceSimulator/Utils.cs +++ b/src/Simulation/Simulators/QCTraceSimulator/Utils.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Microsoft.Quantum.Simulation.Common; using Microsoft.Quantum.Simulation.Core; using Microsoft.Quantum.Simulation.QCTraceSimulatorRuntime; @@ -199,53 +200,4 @@ public static class MetricsCountersNames /// public const string widthCounter = nameof(WidthCounter); } - - static class SimulatorsUtils - { - /// - /// Takes an array of doubles as - /// input, and returns a randomly-selected index into the array - /// as an `Int`. The probability of selecting a specific index - /// is proportional to the value of the array element at that index. - /// Array elements that are equal to zero are ignored and their indices - /// are never returned.If any array element is less than zero, or if - /// no array element is greater than zero, then the operation fails. - /// As a source of randomness uses a number uniformly distributed between 0 and 1. - /// Used for Quantum.Intrinsic.Random - /// - /// Number between Zero and one, uniformly distributed - public static long SampleDistribution(IQArray unnormalizedDistribution, double uniformZeroOneSample) - { - if (unnormalizedDistribution.Any(prob => prob < 0.0)) - { - throw new ExecutionFailException("Random expects array of non-negative doubles."); - } - - var total = unnormalizedDistribution.Sum(); - if (total == 0) - { - throw new ExecutionFailException("Random expects array of non-negative doubles with positive sum."); - } - - var sample = uniformZeroOneSample * total; - - return unnormalizedDistribution - // Get the unnormalized CDF of the distribution. - .SelectAggregates((double acc, double x) => acc + x) - // Look for the first index at which the CDF is bigger - // than the random sample of 𝑈(0, 1) that we were given - // as a parameter. - .Select((cumulativeProb, idx) => (cumulativeProb, idx)) - .Where(item => item.cumulativeProb >= sample) - // Cast that index to long, and default to returning - // the last item. - .Select( - item => (long)item.idx - ) - .DefaultIfEmpty( - unnormalizedDistribution.Length - 1 - ) - .First(); - } - } } diff --git a/src/Simulation/Simulators/QuantumProcessor/random.cs b/src/Simulation/Simulators/QuantumProcessor/random.cs index d9c51cfa202..0b0452c23c8 100644 --- a/src/Simulation/Simulators/QuantumProcessor/random.cs +++ b/src/Simulation/Simulators/QuantumProcessor/random.cs @@ -3,41 +3,13 @@ using Microsoft.Quantum.Simulation.Core; using System; +using System.Linq; +using Microsoft.Quantum.Simulation.Common; namespace Microsoft.Quantum.Simulation.QuantumProcessor { public partial class QuantumProcessorDispatcher { - public static long SampleDistribution(IQArray unnormalizedDistribution, double uniformZeroOneSample) - { - double total = 0.0; - foreach (double prob in unnormalizedDistribution) - { - if (prob < 0) - { - throw new ExecutionFailException("Random expects array of non-negative doubles."); - } - total += prob; - } - - if (total == 0) - { - throw new ExecutionFailException("Random expects array of non-negative doubles with positive sum."); - } - - double sample = uniformZeroOneSample * total; - double sum = unnormalizedDistribution[0]; - for (int i = 0; i < unnormalizedDistribution.Length - 1; ++i) - { - if (sum >= sample) - { - return i; - } - sum += unnormalizedDistribution[i]; - } - return unnormalizedDistribution.Length; - } - public class QuantumProcessorDispatcherRandom : Quantum.Intrinsic.Random { private QuantumProcessorDispatcher Simulator { get; } @@ -48,7 +20,7 @@ public QuantumProcessorDispatcherRandom(QuantumProcessorDispatcher m) : base(m) public override Func, Int64> Body => (p) => { - return SampleDistribution(p, Simulator.random.NextDouble()); + return CommonUtils.SampleDistribution(p, Simulator.random.NextDouble()); }; } }