An Introduction To Q#

Introduction

In this article, I will be explaining about Q# - the new programming language introduced by Microsoft for quantum computing. We will understand the data type, expressions, and statements of Q# with the help of code snippets.

Prerequisites

Please visit my earlier article An Introduction To Quantum Computing to get a basic understanding of Quantum Computing and to also know how to install Quantum Development Kit in Visual Studio 2017.

What is Q#?

According to Microsoft, Q# is a scalable, multi-paradigm, domain-specific programming language for quantum computing.

So, what do these terms actually mean? Let us dive into the details.

  • Scalable
    Q# allows us to write a code that can be executed on machines of varying computing abilities. We can use it to simulate a few Qubits on our local machine or even thousands of Qubits for an enterprise level application.

  • Multi-paradigm
    Q# is a multi-paradigm programming language as it supports both, functional as well as imperative, programming styles. If you are new to the programming paradigm, I suggest you to please refer here.

  • Domain-specific
    Q# is a programming language for quantum computing. It is to be used for writing algorithms/code snippets to be executed on quantum processors.

Validating Q# development environment on your machine

As this point in time, I assume that you have already installed Quantum Development Kit (QDK). If not, then please do it by referring to the prerequisites section.

After you have successfully installed QDK, we need to verify if VS 2017 has all the required dependencies installed to start with Q# development. For this, we will clone and execute the quantum sample programs from GitHub provided by Microsoft.

Open VS 2017 and navigate to Team >> Manage Connections.

Select Clone under Local Git Repositories and enter the URL: - https://github.com/Microsoft/Quantum.git and click "Clone".

 

The repository will be cloned on your local computer and Visual Studio will switch to the Solution Explorer displaying all the cloned libraries and samples.

Now, open QsharpLibraries.sln solution. If you are prompted with the “Install Missing Features popup box, click "Install" to allow the installation of the necessary features. This will download and install F# and other tools used by some of the samples. Make sure that you are connected the to internet.

 

To execute a sample program, right-click on the TeleportationSample project in "Samples > 0.Introduction folder" of QsharpLibraries solution, and then click on "Set as Startup Project" and press F5.



If you can see an output screen similar to the one shown below, then congratulations, your VS 2017 is ready for Q# development.



Note that your output screen may vary because the data that is being teleported is random. But it should send 8 rounds of data with all being successfully teleported. 

Q# Type model

Let us understand what are the various type models provided by Q#

Primitive Type
  • Int: - It represents the 64 -bit signed integer. Notice the upper case ‘I’. This is in contrast to int in C# with lower case ‘i’.
  • Double: - It represents double-precision floating point number. This also has a upper case ‘D’ in contrast to double in C#.
  • Bool: - It represents the Boolean type and can take two values – true or false.
  • Qubit: - This represents the Quantum bit. Qubit is the fundamental unit of processing information in quantum computers, similar to a bit in classical computers.
  • Pauli: - This type is used to denote the base operation for rotations and to specify the basis of a measurement. This can take four possible values PauliI, PauliX, PauliY, PauliZ
  • Result: - This represents the result of a measurement. This can take two possible values Zero or One
  • Range: - This represents a sequence of integers.
  • String: - It represents a sequence of Unicode characters.
Array Type

We can create an array type of any valid Q# primitive type. Q# does not support rectangular multi-dimensional array, instead it supports only jagged arrays.

e.g.: - Int[], Qubit[][]

By default, all variable in Q# are immutable i.e. their values cannot be changes after they are bound. Hence to create an array whose values can be set, we will use mutable keyword
e.g. mutable myArr=new Int [5];

This will create an integer array myArr of size 5. The elements of a new array are initialized to a type-dependent default value, in this case it will be 0, the default value for an integer type.

Arrays passed as arguments are immutable. All arrays in Q# are zero-based, i.e., the first element of an array arr is always arr[0].

Tuple Type

Tuple type represents a tuple of values of any given primitive type. It is represented as (T1, T2, T3,…) where T1, T2, T3 is a primitive type. The Q# tuple is immutable i.e. we cannot change the contents of the tuple once it has been created.

A tuple expression can contain values of multiple primitive type i.e. a tuple of type (Int, Double, Result) is a valid tuple.

We can create a tuple with single element also like (2). This is known as singleton tuple and it is considered equal to the value of enclosed type. This property is called singleton tuple equivalence.

e.g. – (2) is a singleton tuple of type Int, but it is considered equivalent to an integer 2

User Defined type

We can create a user defined type of any primitive type. We can also create an array of user defined type or can also include it in a tuple. User defined type cannot have cyclic dependency on each other i.e. it is not possible to create a recursive type structure. Since, a user-defined type is a subtype of the base type. Hence it can be used anywhere a value of the base type is expected.

Operation Type

A Q# operation is a callable routine, which contains Q# code to carry out a quantum operation. An operation is the basic unit of quantum execution in Q#. The operation can only take single value as input in form of a tuple and return a single value as output specified after a colon and may be a tuple.

An operation has a body section, which contains the implementation of the operation. It can also have adjoint, controlled, and controlled adjoint sections. These are used to specify specific variants of appropriate operations. The arguments to an operation are specified as a tuple, within parentheses. The return type of the operation is.

Refer to a sample operation mentioned below.
  1. operation AddInteger(a: Int, b: Int): Int {  
  2.     body {  
  3.         mutable c = 0;  
  4.         set c = a + b;  
  5.         return (c);  
  6.     }  
  7. }  
Here, we have defined an operation AddInteger which takes a tuple (Int, Int) as input and returns an output of type Int after performing an Add operations on input integers.

Function Type

A Q# Function is classical subroutine used within a Quantum algorithm and can only contains classical code but no quantum operations. Similar to Q# operation, function will also take single value as input and returns a single value as output, both of them can be a tuple. Functions cannot allocate qubits or call operations.

Let’s look at a sample function.
  1. function ProductNumber(a: Double, b: Double): Double {  
  2.     mutable c = 0.0;  
  3.     set c = a * b;  
  4.     return (c);  
  5. }  
Here, we have defined a function ProductNumber, which takes a tuple (Double, Double) as input and returns an output of type Double after performing the product of input values. Also, notice that a function does not have a body section as in case of an operation.

Q# Expressions

Let’s take a look at various expressions provided in Q#.

Numeric Expressions

There are two types of numeric expressions provided by Q#.

  • Integer numbers :- it is represented by Int
  • Floating point numbers :- it is represented by Double

To represent a hexadecimal integer, we use "0x" prefix.

We can also perform binary operations on numeric expressions to form a new numeric expression. The type of the new expression will be Double if both of the input expressions are floating point numbers, or will be an Int if both are integers.

Apart from binary operations, the numeric expressions also support modulus, power, bitwise AND, bitwise OR, bitwise XOR and bitwise complement operations.

Qubit Expressions

Qubit expressions are the symbols that are bound to qubit values or the elements of a qubit array. Q# does not provide any support for qubit literal

Pauli Expressions

As we have discussed earlier that the primitive type Pauli can take four possible possible values PauliI, PauliX, PauliY, PauliZ. These all are valid Pauli expressions. We can also create an array of Pauli and the array elements are considered as a valid Pauli expression.

Result Expressions

The two possible result values Zero and One are valid Result expressions. One important point to note is that One is not same as integer 1 and Zero is not same as integer 0 and also there is no direct conversion between them. This is in contrast to C# where, boolean true is considered same as integer 1 and boolean false is considered same as integer 0. 

Range Expressions

A range expression is represented as start..step..stop where start , step, stop are all integers and the range expression can take values as start, start+step, start+step+step and so on until stop is passed.

If only start and stop is mentioned in a range expression, then it will take the value of the step is set to 1 implicitly.

Let’s understand this with the help of example
  • 1..3 - this indicates the range 1,2,3 i.e. 1, 1+1,1+1+1
  • 1..2..6 – this indicates the range 1,3,5 i.e. 1, 1+2,1+2+2
  • 8..-2..3 – this indicates the range 8,6,4 i.e. 8, 8+(-2), 8+(-2)+(-2)

Array Expressions

In Q# an array can be represented as a set of elements expressions , separated by semicolons, enclosed within []. Similar to C#, all elements of an array in Q# should have same type.

e.g. [1;2;3] is a valid array whereas [1;2.5;Zero] is an invalid array

We can also use the ‘+’ operator to concatenate two arrays of same type.

e.g. [2;4;6] + [8;10;12] will give [2;4;6;8;10;12] as output.

To find the length of an array we use Length built-in function.

E.g. :- If myArr an integer array having 5 elements , then Length(myArr) will return 5 as the output. 

Q# Statements

Symbol binding

Symbols in Q# can be mutable or immutable.

An immutable symbol cannot be changed after it has been bound. We use let keyword to define and bind an immutable symbol.

e.g. let i=8;

This will bind the symbol i as an integer with value 8. If we try to set the value of an immutable expression we will get a compile time error.

Hence set i=10; will give error in this case.

A mutable symbol value can be changed after it has been bound. We use the mutable keyword to define and bind a mutable symbol.

e.g. mutable i=8;

This will bind the symbol i as an integer with value 8.

To change the value of a mutable symbol we use set keyword

  1. set i=10;  

This will update the value of variable i to 10 

for- loop

Q# allows a for-loop to iterate over an integer range. The for statement consists of the keyword for, followed by an identifier, the keyword in, a Range expression, and a statement block.

A range is specified by the first and last integers in the range, for example: 1..5 represents the range 1, 2, 3, 4, and 5. If a step other than +1 is needed, then three integers with .. between them is used: 1..2..10 is the range 1, 3, 5, 7, and 9. The range is inclusive at both ends. 

e.g
  1. for(num in 1..2..10)  
  2. {  
  3.    //Do something  
  4. }  
Repeat-Until-Success Loop

As the name suggest this loop will repeat until successful operation occurs. This loop is based on the quantum “repeat until success” pattern. It consists of the keyword repeat and it's statement block, the keyword until, a Boolean expression, the keyword fixup and it's statement block .

The statement inside the repeat block is executed and then the boolean condition is evaluated, if the boolean condition evaluates to true, then the loop terminates, otherwise the fixup block is executed and the loop repeats once again.

The fixup block is always required even if there is no fixup to be done, in which case it will be empty. 

e.g.
  1. repeat {  
  2.     //do something  
  3. }  
  4. until boolean condition  
  5. fixup {  
  6.     // do something  
  7. }  
Conditional Statement

Q# supports if statement for conditional execution, similar to C#. The if statement consists of the keyword if, followed by a Boolean expression and the statement block. An if block may have an optional else block which is represented by the keyword else
e.g 
  1. if (num % 2 == 0) {  
  2.     return true;  
  3. else {  
  4.     return false;  
  5. }  
A conditional statement can consist of a series of if-elseif-else chain. The else-if clause, is represented by the keyword elif,
  1. if (num == 1) {  
  2.     //do something  
  3. }  
  4. elif(num == 2) {  
  5.     //do something  
  6. }  
  7. else {  
  8.     //do something  
  9. }  

Conclusion

We have learned the basics of Q# programming language. We also installed QDK and verified the Q# execution environment with Visual Studio 2017. Please post your valuable feedback in the comments section and stay tuned for more on Quantum Computing.

You can always refer to my previous articles here.


Similar Articles