Structure of a Q# program

This article explores the general components that make up a Q# program. Note that Q# programs written in Jupyter Notebooks does not use some of these components - these differences are described in each section.

Consider the following Q# program:

namespace Superposition {

    @EntryPoint()
    operation MeasureOneQubit() : Result {
        // Allocate a qubit, by default it is in zero state      
        use q = Qubit();  
        // We apply a Hadamard operation H to the state
        // It now has a 50% chance of being measured 0 or 1  
        H(q);      
        // Now we measure the qubit in Z-basis.
        let result = M(q);
        // We reset the qubit before releasing it.
        Reset(q);
        // Finally, we return the result of the measurement.
        return result;
    }
}

By just reading the comments (//), you can tell that this program allocates a qubit, applies an operation to put it in superposition, measures the state of the qubit, then resets it and returns the result.

To run this program in Visual Studio Code, see Get started with Q# programs and VS Code.

User namespaces

Q# programs typically start with a user-named namespace, such as

namespace Superposition {
    // Your code goes here.
}

Namespaces help you organize related functionality. Namespaces are user-named, and there can only be one namespace per qsharp (*.qs) file.

The Q# standard library has predefined namespaces that contain functions and operations that you can use in quantum programs. For more information, see Built-in namespaces.

Jupyter Notebooks do not use user namespaces.

EntryPoint()

The @EntryPoint() attribute tells the Q# compiler where to begin executing the program. In programs with multiple function and operation definitions, the @EntryPoint() can be placed before any of the functions or operations and program flow starts from there and continues sequentially.

    ...
    @EntryPoint()
    operation MeasureOneQubit() : Result {
        ...

Jupyter Notebooks do not use entry points.

The %%qsharp command

By default, Q# programs in Jupyter Notebooks use the ipykernel Python kernel. In order to add Q# code to a notebook cell, you need to use the %%qsharp command, which is enabled with the qsharp Python package. For example, the previous sample code in a Jupyter Notebook looks like this:

import qsharp
%%qsharp

    operation MeasureOneQubit() : Result {
        // Allocate a qubit, by default it is in zero state      
        use q = Qubit();  
        // We apply a Hadamard operation H to the state
        // It now has a 50% chance of being measured 0 or 1  
        H(q);      
        // Now we measure the qubit in Z-basis.
        let result = M(q);
        // We reset the qubit before releasing it.
        Reset(q);
        // Display the result
        Message($"Result is {result}");
        // Finally, we return the result of the measurement.
        return result;
    
    }
    MeasureOneQubit();

Note the absence of a user namespace or an @EntryPoint(), which are not needed for Jupyter Notebooks. Instead of an entry point, the operation is called directly in the last line. Also note that a Message statement was added to the Jupyter Notebook code to display the result. When you run the earlier Q# program in VS Code, the built-in simulator displays the result by default.

When using the %%qsharp command:

  • You must run import qsharp first to enable the %%qsharp command.
  • The %%qsharp command is scoped to the entire cell in which it appears.
  • The Q# code that follows the command must adhere to standard Q# coding syntax. For example, you denote comments using // instead of # within %%qsharp cells, and code lines must end with a semi-colon ;.
  • The %%qsharp command cannot be preceded by or followed by a Python statement within its cell.

For an example of working with a Jupyter Notebook program, see Get started with Q# programs and VS Code.

Types

Q# provides many built-in types that are common to most languages, including Int, Double, Bool, and String, along with types that are specific to quantum computing. For example, the Result type represents the result of any qubit measurement and can have one of two possible defined values: One and Zero. In the example program, the operation MeasureOneQubit() expects a return type of Result and the M operation measures the qubit and returns the Result.

...
// operation definition expecting a return type of Result
operation MeasureOneQubit() : Result {
    ...
    // Now we measure the qubit in Z-basis, returning a Result type
    let result = M(q);
    ...
}

Q# also provides types that define ranges, arrays, and tuples. You can even define your own custom types.

Allocating qubits

In Q#, qubits are allocated through the use keyword.

Our example defines a single qubit:

// Allocate a qubit.
use q = Qubit();
...

but you can also allocate multiple qubits and access each one through its index:

...
use qubits = Qubit[2];
X(qubits[1]);
H(qubits[0]);
...

By default, every qubit you allocate with the use keyword starts in the zero state. Each qubit must be reset back the zero state before it is released at the end of the program. Failing to reset a qubit triggers a runtime error.

// Reset a qubit.
Reset(q);
...

Quantum operations

Once allocated, a qubit can be passed to operations and functions, also referred to as callables. Operations are the basic building blocks of a Q# program. A Q# operation is a quantum subroutine. That is, it's a callable routine that contains quantum operations that modify the state of the qubit register.

To define a Q# operation, you specify a name for the operation along with its inputs and its output. In our example, the single operation is essentially the entire program. It takes no parameters and expects a return type of Result:

operation MeasureOneQubit() : Result {
    ...
}

Here's a basic example that takes no parameters and expects no return value. The Unit value is equivalent to NULL in other languages.

operation SayHelloQ() : Unit {
    Message("Hello quantum world!");
}

The Q# standard library also provides operations that you can use in your programs, for example the Hadamard or the H operation that is used in the example program. Given a qubit in Z-basis, the H operation puts the qubit into an even superposition. Once in superposition, the qubit has a 50% chance of being measured as zero or one.

Measuring qubits

There are many types of quantum measurements, but Q# focuses on projective measurements on single qubits, also known as Pauli measurements. Upon measurement in a given basis (for example, the computational basis $\ket{0},\ket{1}$) the qubit state is projected onto whichever basis state was measured, hence destroying any superposition between the two.

Our example program uses the M operation, which performs a measurement of a single qubit in the Pauli Z basis and returns a Result type.

Built-in namespaces

The standard Q# library makes use of built-in namespaces that contain functions and operations that you can use in quantum programs. For example, the namespace Microsoft.Quantum.Intrinsic contains commonly used operations and functions such as M, to measure results and Message, to display user messages anywhere in the program.

You can call a function or operation by specifying the full namespace, or use an open statement to make all the functions and operations for that namespace available, and to make your code easier to read. These two examples call the same operation:

 Microsoft.Quantum.Intrinsic.Message("Hello quantum world!");
open Microsoft.Quantum.Intrinsic;
Message("Hello quantum world!");

Notice in the example program, there are no open statements or calls with full namespaces. That is because the Q# development environment automatically loads two namespaces by default - Microsoft.Quantum.Core and Microsoft.Quantum.Intrinsic - which contain commonly used functions and operations.

You can take advantage of the Microsoft.Quantum.Measurement namespace and use the MResetZ operation to optimize the code in the example program. MResetZ combines the measurement and reset operations into one step, as in the following example:

namespace Superposition {

    // open the namespace for the MResetZ operation
    open Microsoft.Quantum.Measurement;

    @EntryPoint()
    operation MeasureOneQubit() : Result {
        // Allocate a qubit, by default it is in zero state      
        use q = Qubit();  
        // We apply a Hadamard operation H to the state
        // It now has a 50% chance of being measured 0 or 1  
        H(q);   
        // Measure and reset the qubit, and return the result value   
        return MResetZ(q);
    }
    
}