forked from microsoft/Quantum
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add a Q# quantum state visualizer (microsoft#221)
* Import visual debugger sample Co-authored-by: Chris Granade <[email protected]> * Fix colors in Edge * Use OnOperationStart and OnOperationEnd * Show operation functor names * Wait for input before starting first operation * Pass the simulator to the debugger constructor * Show operations as a nested call stack * Fix state chart labels * Always show y-axis between -1 and 1 * Re-send history when new client connects * Make operation call stack look a little nicer * Don't show unit return values * Update operation stack font and indent guides * Use 2-column layout for operations and chart * Remove flexbox to fix chart resizing issues * Show arrow pointing to the current operation * Move VisualDebugger.cs to top folder * Allow going backward in time * Show different icons for next op/op just finished * Fix padding at bottom of operation list * Add Grover's search example * Clean up onOperationStarted handler * Use SignalR hub method for advancing simulator * Rename VisualizationHub to match VisualDebugger * Move state dumper to VisualDebugger.cs * Decrease class/method visibility when possible * Fix emoji arrow for next operation * Make operation list wider, allow horizontal scroll * Simplify functor to string conversion * Click an operation to jump to that state * Skip operation end events * Add step in and step over buttons * Fix right padding in operations list * Don't wait for advance before operation starts * Don't wait for next event if all ops have finished * Auto-scroll to the current operation * Fix scrolling when the last operation ends * Simplify AllOnesPhaseOracle * Try rebuild * Update visual debugger namespace * Add copyright notices * Code style changes for VisualDebugger.cs * Replace tabs with spaces * Update RootNamespace in csproj * Create a watch script for npm * Update README * Add note to restart after changing the Q# program * Listen for qubit allocation/release events * Don't create new StateDumper every time * Add descriptions for classes * Add note to install Node.js and .NET Core SDK * Rename VisualDebugger to StateVisualizer * Update state before each allocation/release * Fix "step over" for operations containing allocate/release * Stop simulator when Ctrl+C is pressed * Finish renaming to StateVisualizer * Hide scrollbar for html/body elements Co-Authored-By: Andres Paz <[email protected]> * Update package name in package-lock.json Co-Authored-By: Andres Paz <[email protected]> * Rename more references to the visual debugger Co-Authored-By: Andres Paz <[email protected]> * More renaming * rebuild?
- Loading branch information
Showing
17 changed files
with
6,284 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
wwwroot |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
// Copyright (c) Microsoft Corporation. All rights reserved. | ||
// Licensed under the MIT License. | ||
|
||
using Microsoft.AspNetCore.SignalR; | ||
using System.Threading.Tasks; | ||
|
||
namespace Microsoft.Quantum.Samples.StateVisualizer | ||
{ | ||
/// <summary> | ||
/// The hub recives receives events and commands from the web browser client and sends them to the state visualizer | ||
/// server. | ||
/// </summary> | ||
internal class StateVisualizerHub : Hub | ||
{ | ||
private readonly StateVisualizer visualizer; | ||
|
||
public StateVisualizerHub(StateVisualizer visualizer) | ||
{ | ||
this.visualizer = visualizer; | ||
} | ||
|
||
public override async Task OnConnectedAsync() | ||
{ | ||
await visualizer.ReplayHistory(Clients.Caller); | ||
await base.OnConnectedAsync(); | ||
} | ||
|
||
public bool Advance() => visualizer.Advance(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
// Copyright (c) Microsoft Corporation. All rights reserved. | ||
// Licensed under the MIT License. | ||
|
||
using System; | ||
using System.Threading.Tasks; | ||
using Microsoft.Quantum.Simulation.Simulators; | ||
|
||
namespace Microsoft.Quantum.Samples.StateVisualizer | ||
{ | ||
/// <summary> | ||
/// Runs the Q# state visualizer. See the README for more information, including instructions on how to build and | ||
/// run this program. | ||
/// </summary> | ||
internal class Program | ||
{ | ||
private static void Main(string[] args) | ||
{ | ||
var visualizer = new StateVisualizer(new QuantumSimulator()); | ||
try | ||
{ | ||
visualizer.Run(QsMain.Run).Wait(); | ||
} | ||
catch (AggregateException aggregate) | ||
{ | ||
aggregate.Flatten().Handle(ex => ex is TaskCanceledException); | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
// Copyright (c) Microsoft Corporation. All rights reserved. | ||
// Licensed under the MIT License. | ||
|
||
namespace Microsoft.Quantum.Samples.StateVisualizer { | ||
open Microsoft.Quantum.Canon; | ||
open Microsoft.Quantum.Convert; | ||
open Microsoft.Quantum.Intrinsic; | ||
open Microsoft.Quantum.Math; | ||
open Microsoft.Quantum.Measurement; | ||
|
||
operation QsMain () : Unit { | ||
Teleport(); | ||
GroverSearch(); | ||
} | ||
|
||
// Teleportation | ||
|
||
operation Teleport () : Unit { | ||
using ((msg, here, there) = (Qubit(), Qubit(), Qubit())) { | ||
H(msg); | ||
|
||
H(here); | ||
CNOT(here, there); | ||
|
||
CNOT(msg, here); | ||
H(msg); | ||
|
||
if (MResetZ(msg) == One) { Z(there); } | ||
if (MResetZ(here) == One) { X(there); } | ||
H(there); | ||
} | ||
} | ||
|
||
// Grover's search | ||
// Based on the Grover's algorithm kata | ||
// https://github.com/microsoft/QuantumKatas/tree/master/GroversAlgorithm | ||
|
||
operation AllOnesPhaseOracle (register : Qubit[]) : Unit { | ||
Controlled Z(register[1...], register[0]); | ||
} | ||
|
||
operation AllZeroesPhaseOracle (register : Qubit[]) : Unit { | ||
ApplyWith(ApplyToEachA(X, _), AllOnesPhaseOracle, register); | ||
} | ||
|
||
operation GroverIteration (register : Qubit[], oracle : (Qubit[] => Unit)) : Unit { | ||
oracle(register); | ||
ApplyToEach(H, register); | ||
AllZeroesPhaseOracle(register); | ||
ApplyToEach(H, register); | ||
} | ||
|
||
operation GroverSearch () : Unit { | ||
let n = 3; | ||
using (register = Qubit[n]) { | ||
ApplyToEach(H, register); | ||
for (i in 1 .. Floor(Sqrt(PowD(2.0, IntAsDouble(n))))) { | ||
GroverIteration(register, AllOnesPhaseOracle); | ||
} | ||
ResetAll(register); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
# Q# State Visualizer # | ||
|
||
This sample lets you interactively step through the execution of a Q# program. | ||
It shows the tree of operation calls and a visualization of the quantum state | ||
after each operation. You can also go back to previous states by clicking the | ||
"Previous" button or by clicking on a previous operation in the list. | ||
|
||
Note that since this sample relies on the quantum simulator for information | ||
about the program execution, it can only step through quantum operations, not | ||
classical functions. | ||
|
||
## Running the Sample ## | ||
|
||
Install [Node.js](https://nodejs.org/en/) and the | ||
[.NET Core SDK](https://dotnet.microsoft.com/download) if you do not already | ||
have them installed. | ||
|
||
Then install the dependencies and build the TypeScript component: | ||
|
||
``` | ||
npm install | ||
npm run release | ||
``` | ||
|
||
Finally, start the dotnet host application: | ||
|
||
``` | ||
dotnet run | ||
``` | ||
|
||
This will launch a web server running the state visualizer. Open | ||
http://localhost:5000 in a web browser to use it. | ||
|
||
## Editing the Q# Program ## | ||
|
||
To change the Q# program that is executed by the state visualizer, edit the | ||
`Program.qs` file. The visualizer will start the program by running the `QsMain` | ||
operation. | ||
|
||
Restart the visualizer by running the `dotnet run` command again to see the new | ||
program. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
// Copyright (c) Microsoft Corporation. All rights reserved. | ||
// Licensed under the MIT License. | ||
|
||
using Microsoft.AspNetCore.Builder; | ||
using Microsoft.AspNetCore.Hosting; | ||
using Microsoft.Extensions.DependencyInjection; | ||
|
||
namespace Microsoft.Quantum.Samples.StateVisualizer | ||
{ | ||
/// <summary> | ||
/// Configures the ASP.NET Core web host. This class is used when the web host is created in | ||
/// <see cref="StateVisualizer"/>. | ||
/// </summary> | ||
internal class Startup | ||
{ | ||
// This method gets called by the runtime. Use this method to add services to the container. | ||
public void ConfigureServices(IServiceCollection services) | ||
{ | ||
services.AddSignalR(); | ||
services.AddMvc(); | ||
} | ||
|
||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. | ||
public void Configure(IApplicationBuilder app, IApplicationLifetime lifetime, StateVisualizer visualizer) | ||
{ | ||
app | ||
.UseDefaultFiles() | ||
.UseStaticFiles() | ||
.UseDeveloperExceptionPage() | ||
.UseMvc() | ||
.UseSignalR(routes => routes.MapHub<StateVisualizerHub>("/events")); | ||
lifetime.ApplicationStopping.Register(visualizer.Stop); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,179 @@ | ||
// Copyright (c) Microsoft Corporation. All rights reserved. | ||
// Licensed under the MIT License. | ||
|
||
using Microsoft.AspNetCore; | ||
using Microsoft.AspNetCore.Hosting; | ||
using Microsoft.AspNetCore.SignalR; | ||
using Microsoft.Extensions.DependencyInjection; | ||
using Microsoft.Extensions.Hosting; | ||
using Microsoft.Quantum.Simulation.Core; | ||
using Microsoft.Quantum.Simulation.Simulators; | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Numerics; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
|
||
namespace Microsoft.Quantum.Samples.StateVisualizer | ||
{ | ||
internal class StateVisualizer | ||
{ | ||
private readonly QuantumSimulator simulator; | ||
private readonly StateDumper stateDumper; | ||
private readonly IWebHost host; | ||
private readonly IHubContext<StateVisualizerHub> context; | ||
private readonly ManualResetEvent advanceEvent = new ManualResetEvent(true); | ||
private readonly CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(); | ||
private readonly IList<(string method, object[] args)> history = new List<(string, object[])>(); | ||
|
||
public StateVisualizer(QuantumSimulator simulator) | ||
{ | ||
if (simulator == null) | ||
{ | ||
throw new ArgumentNullException(nameof(simulator)); | ||
} | ||
|
||
this.simulator = simulator; | ||
simulator.OnOperationStart += OnOperationStartHandler; | ||
simulator.OnOperationEnd += OnOperationEndHandler; | ||
simulator.OnAllocateQubits += OnAllocateQubitsHandler; | ||
simulator.OnBorrowQubits += OnBorrowQubitsHandler; | ||
simulator.OnReleaseQubits += OnReleaseQubitsHandler; | ||
simulator.OnReturnQubits += OnReturnQubitsHandler; | ||
stateDumper = new StateDumper(simulator); | ||
|
||
host = WebHost | ||
.CreateDefaultBuilder() | ||
.UseStartup<Startup>() | ||
.ConfigureServices(services => | ||
{ | ||
// Register ourselves as a service so that the different | ||
// hubs and controllers can use us through DI. | ||
services.AddSingleton(typeof(StateVisualizer), this); | ||
}) | ||
.UseUrls("http://localhost:5000") | ||
.UseKestrel() | ||
.Build(); | ||
new Thread(host.Run).Start(); | ||
context = GetService<IHubContext<StateVisualizerHub>>(); | ||
} | ||
|
||
public async Task Run(Func<IOperationFactory, Task<QVoid>> operation) | ||
{ | ||
await operation(simulator); | ||
} | ||
|
||
public bool Advance() => advanceEvent.Set(); | ||
|
||
public void Stop() | ||
{ | ||
cancellationTokenSource.Cancel(); | ||
advanceEvent.Set(); | ||
} | ||
|
||
public async Task ReplayHistory(IClientProxy client) | ||
{ | ||
foreach (var (method, args) in history) | ||
{ | ||
await client.SendCoreAsync(method, args); | ||
} | ||
} | ||
|
||
private T GetService<T>() => | ||
(T) host.Services.GetService(typeof(T)); | ||
|
||
private async Task BroadcastAsync(string method, params object[] args) | ||
{ | ||
history.Add((method, args)); | ||
await context.Clients.All.SendCoreAsync(method, args); | ||
} | ||
|
||
private async Task WaitForAdvance() => | ||
await Task.Run(() => | ||
{ | ||
advanceEvent.Reset(); | ||
advanceEvent.WaitOne(); | ||
}, cancellationTokenSource.Token); | ||
|
||
private void OnOperationStartHandler(ICallable operation, IApplyData arguments) | ||
{ | ||
var variant = operation.Variant == OperationFunctor.Body ? "" : operation.Variant.ToString(); | ||
var qubits = arguments.Qubits?.Select(q => q.Id).ToArray() ?? Array.Empty<int>(); | ||
BroadcastAsync( | ||
"OperationStarted", | ||
$"{variant} {operation.Name}", | ||
qubits, | ||
stateDumper.DumpAndGetAmplitudes() | ||
).Wait(); | ||
WaitForAdvance().Wait(); | ||
} | ||
|
||
private void OnOperationEndHandler(ICallable operation, IApplyData result) | ||
{ | ||
BroadcastAsync("OperationEnded", result?.Value, stateDumper.DumpAndGetAmplitudes()).Wait(); | ||
WaitForAdvance().Wait(); | ||
} | ||
|
||
private void OnAllocateQubitsHandler(long count) | ||
{ | ||
BroadcastAsync("Log", $"Allocate {count} qubit(s)", stateDumper.DumpAndGetAmplitudes()).Wait(); | ||
WaitForAdvance().Wait(); | ||
} | ||
|
||
private void OnBorrowQubitsHandler(long count) | ||
{ | ||
BroadcastAsync("Log", $"Borrow {count} qubit(s)", stateDumper.DumpAndGetAmplitudes()).Wait(); | ||
WaitForAdvance().Wait(); | ||
} | ||
|
||
private void OnReleaseQubitsHandler(IQArray<Qubit> qubits) | ||
{ | ||
BroadcastAsync( | ||
"Log", | ||
$"Release qubit(s) {string.Join(", ", qubits.Select(q => q.Id))}", | ||
stateDumper.DumpAndGetAmplitudes() | ||
).Wait(); | ||
WaitForAdvance().Wait(); | ||
} | ||
|
||
private void OnReturnQubitsHandler(IQArray<Qubit> qubits) | ||
{ | ||
BroadcastAsync( | ||
"Log", | ||
$"Return qubit(s) {string.Join(", ", qubits.Select(q => q.Id))}", | ||
stateDumper.DumpAndGetAmplitudes() | ||
).Wait(); | ||
WaitForAdvance().Wait(); | ||
} | ||
} | ||
|
||
internal class StateDumper : QuantumSimulator.StateDumper | ||
{ | ||
private List<Complex> amplitudes = new List<Complex>(); | ||
|
||
public StateDumper(QuantumSimulator simulator) : base(simulator) | ||
{ | ||
} | ||
|
||
public override bool Callback(uint index, double real, double imaginary) | ||
{ | ||
amplitudes.Add(new Complex(real, imaginary)); | ||
return true; | ||
} | ||
|
||
public override bool Dump(IQArray<Qubit> qubits = null) | ||
{ | ||
amplitudes = new List<Complex>(); | ||
return base.Dump(qubits); | ||
} | ||
|
||
public Complex[] GetAmplitudes() => amplitudes.ToArray(); | ||
|
||
public Complex[] DumpAndGetAmplitudes(IQArray<Qubit> qubits = null) | ||
{ | ||
Dump(qubits); | ||
return GetAmplitudes(); | ||
} | ||
} | ||
} |
Oops, something went wrong.