Conditional Operations & Classical Logic

Quantinuum Systems includes real-time logical operations on bit registers and classically conditioned gate operations on qubits. This capability is usually combined with MCMR operations to enable conditional gate operations during job execution. The total cost for jobs includes conditional gate operations, if included in user program.

For more information, visit Classical and Conditional Operations

Classical Logical Expressions

Classical assignment of registers or bits

() is used to add a qubit (bit) register to a pytket.circuit.Circuit. The user must specify number of qubits (bits) and register name.

from pytket.circuit import Circuit

circuit = Circuit(name="Conditional Example")
qreg = circuit.add_q_register("q", 1)
reg_a = circuit.add_c_register("a", 10)
reg_b = circuit.add_c_register("b", 10)
reg_c = circuit.add_c_register("c", 10)

and is used to set register value.

  • add_c_setbits([1], [reg_a[0]]) is used to set the first bit of the classical register, a[0], to a binary value, 1.

  • add_c_setreg(2, reg_a) is used to set the entire register to a decimal value, 2. This equates the bits in the register (reg_a[0], reg_b[1]) having the value 10.

  • add_c_setreg(3, reg_b) is used to set the entire register to a decimal value, 3. This equates the bits in the register (reg_b[0], reg_b[1]) having the value 11.

circuit.add_c_setbits([1], [reg_a[0]])  # a[0] = 1
circuit.add_c_setreg(2, reg_a)  # a = 2
circuit.add_c_setreg(3, reg_b)  # b = 3

Binary operators

Bitwise binary operators are avilable and can be applied to entire classical registers or bits. This allows one to update values based on mid-circuit measurement results as well as allow more advanced classical register comparisons.

  • add_classicalexpbox_register(reg_a ^ reg_b, reg_c) sets the register, reg_c, to the bitwise XOR of reg_a and reg_b (reg_c = reg_a ^ reg_b).

  • add_classicalexpbox_register(reg_a[0] ^ reg_b[0], [reg_c[0]]) sets the bit, reg_c[0], to the bitwise XOR between two bits, reg_a[0] and reg_b[0] (reg_c = reg_a ^ reg_b).

  • reg_a & reg_b is the bitwise AND between registers reg_a and reg_b.

  • reg_a | reg_b is the bitwise OR between registers reg_a and reg_b.

circuit.add_classicalexpbox_register(reg_a ^ reg_b, reg_c)  # c = a ^ b
circuit.add_classicalexpbox_bit(reg_a[0] ^ reg_b[0], [reg_c[0]])  # c[0] = a[0] & b[0]
circuit.add_classicalexpbox_register(reg_a & reg_b, reg_c)  # c = a & b
circuit.add_classicalexpbox_register(reg_a | reg_b, reg_c)  # c = a | b

Compound Logical Expressions

Comparison operators in addition to the == operator are available and you can evaluate bits. Note, the != operator can be useful in identifying if measurement results were trivial (for example, meas!=0) or not.We can operate a quantum gate on a quantum circuit when such a logical formula is satisfied as below.

from pytket.circuit.logic_exp import (
    if_bit,
    if_not_bit,
    reg_eq,
    reg_neq,
    reg_gt,
    reg_lt,
    reg_geq,
    reg_leq
)

circuit.X(qreg[0], condition=reg_a[0]) # if(a[0]==1) x q[0], evaluation of a bit
circuit.X(qreg[0], condition=if_bit(reg_a[0]))  # if(a[0]==1) x q[0], evaluation, same function as above
circuit.X(qreg[0], condition=if_not_bit(reg_a[0]))  # if(a[0]==0) x q[0]
circuit.X(qreg[0], condition=reg_eq(reg_a, 1))  # if(a==1) x q[0]
circuit.X(qreg[0], condition=reg_neq(reg_a, 1))  # if(a!=1) x q[0]
circuit.X(qreg[0], condition=reg_gt(reg_a, 1))  # if (reg_a > 1)
circuit.X(qreg[0], condition=reg_lt(reg_a, 1))  # if (reg_a < 1)
circuit.X(qreg[0], condition=reg_geq(reg_a, 1))  # if (reg_a >= 1)
circuit.X(qreg[0], condition=reg_leq(reg_a, 1))  # if (reg_a <= 1)

Conditional Classical Assignments

Classical bits can have conditional assignments based on classical conditions on other bits or bit registers being satsified.

circuit.add_c_setreg(1, reg_b, condition=reg_eq(reg_a, 10))  # if (a==10) b=1
circuit.add_c_setreg(1, reg_b, condition=if_not_bit(reg_a[0]))  # if (a[0]==0) b=1

Execution of conditional operations and classical expressions

A Bell state is prepared using OpType.X, OpType.CX, OpType.H and OpType.Measure operations.

from pytket.circuit import Circuit

circ = Circuit(name="Conditional Gates Example")
qreg = circ.add_q_register("q", 3)
creg = circ.add_c_register("b", 2)
circ.X(qreg[0]).H(qreg[0])
circ.H(qreg[1])
circ.CX(qreg[1], qreg[2])
circ.CX(qreg[0], qreg[1])
circ.H(qreg[0])
circ.Measure(qreg[0], creg[0])
circ.Measure(qreg[1], creg[1])

OpType.X (OpType.Z) operation is applied if the bit, creg[1] (creg[0]) has the value, 1.

circ.X(qreg[2], condition=if_bit(creg[1]))
circ.Z(qreg[2], condition=if_bit(creg[0]))

pytket._tket.circuit.ProjectorAssertionBox enables users to assert a quantum state. This assertion is executed during the coherence time of the qubit and carries a user-specified name.

from pytket.circuit import ProjectorAssertionBox
import numpy as np

proj = np.array([[0.5, -0.5], [-0.5, 0.5]])
circ.add_assertion(ProjectorAssertionBox(proj), [qreg[2]], name="debug")

The Nexus project Conditional Operations is set as the active project in the python session. The Bell state circuit is uploaded and the circuit reference is used for the compile job and execute job routines.

import qnexus as qnx
import datetime

config = qnx.QuantinuumConfig(device_name="H1-Emulator")

project = qnx.projects.get_or_create("Conditional Operations")
qnx.context.set_active_project(project)

name_suffix = datetime.datetime.now().strftime("%Y_%m_%d")
ref_circuit = qnx.circuits.upload(circuit=circ, name=f"circuit-{name_suffix}", description="Bell State with conditionals and projector")
ref_compile_job = qnx.start_compile_job(
    circuits=[ref_circuit],
    optimisation_level=0,
    backend_config=config,
    name=f"compile-job-{name_suffix}"
)
qnx.jobs.wait_for(ref_compile_job)
ref_compiled_circuit = qnx.jobs.results(ref_compile_job)[0].get_output()

The circuit is costed below with 100 shots. All the gate operations on qubits are included in the total cost, including conditional gate operations.

n_shots = 100
h1_cost = qnx.client.circuits.cost(
    ref_compiled_circuit, 
    n_shots=n_shots, 
    backend_config=qnx.QuantinuumConfig(device_name="H1-1")
)
print(f"Cost: {h1_cost} HQC")
ref_execute_job = qnx.start_execute_job(
    circuits=[ref_compiled_circuit],
    backend_config=config,
    n_shots=[n_shots],
    name=f"execute-job-{name_suffix}"
)
qnx.jobs.wait_for(ref_execute_job)
ref_result = qnx.jobs.results(ref_execute_job)[0]
result = ref_result.download_result()

The function, , the success rate of the state assertion operation averaged across shots. Shots fail due to system error.

result.get_debug_info()