Run hybrid quantum computing jobs with adaptive target profile

Hybrid computing combines classical and quantum computing processes to solve complex problems.

In hybrid computing, the classical code controls the execution of quantum operations based on mid-circuit measurements while the physical qubits remain active. You can use common programming techniques, such as nested conditionals, loops, and function calls, in a single quantum program to run complex problems, reducing the number of shots needed. With qubit reuse techniques, larger programs can run on machines using a smaller number of qubits.

This article explains how to submit hybrid jobs to Azure Quantum using the QIR Adaptive RI target profile. The Adaptive RI target profile offers support for mid-circuit measurements, measurement-based control flow, qubit reset, and classical integer computation.

Prerequisites

Supported targets

To run hybrid quantum computing jobs, you need to select a quantum provider that supports the Adaptive RI target profile.

Currently, adaptive target profile in Azure Quantum is supported on Quantinuum targets.

Submitting adaptive RI jobs

To submit hybrid quantum computing jobs, you need to configure the target profile as QIR Adaptive RI, where RI stands for "qubit Reset and Integer computations".

The QIR Adaptive RI target profile offers support for mid-circuit measurements, measurement-based control flow, qubit reset, and classical integer computation.

You can submit hybrid quantum jobs to Azure Quantum as Q# standalone programs or Python + Q# programs. To configure the target profile for hybrid quantum jobs, see the following sections.

To configure the target profile for hybrid jobs in Visual Studio Code, follow these steps:

  1. Open a Q# program in Visual Studio Code.
  2. Select View -> Command Palette and type Q#: Set the Azure Quantum QIR target profile. Press Enter.
  3. Select QIR Adaptive RI.

Once you set QIR Adaptive RI as target profile, you can submit your Q# program as a hybrid quantum job to Quantinuum.

  1. Select View -> Command Palette and type Q#: Connect to an Azure Quantum workspace. Press Enter.
  2. Select Azure account, and follow the prompts to connect to your preferred directory, subscription, and workspace.
  3. Once you are connected, in the Explorer pane, expand Quantum Workspaces.
  4. Expand your workspace and expand the Quantinuum provider.
  5. Select any Quantinuum available target, for example quantinuum.sim.h1-1e.
  6. Select the play icon to the right of the target name to start submitting the current Q# program.
  7. Add a name to identify the job, and the number of shots.
  8. Press Enter to submit the job. The job status displays at the bottom of the screen.
  9. Expand Jobs and hover over your job, which displays the times and status of your job.

Supported features

The following table lists the supported features for hybrid quantum computing with Quantinuum in Azure Quantum.

Supported feature Notes
Dynamics values Bools and integers whose value depend on a measurement result
Loops Classically-bounded loops only
Arbitrary control flow Use of if/else branching
Mid-circuit measurement Utilizes classical register resources
Qubit reuse Supported
Real-time classical compute 64-bit signed integer arithmetic
Utilizes classical register resources

The QDK provides target-specific feedback when Q# language features aren't supported for the selected target. If your Q# program contains unsupported features when running hybrid quantum jobs, you'll receive an error message at design-time. For more information, see the QIR wiki page.

Note

You need to select the appropriate QIR Adaptive RI target profile to obtain appropriate feedback when using Q# features that the target doesn't support.

To see the supported features in action, copy the following code into a Q# file and add the subsequent code snippets.

namespace HybridIntegrated {
    open Microsoft.Quantum.Measurement;
    open Microsoft.Quantum.Math;
    open Microsoft.Quantum.Convert;

    @EntryPoint()
    operation Main() : Result {
        use (q0, q1) = (Qubit(), Qubit());
        H(q0);
        let r0 = MResetZ(q0);

        // Copy here the code snippets below to see the supported features 
        // in action.
        // Supported features include dynamic values, classically-bounded loops, 
        // arbitrary control flow, and mid-circuit measurement.

        r0
    }

}

Quantinuum supports dynamic bools and integers, which means bools and integers that depend on measurement results. Note that r0 is a Result type that can be used to generate dynamic bool and integer values.

        let dynamicBool = r0 != Zero; 
        let dynamicBool = ResultAsBool(r0); 
        let dynamicInt = dynamicBool ? 0 | 1; 

Quantinuum supports dynamic bools and integers, however, it doesn't support dynamic values for other data types, such as double. Copy the following code to see feedback about the limitations of dynamic values.

        let dynamicDouble = r0 == One ? 1. | 0.; // cannot use a dynamic double value
        let dynamicInt = r0 == One ? 1 | 0;
        let dynamicDouble = IntAsDouble(dynamicInt); // cannot use a dynamic double value
        let dynamicRoot = Sqrt(dynamicDouble); // cannot use a dynamic double value

Even though dynamic values are not supported for some data types, those data types can still be used with static values.

    let staticRoot = Sqrt(4.0);
    let staticBigInt = IntAsBigInt(2);

Even dynamic values of supported typed can't be used in certain situations. For example, Quantinuum doesn't support dynamic arrays, that is, arrays whose size depends on a measurement result. Quantinuum doesn't support dynamically-bounded loops either. Copy the following code to see the limitations of dynamic values.

        let dynamicInt = r0 == Zero ? 2 | 4;
        let dynamicallySizedArray = [0, size = dynamicInt]; // cannot use a dynamically-sized array
        let staticallySizedArray = [0, size = 10];
        // Loops with a dynamic condition are not supported by Quantinuum.
        for _ in 0..dynamicInt {
            Rx(PI(), q1);
        }
        
        // Loops with a static condition are supported.
        let staticInt = 3;
        for _ in 0..staticInt {  
            Rx(PI(), q1);  
        }

Quantinuum supports control flow, including if/else branching, using both static and dynamic conditions. Branching on dynamic conditions is also known as branching based on measurement results.

        let dynamicInt = r0 == Zero ? 0 | 1; 
        if dynamicInt > 0 {
            X(q1);
        }
        let staticInt = 1;
        if staticInt > 5 {
            Y(q1);
        } else {
            Z(q1);
        }

Quantinuum supports loops with classical conditions and including if expressions.

        for idx in 0..3 {
            if idx % 2 == 0 {
                Rx(ArcSin(1.), q0);
                Rz(IntAsDouble(idx) * PI(), q1)
            } else {
                Ry(ArcCos(-1.), q1);
                Rz(IntAsDouble(idx) * PI(), q1)
            }
        }

Quantinuum supports mid-circuit measurement, that is, branching based on measurement results.

        if r0 == One {
            X(q1);
        }
        let r1 = MResetZ(q1);
        if r0 != r1 {
            let angle = PI() + PI() + PI()* Sin(PI()/2.0);
            Rxx(angle, q0, q1);
        } else {
            Rxx(PI() + PI() + 2.0 * PI() * Sin(PI()/2.0), q1, q0);
        }

Estimating the cost of a hybrid quantum computing job

You can estimate the cost of running a hybrid quantum computing job on Quantinuum hardware by running it on an emulator first.

After a successful run on the emulator:

  1. In your Azure Quantum workspace, select Job management.
  2. Select the job you submitted.
  3. In the Job details popup, select Cost Estimation to view how many eHQCs (Quantinuum emulator credits) were used. This number translates directly to the number of HQCs (Quantinuum quantum credits) that are needed to run the job on Quantinuum hardware.

Cost estimation

Note

Quantinuum unrolls the entire circuit and calculates the cost on all code paths, whether they're conditionally executed or not.

Hybrid quantum computing samples

The following samples can be found in Q# code samples repository. They demonstrate the current feature set for hybrid quantum computing.

Three-qubit repetition code

This sample demonstrates how to create a three-qubit repetition code that can be used to detect and correct bit flip errors.

It leverages integrated hybrid computing features to count the number of times error correction was performed while the state of a logical qubit register is coherent.

You can find the code sample here.

Iterative phase estimation

This sample program demonstrates an iterative phase estimation within Q#. It uses iterative phase estimation to calculate an inner product between two 2-dimensional vectors encoded on a target qubit and an ancilla qubit. An additional control qubit is also initialized which is the only qubit used for measurement.

The circuit begins by encoding the pair of vectors on the target qubit and the ancilla qubit. It then applies an Oracle operator to the entire register, controlled off the control qubit, which is set up in the $\ket +$ state. The controlled Oracle operator generates a phase on the $\ket 1$ state of the control qubit. This can then be read by applying an H gate to the control qubit to make the phase observable when measuring.

You can find the code sample here.

Note

This sample code was written by members of KPMG Quantum team in Australia and falls under an MIT License. It demonstrates expanded capabilities of QIR Adaptive RI targets and makes use of bounded loops, classical function calls at run time, nested conditional if statements, mid circuit measurements, and qubit reuse.