September 2019
Volume 34 Number 9
[Quantum Computing]
Quantum Messaging with Q# and Blazor
In this article I explore quantum messaging, leveraging the remarkable phenomenon of quantum entanglement to instantly transfer half a message across potentially vast distances, eliminating the risk of eavesdropping. I’ll look at implementing a quantum algorithm for superdense coding in Q#, Microsoft’s new quantum programming language; how to entangle qubits using quantum gates; and how to encode ASCII messages as qubits. Then I’ll build a Web-based Blazor-powered UI that leverages the quantum algorithm and simulates sending quantum particles to different parties. I’ll show you how to consume a Q# library in a Blazor server application and how to launch and coordinate multiple browser windows. You’ll also learn how to employ Model-View-ViewModel (MVVM) in a Blazor application.
There’s quite a bit of quantum theory used in this article. If you’re new to quantum computing, I advise reading my “Quantum Computation Primer.” You’ll find the first part at tinyurl.com/quantumprimer1.
Let’s begin by taking a look at superdense coding.
Understanding Superdense Coding
Superdense coding leverages the phenomenon of quantum entanglement, where one particle from an entangled pair can affect the shared state of both, despite being separated across potentially vast distances.
To understand the superdense coding protocol, let’s say you have three actors: Alice, Bob and Charlie (A, B and C). Charlie creates an entangled pair of qubits and sends one to Alice and one to Bob. When Alice wants to send a 2-bit message to Bob, all she has to do is manipulate her own qubit—which influences the quantum state of the pair—and then send her single qubit to Bob. Bob then measures both Alice’s qubit and his own to receive the 2-bit message.
The important point is that there’s no way to encode more than one bit of information into a single qubit—yet only one qubit changes hands to deliver a 2-bit message. Despite any distance between the qubits, the shared state of the entangled qubits allows the entire 2-bit message to be encoded using just one of the qubits.
Moreover, when Alice sends Bob a message, a long time may have passed since Charlie sent them each an entangled qubit. You can think of entangled qubits in this way as a resource, waiting to be consumed as part of the communication process.
The pre-sharing of qubits also brings an important security benefit: Anyone who wants to decode the message needs to be in possession of both Alice’s and Bob’s qubits. Intercepting just Alice’s qubit isn’t enough to decode the message, and as the entangled qubits are pre-shared, the risk of eavesdropping is eliminated.
If you’re skeptical about the physics underpinning superdense coding—and that’s a not a bad thing—the phenomenon has been experimentally validated (tinyurl.com/75entanglement), even with satellites and lasers (tinyurl.com/spookyrecord).
Getting Started with Q#
Q# language support for Visual Studio comes with the Microsoft Quantum Development Kit. The easiest way to install the kit for Visual Studio is to use the Visual Studio Extension Manager.
From the Extensions menu in Visual Studio 2019, select Manage Extensions to display the Manage Extensions dialog. Enter “quantum” into the search box to locate the kit. Once it’s installed, you can create new Q# project types, Q# syntax highlighting is enabled and debugging of Q# projects works as you’d expect.
Q# is a procedural, domain-specific language, syntactically influenced by C# and F#. Q# files contain operations, functions and custom type definitions, the names of which must be unique within a namespace, as shown in Figure 1. Members of other namespaces can be made available using the “open” directive.
Figure 1 Functions, Operations and Custom Type Definitions
namespace Quantum.SuperdenseCoding
{
open Microsoft.Quantum.Diagnostics;
open Microsoft.Quantum.Intrinsic;
open Microsoft.Quantum.Canon;
function SayHellow(name : String) : String
{
return “Hello “ + name;
}
operation EntangledPair(qubit1 : Qubit, qubit2 : Qubit) : Unit
is Ctl + Adj
{
H(qubit1);
CNOT(qubit1, qubit2);
}
newtype IntTuple = (Int, Int);
}
Functions are analogous to functions or methods in other procedural languages. A function accepts zero or more arguments and may return a single object, value or tuple. Each parameter to a function consists of a variable name followed by a colon and then its type. The return value for a function is specified after a colon, at the end of the parameter list.
Recall from Part 3 of my “Quantum Computation Primer” (tinyurl.com/controlledgates) that controlled-U gates allow you to add a control input for an arbitrary quantum gate. Q# has a Controlled keyword for this purpose, which allows you to apply a control to an operator, for example:
Controlled X([controlQubit1, controlQubit2], targetQubit)
Here, the Controlled statement places two control inputs on the Pauli-X gate.
Operations share the same traits as functions, but operations can also represent unitary operators. You can think of them as composite quantum gates. They allow you to define what happens when they’re used as part of a controlled operation.
In addition, operations allow you to explicitly define the reverse of the operation (via the adjoint keyword). Adjoint is the complex conjugate transpose of the operator. For more information, see tinyurl.com/brafromket.
The newtype keyword allows you to define a custom type, which you can then use in operations and functions.
Let’s now examine some of the most common expression types.
Variables are immutable in Q# by default. To declare a new variable, use the let keyword, like so:
let foo = 3;
The compiler infers the type of the variable from the value you assign it. If you need to change a variable’s value, use the mutable keyword when declaring it:
mutable foo = 0;
set foo += 1;
To create an immutable array in Q#, use the following:
let immutableArray = [11, 21, 3, 0];
An array in Q# is a value type and is, for the most part, immutable, even when created using the mutable keyword. When the mutable keyword is used, you can assign another array to the identifier, but can’t replace items in the array:
mutable foo = new Int[4];
set foo = foo w/ 0 <- 1;
The w/ operator is an abbreviation of the word “with.” On the second line, all the values in foo are copied to a new array, with the first item (index 0) in the array set to 1. It has a O(n) runtime complexity. The previous assignment statement can be expressed more succinctly by combining it with an equal sign, like so:
set foo w/= 0 <- 1;
When the w/= operator is used, where possible an in-place replacement is performed, reducing the runtime complexity to O(1).
You can create a loop by using a for statement:
for (i in 1..10)
{
Message($”i={i},”);
}
The built-in Message function writes messages to the console. Q# supports string interpolation. By prepending a “$” to a string literal, you can place expressions in curly brackets.
To loop over a collection, use a for statement, like so:
for (qubit in qubits) // Qubits is an array of Qubits
{
H(qubit); // Apply a Hadamard gate to the Qubit
}
This was a whirlwind tour of Q#. For more in-depth coverage, please visit tinyurl.com/qsharp. I also recommend working through the Microsoft Quantum Katas (tinyurl.com/quantumkatas).
Implementing Superdense Coding with Q#
Now let’s look at implementing the Quantum superdense coding protocol in Q#. I’m assuming you’ve read the first and second part of my series or already have an understanding of Dirac notation and quantum computation fundamentals.
In the opening description of superdense coding, recall that Charlie entangles a pair of qubits and sends one to Alice and one to Bob.
To entangle a pair of qubits, which start off in a |0… state, you send the first qubit through a Hadamard gate, which changes its state to:
The Hadamard gate places the first qubit in a superposition of |0… and |1…, sending it to the |+… state. Here’s the code:
operation CreateEntangledPair(qubit1 : Qubit, qubit2 : Qubit) : Unit
{
H(qubit1);
CNOT(qubit1, qubit2);
}
The CNOT gate inverts qubit2 if the state of qubit1 is |1…, resulting in the |Ф+… state:
The state of the qubits when measured will have a 50 percent chance of being either 00 or 11.
With this superposition created, if you were to measure just one of the qubits, it would cause the other’s state to collapse to the same value, regardless of the distance between the qubits. The qubits are said to be entangled.
Once Alice and Bob have each been sent one qubit from an entangled pair, Alice can affect the overall quantum state of the two qubits by simply manipulating her own qubit. A single qubit can encode only 1 bit of information. But when entangled with another qubit, that qubit can be used by itself to encode 2 bits of information into the shared quantum state.
Alice is able to transmit 2 bits of information by passing her qubit through one or more gates. The gates and resulting quantum state of the pair of qubits are shown in Figure 2. The subscript on the qubits identifies the owner: A for Alice, B for Bob.
Figure 2 Gates Used to Produce Bell States
Sending a 2-bit message of 00 requires no change to Alice’s qubit (“I” is the identity gate, which does nothing). Sending 01 or 10 requires a single gate: X or Z, respectively; and sending 11 requires the application of both an X and a Z gate.
In quantum mechanics, the only perfectly distinguishable states are those that are orthogonal. For qubits, these are states that are perpendicular on the Bloch sphere. It so happens that the Bell states are orthogonal, so when it comes time for Bob to measure the qubits, he can perform an orthogonal measurement on the two qubits to derive Alice’s original 2-bit message.
To encode Alice’s qubit, it’s sent to the EncodeMessageInQubit Q# operation, as shown in Figure 3. Along with Alice’s qubit, EncodeMessageInQubit accepts two Bool values that represent the 2 bits of information to be sent to Bob. The first let statement concatenates the pair of bit values into an integer between 002 and 112, with bit1 occupying the high bit. The resulting bit sequence indicates the correct gate or gates to apply.
Figure 3 EncodeMessageInQubit Operation
operation EncodeMessageInQubit(
aliceQubit : Qubit, bit1 : Bool, bit2 : Bool) : Unit
{
let bits = (bit1 ? 1 | 0) * 2 + (bit2 ? 1 | 0);
if (bits == 0b00)
{
I(aliceQubit);
}
elif (bits == 0b01)
{
X(aliceQubit);
}
elif (bits == 0b10)
{
Z(aliceQubit);
}
elif (bits == 0b11)
{
X(aliceQubit);
Z(aliceQubit);
}
}
Q# supports many of the language features present in C# and F#. Conveniently, that includes binary literals.
After Alice encodes her message and sends her qubit to Bob, Bob receives the message and applies a CNOT gate to the qubits, and the Hadamard gate to Alice’s qubit:
operation DecodeMessageFromQubits (bobQubit : Qubit, aliceQubit : Qubit) : (Bool, Bool)
{
CNOT(aliceQubit, bobQubit);
H(aliceQubit);
return (M(aliceQubit) == One, M(bobQubit) == One);
}
Each qubit is then measured. The result is a tuple consisting of the two original message bits sent by Alice. The measured values of each qubit correspond to the classical bit message encoded by Alice.
Let’s look more closely at how this superdense coding protocol works. Recall that both Alice and Bob are each given a qubit belonging to an entangled pair. The matrix representation of the quantum state of the two qubits is given by:
I’ll use this matrix result in a moment to demonstrate the encoding process.
If Alice wishes to send the message 012 to Bob, she applies the X gate to her own qubit, which changes the quantum state to:
Converting that to its matrix form yields:
Alice then sends her qubit to Bob.
To decode the message, Bob begins by applying a CNOT gate to the qubits:
You can now take the matrix result from the previous step and multiply it by the CNOT matrix, remembering that the scalar multiplier is commutable:
When you factor the resulting matrix, you get:
Bob then applies a Hadamard gate to Alice’s qubit:
You can substitute in the matrix representation of the Hadamard gate and qubits:
Dividing top and bottom by √2 , you eliminate the common divisor, which leaves some matrix arithmetic to do:
As you can see, the final result is the original message sent by Alice!
When Alice passed her qubit through the X gate, it affected the overall superposition of the two qubits. Bob was able to determine the change Alice made by unwinding the superposed state.
Allocating Qubits
When a Qubit object is allocated in Q#, it must be returned to the simulator. This is enforced syntactically. A using statement is required to allocate qubits.
To allocate a single Qubit, use the Qubit expression, as shown here:
using (qubit = Qubit())
{
Reset(qubit);
}
The Reset function returns the Qubit to the |0… state.
When exiting the using block, a qubit must be in the |0… state. This is a runtime requirement, enforced by the quantum simulator. You can pass a Qubit to other operations and change its state and so forth, but if execution reaches the end of the using block without the Qubit being returned to the |0… state, an exception is raised. You can disable exceptions for non-reset qubits via the QuantumSimulator constructor.
You can also create multiple Qubit objects using the array expression Qubit[]:
using (qubits = Qubit[2])
{
// Do something with the qubits...
ResetAll(qubits);
}
The qubits created with the array expression still need to be reset when you’re done. Use the built-in ResetAll function to reset multiple qubits.
Be aware that simulated qubits use an exponential amount of memory. Each allocated qubit doubles the amount of RAM required. In my tests on a machine with 16GB of RAM, usage got to around 13GB for 26 qubits. Exceeding that, virtual memory kicks in. I was able to achieve 31 qubits; however, the memory usage ballooned to 53GB. Unsurprisingly, 32 qubits produced an interop exception.
Bending Q# to Your Will
The example project passes qubits around asynchronously among objects that represent Alice, Bob and Charlie. For that reason, I needed to find a way to retain Qubit objects and to circumvent Q#’s requirement that a using block be used.
There’s a strong affinity between the Q# and C# tooling in Visual Studio. When you build a Q# project, the .qs files in that project are translated into C#. The Q# tooling outputs its generated C# files with a “.g.cs” file extension, placing them in the \obj\qsharp\src directory of the parent project.
I suspect this translation step might be replaced with direct compilation to IL or something else in the future, given the investment Microsoft is making in the Q# compiler. For the moment, however, I found it useful to explore the generated .cs files by peeking at how things work behind the scenes. I soon realized that it’s possible to leverage the Q# SDK assemblies from C# to achieve what I needed.
The QOperations class, in the downloadable sample code, directly calls the Q# APIs, which allows it to request a Qubit from the simulator without having to immediately release it. Instead of restricting myself to Q# for all quantum activities, I use C# for allocating, entangling and deallocating qubits.
The QOperations class creates an instance of the QuantumSimulator class, which is located in the Microsoft.Quantum.Simulation.Simulators namespace. QuantumSimulator provides an API for manipulating the native simulator component. The simulator is installed with the Microsoft Quantum Development Kit.
QuantumSimulator has an internal IoC container that allows you to retrieve various objects that are commonly used by its generated C# code. For example, within a C# file you can use the Get<T> method of the QuantumSimulator object to retrieve an object for resetting a qubit’s state and releasing a qubit, performing the same action as a using block in Q#:
resetOperation = simulator.Get<ICallable<Qubit, QVoid>>(typeof(Reset));
releaseOperation = simulator.Get<Release>(typeof(Release));
To allocate a new Qubit, you use the Microsoft.Quantum.Intrinsic.Allocate object. You also need the CNOT and H (Hadamard) gate objects, which are retrieved from the container, like so:
allocator = simulator.Get<Allocate>(typeof(Allocate));
cnotGate = simulator.Get<IUnitary<(Qubit, Qubit)>>(typeof(CNOT));
hGate = simulator.Get<IUnitary<Qubit>>(typeof(H));
With these objects you can generate entangled pairs of qubits. The GetEntangledPairsAsync method of the QOperations class creates a list of Qubit tuples (see Figure 4).
Figure 4 Creating Entangled Qubits in C#
public Task<IList<(Qubit, Qubit)>> GetEntangledPairsAsync(int count)
{
IList<(Qubit, Qubit)> result = new List<(Qubit, Qubit)>(count);
for (int i = 0; i < count; i++)
{
var qubits = allocator.Apply(2);
hGate.Apply(qubits[0]);
cnotGate.Apply((qubits[0], qubits[1]));
result.Add((qubits[0], qubits[1]));
}
return Task.FromResult(result);
}
I use the Apply method of the Allocate object to retrieve two free qubits from the simulator. I apply the Hadamard gate to the first qubit, and then the CNOT to both, placing them in an entangled state. There’s no need to reset and release them immediately; I’m able to return the Qubit pairs from the method. Qubits still need to be released—otherwise the application would quickly run out of memory. I just postpone that step.
Qubits are released with the ReleaseQubitsAsync method, as shown in Figure 5.
Figure 5 Releasing Qubits in C#
public Task ReleaseQubitsAsync(IEnumerable<Qubit> qubits)
{
foreach (Qubit qubit in qubits)
{
resetOperation.Apply(qubit);
releaseOperation.Apply(qubit);
/* Alternatively, we could do: */
// simulator.QubitManager.Release(qubit);
}
return Task.CompletedTask;
}
For each supplied Qubit, I reset the qubit using the Reset object’s Apply method. I then inform the simulator that the qubit is free using the Release object’s Apply method. Alternatively, you could use the simulator’s QubitManager property to release the qubit.
That completes the quantum element of this article. Now let’s look at Blazor and at implementing a server-side Blazor project with MVVM.
Using the MVVM Pattern with Blazor
I’ve been a longtime fan of XAML. I like the way I can write view-model logic in C# and combine it with a designer-friendly reusable layout file glued together with data bindings. Blazor allows the same approach, but instead of XAML it uses Razor, which has a concise syntax for marking up dynamic content within HTML.
If you’re new to Blazor, see the installation instructions at tinyurl.com/installblazor for your IDE of choice.
I first implemented this project using Universal Windows Platform (UWP). UWP is compatible with .NET Core and it made sense to rapidly build out the UI in a XAML-based technology. In fact, the UWP version of the application is located in the downloadable sample code. If you don’t feel like installing Blazor, and just wish to see the app in action, feel free to just run the UWP version. Both applications are identical in behavior.
Porting the app to Blazor was a piece of cake. No code changes were required; NuGet references to the Codon (codonfx.com) MVVM framework were fine and everything just worked.
I chose Blazor’s server-side model for this project to allow me to share a single QuantumSimulator object among the three view-models representing Alice, Bob, and Charlie, and to pass Qubit objects around.
Dissecting the Blazor Application The Blazor application contains three Razor pages, shown in Figure 6, representing the three actors: Alice.razor, Bob.razor and Charlie.razor.
Figure 6 Alice, Bob and Charlie Razor Pages
Each page has an associated view-model. All three view-models in the project subclass Codon’s ViewModelBase class, which provides built-in support for INotifyPropertyChanged (INPC) events, an IoC container reference, and loosely coupled messaging between components within the application.
Each razor page has a functions block that includes a property for its respective view-model. Here’s the functions block in Alice.razor:
@functions {
AliceViewModel ViewModel { get; set; }
protected override async Task OnInitAsync()
{
ViewModel = Dependency.Resolve<AliceViewModel>();
ViewModel.PropertyChanged += (o, e) => Invoke(StateHasChanged);
}
}
The view-model is retrieved from Codon’s IoC container during the OnInitAsync method.
View-model property changes don’t automatically cause the page content to update. For that you subscribe to the ViewModelBase classes PropertyChanged event. When the event occurs, the razor page’s StateHasChanged method is called, which triggers an update of the HTML elements that are data-bound to the view-model.
You use the page’s Invoke method to ensure that updates occur on the UI thread. If the StateHasChanged method is called from a non-UI thread, an exception is raised.
Requesting Qubits from Charlie The Charlie razor page indicates the number of qubits sent to Alice and Bob. As Figure 7 shows, this page has slightly more going on in its functions block.
Figure 7 Charlie.razor Function Block Excerpt
@functions {
bool scriptInvoked;
...
protected override void OnAfterRender()
{
if (!scriptInvoked)
{
scriptInvoked = true;
jsRuntime.InvokeAsync<object>(“eval”, evalScript);
}
}
const string evalScript = @”var w1 = window.open(‘/alice’, ‘_blank’,
‘left=100,top=50,width=500,height=350,toolbar=0,resizable=0’);
var w2 = window.open(‘/bob’, ‘_blank’,
‘left=100,top=500,width=500,height=350,toolbar=0,resizable=0’);
window.addEventListener(‘beforeunload’, function (e) {
try {
w1.close();
} catch (err) {}
try {
w2.close();
} catch (err) {}
(e || window.event).returnValue = null;
return null;
});”;
}
Besides view-model initialization, the page is also tasked with opening a new browser window for both Alice and Bob. It does this by calling JSRuntime.InvokeAsync and using the JavaScript eval function to run JavaScript in the browser. The eval script opens and retains the windows as variables. When the Charlie page’s beforeunload event occurs, the windows are closed. This prevents the accumulation of orphaned browser windows when you stop the debugger.
Be aware that most browsers today block popups by default, or at least request consent before opening one. If this happens, you’ll need to OK the popups and refresh the page. My use of popups in this project is merely as a convenience to open all three actor windows at once.
The Charlie.razor page contains a single dynamic element—a counter that displays the number of qubits that have been dispatched:
<p>Qubit sent count: @Model.QubitSentCount</p>
The value within the paragraph is bound to the view-model’s QubitSentCount property.
When classes derive from Codon.UIModel.ViewModelBase, any implementation of IMessageSubscriber<T> automatically subscribes to messages of type T. CharlieViewModel implements IMessageSubscriber<RequestQubitsMessage> and IMessageSubscriber<ReleaseQubitsMessage>; therefore, when a RequestQubitsMessage is published by the AliceViewModel or a ReleaseQubitsMessage is published by BobViewModel, it’s handled within the CharlieViewModel class. This is all Charlie is tasked with; he sends out entangled qubits to Alice and Bob when they’re needed.
When CharlieViewModel receives a RequestQubitsMessage, it uses the QOperations instance to retrieve a list of entangled qubit pairs, as shown in Figure 8.
Figure 8 CharlieViewModel ReceiveMessageAsync(RequestQubitsMessage) Method
async Task IMessageSubscriber<RequestQubitsMessage>.ReceiveMessageAsync(
RequestQubitsMessage message)
{
IList<(Qubit, Qubit)> qubits =
await QOperations.GetEntangledPairsAsync(message.QubitCount);
await Messenger.PublishAsync(new BobQubitMessage(qubits.Select(x => x.Item2)));
await Messenger.PublishAsync(new AliceQubitMessage(qubits.Select(x => x.Item1)));
QubitSentCount += message.QubitCount;
}
CharlieViewModel then sends the first item of each pair to Alice, and the second to Bob, via the IMessenger.
Finally, QubitSentCount is increased, which updates the Charlie page.
Sending Messages as Alice Let’s take a look at the Alice page and its associated view-model. The AliceViewModel class contains an AsyncActionCommand, which is defined like so:
ICommand sendCommand;
public ICommand SendCommand => sendCommand ??
(sendCommand = new AsyncActionCommand(SendAsync));
I’m still chuffed seeing properties written with such conciseness, where a lambda expression combined with a null coalescing operator brings lazy loading in just a couple lines of code.
Codon’s command infrastructure supports async command handlers. By using Codon’s AsyncActionCommand, you can specify the async method SendAsync as the command handler.
Alice.razor contains a text field and a button:
<input type=”text” bind=”@ViewModel.Message” />
<button class=”btn btn-primary”
onclick=”@ViewModel.SendCommand.Execute”>Send to Bob</button>
The input box is bound to the view-model’s Message property. When the Send to Bob button is clicked, the view-model’s SendCommand executes, and its SendAsync method is called (see Figure 9).
Figure 9 AliceViewModel.SendAsync Method
async Task SendAsync(object arg)
{
var bytes = Encoding.ASCII.GetBytes(Message);
foreach (byte b in bytes)
{
byteQueue.Enqueue(b);
await Messenger.PublishAsync(
new RequestQubitsMessage(qubitsRequiredForByte));
}
Message = string.Empty;
}
The view-model has a queue that contains the bytes of each message that’s encoded and sent to Bob. Codon’s IMessenger is used to send off a message, which ultimately reaches Charlie, requesting four qubits be entangled and sent out, two for Alice and two for Bob.
Finally, the Message property is reset to string.empty, which clears the input box and leaves it ready for a new message.
AliceViewModel implements IMessageSubscriber<AliceQubitMessage> and thus receives all messages of that type:
gasync Task IMessageSubscriber<AliceQubitMessage>.ReceiveMessageAsync(
AliceQubitMessage message)
{
foreach (var qubit in message.Qubits)
{
qubitQueue.Enqueue(qubit);
}
await DispatchItemsInQueue();
}
When ReceiveMessageAsync is called, the payload contains a collection of Qubit instances from Charlie. AliceViewModel also retains a queue of Qubit objects, to which the newly arrived qubits are added.
AliceViewModel is now ready to send at least part of a message to Bob. DispatchItemsInQueue is called, as shown in Figure 10.
Figure 10 AliceViewModel.DispatchItemsInQueue Method
async Task DispatchItemsInQueue()
{
var qOps = Dependency.Resolve<QOperations, QOperations>(true);
while (byteQueue.Any())
{
if (qubitQueue.Count < qubitsRequiredForByte)
{
return;
}
IList<Qubit> qubits =
qubitQueue.DequeueMany(qubitsRequiredForByte).ToList();
byte b = byteQueue.Dequeue();
BitArray bitArray = new BitArray(new[] { b });
/* Convert classical bit pairs to single qubits. */
for (int i = 0, j = 0; i < bitArray.Length; i += 2, j++)
{
await qOps.EncodeMessageInQubitAsync(
qubits[j],
bitArray[i],
bitArray[i + 1]);
}
await Messenger.PublishAsync(new DecodeQubitsMessage(qubits));
}
}
DispatchItemsInQueue first retrieves the QOperations instance from the IoC container. Dependency.Resolve<T,T>(bool singleton) causes a new instance to be created if one hasn’t been already. That instance is then retained as a singleton so that future requests will resolve the same object.
An extension method is used to dequeue four qubits from the qubit queue. The message byte is also dequeued and placed into a BitArray. The QOperations object is then tasked with placing each qubit into a superposition representing the 2-bit message. Notice again how one qubit is able to represent two classical bits because its state affects the quantum state of the pair.
The IMessenger is then used to dispatch a DecodeQubitsMessage that’s ultimately received by Bob.
Receiving Messages as Bob Like Alice, Bob has a queue of Qubits that arrive from Charlie. BobViewModel implements IMessageSubscriber<BobQubitMessage>. When Charlie sends qubits wrapped in a BobQubitMessage, they’re placed in the queue.
BobViewModel also implements IMessageSubscriber<DecodeQubitsMessage>. When Alice sends a message to Bob, it comes wrapped in a DecodeQubitsMessage object and is handled in the ReceiveMessageAsync(DecodeQubitsMessage) method (see Figure 11). The method dequeues an equal number of qubits from its queue. The QOperations DecodeQubits method is used to convert each pair of qubits from Alice and Bob into 2 bits of data. Each byte in the message consists of four 2-bit pairs (8 bits), hence the loop increments for i and j.
Figure 11 ReceiveMessageAsync(DecodeQubitsMessage) Method
async Task IMessageSubscriber<DecodeQubitsMessage>.ReceiveMessageAsync(
DecodeQubitsMessage message)
{
IList<Qubit> aliceQubits = message.Qubits;
List<Qubit> bobQubits = qubits.DequeueMany(aliceQubits.Count).ToList();
var qOps = Dependency.Resolve<QOperations, QOperations>(true);
var bytes = new List<byte>();
for (int i = 0; i < bobQubits.Count; i += 4)
{
byte b = 0;
for (int j = 0; j < 4; j++)
{
(bool aliceBit, bool bobBit) = await qOps.DecodeQubits(
bobQubits[i + j], aliceQubits[i + j]);
if (bobBit)
{
b |= (byte)(1 << (j * 2 + 1));
}
if (aliceBit)
{
b |= (byte)(1 << (j * 2));
}
}
bytes.Add(b);
}
Message += Encoding.ASCII.GetString(bytes.ToArray());
await Messenger.PublishAsync(new ReleaseQubitsMessage(aliceQubits));
await Messenger.PublishAsync(new ReleaseQubitsMessage(bobQubits));
}
Each byte is constructed by left-shifting bits according to the values of the decoded bits. The decoded bytes are turned back into a string using the Encoding.ASCII.GetString method and appended to the Message property that’s displayed on the page.
Once the qubits have been decoded, they’re released by publishing a ReleaseQubitsMessage, which is received by the CharlieViewModel. QOperations is then used to release the qubits:
async Task IMessageSubscriber<ReleaseQubitsMessage>.ReceiveMessageAsync(
ReleaseQubitsMessage message)
{
await QOperations.ReleaseQubitsAsync(message.Qubits);
}
Wrapping Up
In this article I implemented a quantum algorithm for superdense coding in Q#, the new Microsoft quantum programming language. I explained how to entangle qubits using quantum gates, and how to encode ASCII messages as qubits. I looked at a Web-based Blazor-powered UI that leveraged the quantum algorithm and simulated sending quantum particles to different parties. I also showed you how to consume a Q# library in a Blazor server application and how to launch and coordinate multiple browser windows. Finally, I explained how to employ MVVM in a Blazor application.
References
- “Statements and Other Constructs,” retrieved on July 5, 2019, from bit.ly/2Ofx1Ld
- Yanofsky, N., & Mannucci, M. (2008) “Quantum Computing for Computer Scientists,” Cambridge University Press
- Nielsen, M. & Chuang, I., (2010) “Quantum Computation and Quantum Information,” 10th edition, Cambridge University Press, Cambridge UK
- Watrous, J., (2006) “Lecture 3: Superdense coding, quantum circuits, and partial measurements,” retrieved on July 5, 2019, from bit.ly/2XTPEDN
Daniel Vaughan is an author and software engineer working for Microsoft in Redmond, Wash. He’s a former nine-time Microsoft MVP, and is the developer behind several acclaimed consumer and enterprise mobile apps, such as Surfy Browser for Android and Windows Phone and Airlock Browser for Android. He’s also the creator of a number of popular open source projects, including the Codon and Calcium frameworks. Vaughan blogs at danielvaughan.org and tweets as @dbvaughan.
Thanks to the following Microsoft technical expert for reviewing this article: Bettina Heim (Microsoft Quantum Team)
Bettina Heim is a member of the Microsoft Quantum team.