Pytket to QIR¶
pytket-qir is an client-side extension (plugin) for pytket, the quantum circuit toolkit & compiler framework by Quantinuum. Its purpose is to provide functionality for 1-way conversion of pytket circuits into the QIR (Quantum Intermediate Representation) format. It allows users of pytket to target QIR-compliant hardware providers or off-the-shelf simulation tool.
pytket-qir is cross-platform on Windows, Linux and macOS. It is compatible with Python 3.10 - 3.13, and available on PyPi. An API reference is available at https://docs.quantinuum.com/tket/extensions/pytket-qir/. The package is opensource (Apache 2.0), available at https://github.com/CQCL/pytket-qir.git, and downloadable via PyPI.
pip install pytket-qir
Basic Usage¶
from pytket.circuit import Circuit, Qubit
from pytket.circuit.clexpr import wired_clexpr_from_logic_exp
circ = Circuit(3)
a = circ.add_c_register("a", 5)
b = circ.add_c_register("b", 5)
c = circ.add_c_register("c", 5)
d = circ.add_c_register("d", 5)
circ.H(0)
circ.add_clexpr(*wired_clexpr_from_logic_exp(a | b, c)) # type: ignore
circ.add_clexpr(*wired_clexpr_from_logic_exp(c | b, d)) # type: ignore
circ.add_clexpr(*wired_clexpr_from_logic_exp(c | b, d), condition=a[4]) # type: ignore
circ.H(0)
circ.Measure(Qubit(0), d[4])
circ.H(1)
circ.Measure(Qubit(1), d[3])
circ.H(2)
circ.Measure(Qubit(2), d[2])
[ClExpr a[0], a[1], a[2], a[3], a[4], b[0], b[1], b[2], b[3], b[4], c[0], c[1], c[2], c[3], c[4]; H q[0]; H q[1]; H q[2]; ClExpr c[0], c[1], c[2], c[3], c[4], b[0], b[1], b[2], b[3], b[4], d[0], d[1], d[2], d[3], d[4]; H q[0]; IF ([a[4]] == 1) THEN ClExpr c[0], c[1], c[2], c[3], c[4], b[0], b[1], b[2], b[3], b[4], d[0], d[1], d[2], d[3], d[4]; Measure q[2] --> d[2]; Measure q[1] --> d[3]; Measure q[0] --> d[4]; ]
from pytket.qir.conversion.api import QIRFormat, QIRProfile, pytket_to_qir
gen_qir_ll = pytket_to_qir(
circ,
name="conditional",
qir_format=QIRFormat.STRING,
profile=QIRProfile.ADAPTIVE,
cut_pytket_register=True,
)
print(gen_qir_ll)
; ModuleID = 'conditional'
source_filename = "conditional"
%Qubit = type opaque
%Result = type opaque
@0 = internal constant [2 x i8] c"a\00"
@1 = internal constant [2 x i8] c"b\00"
@2 = internal constant [2 x i8] c"c\00"
@3 = internal constant [2 x i8] c"d\00"
define void @main() #0 {
entry:
call void @__quantum__qis__h__body(%Qubit* null)
call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 1 to %Qubit*))
call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 2 to %Qubit*))
call void @__quantum__qis__h__body(%Qubit* null)
br i1 false, label %condb0, label %contb0
condb0: ; preds = %entry
br label %contb0
contb0: ; preds = %condb0, %entry
%0 = phi i64 [ 0, %condb0 ], [ 0, %entry ]
call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 2 to %Qubit*), %Result* inttoptr (i64 2 to %Result*))
%1 = call i1 @__quantum__qis__read_result__body(%Result* inttoptr (i64 2 to %Result*))
%2 = zext i1 %1 to i64
%3 = mul i64 %2, 4
%4 = or i64 %3, %0
%5 = sub i64 1, %2
%6 = mul i64 %5, 4
%7 = xor i64 9223372036854775807, %6
%8 = and i64 %7, %4
call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Result* inttoptr (i64 1 to %Result*))
%9 = call i1 @__quantum__qis__read_result__body(%Result* inttoptr (i64 1 to %Result*))
%10 = zext i1 %9 to i64
%11 = mul i64 %10, 8
%12 = or i64 %11, %8
%13 = sub i64 1, %10
%14 = mul i64 %13, 8
%15 = xor i64 9223372036854775807, %14
%16 = and i64 %15, %12
call void @__quantum__qis__mz__body(%Qubit* null, %Result* null)
%17 = call i1 @__quantum__qis__read_result__body(%Result* null)
%18 = zext i1 %17 to i64
%19 = mul i64 %18, 16
%20 = or i64 %19, %16
%21 = sub i64 1, %18
%22 = mul i64 %21, 16
%23 = xor i64 9223372036854775807, %22
%24 = and i64 %23, %20
call void @__quantum__rt__int_record_output(i64 0, i8* getelementptr inbounds ([2 x i8], [2 x i8]* @0, i32 0, i32 0))
call void @__quantum__rt__int_record_output(i64 0, i8* getelementptr inbounds ([2 x i8], [2 x i8]* @1, i32 0, i32 0))
call void @__quantum__rt__int_record_output(i64 0, i8* getelementptr inbounds ([2 x i8], [2 x i8]* @2, i32 0, i32 0))
call void @__quantum__rt__int_record_output(i64 %24, i8* getelementptr inbounds ([2 x i8], [2 x i8]* @3, i32 0, i32 0))
ret void
}
declare i1 @__quantum__qis__read_result__body(%Result*)
declare void @__quantum__rt__int_record_output(i64, i8*)
declare void @__quantum__qis__h__body(%Qubit*)
declare void @__quantum__qis__mz__body(%Qubit*, %Result* writeonly) #1
attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="custom" "required_num_qubits"="3" "required_num_results"="3" }
attributes #1 = { "irreversible" }
!llvm.module.flags = !{!0, !1, !2, !3}
!0 = !{i32 1, !"qir_major_version", i32 1}
!1 = !{i32 7, !"qir_minor_version", i32 0}
!2 = !{i32 1, !"dynamic_qubit_management", i1 false}
!3 = !{i32 1, !"dynamic_result_management", i1 false}
Use Cases¶
Wasm Callouts¶
from pytket import wasm
from pytket.circuit import Bit, Circuit, Qubit
from pytket.wasm import WasmFileHandler
w = WasmFileHandler("testfile.wasm")
c = Circuit(6, 6)
c0 = c.add_c_register("c0", 3)
c1 = c.add_c_register("c1", 4)
c2 = c.add_c_register("c2", 5)
c.add_wasm_to_reg("multi", w, [c0, c1], [c2])
c.add_wasm_to_reg("add_one", w, [c2], [c2])
c.add_wasm_to_reg("no_return", w, [c2], [])
c.add_wasm_to_reg("init", w, [], [])
c.add_wasm_to_reg("no_parameters", w, [], [c2])
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
Cell In[3], line 5
2 from pytket.circuit import Bit, Circuit, Qubit
3 from pytket.wasm import WasmFileHandler
----> 5 w = WasmFileHandler("testfile.wasm")
6 c = Circuit(6, 6)
7 c0 = c.add_c_register("c0", 3)
File c:\Users\Irfan.Khan\Projects\nexus-docs\.venv\Lib\site-packages\pytket\wasm\wasm.py:315, in WasmFileHandler.__init__(self, filepath, check_file, int_size)
305 """
306 Construct a wasm file handler using a filepath to read a wasm module into
307 memory.
(...) 312 :param int_size: length of the integer that is used in the wasm file
313 """
314 if not exists(filepath):
--> 315 raise ValueError("wasm file not found at given path")
317 with open(filepath, "rb") as file:
318 self._wasm_file: bytes = file.read()
ValueError: wasm file not found at given path
from pytket.qir.conversion.api import QIRFormat, QIRProfile, pytket_to_qir
profile = QIRProfile.ADAPTIVE
result = pytket_to_qir(
c,
name=f"pytket_qir_wasm_2-{profile}",
qir_format=QIRFormat.STRING,
int_type=32,
profile=profile,
)
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
Cell In[4], line 5
1 from pytket.qir.conversion.api import QIRFormat, QIRProfile, pytket_to_qir
3 profile = QIRProfile.ADAPTIVE
----> 5 result = pytket_to_qir(
6 c,
7 name=f"pytket_qir_wasm_2-{profile}",
8 qir_format=QIRFormat.STRING,
9 int_type=32,
10 profile=profile,
11 )
File c:\Users\Irfan.Khan\Projects\nexus-docs\.venv\Lib\site-packages\pytket\qir\conversion\api.py:117, in pytket_to_qir(circ, name, qir_format, int_type, cut_pytket_register, profile)
114 cpass = scratch_reg_resize_pass(int_type)
115 cpass.apply(circ)
--> 117 check_circuit(circ, int_type)
119 m = tketqirModule(
120 name=name,
121 num_qubits=circ.n_qubits,
122 num_results=circ.n_qubits,
123 )
125 trunc = False
File c:\Users\Irfan.Khan\Projects\nexus-docs\.venv\Lib\site-packages\pytket\qir\conversion\api.py:230, in check_circuit(circuit, int_type)
217 def check_circuit(
218 circuit: Circuit,
219 int_type: int = 64,
220 ) -> None:
221 """Checks the validity of the circuit.
222
223 Running this check before conversion is recommended for big circuits that
(...) 228 :raises ValueError: with a suggestion on how to resolve the problems
229 """
--> 230 if len(circuit.q_registers) > 1 or (
231 len(circuit.q_registers) == 1 and circuit.q_registers[0].name != "q"
232 ):
233 raise ValueError(
234 """The circuit that should be converted should only have the default
235 quantum register. You can convert it using the pytket
236 compiler pass `FlattenRelabelRegistersPass`.""",
237 )
239 if int_type not in {32, 64}:
AttributeError: 'pytket._tket.unit_id.BitRegister' object has no attribute 'q_registers'
RNG Callouts¶
circ = Circuit()
creg = circ.add_c_register("c", 64)
circ.add_c_setbits([True, True], [creg[3], creg[11]])
circ.set_rng_seed(creg)
[SetBits(11) c[3], c[11]; RNGSeed c[0], c[1], c[2], c[3], c[4], c[5], c[6], c[7], c[8], c[9], c[10], c[11], c[12], c[13], c[14], c[15], c[16], c[17], c[18], c[19], c[20], c[21], c[22], c[23], c[24], c[25], c[26], c[27], c[28], c[29], c[30], c[31], c[32], c[33], c[34], c[35], c[36], c[37], c[38], c[39], c[40], c[41], c[42], c[43], c[44], c[45], c[46], c[47], c[48], c[49], c[50], c[51], c[52], c[53], c[54], c[55], c[56], c[57], c[58], c[59], c[60], c[61], c[62], c[63], _r[0]; ]
from pytket.qir.conversion.api import QIRFormat, QIRProfile, pytket_to_qir
profile = QIRProfile.ADAPTIVE
result = pytket_to_qir(
circ,
name=f"pytket_rng_wasm-{profile}",
qir_format=QIRFormat.STRING,
int_type=64,
profile=profile,
cut_pytket_register=True
)