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
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
75 changes: 51 additions & 24 deletions src/Simulation/Common/SimulatorBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

using Microsoft.Quantum.Simulation.Core;

#nullable enable

namespace Microsoft.Quantum.Simulation.Common
{
/// <summary>
Expand All @@ -19,16 +21,17 @@ namespace Microsoft.Quantum.Simulation.Common
/// </summary>
public abstract class SimulatorBase : AbstractFactory<AbstractCallable>, IOperationFactory
{
public event Action<ICallable, IApplyData> OnOperationStart = null;
public event Action<ICallable, IApplyData> OnOperationEnd = null;
public event Action<System.Runtime.ExceptionServices.ExceptionDispatchInfo> OnFail = null;
public event Action<long> OnAllocateQubits = null;
public event Action<IQArray<Qubit>> OnReleaseQubits = null;
public event Action<long> OnBorrowQubits = null;
public event Action<IQArray<Qubit>> OnReturnQubits = null;
public event Action<string> OnLog = null;

public IQubitManager QubitManager { get; }
public event Action<ICallable, IApplyData>? OnOperationStart = null;
public event Action<ICallable, IApplyData>? OnOperationEnd = null;
public event Action<System.Runtime.ExceptionServices.ExceptionDispatchInfo>? OnFail = null;
public event Action<long>? OnAllocateQubits = null;
public event Action<IQArray<Qubit>>? OnReleaseQubits = null;
public event Action<long>? OnBorrowQubits = null;
public event Action<IQArray<Qubit>>? OnReturnQubits = null;
public event Action<string>? OnLog = null;
public event Action<Exception, IEnumerable<StackFrame>>? OnException = null;

public IQubitManager? QubitManager { get; }

public abstract string Name { get; }

Expand All @@ -42,15 +45,16 @@ public abstract class SimulatorBase : AbstractFactory<AbstractCallable>, IOperat
/// Only Q# operations are tracked and reported in the stack trace. Q# functions or calls from
/// classical hosts like C# or Python are not included.
/// </remarks>
public StackFrame[] CallStack { get; private set; }
public StackFrame[]? CallStack { get; private set; }

public SimulatorBase(IQubitManager qubitManager = null)
public SimulatorBase(IQubitManager? qubitManager = null)
{
this.QubitManager = qubitManager;

this.InitBuiltinOperations(this.GetType());

EnableLogToConsole();
EnableExceptionPrinting();

if (this.QubitManager != null)
{
Expand Down Expand Up @@ -92,7 +96,7 @@ public override AbstractCallable CreateInstance(Type t)
return new GenericCallable(this, t);
}

AbstractCallable result = null;
AbstractCallable? result = null;

t = t.GetNativeImplementation() ?? t;

Expand All @@ -110,6 +114,19 @@ public override AbstractCallable CreateInstance(Type t)
return result;
}

protected void WriteStackTraceToLog(Exception exception, IEnumerable<StackFrame> callStack)
{
OnLog?.Invoke($"Unhandled exception. {exception.GetType().FullName}: {exception.Message}");
var first = true;
foreach (var sf in callStack)
{
var msg = (first ? " ---> " : " at ") + sf.ToStringWithBestSourceLocation();
OnLog?.Invoke(msg);
first = false;
}
OnLog?.Invoke("");
}

public virtual O Execute<T, I, O>(I args) where T : AbstractCallable, ICallable
{
StackTraceCollector stackTraceCollector = new StackTraceCollector(this);
Expand All @@ -124,16 +141,7 @@ public virtual O Execute<T, I, O>(I args) where T : AbstractCallable, ICallable
catch (Exception e) // Dumps q# call-stack in case of exception if CallStack tracking was enabled
{
this.CallStack = stackTraceCollector.CallStack;
OnLog?.Invoke($"Unhandled exception. {e.GetType().FullName}: {e.Message}");
bool first = true;
foreach (StackFrame sf in this.CallStack)
{
var msg = (first ? " ---> " : " at ") + sf.ToStringWithBestSourceLocation();
OnLog?.Invoke(msg);
first = false;
}
OnLog?.Invoke("");

this.OnException?.Invoke(e, this.CallStack);
throw;
}
finally
Expand Down Expand Up @@ -165,6 +173,25 @@ public void DisableLogToConsole()
OnLog -= Console.WriteLine;
}

/// <summary>
/// Adds an event to the OnException event that logs stack traces
/// as plain text via the OnLog event (e.g.: for console output).
/// </summary>
public void EnableExceptionPrinting()
{
OnException += WriteStackTraceToLog;
}


/// <summary>
/// Disables default handling of stack traces, such that stack
/// traces are not written to the OnLog event handler.
/// </summary>
public void DisableExceptionPrinting()
{
OnException -= WriteStackTraceToLog;
}

/// <summary>
/// Verifies that the Qubit can be used as part of an operation.
/// </summary>
Expand Down Expand Up @@ -210,7 +237,7 @@ public void CheckQubits(IQArray<Qubit> qubits, string arrayName)
public class Allocate : Intrinsic.Allocate
{
private SimulatorBase sim;
private IQubitManager manager;
private IQubitManager? manager;

public Allocate(SimulatorBase m) : base(m)
{
Expand Down
38 changes: 18 additions & 20 deletions src/Simulation/Common/StackTrace.cs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,22 @@ public override string ToString()
return string.Format(messageFormat, Callable.FullName, $"{SourceFile}:line {FailedLineNumber}");
}

/// <summary>
/// Gets the best possible source location for this stack frame.
/// If the source is not available on local machine, the source
// location will be replaced by a URL pointing to GitHub repository.
/// </summary>
/// <remarks>
/// This is more costly than <see cref="SourceFile"/> because it
/// checks if source file exists on disk.
/// If the file does not exist it calls <see cref="GetURLFromPDB"/> to get the URL
/// which is also more costly than <see cref="SourceFile"/>.
/// </remarks>
public virtual string GetBestSourceLocation() =>
System.IO.File.Exists(SourceFile)
? SourceFile
: GetURLFromPDB() ?? SourceFile;

/// <summary>
/// The same as <see cref="ToString"/>, but tries to point to best source location.
/// If the source is not available on local machine, source location will be replaced
Expand All @@ -97,26 +113,8 @@ public override string ToString()
/// If the file does not exist it calls <see cref="GetURLFromPDB"/> to get the URL
/// which is also more costly than <see cref="ToString"/>.
/// </summary>
public virtual string ToStringWithBestSourceLocation()
{
string message = ToString();
if (System.IO.File.Exists(SourceFile))
{
return message;
}
else
{
string url = GetURLFromPDB();
if (url == null)
{
return message;
}
else
{
return string.Format(messageFormat, Callable.FullName, url);
}
}
}
public virtual string ToStringWithBestSourceLocation() =>
string.Format(messageFormat, Callable.FullName, $"{GetBestSourceLocation()}:line {FailedLineNumber}");

/// <summary>
/// Finds correspondence between Q# and C# stack frames and populates Q# stack frame information from C# stack frames
Expand Down