diff --git a/src/Core/Core.csproj b/src/Core/Core.csproj index bd0ac90cff..3e8942e5af 100644 --- a/src/Core/Core.csproj +++ b/src/Core/Core.csproj @@ -34,9 +34,9 @@ - - - + + + diff --git a/src/Jupyter/Extensions.cs b/src/Jupyter/Extensions.cs index 7ad662401c..624b4935f6 100644 --- a/src/Jupyter/Extensions.cs +++ b/src/Jupyter/Extensions.cs @@ -9,6 +9,7 @@ using Microsoft.Jupyter.Core; using Microsoft.Jupyter.Core.Protocol; using Microsoft.Quantum.IQSharp.Common; +using Microsoft.Quantum.Simulation.Common; using Microsoft.Quantum.Simulation.Core; using Microsoft.Quantum.Simulation.Simulators; @@ -153,5 +154,20 @@ public static QuantumSimulator WithJupyterDisplay(this QuantumSimulator simulato return simulator; } + + public static T WithStackTraceDisplay(this T simulator, IChannel channel) + where T: SimulatorBase + { + simulator.DisableExceptionPrinting(); + simulator.OnException += (exception, stackTrace) => + { + channel.Display(new DisplayableException + { + Exception = exception, + StackTrace = stackTrace + }); + }; + return simulator; + } } } diff --git a/src/Jupyter/IQSharpEngine.cs b/src/Jupyter/IQSharpEngine.cs index 8c2a194b84..f277e61519 100644 --- a/src/Jupyter/IQSharpEngine.cs +++ b/src/Jupyter/IQSharpEngine.cs @@ -54,6 +54,8 @@ IShellRouter shellRouter RegisterDisplayEncoder(new StateVectorToTextResultEncoder(configurationSource)); RegisterDisplayEncoder(new DataTableToHtmlEncoder()); RegisterDisplayEncoder(new DataTableToTextEncoder()); + RegisterDisplayEncoder(new DisplayableExceptionToHtmlEncoder()); + RegisterDisplayEncoder(new DisplayableExceptionToTextEncoder()); RegisterJsonEncoder(JsonConverters.AllConverters); RegisterSymbolResolver(this.SymbolsResolver); @@ -119,3 +121,4 @@ public override ExecutionResult ExecuteMundane(string input, IChannel channel) } } } + diff --git a/src/Jupyter/Magic/EstimateMagic.cs b/src/Jupyter/Magic/EstimateMagic.cs index ae8e2c668b..c2016c3c4b 100644 --- a/src/Jupyter/Magic/EstimateMagic.cs +++ b/src/Jupyter/Magic/EstimateMagic.cs @@ -56,7 +56,7 @@ public async Task RunAsync(string input, IChannel channel) var symbol = SymbolResolver.Resolve(name) as IQSharpSymbol; if (symbol == null) throw new InvalidOperationException($"Invalid operation name: {name}"); - var qsim = new ResourcesEstimator(); + var qsim = new ResourcesEstimator().WithStackTraceDisplay(channel); qsim.DisableLogToConsole(); await symbol.Operation.RunAsync(qsim, args); diff --git a/src/Jupyter/Magic/Simulate.cs b/src/Jupyter/Magic/Simulate.cs index 495dcadce6..837e897372 100644 --- a/src/Jupyter/Magic/Simulate.cs +++ b/src/Jupyter/Magic/Simulate.cs @@ -59,7 +59,9 @@ public async Task RunAsync(string input, IChannel channel) var symbol = SymbolResolver.Resolve(name) as IQSharpSymbol; if (symbol == null) throw new InvalidOperationException($"Invalid operation name: {name}"); - using var qsim = new QuantumSimulator().WithJupyterDisplay(channel, ConfigurationSource); + using var qsim = new QuantumSimulator() + .WithJupyterDisplay(channel, ConfigurationSource) + .WithStackTraceDisplay(channel); var value = await symbol.Operation.RunAsync(qsim, args); return value.ToExecutionResult(); } diff --git a/src/Jupyter/Magic/ToffoliMagic.cs b/src/Jupyter/Magic/ToffoliMagic.cs index b63ec1cb02..4185b1c4a3 100644 --- a/src/Jupyter/Magic/ToffoliMagic.cs +++ b/src/Jupyter/Magic/ToffoliMagic.cs @@ -47,7 +47,7 @@ public async Task RunAsync(string input, IChannel channel) var symbol = SymbolResolver.Resolve(name) as IQSharpSymbol; if (symbol == null) throw new InvalidOperationException($"Invalid operation name: {name}"); - var qsim = new ToffoliSimulator(); + var qsim = new ToffoliSimulator().WithStackTraceDisplay(channel); qsim.DisableLogToConsole(); qsim.OnLog += channel.Stdout; diff --git a/src/Jupyter/Visualization/DisplayableExceptionEncoders.cs b/src/Jupyter/Visualization/DisplayableExceptionEncoders.cs new file mode 100644 index 0000000000..c7a28e7ec2 --- /dev/null +++ b/src/Jupyter/Visualization/DisplayableExceptionEncoders.cs @@ -0,0 +1,123 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +#nullable enable + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Data; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using Microsoft.Jupyter.Core; +using Microsoft.Quantum.QsCompiler.SyntaxTree; +using Microsoft.Quantum.Simulation.Common; +using Microsoft.Quantum.Simulation.Core; +using Microsoft.Quantum.Simulation.Simulators; + +namespace Microsoft.Quantum.IQSharp +{ + internal struct DisplayableException + { + public Exception Exception; + public IEnumerable StackTrace; + + public string Header => + $"Unhandled exception. {Exception.GetType().FullName}: {Exception.Message}"; + } + + internal static class StackFrameExtensions + { + internal static string ToFriendlyName(this ICallable callable) + { + var fullName = callable.FullName; + return fullName.StartsWith(Snippets.SNIPPETS_NAMESPACE) + ? fullName.Substring(Snippets.SNIPPETS_NAMESPACE.Length + 1) + : fullName; + } + + internal static string ToSourceLink(this StackFrame frame) => + Regex.Match(frame.SourceFile, "snippet_[0-9]*.qs$").Success + ? "(notebook)" + : $"{frame.SourceFile}:{frame.FailedLineNumber}"; + } + + /// + /// Encodes exceptions augmented with Q# metadata into HTML tables. + /// + public class DisplayableExceptionToHtmlEncoder : IResultEncoder + { + /// + public string MimeType => MimeTypes.Html; + + /// + public EncodedData? Encode(object displayable) + { + if (displayable is DisplayableException ex) + { + var rows = ex + .StackTrace + .Select(frame => $@" + + {frame.ToSourceLink()} + {frame.Callable.ToFriendlyName()} + + "); + var table = $@" +
+ + Unhandled exception of type {ex.Exception.GetType().FullName}: {ex.Exception.Message} + + + + + + + + + + + {String.Join("\n", rows)} + +
SourceCallable
+
+ "; + return table.ToEncodedData(); + } + else return null; + } + } + + /// + /// Encodes exceptions augmented with Q# metadata into plain text + /// tables. + /// + public class DisplayableExceptionToTextEncoder : IResultEncoder + { + /// + public string MimeType => MimeTypes.PlainText; + + /// + public EncodedData? Encode(object displayable) + { + if (displayable is DisplayableException ex) + { + var builder = new StringBuilder(); + builder.AppendLine(ex.Header); + var first = true; + foreach (var frame in ex.StackTrace) + { + builder.AppendLine( + (first ? " ---> " : " at ") + + frame.ToStringWithBestSourceLocation() + ); + first = false; + } + return builder.ToString().ToEncodedData(); + } + else return null; + } + } + +} diff --git a/src/Tool/appsettings.json b/src/Tool/appsettings.json index 4530b0a131..68275089e3 100644 --- a/src/Tool/appsettings.json +++ b/src/Tool/appsettings.json @@ -6,18 +6,18 @@ }, "AllowedHosts": "*", "DefaultPackageVersions": [ - "Microsoft.Quantum.Compiler::0.10.2001.2831", + "Microsoft.Quantum.Compiler::0.10.2003.1102-beta", - "Microsoft.Quantum.CsharpGeneration::0.10.2001.2831", - "Microsoft.Quantum.Development.Kit::0.10.2001.2831", - "Microsoft.Quantum.Simulators::0.10.2001.2831", - "Microsoft.Quantum.Xunit::0.10.2001.2831", + "Microsoft.Quantum.CsharpGeneration::0.10.2003.1102-beta", + "Microsoft.Quantum.Development.Kit::0.10.2003.1102-beta", + "Microsoft.Quantum.Simulators::0.10.2003.1102-beta", + "Microsoft.Quantum.Xunit::0.10.2003.1102-beta", - "Microsoft.Quantum.Standard::0.10.2001.2831", - "Microsoft.Quantum.Chemistry::0.10.2001.2831", - "Microsoft.Quantum.Chemistry.Jupyter::0.10.2001.2831", - "Microsoft.Quantum.Numerics::0.10.2001.2831", + "Microsoft.Quantum.Standard::0.10.2003.1102-beta", + "Microsoft.Quantum.Chemistry::0.10.2003.1102-beta", + "Microsoft.Quantum.Chemistry.Jupyter::0.10.2003.1102-beta", + "Microsoft.Quantum.Numerics::0.10.2003.1102-beta", - "Microsoft.Quantum.Research::0.10.2001.2831" + "Microsoft.Quantum.Research::0.10.2003.1102-beta" ] }