Emulation in Q#
Sometimes it’s helpful to be able to implement Q# functions or operations directly in C#. This can be used to access some API that is not directly accessible in Q#, or to provide alternative implementations based on the context in which the Q# program is executed. As one example, we might want to use slightly different implementations based on whether the program is executed for resource estimation or for simulation. This blog post describes techniques to programmatically replace a Q# function by another one. For the sake of simplicity, we focus on Q# functions in this post, but the techniques apply to Q# operations analogously. Also, the main goal of this post is to illustrate this feature conceptually, whereas the details of the API may change in the future.
Intrinsic functions
The first technique employs intrinsic functions. It can be used to implement a Q# function using a .NET language, for example C#. This is particularly useful when the required library functionality is not available in Q#. Let’s consider the following example, typical for this season. We often find ourselves in need of the number of the remaining days until Christmas as a value in our Q# operation. This value can be used as the secret shift in the hidden shift algorithm or as the special element in a Grover search. We cannot retrieve the current date in Q#, and therefore we have to provide this function as an intrinsic:
function DaysUntilChristmas() : Int { body intrinsic; }
This Q# code gets translated into a partial C# class called DaysUntilChristmas
.
We can add the implementation by creating a nested class called Native
that
derives from DaysUntilChristmas
. In its constructor this class accepts an instance m
of type IOperationFactory
, which is the base class to
all simulators. Therefore, m
holds the simulator object for which the
function is called. This will enable us to perform simulator-dependent code.
We’ll use this variable when describing the second technique, however, in this
example we compute the same function independent of the simulator.
public partial class DaysUntilChristmas { public class Native : DaysUntilChristmas { public Native(IOperationFactory m) : base(m) {} // can be adjusted (e.g., 1/7, 1/6, 1/19, ...) private static int ChristmasMonth => 12; private static int ChristmasDay => 25; // C# function with matching signature private long DaysUntilChristmasFunction(QVoid input) { var today = DateTime.Today; var christmas = new DateTime(today.Year, ChristmasMonth, ChristmasDay); // make sure the next Christmas is today or in the future if (christmas < today) christmas = christmas.AddYears(1); // return difference in days return (christmas - today).Days; } // Override __Body__ property to use C# function public override Func__Body__ => DaysUntilChristmasFunction; } }
The function implementation is provided in terms of the __Body__
property,
which is of type Func<__In__, __Out__>
, where __In__
is the type of the input
argument tuple in the Q# function and __Out__
is the type of the output
argument tuple. In this case, there are no input arguments,
represented by the empty tuple type QVoid
, and there is a single output
argument of Q# type Int
, which corresponds to the C# type long
(= Int64
).
Overriding functions
We can use a similar technique to replace an existing function with an
alternative one. It would be nice to make Q#’s Message
outputs a little more
festive this month. For this purpose, we implement an intrinsic function called
ChristmasMessage
:
function ChristmasMessage(msg : String) : Unit { body intrinsic; }
The implementation is provided in C# and it prefixes each message with Santa’s way of expressing his happiness and joy.
public partial class ChristmasMessage { public class Native : ChristmasMessage { private readonly IOperationFactory simulator; public Native(IOperationFactory m) : base(m) { simulator = m; } private QVoid ChristmasMessageFunction(string message) { if (simulator is ToffoliSimulator) { WriteLine(message); } else { WriteLine($"Ho ho ho: {message}"); } return QVoid.Instance; } public override Func__Body__ => ChristmasMessageFunction; } }
Note that we do not override the behavior when using the ToffoliSimulator
,
since the Toffoli simulator does not support the H
in Ho ho ho.
Instead of replacing all occurrences of Message
in our Q# programs by hand, we
wish to do this automatically and hide the existence of the ChristmasMessage
from our users. When constructing a simulator instance in a C# host program, we
can use the Register
method to replace an existing function with an
alternative one.
var simulator = new QuantumSimulator(); simulator.Register(typeof(Message), typeof(ChristmasMessage), typeof(ICallable));
The first parameter is the type of the existing function, in this case
Message
, the second parameter is the type of the replacement, in this case
ChristmasMessage
, and the third parameter is the type of either ICallable
,
IAdjointable
, IControllable
, or IUnitary
. For functions and operations
that are neither adjoint nor controlled, use ICallable
. For operations that
are marked Adj
but not Ctl
, use IAdjointable
, for those marked Ctl
but
not Adj
, use IControllable
, and if they have both, use IUnitary
.
It’s worth to note that we couldn’t just have implemented ChristmasMessage
by
calling Message
inside and then use Register
for the replacement. This
would have lead to an infinite recursive loop, since the Message
call inside
ChristmasMessage
would also be replaced.
The same technique can be used to replace one Q# operation by another Q# operation, the replacement operation doesn’t need to be implemented in C#. Consider the following operation that swaps two qubits using three CNOTs:
operation SwapWithCNOTs(a : Qubit, b : Qubit) is Adj+Ctl { CNOT(a, b); CNOT(b, a); CNOT(a, b); }
To replace the SWAP
operation in the Microsoft.Quantum.Intrinsic
namespace,
we can simply call Register
on the simulator like this:
simulator.Register(typeof(SWAP), typeof(SwapWithCNOTs), typeof(IUnitary));
Further reading
The emulation technique was demonstrated for Q# functions in this post, but it
applies similarly to Q# operations as well. However, depending on the supported
functors (such as Adj
and Ctl
), operations for __AdjointBody__
,
__ControlledBody__
, and __AdjointControlledBody__
might also need to be implemented.
The technique is used in several places in the Q# and QDK code base. Some examples include:
- Many functions in the
Microsoft.Quantum.Math
namespace. - Speeding up full state simulation for quantum oracles by applying the permutation directly to the state vector.
- A custom gate counting simulator in the Quantum Katas that keeps track of gate and qubit resources to power the testing harnesses used to validate the learner’s solutions.
- Replacing
ApplyAnd
andApplyLowDepthAnd
withCCNOT
when used with the Toffoli simulator.
The complete code for the project can be found in this gist.
It’s DaysUntilChristmas()
days until Christmas, ChristmasMessage("Happy
holidays!")
.
0 comments