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 ofreg_a
andreg_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]
andreg_b[0]
(reg_c
=reg_a ^ reg_b
).reg_a & reg_b
is the bitwise AND between registersreg_a
andreg_b
.reg_a | reg_b
is the bitwise OR between registersreg_a
andreg_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()