Skip to content
This repository was archived by the owner on Jan 12, 2024. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
1dbe742
Initial Q# API for new Random namespace
cgranade Aug 12, 2020
445dd5f
Consolidate RNG logic in SimulatorBase.
cgranade Aug 12, 2020
6410342
Consolidate RNG logic in Toffoli simulator as well.
cgranade Aug 12, 2020
c036b7f
Fix build errors.
cgranade Aug 12, 2020
045b493
Fix typo in previous commit.
cgranade Aug 12, 2020
e112042
Adapt namespaces in test project.
cgranade Aug 12, 2020
90281c1
One more namespace fix.
cgranade Aug 12, 2020
4e7bc13
Started some API documentation comments.
cgranade Aug 12, 2020
c620ef6
Add more API docs and tests.
cgranade Aug 13, 2020
2acbaf1
Merge branch 'master' into cgranade/random
Aug 13, 2020
b35b26a
Fix open statements.
cgranade Aug 13, 2020
990750a
Merge branch 'cgranade/random' of https://github.com/microsoft/qsharp…
cgranade Aug 13, 2020
586c6fb
Fix SampleNBits.
cgranade Aug 13, 2020
98c423d
Merge branch 'master' into cgranade/random
Aug 13, 2020
13250ef
Update src/Simulation/Common/SimulatorBase.cs
Aug 14, 2020
65aafb0
Update src/Simulation/Common/SimulatorBase.cs
Aug 14, 2020
b320b94
Update src/Simulation/QsharpCore/Random/Convienence.qs
Aug 14, 2020
6536eb7
Update src/Simulation/QsharpCore/Random/Types.qs
Aug 14, 2020
ff41894
Merge branch 'master' into cgranade/random
Aug 14, 2020
01801de
API docs, review feedback, more tests.
cgranade Aug 14, 2020
5be6285
fixed typo
cgranade Aug 14, 2020
dbc2458
Added missing open.
cgranade Aug 14, 2020
0599d92
Add another missing open.
cgranade Aug 14, 2020
88f3482
Discrete → continuous
cgranade Aug 14, 2020
3ad221e
Split out intrinsic tests to avoid duplicated types.
cgranade Aug 14, 2020
4ebe4b1
Fix off by one error in unit tests.
cgranade Aug 14, 2020
9af99a4
Added a little bit more testing.
cgranade Aug 17, 2020
9467ede
Merge branch 'master' into cgranade/random
Aug 17, 2020
3622495
Merge branch 'cgranade/random' of https://github.com/microsoft/qsharp…
cgranade Aug 17, 2020
c7e9d32
A bit more testing.
cgranade Aug 17, 2020
cf02e6b
Avoid depending on Delay.
cgranade Aug 18, 2020
d938e4b
Break down tests a bit more.
cgranade Aug 18, 2020
ec19b73
Fix categorical tests.
cgranade Aug 18, 2020
e18a5a0
Update src/Simulation/Simulators.Tests/TestProjects/IntrinsicTests/In…
Aug 18, 2020
87e26c1
Update src/Simulation/Simulators.Tests/TestProjects/IntrinsicTests/Ra…
Aug 18, 2020
53161bc
Update src/Simulation/Simulators.Tests/TestProjects/IntrinsicTests/Ra…
Aug 18, 2020
7c1424a
Merge branch 'master' into cgranade/random
Aug 18, 2020
5ba050b
Merge branch 'master' into cgranade/random
Aug 18, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions Simulation.sln
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,16 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "QCIExe", "src\Simulation\Si
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TargetedExe", "src\Simulation\Simulators.Tests\TestProjects\TargetedExe\TargetedExe.csproj", "{D292BF18-3956-4827-820E-254C3F81EF09}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{EBDE31D8-BB73-4E7E-B035-DE92657F3700}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Simulation", "Simulation", "{5497B844-8266-4B66-B51B-AE26148E9F78}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Simulators.Tests", "Simulators.Tests", "{C64D5562-CF69-4FF0-8A44-19FE8BEB8CE4}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "TestProjects", "TestProjects", "{877C3E74-5533-4517-8EB1-CA24EBAB4A25}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IntrinsicTests", "src\Simulation\Simulators.Tests\TestProjects\IntrinsicTests\IntrinsicTests.csproj", "{D5D41201-101F-4C0D-B6A0-201D8FC3AB91}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -477,6 +487,22 @@ Global
{D292BF18-3956-4827-820E-254C3F81EF09}.RelWithDebInfo|Any CPU.Build.0 = Release|Any CPU
{D292BF18-3956-4827-820E-254C3F81EF09}.RelWithDebInfo|x64.ActiveCfg = Release|Any CPU
{D292BF18-3956-4827-820E-254C3F81EF09}.RelWithDebInfo|x64.Build.0 = Release|Any CPU
{D5D41201-101F-4C0D-B6A0-201D8FC3AB91}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D5D41201-101F-4C0D-B6A0-201D8FC3AB91}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D5D41201-101F-4C0D-B6A0-201D8FC3AB91}.Debug|x64.ActiveCfg = Debug|Any CPU
{D5D41201-101F-4C0D-B6A0-201D8FC3AB91}.Debug|x64.Build.0 = Debug|Any CPU
{D5D41201-101F-4C0D-B6A0-201D8FC3AB91}.MinSizeRel|Any CPU.ActiveCfg = Debug|Any CPU
{D5D41201-101F-4C0D-B6A0-201D8FC3AB91}.MinSizeRel|Any CPU.Build.0 = Debug|Any CPU
{D5D41201-101F-4C0D-B6A0-201D8FC3AB91}.MinSizeRel|x64.ActiveCfg = Debug|Any CPU
{D5D41201-101F-4C0D-B6A0-201D8FC3AB91}.MinSizeRel|x64.Build.0 = Debug|Any CPU
{D5D41201-101F-4C0D-B6A0-201D8FC3AB91}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D5D41201-101F-4C0D-B6A0-201D8FC3AB91}.Release|Any CPU.Build.0 = Release|Any CPU
{D5D41201-101F-4C0D-B6A0-201D8FC3AB91}.Release|x64.ActiveCfg = Release|Any CPU
{D5D41201-101F-4C0D-B6A0-201D8FC3AB91}.Release|x64.Build.0 = Release|Any CPU
{D5D41201-101F-4C0D-B6A0-201D8FC3AB91}.RelWithDebInfo|Any CPU.ActiveCfg = Debug|Any CPU
{D5D41201-101F-4C0D-B6A0-201D8FC3AB91}.RelWithDebInfo|Any CPU.Build.0 = Debug|Any CPU
{D5D41201-101F-4C0D-B6A0-201D8FC3AB91}.RelWithDebInfo|x64.ActiveCfg = Debug|Any CPU
{D5D41201-101F-4C0D-B6A0-201D8FC3AB91}.RelWithDebInfo|x64.Build.0 = Debug|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -508,6 +534,10 @@ Global
{55833C6C-6E91-4413-9F77-96B3A09666B8} = {09C842CB-930C-4C7D-AD5F-E30DE4A55820}
{C015FF41-9A51-4AF0-AEFC-2547D596B10A} = {09C842CB-930C-4C7D-AD5F-E30DE4A55820}
{D292BF18-3956-4827-820E-254C3F81EF09} = {09C842CB-930C-4C7D-AD5F-E30DE4A55820}
{5497B844-8266-4B66-B51B-AE26148E9F78} = {EBDE31D8-BB73-4E7E-B035-DE92657F3700}
{C64D5562-CF69-4FF0-8A44-19FE8BEB8CE4} = {5497B844-8266-4B66-B51B-AE26148E9F78}
{877C3E74-5533-4517-8EB1-CA24EBAB4A25} = {C64D5562-CF69-4FF0-8A44-19FE8BEB8CE4}
{D5D41201-101F-4C0D-B6A0-201D8FC3AB91} = {877C3E74-5533-4517-8EB1-CA24EBAB4A25}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {929C0464-86D8-4F70-8835-0A5EAF930821}
Expand Down
52 changes: 52 additions & 0 deletions src/Simulation/Common/Extensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,5 +49,57 @@ where op.IsSubclassOf(typeof(T))
factory.Register(op.BaseType, op);
}
}

internal static long NextLongBelow(this System.Random random, long upperExclusive)
{
// Don't allow sampling non-positive numbers, so that we don't break
// Math.Log2.
if (upperExclusive <= 0)
{
throw new ArgumentException(
$"Must be positive, got {upperExclusive}.",
nameof(upperExclusive)
);
}
long SampleNBits(int nBits)
{
// Note that we can assume that nBytes is never more than 8,
// since we got there by looking at the bit length of
// upperExclusive, which is itself a 64-bit integer.
var nBytes = (nBits + 7) / 8;
var nExcessBits = nBytes * 8 - nBits;
var bytes = new byte[nBytes];
random.NextBytes(bytes);

// ToInt64 requires an array of exactly eight bytes.
// We can use IsLittleEndian to check which side we
// need to pad on to get there.
var padded = new byte[8];
bytes.CopyTo(padded,
// ToInt64 requires an array of exactly eight bytes.
// We can use IsLittleEndian to check which side we
// need to pad on to get there.
System.BitConverter.IsLittleEndian
? 0
: 8 - nBytes
);
return System.BitConverter.ToInt64(padded) >> nExcessBits;
};

var nBits = (int) (System.Math.Log(upperExclusive, 2) + 1);
var sample = SampleNBits(nBits);
while (sample >= upperExclusive)
{
sample = SampleNBits(nBits);
}
return sample;
}

internal static long NextLong(this System.Random random, long lower, long upper)
{
var delta = upper - lower;
return lower + random.NextLongBelow(delta + 1);
}

}
}
62 changes: 61 additions & 1 deletion src/Simulation/Common/SimulatorBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,13 @@ public abstract class SimulatorBase : AbstractFactory<AbstractCallable>, IOperat
public event Action<string>? OnLog = null;
public event Action<Exception, IEnumerable<StackFrame>>? OnException = null;


protected readonly int randomSeed;
protected readonly Lazy<System.Random> randomGenerator;
public int Seed => randomSeed;
protected System.Random RandomGenerator => randomGenerator.Value;



/// <summary>
/// An event fired whenever a simulator has additional diagnostic data
Expand All @@ -55,8 +62,12 @@ public abstract class SimulatorBase : AbstractFactory<AbstractCallable>, IOperat
/// </remarks>
public StackFrame[]? CallStack { get; private set; }

public SimulatorBase(IQubitManager? qubitManager = null)
public SimulatorBase(IQubitManager? qubitManager = null, int? seed = null)
{
this.randomSeed = seed ?? Guid.NewGuid().GetHashCode();
this.randomGenerator = new Lazy<System.Random>(
() => new System.Random(Seed)
);
this.QubitManager = qubitManager;

this.InitBuiltinOperations(this.GetType());
Expand Down Expand Up @@ -420,6 +431,55 @@ public GetQubitsAvailableToBorrow(SimulatorBase m) : base(m)
sim.QubitManager.GetFreeQubitsCount();
}

/// <summary>
/// Implements the DrawRandomInt operation from the
/// Microsoft.Quantum.Random namespace.
/// </summary>
public class DrawRandomInt : Random.DrawRandomInt
{
private SimulatorBase sim;

public DrawRandomInt(SimulatorBase m) : base(m)
{
sim = m;
}

public override Func<(long, long), long> Body => arg =>
{
var (min, max) = arg;
if (max <= min)
{
throw new ExecutionFailException($"Max must be greater than min, but {max} <= {min}.");
}
return sim.RandomGenerator.NextLong(min, max);
};
}

/// <summary>
/// Implements the DrawRandomInt operation from the
/// Microsoft.Quantum.Random namespace.
/// </summary>
public class DrawRandomDouble : Random.DrawRandomDouble
{
private SimulatorBase sim;

public DrawRandomDouble(SimulatorBase m) : base(m)
{
sim = m;
}

public override Func<(double, double), double> Body => arg =>
{
var (min, max) = arg;
if (max <= min)
{
throw new ExecutionFailException($"Max must be greater than min, but {max} <= {min}.");
}
var delta = max - min;
return min + delta * sim.RandomGenerator.NextDouble();
};
}

public virtual void StartOperation(ICallable operation, IApplyData inputValue)
{
OnOperationStart?.Invoke(operation, inputValue);
Expand Down
51 changes: 51 additions & 0 deletions src/Simulation/QsharpCore/Diagnostics/Facts.qs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

namespace Microsoft.Quantum.Diagnostics {

/// # Summary
/// Declares that a classical condition is true.
///
/// # Input
/// ## actual
/// The condition to be declared.
/// ## message
/// Failure message string to be printed in the case that the classical
/// condition is false.
///
/// # See Also
/// - Microsoft.Quantum.Diagnostics.Contradiction
///
/// # Example
/// The following Q# snippet will fail:
/// ```Q#
/// Fact(false, "Expected true.");
/// ```
function Fact(actual : Bool, message : String) : Unit {
if (not actual) { fail message; }
}

/// # Summary
/// Declares that a classical condition is false.
///
/// # Input
/// ## actual
/// The condition to be declared.
/// ## message
/// Failure message string to be printed in the case that the classical
/// condition is true.
///
/// # See Also
/// - Microsoft.Quantum.Diagnostics.Fact
///
/// # Example
/// The following Q# code will print "Hello, world":
/// ```Q#
/// Contradiction(2 == 3, "2 is not equal to 3.");
/// Message("Hello, world.");
/// ```
function Contradiction(actual : Bool, message : String) : Unit {
if (actual) { fail message; }
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

/// # Summary
/// This namespace contains functions and operations useful for diagnostic
/// purposes, including assert operations and claim functions.
namespace Microsoft.Quantum.Diagnostics {}
21 changes: 2 additions & 19 deletions src/Simulation/QsharpCore/Intrinsic.qs
Original file line number Diff line number Diff line change
Expand Up @@ -5,27 +5,10 @@ namespace Microsoft.Quantum.Intrinsic {
open Microsoft.Quantum.Math;
open Microsoft.Quantum.Convert;

/// # Summary
/// The random operation 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.
///
/// # Input
/// ## probs
/// An array of floating-point numbers proportional to the probability of
/// selecting each index.
///
/// # Output
/// An integer $i$ with probability $\Pr(i) = p_i / \sum_i p_i$, where $p_i$
/// is the $i$th element of `probs`.
@Deprecated("Microsoft.Quantum.Random.DrawCategorical")
operation Random (probs : Double[]) : Int {
body intrinsic;
}

}

@Deprecated("Microsoft.Quantum.Diagnostics.AssertMeasurement")
operation Assert (bases : Pauli[], qubits : Qubit[], result : Result, msg : String) : Unit
Expand Down
115 changes: 115 additions & 0 deletions src/Simulation/QsharpCore/Random/Convienence.qs
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

namespace Microsoft.Quantum.Random {
open Microsoft.Quantum.Intrinsic;
open Microsoft.Quantum.Math;
open Microsoft.Quantum.Diagnostics;

/// # Summary
/// Draws a random sample from a categorical distribution specified by a
/// list of probablities.
///
/// # Description
/// 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.
///
/// # Input
/// ## probs
/// An array of floating-point numbers proportional to the probability of
/// selecting each index.
///
/// # Output
/// An integer $i$ with probability $\Pr(i) = p_i / \sum_i p_i$, where $p_i$
/// is the $i$th element of `probs`.
///
/// # See Also
/// - Microsoft.Quantum.Random.CategoricalDistribution
operation DrawCategorical(probs : Double[]) : Int {
// There are nicer ways of doing this, but they require the full
// standard library to be available.
mutable sum = 0.0;
for (prob in probs) {
Fact(prob >= 0.0, "Probabilities must be positive.");
set sum += prob;
}

let variate = DrawRandomDouble(0.0, sum);
mutable acc = 0.0;
for (idx in 0..Length(probs) - 1) {
set acc += probs[idx];
if (variate <= acc) {
return idx;
}
}

return Length(probs) - 1;
}

/// # Summary
/// Draws a random Pauli value.
///
/// # Output
/// Either `PauliI`, `PauliX`, `PauliY`, or `PauliZ` with equal
/// probability.
operation DrawRandomPauli() : Pauli {
return [PauliI, PauliX, PauliY, PauliZ][DrawRandomInt(0, 3)];
}

/// # Summary
/// Given an array of data and an a distribution over its indices,
/// attempts to choose an element at random.
///
/// # Input
/// ## data
/// The array from which an element should be chosen.
/// ## indexDistribution
/// A distribution over the indices of `data`.
///
/// # Ouput
/// A tuple `(succeeded, element)` where `succeeded` is `true` if and only
/// if the sample chosen from `indexDistribution` was a valid index into
/// `data`, and where `element` is an element of `data` chosen at random.
///
/// # Example
/// The following Q# snippet chooses an element at random from an array:
/// ```Q#
/// let (succeeded, element) = MaybeChooseElement(
/// data,
/// DiscreteUniformDistribution(0, Length(data) - 1)
/// );
/// Fact(succeeded, "Index chosen by MaybeChooseElement was not valid.");
/// ```
operation MaybeChooseElement<'T>(data : 'T[], indexDistribution : DiscreteDistribution) : (Bool, 'T) {
let index = indexDistribution::Sample();
if (index >= 0 and index < Length(data)) {
return (true, data[index]);
} else {
return (false, Default<'T>());
}
}

/// # Summary
/// Given a success probability, returns a single Bernoulli trial that
/// is true with the given probability.
///
/// # Input
/// ## successProbability
/// The probability with which `true` should be returned.
///
/// # Output
/// `true` with probability `successProbability` and `false` with
/// probability `1.0 - successProbability`.
///
/// # Example
/// The following Q# snippet samples flips from a biased coin:
/// ```Q#
/// let flips = DrawMany(DrawRandomBool, 10, 0.6);
/// ```
operation DrawRandomBool(successProbability : Double) : Bool {
return DrawRandomDouble(0.0, 1.0) <= successProbability;
}
}
Loading