Integrated hybrid computing

Integrated hybrid computing brings the classical and quantum processes together, allowing classical code to control the execution of quantum operations based on mid-circuit measurements while the physical qubits remain alive. Using common programming techniques, such as nested conditionals, loops, and function calls, a single quantum program can run complex problems, reducing the number of shots needed. Using qubit reuse techniques, larger programs can run on machines utilizing a smaller number of qubits.

For more discussion, see:

Prerequisites

Supported targets

Currently, the integrated hybrid computing model in Azure Quantum is supported on Quantinuum targets.

Submitting integrated hybrid jobs

When submitting an integrated hybrid job, you need to configure the target profile as QIR Adaptive RI. 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 integrated hybrid Q# standalone programs or Python + Q# programs to Azure Quantum. To configure the target profile for integrated hybrid jobs, follow the instructions below.

To configure the target profile for integrated 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 integrated hybrid 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 integrated hybrid 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 integrated hybrid jobs, you 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 does not support.

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

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 an integrated hybrid job

You can estimate the cost of running an integrated hybrid 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 are conditionally executed or not.

Integrated hybrid samples

The following samples can be found in Q# code samples repository. They demonstrate the current feature set for integrated hybrid 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.

*This sample code was written by members of KPMG Quantum team in Australia and falls under an MIT License. It aims to demonstrate 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.