: ICallable
ICallable Partial
(Func
mapper);
}
-
+ ///
+ /// An operation that wraps another operation, for example
+ /// , ,
+ /// ,
+ ///
+ public interface IOperationWrapper
+ {
+ ICallable BaseOperation { get; }
+ }
///
/// The base class for all ClosedType quantum operations.
diff --git a/src/Simulation/Core/Operations/OperationPartial.cs b/src/Simulation/Core/Operations/OperationPartial.cs
index 4b059962132..12e03f01913 100644
--- a/src/Simulation/Core/Operations/OperationPartial.cs
+++ b/src/Simulation/Core/Operations/OperationPartial.cs
@@ -17,7 +17,7 @@ namespace Microsoft.Quantum.Simulation.Core
/// Optionally it can receive a Mapper to do the same.
///
[DebuggerTypeProxy(typeof(OperationPartial<,,>.DebuggerProxy))]
- public class OperationPartial
: Operation
, IUnitary
+ public class OperationPartial
: Operation
, IUnitary
, IOperationWrapper
{
private Lazy __qubits = null;
@@ -59,6 +59,7 @@ public OperationPartial(Operation op, object partialTuple) : base(op.Facto
public override void Init() { }
public Operation BaseOp { get; }
+ ICallable IOperationWrapper.BaseOperation => BaseOp;
public Func Mapper { get; }
@@ -137,6 +138,7 @@ ICallable ICallable.Partial(Func mapper)
IUnitary IUnitary
.Adjoint => base.Adjoint;
IUnitary<(IQArray, P)> IUnitary.Controlled => base.Controlled;
+
IUnitary IUnitary.Partial(Func mapper) => new OperationPartial(this, mapper);
public override string ToString() => $"{this.BaseOp}{{_}}";
diff --git a/src/Simulation/Core/TypeExtensions.cs b/src/Simulation/Core/TypeExtensions.cs
index 9822b37f373..94bc5ebabf6 100644
--- a/src/Simulation/Core/TypeExtensions.cs
+++ b/src/Simulation/Core/TypeExtensions.cs
@@ -368,5 +368,15 @@ public static string QSharpType(this Type t)
return t.Name;
}
+
+ public static ICallable UnwrapCallable(this ICallable op)
+ {
+ ICallable res = op;
+ while (res as IOperationWrapper != null)
+ {
+ res = (res as IOperationWrapper).BaseOperation;
+ }
+ return res;
+ }
}
}
diff --git a/src/Simulation/Simulators.Tests/Circuits/Fail.qs b/src/Simulation/Simulators.Tests/Circuits/Fail.qs
index 3fb9302255c..ce8e75ca30c 100644
--- a/src/Simulation/Simulators.Tests/Circuits/Fail.qs
+++ b/src/Simulation/Simulators.Tests/Circuits/Fail.qs
@@ -3,10 +3,88 @@
namespace Microsoft.Quantum.Simulation.Simulators.Tests.Circuits {
- operation AlwaysFail() : Unit {
- fail "Always fail";
+ operation AlwaysFail() : Unit is Adj + Ctl{
+ Fail();
}
-}
+ operation AlwaysFail1() : Unit is Adj + Ctl{
+ AlwaysFail();
+ }
+ operation AlwaysFail2() : Unit is Adj + Ctl {
+ Controlled AlwaysFail1(new Qubit[0],());
+ }
+ operation AlwaysFail3() : Unit is Adj + Ctl {
+ Adjoint AlwaysFail2();
+ }
+ operation AlwaysFail4() : Unit is Adj + Ctl {
+ Adjoint AlwaysFail3();
+ }
+ operation GenericFail<'T,'U>( a : 'T, b : 'U ) : Unit is Adj + Ctl {
+ AlwaysFail();
+ }
+
+ operation GenericFail1() : Unit is Adj + Ctl {
+ GenericFail(5,6);
+ }
+
+ operation PartialFail( a : Int, b : Int ) : Unit is Adj + Ctl {
+ AlwaysFail();
+ }
+
+ operation PartialFail1() : Unit is Adj + Ctl {
+ let op = PartialFail(0,_);
+ op(2);
+ }
+
+ operation PartialAdjFail1() : Unit is Adj + Ctl {
+ let op = PartialFail(0,_);
+ Adjoint op(2);
+ }
+
+ operation PartialCtlFail1() : Unit is Adj + Ctl {
+ let op = PartialFail(0,_);
+ Controlled op(new Qubit[0], 2);
+ }
+
+ operation GenericAdjFail1() : Unit is Adj + Ctl {
+ Adjoint GenericFail(5,6);
+ }
+
+ operation GenericCtlFail1() : Unit is Adj + Ctl {
+ Controlled GenericFail( new Qubit[0], (5,6));
+ }
+
+ function Fail() : Unit {
+ fail "Always fail";
+ }
+
+ operation RecursionFail( a : Int) : Unit is Adj {
+ if ( a >= 1 )
+ {
+ RecursionFail(a-1);
+ }
+ else
+ {
+ Fail();
+ }
+ }
+
+ operation RecursionFail1() : Unit {
+ RecursionFail(2);
+ }
+
+ operation DivideBy0() : Int {
+ let z = 0;
+ return 3 / z;
+ }
+
+ operation AllGood() : Unit {
+ Microsoft.Quantum.Intrinsic.Message("All good!");
+ }
+
+ operation AllGood1() : Unit {
+ AllGood();
+ }
+}
\ No newline at end of file
diff --git a/src/Simulation/Simulators.Tests/StackTraceTests.cs b/src/Simulation/Simulators.Tests/StackTraceTests.cs
new file mode 100644
index 00000000000..e7d28b2d49b
--- /dev/null
+++ b/src/Simulation/Simulators.Tests/StackTraceTests.cs
@@ -0,0 +1,351 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using Xunit;
+
+using System;
+using System.Threading.Tasks;
+
+using Microsoft.Quantum.Simulation.Core;
+using Microsoft.Quantum.Simulation.Common;
+using Microsoft.Quantum.Simulation.Simulators.Tests.Circuits;
+using Microsoft.Quantum.Simulation.Simulators.Exceptions;
+using Xunit.Abstractions;
+using System.Text;
+using System.Collections.Generic;
+
+namespace Microsoft.Quantum.Simulation.Simulators.Tests
+{
+ public class StackTraceTests
+ {
+ const string namespacePrefix = "Microsoft.Quantum.Simulation.Simulators.Tests.Circuits.";
+
+ private readonly ITestOutputHelper output;
+ public StackTraceTests(ITestOutputHelper output)
+ {
+ this.output = output;
+ }
+
+ [Fact]
+ public void AlwaysFail4Test()
+ {
+ using (var sim = new QuantumSimulator())
+ {
+ try
+ {
+ QVoid res = AlwaysFail4.Run(sim).Result;
+ }
+ catch (AggregateException ex)
+ {
+ Assert.True(ex.InnerException is ExecutionFailException);
+
+ StackFrame[] stackFrames = sim.CallStack;
+
+ Assert.Equal(5, stackFrames.Length);
+
+ Assert.Equal(namespacePrefix + "AlwaysFail", stackFrames[0].Callable.FullName);
+ Assert.Equal(namespacePrefix + "AlwaysFail1", stackFrames[1].Callable.FullName);
+ Assert.Equal(namespacePrefix + "AlwaysFail2", stackFrames[2].Callable.FullName);
+ Assert.Equal(namespacePrefix + "AlwaysFail3", stackFrames[3].Callable.FullName);
+ Assert.Equal(namespacePrefix + "AlwaysFail4", stackFrames[4].Callable.FullName);
+
+ Assert.Equal(OperationFunctor.Controlled, stackFrames[0].Callable.Variant);
+ Assert.Equal(OperationFunctor.Controlled, stackFrames[1].Callable.Variant);
+ Assert.Equal(OperationFunctor.Body, stackFrames[2].Callable.Variant);
+ Assert.Equal(OperationFunctor.Adjoint, stackFrames[3].Callable.Variant);
+ Assert.Equal(OperationFunctor.Body, stackFrames[4].Callable.Variant);
+
+ Assert.Equal(14, stackFrames[2].FailedLineNumber);
+ Assert.Equal(21, stackFrames[4].FailedLineNumber);
+
+ // For Adjoint and Controlled we expect failedLineNumber to be equal to declarationStartLineNumber
+ Assert.Equal(stackFrames[0].DeclarationStartLineNumber, stackFrames[0].FailedLineNumber);
+ Assert.Equal(stackFrames[1].DeclarationStartLineNumber, stackFrames[1].FailedLineNumber);
+ Assert.Equal(stackFrames[3].DeclarationStartLineNumber, stackFrames[3].FailedLineNumber);
+
+ for (int i = 0; i < stackFrames.Length; ++i)
+ {
+ Assert.StartsWith(@"https://github.com/", stackFrames[i].GetURLFromPDB());
+ Assert.EndsWith($"#L{stackFrames[i].FailedLineNumber}", stackFrames[i].GetURLFromPDB());
+ }
+
+ StringBuilder builder = new StringBuilder();
+ builder.Append("13 ".PadLeft(PortablePDBEmbeddedFilesCache.lineNumberPaddingWidth));
+ builder.AppendLine(" operation AlwaysFail2() : Unit is Adj + Ctl {");
+ builder.Append("14 ".PadLeft(PortablePDBEmbeddedFilesCache.lineNumberPaddingWidth) + PortablePDBEmbeddedFilesCache.lineMarkPrefix);
+ builder.AppendLine(" Controlled AlwaysFail1(new Qubit[0],());");
+ builder.Append("15 ".PadLeft(PortablePDBEmbeddedFilesCache.lineNumberPaddingWidth));
+ builder.AppendLine(" }");
+ Assert.Equal(builder.ToString(), stackFrames[2].GetOperationSourceFromPDB());
+
+ for (int i = 0; i < stackFrames.Length; ++i)
+ {
+ output.WriteLine($"operation:{stackFrames[i].Callable.FullName}");
+ output.WriteLine(stackFrames[i].GetOperationSourceFromPDB());
+ }
+ }
+ }
+ }
+
+ [Fact]
+ public void GenericFail1Test()
+ {
+ ResourcesEstimator sim = new ResourcesEstimator();
+
+ {
+ try
+ {
+ QVoid res = sim.Execute(QVoid.Instance);
+ }
+ catch (ExecutionFailException)
+ {
+ StackFrame[] stackFrames = sim.CallStack;
+
+ Assert.Equal(3, stackFrames.Length);
+
+ Assert.Equal(namespacePrefix + "AlwaysFail", stackFrames[0].Callable.FullName);
+ Assert.Equal(namespacePrefix + "GenericFail", stackFrames[1].Callable.FullName);
+ Assert.Equal(namespacePrefix + "GenericFail1", stackFrames[2].Callable.FullName);
+
+ Assert.Equal(OperationFunctor.Body, stackFrames[0].Callable.Variant);
+ Assert.Equal(OperationFunctor.Body, stackFrames[1].Callable.Variant);
+ Assert.Equal(OperationFunctor.Body, stackFrames[2].Callable.Variant);
+
+ Assert.Equal(7, stackFrames[0].FailedLineNumber);
+ Assert.Equal(25, stackFrames[1].FailedLineNumber);
+ Assert.Equal(29, stackFrames[2].FailedLineNumber);
+ }
+ }
+
+ {
+ try
+ {
+ QVoid res = sim.Execute(QVoid.Instance);
+ }
+ catch (ExecutionFailException)
+ {
+ StackFrame[] stackFrames = sim.CallStack;
+
+ Assert.Equal(3, stackFrames.Length);
+
+ Assert.Equal(namespacePrefix + "AlwaysFail", stackFrames[0].Callable.FullName);
+ Assert.Equal(namespacePrefix + "GenericFail", stackFrames[1].Callable.FullName);
+ Assert.Equal(namespacePrefix + "GenericAdjFail1", stackFrames[2].Callable.FullName);
+
+ Assert.Equal(OperationFunctor.Adjoint, stackFrames[0].Callable.Variant);
+ Assert.Equal(OperationFunctor.Adjoint, stackFrames[1].Callable.Variant);
+ Assert.Equal(OperationFunctor.Body, stackFrames[2].Callable.Variant);
+
+ Assert.Equal(6, stackFrames[0].FailedLineNumber);
+ Assert.Equal(24, stackFrames[1].FailedLineNumber);
+ Assert.Equal(52, stackFrames[2].FailedLineNumber);
+ }
+ }
+
+ {
+ try
+ {
+ QVoid res = sim.Execute(QVoid.Instance);
+ }
+ catch (ExecutionFailException)
+ {
+ StackFrame[] stackFrames = sim.CallStack;
+
+ Assert.Equal(3, stackFrames.Length);
+
+ Assert.Equal(namespacePrefix + "AlwaysFail", stackFrames[0].Callable.FullName);
+ Assert.Equal(namespacePrefix + "GenericFail", stackFrames[1].Callable.FullName);
+ Assert.Equal(namespacePrefix + "GenericCtlFail1", stackFrames[2].Callable.FullName);
+
+ Assert.Equal(OperationFunctor.Controlled, stackFrames[0].Callable.Variant);
+ Assert.Equal(OperationFunctor.Controlled, stackFrames[1].Callable.Variant);
+ Assert.Equal(OperationFunctor.Body, stackFrames[2].Callable.Variant);
+
+ Assert.Equal(6, stackFrames[0].FailedLineNumber);
+ Assert.Equal(24, stackFrames[1].FailedLineNumber);
+ Assert.Equal(56, stackFrames[2].FailedLineNumber);
+ }
+ }
+ }
+
+ [Fact]
+ public void PartialFail1Test()
+ {
+ ToffoliSimulator sim = new ToffoliSimulator();
+
+ {
+ try
+ {
+ QVoid res = PartialFail1.Run(sim).Result;
+ }
+ catch (AggregateException ex)
+ {
+ Assert.True(ex.InnerException is ExecutionFailException);
+ StackFrame[] stackFrames = sim.CallStack;
+
+ Assert.Equal(3, stackFrames.Length);
+
+ Assert.Equal(namespacePrefix + "AlwaysFail", stackFrames[0].Callable.FullName);
+ Assert.Equal(namespacePrefix + "PartialFail", stackFrames[1].Callable.FullName);
+ Assert.Equal(namespacePrefix + "PartialFail1", stackFrames[2].Callable.FullName);
+
+ Assert.Equal(OperationFunctor.Body, stackFrames[0].Callable.Variant);
+ Assert.Equal(OperationFunctor.Body, stackFrames[1].Callable.Variant);
+ Assert.Equal(OperationFunctor.Body, stackFrames[2].Callable.Variant);
+
+ Assert.Equal(7, stackFrames[0].FailedLineNumber);
+ Assert.Equal(33, stackFrames[1].FailedLineNumber);
+ Assert.Equal(38, stackFrames[2].FailedLineNumber);
+ }
+ }
+
+ {
+ try
+ {
+ QVoid res = PartialAdjFail1.Run(sim).Result;
+ }
+ catch (AggregateException ex)
+ {
+ Assert.True(ex.InnerException is ExecutionFailException);
+ StackFrame[] stackFrames = sim.CallStack;
+
+ Assert.Equal(3, stackFrames.Length);
+
+ Assert.Equal(namespacePrefix + "AlwaysFail", stackFrames[0].Callable.FullName);
+ Assert.Equal(namespacePrefix + "PartialFail", stackFrames[1].Callable.FullName);
+ Assert.Equal(namespacePrefix + "PartialAdjFail1", stackFrames[2].Callable.FullName);
+
+ Assert.Equal(OperationFunctor.Adjoint, stackFrames[0].Callable.Variant);
+ Assert.Equal(OperationFunctor.Adjoint, stackFrames[1].Callable.Variant);
+ Assert.Equal(OperationFunctor.Body, stackFrames[2].Callable.Variant);
+
+ Assert.Equal(6, stackFrames[0].FailedLineNumber);
+ Assert.Equal(32, stackFrames[1].FailedLineNumber);
+ Assert.Equal(43, stackFrames[2].FailedLineNumber);
+ }
+ }
+
+ {
+ try
+ {
+ QVoid res = PartialCtlFail1.Run(sim).Result;
+ }
+ catch (AggregateException ex)
+ {
+ Assert.True(ex.InnerException is ExecutionFailException);
+ StackFrame[] stackFrames = sim.CallStack;
+
+ Assert.Equal(3, stackFrames.Length);
+
+ Assert.Equal(namespacePrefix + "AlwaysFail", stackFrames[0].Callable.FullName);
+ Assert.Equal(namespacePrefix + "PartialFail", stackFrames[1].Callable.FullName);
+ Assert.Equal(namespacePrefix + "PartialCtlFail1", stackFrames[2].Callable.FullName);
+
+ Assert.Equal(OperationFunctor.Controlled, stackFrames[0].Callable.Variant);
+ Assert.Equal(OperationFunctor.Controlled, stackFrames[1].Callable.Variant);
+ Assert.Equal(OperationFunctor.Body, stackFrames[2].Callable.Variant);
+
+ Assert.Equal(6, stackFrames[0].FailedLineNumber);
+ Assert.Equal(32, stackFrames[1].FailedLineNumber);
+ Assert.Equal(48, stackFrames[2].FailedLineNumber);
+ }
+ }
+ }
+
+ [Fact]
+ public void RecursionFail1Test()
+ {
+ ToffoliSimulator sim = new ToffoliSimulator();
+
+ {
+ StackTraceCollector sc = new StackTraceCollector(sim);
+ ICallable op = sim.Get();
+ try
+ {
+ QVoid res = op.Apply(QVoid.Instance);
+ }
+ catch (ExecutionFailException)
+ {
+ StackFrame[] stackFrames = sc.CallStack;
+
+ Assert.Equal(4, stackFrames.Length);
+
+ Assert.Equal(namespacePrefix + "RecursionFail", stackFrames[0].Callable.FullName);
+ Assert.Equal(namespacePrefix + "RecursionFail", stackFrames[1].Callable.FullName);
+ Assert.Equal(namespacePrefix + "RecursionFail", stackFrames[2].Callable.FullName);
+ Assert.Equal(namespacePrefix + "RecursionFail1", stackFrames[3].Callable.FullName);
+
+ Assert.Equal(OperationFunctor.Body, stackFrames[0].Callable.Variant);
+ Assert.Equal(OperationFunctor.Body, stackFrames[1].Callable.Variant);
+ Assert.Equal(OperationFunctor.Body, stackFrames[2].Callable.Variant);
+ Assert.Equal(OperationFunctor.Body, stackFrames[3].Callable.Variant);
+
+ Assert.Equal(70, stackFrames[0].FailedLineNumber);
+ Assert.Equal(66, stackFrames[1].FailedLineNumber);
+ Assert.Equal(66, stackFrames[2].FailedLineNumber);
+ Assert.Equal(75, stackFrames[3].FailedLineNumber);
+ }
+ }
+ }
+
+ [Fact]
+ public void DivideByZeroTest()
+ {
+ ToffoliSimulator sim = new ToffoliSimulator();
+
+ try
+ {
+ sim.Execute(QVoid.Instance);
+ }
+ catch (Exception)
+ {
+ StackFrame[] stackFrames = sim.CallStack;
+
+ Assert.Single(stackFrames);
+ Assert.Equal(namespacePrefix + "DivideBy0", stackFrames[0].Callable.FullName);
+ }
+ }
+
+ [Fact]
+ public void AllGoodTest()
+ {
+ ToffoliSimulator sim = new ToffoliSimulator();
+
+ QVoid res = sim.Execute(QVoid.Instance);
+ StackFrame[] stackFrames = sim.CallStack;
+ Assert.Null(stackFrames);
+ }
+
+ [Fact]
+ public void UrlMappingTest()
+ {
+ const string rawUrl = @"https://raw.githubusercontent.com/microsoft/qsharp-runtime/af6262c05522d645d0a0952272443e84eeab677a/src/Xunit/TestCaseDiscoverer.cs";
+ const string expectedURL = @"https://github.com/microsoft/qsharp-runtime/blob/af6262c05522d645d0a0952272443e84eeab677a/src/Xunit/TestCaseDiscoverer.cs#L13";
+ Assert.Equal(expectedURL, PortablePdbSymbolReader.TryFormatGitHubUrl(rawUrl, 13));
+ }
+
+ [Fact]
+ public void ErrorLogTest()
+ {
+ ToffoliSimulator sim = new ToffoliSimulator();
+
+ var logs = new List();
+ sim.OnLog += (msg) => logs.Add(msg);
+ try
+ {
+ QVoid res = sim.Execute(QVoid.Instance);
+ }
+ catch (ExecutionFailException)
+ {
+ Assert.Equal(7, logs.Count);
+ Assert.StartsWith("Unhandled exception. Microsoft.Quantum.Simulation.Core.ExecutionFailException: Always fail", logs[0]);
+ Assert.StartsWith(" ---> Microsoft.Quantum.Simulation.Simulators.Tests.Circuits.AlwaysFail", logs[1]);
+ Assert.StartsWith(" at Microsoft.Quantum.Simulation.Simulators.Tests.Circuits.AlwaysFail1 on", logs[2]);
+ Assert.StartsWith(" at Microsoft.Quantum.Simulation.Simulators.Tests.Circuits.AlwaysFail2 on", logs[3]);
+ Assert.StartsWith(" at Microsoft.Quantum.Simulation.Simulators.Tests.Circuits.AlwaysFail3 on", logs[4]);
+ Assert.StartsWith(" at Microsoft.Quantum.Simulation.Simulators.Tests.Circuits.AlwaysFail4 on", logs[5]);
+ Assert.Equal("", logs[6]);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Simulation/Simulators.Tests/Tests.Microsoft.Quantum.Simulation.Simulators.csproj b/src/Simulation/Simulators.Tests/Tests.Microsoft.Quantum.Simulation.Simulators.csproj
index 64d3e4bb24e..565987988c9 100644
--- a/src/Simulation/Simulators.Tests/Tests.Microsoft.Quantum.Simulation.Simulators.csproj
+++ b/src/Simulation/Simulators.Tests/Tests.Microsoft.Quantum.Simulation.Simulators.csproj
@@ -1,6 +1,7 @@
+
netcoreapp3.0