March 30th, 2022

Introducing Q# Lambda Expressions

Scott Carda
Software Engineer

Lambda expressions have become a staple in most modern programming languages since they allow the developers flexibility and convenience in writing their code. They are often a straightforward way of expressing some single-use action or transformation without having to go through the overhead of creating a separate named function that will never get used outside of that one specific place in your code. Starting with release 0.23, we are bringing the power of lambda expressions to Q#!

Lambda functions and operations are expressed as a sequence of zero or more parameters, followed by the arrow operator, followed by the expression the lambda equates to. Currently, lambdas in Q# only support single expressions as their bodies. Here is an example of the syntax: (x, y) -> x + y. This makes use of the familiar arrow notation denoting lambda expressions from other languages, but with a twist: Q# requires you to specify whether the lambda expression is an anonymous function or operation with the -> or => arrow, respectively. Let’s look at how a lambda like this can be used in context to sum over a list of integers:

let intArray = [14, 15, 3, -4, 18];
…
let sum = Fold((x, y) -> x + y, 0, intArray);

Lambdas can also utilize variables from the surrounding scope in closures. For example, here we have a lambda that uses the qubit control allocated above:

use control = Qubit();
…
let cnotOnControl = q => CNOT(control, q);
…
// Now cnotOnControl can be called on any qubit
cnotOnControl(myQubit);

Currently the lambda expressions don’t let you specify the type for the parameters manually; the types are deduced via Q# type inference. Because of this, when you first write out your lambda, you may get a red underlining over the parameters complaining about the type. This shouldn’t be an issue though as it will be resolved as soon as you call the lambda expression by passing arguments to it, which is how the type inference will be able to figure out what types the parameters should be. It is also important to note that lambdas are not generic; the parameters to the lambda expressions do indeed have specific types. It’s just that those types are inferred by their usage in the code. Once the types of the lambda parameters have been set by the first use of the lambda, the lambda can’t be called with different parameter types later. 

Before we wrap up, let’s go through a more complete example to show how lambdas can help tidy up your code. In Q# it can be very useful to check if certain operations are equivalent. This is especially useful when defining operation decompositions, expressing an operation in terms of an equivalent set of operations. To make this equivalence check easier, the Q# libraries has an operation under the Microsoft.Quantum.Diagnostics namespace called AssertOperationsEqualReferenced. Here we are going to check that my custom decomposition for the Z gate is equivalent to an actual Z operation. 

open Microsoft.Quantum.Diagnostics;
open Microsoft.Quantum.Intrinsic;

operation CustomZDecomp(q : Qubit) : Unit is Ctl + Adj {
    S(q);
    S(q);
}

operation DecompWrapper(qs : Qubit[]) : Unit is Ctl + Adj {
    CustomZDecomp(qs[0]);
}

operation ZWrapper(qs : Qubit[]) : Unit is Ctl + Adj {
    Z(qs[0]);
}

@Test("QuantumSimulator")
operation TestDecomp() : Unit {
    AssertOperationsEqualReferenced(1, DecompWrapper, ZWrapper);
} 

Notice how we needed to have wrapper operations for the arguments to the assert. That’s because the assert acts on operations that take qubit arrays as arguments, and the first argument to the assert is the size of those arrays. With lambdas we can make this a bit simpler: 

open Microsoft.Quantum.Diagnostics;
open Microsoft.Quantum.Intrinsic;

operation CustomZDecomp(q : Qubit) : Unit is Ctl + Adj {
    S(q);
    S(q);
}

@Test("QuantumSimulator")
operation TestDecomp() : Unit {
    AssertOperationsEqualReferenced(
        1,
        (qs => CustomZDecomp(qs[0])),
        (qs => Z(qs[0])));
}

The lambdas let us remove those wrappers and express the mapping from qubit arrays to qubits directly in the argument to the assert. 

Lambda expressions are a great way to add flexibility and convenience to Q# programs and help save you on some clutter and overhead while developing. We hope you find them useful and enjoyable.

Author

Scott Carda
Software Engineer

I am a software engineer working on the compiler for Microsoft's Q# programming language. I enjoy taking walks, hiking, cooking, board games, video games, tennis, and philosophy.

0 comments

Discussion are closed.