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
47 changes: 46 additions & 1 deletion src/Jupyter/ChannelWithNewLines.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,27 +9,72 @@
namespace Microsoft.Quantum.IQSharp.Jupyter
{
/// <summary>
/// This is a Jupyter Core IChannel that wraps an existing IChannel and
/// This is a Jupyter Core IChannel that wraps an existing IChannel and
/// adds NewLine symbols (\\n)
/// to every message that gets logged to Stdout and Stderror.
/// </summary>
public class ChannelWithNewLines : IChannel
{
/// <summary>
/// The existing channel that this channel wraps with new lines.
/// </summary>
public IChannel BaseChannel { get; }

/// <summary>
/// Constructs a new channel, given a base channel to be wrapped
/// with newlines.
/// </summary>
public ChannelWithNewLines(IChannel original)
{
BaseChannel = original;
}

/// <summary>
/// Formats a given message for display to stdout or stderr.
/// </summary>
/// <param name="msg">The message to be formatted.</param>
/// <returns>
/// <paramref name="msg" />, formatted with a trailing newline
/// (<c>\n</c>).
/// </returns>
public static string Format(string msg) => $"{msg}\n";

/// <summary>
/// Writes a given message to the base channel's standard output,
/// but with a trailing newline appended.
/// </summary>
/// <param name="message">The message to be written.</param>
public void Stdout(string message) => BaseChannel?.Stdout(Format(message));

/// <summary>
/// Writes a given message to the base channel's standard error,
/// but with a trailing newline appended.
/// </summary>
/// <param name="message">The message to be written.</param>
public void Stderr(string message) => BaseChannel?.Stderr(Format(message));

/// <summary>
/// Displays a given object using the base channel.
/// </summary>
/// <param name="displayable">The object to be displayed.</param>
/// <remarks>
/// Note that no newline is appended by this method, as the
/// displayable object need not be a string.
/// </remarks>
public void Display(object displayable) => BaseChannel?.Display(displayable);

/// <summary>
/// Displays a given object using the base channel, allowing for
/// future updates.
/// </summary>
/// <param name="displayable">The object to be displayed.</param>
/// <remarks>
/// Note that no newline is appended by this method, as the
/// displayable object need not be a string.
/// </remarks>
/// <returns>
/// An object that can be used to update the display in the future.
/// </returns>
public IUpdatableDisplay DisplayUpdatable(object displayable) => BaseChannel?.DisplayUpdatable(displayable);
}
}
45 changes: 45 additions & 0 deletions src/Jupyter/ConfigurationSource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,35 +13,76 @@
namespace Microsoft.Quantum.IQSharp.Jupyter
{

/// <summary>
/// A service that controls configuration options, such as those set
/// by preferences, the <c>%config</c> magic command, and so forth.
/// </summary>
public interface IConfigurationSource
{
/// <summary>
/// A dictionary of configuration options available from this
/// source.
/// </summary>
IDictionary<string, JToken> Configuration { get; }

/// <summary>
/// Persists the current configuration to disk such that a future
/// kernel session launched for the same notebook has the given
/// configuration.
/// </summary>
void Persist();

private T GetOptionOrDefault<T>(string optionName, T defaultValue) =>
Configuration.TryGetValue(optionName, out var token)
? token.ToObject<T>() ?? defaultValue
: defaultValue;

/// <summary>
/// The labeling convention to be used when labeling computational
/// basis states (bit string, little-endian or big-endian).
/// </summary>
public BasisStateLabelingConvention BasisStateLabelingConvention =>
GetOptionOrDefault("dump.basisStateLabelingConvention", BasisStateLabelingConvention.LittleEndian);

/// <summary>
/// Whether small amplitudes should be truncated when dumping
/// states.
/// </summary>
public bool TruncateSmallAmplitudes =>
GetOptionOrDefault("dump.truncateSmallAmplitudes", false);

/// <summary>
/// The threshold for truncating measurement probabilities when
/// dumping states. Computational basis states whose measurement
/// probabilities (i.e: squared magnitudes) are below this threshold
/// are subject to truncation when
/// <see cref="Microsoft.Quantum.IQSharp.Jupyter.IConfigurationSource.TruncateSmallAmplitudes" />
/// is <c>true</c>.
/// </summary>
public double TruncationThreshold =>
GetOptionOrDefault("dump.truncationThreshold", 1e-10);
}

/// <summary>
/// An implementation of the
/// <see cref="Microsoft.Quantum.IQSharp.Jupyter.IConfigurationSource" />
/// service interface that loads and persists configuration values from
/// and to a local JSON file.
/// </summary>
public class ConfigurationSource : IConfigurationSource
{
/// <inheritdoc />
public IDictionary<string, JToken> Configuration => _Configuration;
private readonly IDictionary<string, JToken> _Configuration;

private string ConfigPath =>
Path.Join(Directory.GetCurrentDirectory(), ".iqsharp-config.json");

/// <summary>
/// Constructs a new configuration source, loading initial
/// configuration options from the file <c>.iqsharp-config.json</c>,
/// if that file exists.
/// </summary>
public ConfigurationSource(bool skipLoading = false)
{
// Try loading configuration from a JSON file in the current working
Expand All @@ -65,6 +106,10 @@ public ConfigurationSource(bool skipLoading = false)
}
}

/// <summary>
/// Persists the current configuration to
/// <c>.iqsharp-config.json</c>.
/// </summary>
public void Persist()
{
// Try writing the configuration back to JSON.
Expand Down
54 changes: 54 additions & 0 deletions src/Jupyter/Extensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@

namespace Microsoft.Quantum.IQSharp.Jupyter
{
/// <summary>
/// Extension methods to be used with various IQ# and Jupyter objects.
/// </summary>
public static class Extensions
{

Expand All @@ -24,13 +27,22 @@ public static class Extensions
public static ChannelWithNewLines WithNewLines(this IChannel original) =>
(original is ChannelWithNewLines ch) ? ch : new ChannelWithNewLines(original);

/// <summary>
/// Adds services required for the IQ# kernel to a given service
/// collection.
/// </summary>
public static void AddIQSharpKernel(this IServiceCollection services)
{
services.AddSingleton<ISymbolResolver, Jupyter.SymbolResolver>();
services.AddSingleton<IExecutionEngine, Jupyter.IQSharpEngine>();
services.AddSingleton<IConfigurationSource, ConfigurationSource>();
}

/// <summary>
/// Given a configuration source, applies an action if that
/// configuration source defines a value for a particular
/// configuration key.
/// </summary>
internal static IConfigurationSource ApplyConfiguration<T>(
this IConfigurationSource configurationSource,
string keyName, Action<T> action
Expand All @@ -52,6 +64,15 @@ internal static IConfigurationSource ApplyConfiguration<T>(
(1L << 10, "KiB")
}.ToImmutableList();

/// <summary>
/// Given a number of bytes, formats that number as a human
/// readable string by appending unit suffixes (i.e.: indicating
/// kilobytes, megabytes, etc.).
/// </summary>
/// <param name="nBytes">A number of bytes to be formatted.</param>
/// <returns>
/// The number of bytes formatted as a human-readable string.
/// </returns>
public static string ToHumanReadableBytes(this long nBytes)
{
foreach (var (scale, suffix) in byteSuffixes)
Expand All @@ -66,11 +87,42 @@ public static string ToHumanReadableBytes(this long nBytes)
return $"{nBytes} B";
}

/// <summary>
/// Adds functionality to a given quantum simulator to display
/// diagnostic output with rich Jupyter formatting.
/// </summary>
/// <param name="simulator">
/// The simulator to be augmented with Jupyter display
/// functionality.
/// </param>
/// <param name="channel">
/// The Jupyter display channel to be used to display diagnostic
/// output.
/// </param>
/// <param name="configurationSource">
/// A source of configuration options to be used to set display
/// preferences. Typically, this will be provided by the service
/// provider configured when an execution engine is constructed.
/// </param>
/// <returns>
/// The value of <paramref name="simulator" />.
/// </returns>
public static QuantumSimulator WithJupyterDisplay(this QuantumSimulator simulator, IChannel channel, IConfigurationSource configurationSource)
{
// First, we disable console-based logging so as to not
// duplicate messages.
simulator.DisableLogToConsole();
// Next, we attach the display channel's standard output handling
// to the log event.
simulator.OnLog += channel.Stdout;

// Next, we register the generic version of the DumpMachine callable
// as an ICallable with the simulator. Below, we'll provide our
// implementation of DumpMachine with the channel and configuration
// source we got as arguments. At the moment, there's no direct
// way to do this when registering an implementation, so we instead
// get an instance of the newly registered callable and set its
// properties accordingly.
simulator.Register(
typeof(Diagnostics.DumpMachine<>), typeof(JupyterDumpMachine<>),
signature: typeof(ICallable)
Expand All @@ -84,6 +136,8 @@ public static QuantumSimulator WithJupyterDisplay(this QuantumSimulator simulato
((JupyterDumpMachine<string>)concreteOp).Channel = channel;
((JupyterDumpMachine<string>)concreteOp).ConfigurationSource = configurationSource;

// Next, we repeat the whole process for DumpRegister instead of
// DumpMachine.
simulator.Register(
typeof(Diagnostics.DumpRegister<>), typeof(JupyterDumpRegister<>),
signature: typeof(ICallable)
Expand Down
1 change: 1 addition & 0 deletions src/Jupyter/Jupyter.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
<PlatformTarget>x64</PlatformTarget>
<RootNamespace>Microsoft.Quantum.IQSharp.Jupyter</RootNamespace>
<AssemblyName>Microsoft.Quantum.IQSharp.Jupyter</AssemblyName>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>

<ItemGroup>
Expand Down
6 changes: 5 additions & 1 deletion src/Jupyter/KernelProperties.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ namespace Microsoft.Quantum.IQSharp.Jupyter
/// </summary>
public static class Constants
{
/// <summary>
/// The properties for this kernel (e.g. versions, language name,
/// etc.).
/// </summary>
public static readonly KernelProperties IQSharpKernelProperties = new KernelProperties
{
FriendlyName = "Q#",
Expand All @@ -18,7 +22,7 @@ public static class Constants
DisplayName = "Q#",

LanguageName = "qsharp",
LanguageVersion = "0.4",
LanguageVersion = "0.10",
LanguageMimeType = "text/x-qsharp",
LanguageFileExtension = ".qs",

Expand Down
22 changes: 21 additions & 1 deletion src/Jupyter/Magic/AbstractMagic.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,14 @@

namespace Microsoft.Quantum.IQSharp.Jupyter
{
/// <summary>
/// Abstract base class for IQ# magic symbols.
/// </summary>
public abstract class AbstractMagic : MagicSymbol
{
/// <summary>
/// Constructs a new magic symbol given its name and documentation.
/// </summary>
public AbstractMagic(string keyword, Documentation docs)
{
this.Name = $"%{keyword}";
Expand All @@ -21,7 +27,14 @@ public AbstractMagic(string keyword, Documentation docs)
this.Execute = SafeExecute(this.Run);
}

public Func<string, IChannel, ExecutionResult> SafeExecute(Func<string, IChannel, ExecutionResult> magic) =>
/// <summary>
/// Given a function representing the execution of a magic command,
/// returns a new function that executes <paramref name="magic" />
/// and catches any exceptions that occur during execution. The
/// returned execution function displays the given exceptions to its
/// display channel.
/// </summary>
public Func<string, IChannel, ExecutionResult> SafeExecute(Func<string, IChannel, ExecutionResult> magic) =>
(input, channel) =>
{
channel = channel.WithNewLines();
Expand All @@ -47,6 +60,10 @@ public Func<string, IChannel, ExecutionResult> SafeExecute(Func<string, IChannel
}
};

/// <summary>
/// Parses the input to a magic command, interpreting the input as
/// a name followed by a JSON-serialized dictionary.
/// </summary>
public static (string, Dictionary<string, string>) ParseInput(string input)
{
if (input == null) return (string.Empty, new Dictionary<string, string> { });
Expand All @@ -61,6 +78,9 @@ public static (string, Dictionary<string, string>) ParseInput(string input)
return (name, args);
}

/// <summary>
/// A method to be run when the magic command is executed.
/// </summary>
public abstract ExecutionResult Run(string input, IChannel channel);
}
}
13 changes: 13 additions & 0 deletions src/Jupyter/Magic/ConfigMagic.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,15 @@

namespace Microsoft.Quantum.IQSharp.Jupyter
{
/// <summary>
/// A magic command that sets or queries configuration options.
/// </summary>
public class ConfigMagic : AbstractMagic
{
/// <summary>
/// Constructs a magic command that sets or queries configuration
/// options using a given configuration source.
/// </summary>
public ConfigMagic(IConfigurationSource configurationSource) : base(
"config",
new Documentation {
Expand All @@ -24,8 +31,14 @@ public ConfigMagic(IConfigurationSource configurationSource) : base(
this.ConfigurationSource = configurationSource;
}

/// <summary>
/// The configuration source which this magic command queries or
/// sets configuration options in.
/// </summary>
public IConfigurationSource ConfigurationSource { get; }


/// <inheritdoc />
public override ExecutionResult Run(string? input, IChannel channel)
{
// If we didn't get any input, treat it as a query.
Expand Down
Loading