diff --git a/docs/changelog.rst b/docs/changelog.rst index 65a95c1a..28bcbf53 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -6,6 +6,8 @@ Unreleased * Fix handling of unused bits in local emulator. * Update machine specs in offline API. +* Update pytket-qir version requirement to 0.13. +* Add language option for profile compatible QIR 0.37.0 (August 2024) -------------------- diff --git a/pytket/extensions/quantinuum/backends/quantinuum.py b/pytket/extensions/quantinuum/backends/quantinuum.py index 22dad2a7..b0deaf21 100644 --- a/pytket/extensions/quantinuum/backends/quantinuum.py +++ b/pytket/extensions/quantinuum/backends/quantinuum.py @@ -190,8 +190,17 @@ class DeviceNotAvailable(Exception): class Language(Enum): """Language used for submission of circuits.""" - QASM = "OPENQASM 2.0" - QIR = "QIR 1.0" + QASM = 0 # "OPENQASM 2.0" + QIR = 1 # pytket qir with classical functions: "QIR 1.0" + PQIR = 2 # profile QIR: "QIR 1.0" + + +def _language2str(language: Language) -> str: + """returns matching string for Language enum""" + if language == Language.QASM: + return "OPENQASM 2.0" + else: + return "QIR 1.0" # DEFAULT_CREDENTIALS_STORAGE for use with the DEFAULT_API_HANDLER. @@ -835,7 +844,7 @@ def submit_program( "name": name or f"{self._label}", "count": n_shots, "machine": self._device_name, - "language": language.value, + "language": _language2str(language), "program": program, "priority": "normal", "options": { @@ -858,6 +867,8 @@ def submit_program( if wasm_file_handler is not None: if self.backend_info and not self.backend_info.misc.get("wasm", False): raise WasmUnsupported("Backend does not support wasm calls.") + # body["bytecode_base64"] = wasm_file_handler._wasm_file_encoded + # see https://github.com/CQCL/pytket-quantinuum/issues/496 body["cfl"] = wasm_file_handler._wasm_file_encoded.decode("utf-8") body["options"].update(self._process_circuits_options) @@ -1032,8 +1043,10 @@ def process_circuits( ).items(): for i in range(count): results_selection.append((name, i)) + else: - assert language == Language.QIR + assert language == Language.QIR or language == Language.PQIR + profile = language == Language.PQIR for name, count in Counter(bit.reg_name for bit in c0.bits).items(): for i in range(count): results_selection.append((name, i)) @@ -1046,6 +1059,7 @@ def process_circuits( "circuit generated by pytket-qir", QIRFormat.BINARY, wfh=wasm_fh, + profile=profile, ), ) ).decode("utf-8") @@ -1490,10 +1504,29 @@ def _convert_result( c_bits = [Bit(name, ind) for name, ind in results_selection] # Construct the shots table - stacked_array = [ - [int(resultdict[name][i][-1 - ind]) for name, ind in results_selection] - for i in range(n_shots) - ] + + try: + stacked_array = [ + [int(resultdict[name][i][-1 - ind]) for name, ind in results_selection] + for i in range(n_shots) + ] + except IndexError: + # this is only a temporary solution and not fully working + # see issue https://github.com/CQCL/pytket-quantinuum/issues/501 + stacked_array = [ + [ + int( + bin(int(resultdict[name][i])).replace( + "0b", + "0000000000000000000000000000000000000\ +00000000000000000000000000", # 0 * 63 + )[-1 - ind] + ) + for name, ind in results_selection + ] + for i in range(n_shots) + ] + return BackendResult( c_bits=c_bits, shots=OutcomeArray.from_readouts(stacked_array), diff --git a/setup.py b/setup.py index 9b489f36..5b09a625 100644 --- a/setup.py +++ b/setup.py @@ -45,8 +45,10 @@ packages=find_namespace_packages(include=["pytket.*"]), include_package_data=True, install_requires=[ + "pytket >= 1.31.0", + "pytket-qir >= 0.13", "pytket >= 1.33.0", - "pytket-qir >= 0.12.0", + "pytket-qir >= 0.13", "requests >= 2.2", "types-requests", "websockets >= 7.0", diff --git a/tests/integration/backend_test.py b/tests/integration/backend_test.py index 26f95114..202a3da5 100644 --- a/tests/integration/backend_test.py +++ b/tests/integration/backend_test.py @@ -76,7 +76,7 @@ @pytest.mark.skipif(skip_remote_tests, reason=REASON) @pytest.mark.parametrize("authenticated_quum_backend_qa", [None], indirect=True) -@pytest.mark.parametrize("language", [Language.QASM, Language.QIR]) +@pytest.mark.parametrize("language", [Language.QASM, Language.QIR, Language.PQIR]) @pytest.mark.timeout(120) def test_quantinuum( authenticated_quum_backend_qa: QuantinuumBackend, language: Language @@ -144,7 +144,7 @@ def test_max_classical_register( @pytest.mark.parametrize( "authenticated_quum_backend_qa", [{"device_name": "H1-1SC"}], indirect=True ) -@pytest.mark.parametrize("language", [Language.QASM, Language.QIR]) +@pytest.mark.parametrize("language", [Language.QASM, Language.QIR, Language.PQIR]) @pytest.mark.timeout(120) def test_bell( authenticated_quum_backend_qa: QuantinuumBackend, language: Language @@ -166,7 +166,7 @@ def test_bell( [{"device_name": "H1-1SC", "label": "test 3"}], indirect=True, ) -@pytest.mark.parametrize("language", [Language.QASM, Language.QIR]) +@pytest.mark.parametrize("language", [Language.QASM, Language.QIR, Language.PQIR]) @pytest.mark.timeout(120) def test_multireg( authenticated_quum_backend_qa: QuantinuumBackend, language: Language @@ -382,7 +382,14 @@ def test_cost_estimate_bad_syntax_checker( [{"device_name": name} for name in pytest.ALL_SYNTAX_CHECKER_NAMES], # type: ignore indirect=True, ) -@pytest.mark.parametrize("language", [Language.QASM, Language.QIR]) +@pytest.mark.parametrize( + "language", + [ + Language.QASM, + Language.QIR, + Language.PQIR, + ], +) @pytest.mark.timeout(120) def test_classical( authenticated_quum_backend_qa: QuantinuumBackend, language: Language @@ -435,6 +442,7 @@ def test_classical( "language", [ Language.QIR, + Language.PQIR, pytest.param( Language.QASM, marks=pytest.mark.xfail(reason="https://github.com/CQCL/tket/issues/1173"), @@ -467,7 +475,7 @@ def test_division( [{"device_name": name} for name in pytest.ALL_SYNTAX_CHECKER_NAMES], # type: ignore indirect=True, ) -@pytest.mark.parametrize("language", [Language.QASM, Language.QIR]) +@pytest.mark.parametrize("language", [Language.QASM, Language.QIR, Language.PQIR]) @pytest.mark.timeout(120) def test_postprocess( authenticated_quum_backend_qa: QuantinuumBackend, language: Language @@ -543,7 +551,7 @@ def test_shots_bits_edgecases(n_shots, n_bits) -> None: @pytest.mark.parametrize( "authenticated_quum_backend_qa", [{"device_name": "H1-1E"}], indirect=True ) -@pytest.mark.parametrize("language", [Language.QASM, Language.QIR]) +@pytest.mark.parametrize("language", [Language.QASM, Language.QIR, Language.PQIR]) @pytest.mark.timeout(200) def test_simulator( authenticated_quum_handler: QuantinuumAPI, @@ -637,7 +645,7 @@ def test_batching( [{"device_name": name} for name in pytest.ALL_SYNTAX_CHECKER_NAMES], # type: ignore indirect=True, ) -@pytest.mark.parametrize("language", [Language.QASM, Language.QIR]) +@pytest.mark.parametrize("language", [Language.QASM, Language.QIR, Language.PQIR]) @pytest.mark.timeout(120) def test_submission_with_group( authenticated_quum_backend_qa: QuantinuumBackend, language: Language @@ -662,7 +670,7 @@ def test_submission_with_group( @pytest.mark.parametrize( "authenticated_quum_backend_qa", [{"device_name": "H1-1SC"}], indirect=True ) -@pytest.mark.parametrize("language", [Language.QASM, Language.QIR]) +@pytest.mark.parametrize("language", [Language.QASM, Language.QIR, Language.PQIR]) @pytest.mark.timeout(120) def test_zzphase( authenticated_quum_backend_qa: QuantinuumBackend, language: Language @@ -763,6 +771,7 @@ def test_device_state( [ Language.QASM, Language.QIR, + Language.PQIR, ], ) @pytest.mark.timeout(120) @@ -796,6 +805,7 @@ def test_wasm_qa( [ Language.QASM, Language.QIR, + Language.PQIR, ], ) @pytest.mark.timeout(120) @@ -883,7 +893,7 @@ def test_submit_qasm( [{"device_name": name} for name in pytest.ALL_SYNTAX_CHECKER_NAMES], # type: ignore indirect=True, ) -@pytest.mark.parametrize("language", [Language.QASM, Language.QIR]) +@pytest.mark.parametrize("language", [Language.QASM, Language.QIR, Language.PQIR]) @pytest.mark.timeout(120) def test_options( authenticated_quum_backend_qa: QuantinuumBackend, language: Language @@ -905,7 +915,7 @@ def test_options( [{"device_name": name} for name in pytest.ALL_SYNTAX_CHECKER_NAMES], # type: ignore indirect=True, ) -@pytest.mark.parametrize("language", [Language.QASM, Language.QIR]) +@pytest.mark.parametrize("language", [Language.QASM, Language.QIR, Language.PQIR]) @pytest.mark.timeout(120) def test_tk2( authenticated_quum_backend_qa: QuantinuumBackend, language: Language @@ -1055,7 +1065,7 @@ def test_qir_conversion(authenticated_quum_backend_qa: QuantinuumBackend) -> Non c0 = Circuit(2).H(0).CX(0, 1).measure_all() b = authenticated_quum_backend_qa c = b.get_compiled_circuit(c0) - h = b.process_circuit(c, n_shots=10, language=Language.QIR) + h = b.process_circuit(c, n_shots=10, language=Language.PQIR) r = b.get_result(h) shots = r.get_shots() assert len(shots) == 10 @@ -1142,13 +1152,14 @@ def test_scratch_removal(authenticated_quum_backend_qa: QuantinuumBackend) -> No [ Language.QASM, Language.QIR, + Language.PQIR, ], ) @pytest.mark.timeout(120) def test_wasm_collatz( authenticated_quum_backend_qa: QuantinuumBackend, language: Language ) -> None: - wasfile = WasmFileHandler( + wasmfile = WasmFileHandler( str(Path(__file__).parent.parent / "wasm" / "collatz.wasm") ) c = Circuit(8) @@ -1160,13 +1171,13 @@ def test_wasm_collatz( c.H(i) c.Measure(Qubit(i), Bit("a", i)) # Compute the value of the Collatz function on this value. - c.add_wasm_to_reg("collatz", wasfile, [a], [b]) + c.add_wasm_to_reg("collatz", wasmfile, [a], [b]) backend = authenticated_quum_backend_qa c = backend.get_compiled_circuit(c) h = backend.process_circuit( - c, n_shots=10, wasm_file_handler=wasfile, language=language + c, n_shots=10, wasm_file_handler=wasmfile, language=language ) r = backend.get_result(h) @@ -1199,13 +1210,16 @@ def collatz(n: int) -> int: [ Language.QASM, Language.QIR, + Language.PQIR, ], ) @pytest.mark.timeout(120) def test_wasm_state( authenticated_quum_backend_qa: QuantinuumBackend, language: Language ) -> None: - wasfile = WasmFileHandler(str(Path(__file__).parent.parent / "wasm" / "state.wasm")) + wasmfile = WasmFileHandler( + str(Path(__file__).parent.parent / "wasm" / "state.wasm") + ) c = Circuit(8) a = c.add_c_register("a", 8).to_list() # measurement results b = c.add_c_register("b", 4) # final count @@ -1216,20 +1230,20 @@ def test_wasm_state( c.H(i) c.Measure(Qubit(i), a[i]) # Count the number of 1s in the "a" register and store in the "b" register. - c.add_wasm_to_reg("set_c", wasfile, [s], []) # set c to zero + c.add_wasm_to_reg("set_c", wasmfile, [s], []) # set c to zero for i in range(8): # Copy a[i] to s c.add_c_copybits([a[i]], [Bit("s", 0)]) # Conditionally increment the counter - c.add_wasm_to_reg("conditional_increment_c", wasfile, [s], []) + c.add_wasm_to_reg("conditional_increment_c", wasmfile, [s], []) # Put the counter into "b" - c.add_wasm_to_reg("get_c", wasfile, [], [b]) + c.add_wasm_to_reg("get_c", wasmfile, [], [b]) backend = authenticated_quum_backend_qa c = backend.get_compiled_circuit(c) h = backend.process_circuit( - c, n_shots=10, wasm_file_handler=wasfile, language=language + c, n_shots=10, wasm_file_handler=wasmfile, language=language ) r = backend.get_result(h) @@ -1278,7 +1292,7 @@ def test_Rz_removal_before_measurements() -> None: @pytest.mark.parametrize( "authenticated_quum_backend_qa", [{"device_name": "H1-1E"}], indirect=True ) -@pytest.mark.parametrize("language", [Language.QASM, Language.QIR]) +@pytest.mark.parametrize("language", [Language.QASM, Language.QIR, Language.PQIR]) @pytest.mark.timeout(120) def test_noiseless_emulation( authenticated_quum_backend_qa: QuantinuumBackend, language: Language diff --git a/tests/integration/qir/test_pytket_qir_6.ll b/tests/integration/qir/test_pytket_qir_6.ll index 4b1731db..a3ccb39c 100644 --- a/tests/integration/qir/test_pytket_qir_6.ll +++ b/tests/integration/qir/test_pytket_qir_6.ll @@ -24,6 +24,8 @@ entry: call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 2 to %Qubit*)) call void @mz_to_creg_bit(%Qubit* null, i1* %2, i64 4) %7 = call i1 @get_creg_bit(i1* %2, i64 4) + %8 = zext i1 %7 to i64 + call void @set_creg_to_int(i1* %2, i64 %8) br i1 %7, label %then, label %else then: ; preds = %entry @@ -36,14 +38,14 @@ else: ; preds = %entry continue: ; preds = %else, %then call void @__quantum__qis__h__body(%Qubit* null) call void @__quantum__rt__tuple_start_record_output() - %8 = call i64 @get_int_from_creg(i1* %0) - call void @__quantum__rt__int_record_output(i64 %8, i8* getelementptr inbounds ([2 x i8], [2 x i8]* @0, i32 0, i32 0)) - %9 = call i64 @get_int_from_creg(i1* %1) - call void @__quantum__rt__int_record_output(i64 %9, i8* getelementptr inbounds ([2 x i8], [2 x i8]* @1, i32 0, i32 0)) - %10 = call i64 @get_int_from_creg(i1* %2) - call void @__quantum__rt__int_record_output(i64 %10, i8* getelementptr inbounds ([2 x i8], [2 x i8]* @2, i32 0, i32 0)) - %11 = call i64 @get_int_from_creg(i1* %3) - call void @__quantum__rt__int_record_output(i64 %11, i8* getelementptr inbounds ([2 x i8], [2 x i8]* @3, i32 0, i32 0)) + %9 = call i64 @get_int_from_creg(i1* %0) + call void @__quantum__rt__int_record_output(i64 %9, i8* getelementptr inbounds ([2 x i8], [2 x i8]* @0, i32 0, i32 0)) + %10 = call i64 @get_int_from_creg(i1* %1) + call void @__quantum__rt__int_record_output(i64 %10, i8* getelementptr inbounds ([2 x i8], [2 x i8]* @1, i32 0, i32 0)) + %11 = call i64 @get_int_from_creg(i1* %2) + call void @__quantum__rt__int_record_output(i64 %11, i8* getelementptr inbounds ([2 x i8], [2 x i8]* @2, i32 0, i32 0)) + %12 = call i64 @get_int_from_creg(i1* %3) + call void @__quantum__rt__int_record_output(i64 %12, i8* getelementptr inbounds ([2 x i8], [2 x i8]* @3, i32 0, i32 0)) call void @__quantum__rt__tuple_end_record_output() ret void } diff --git a/tests/unit/offline_backend_test.py b/tests/unit/offline_backend_test.py index a8aa8238..ae52ad25 100644 --- a/tests/unit/offline_backend_test.py +++ b/tests/unit/offline_backend_test.py @@ -33,7 +33,7 @@ ) -@pytest.mark.parametrize("language", [Language.QASM, Language.QIR]) +@pytest.mark.parametrize("language", [Language.QASM, Language.QIR, Language.PQIR]) def test_quantinuum_offline(language: Language) -> None: qapioffline = QuantinuumAPIOffline() backend = QuantinuumBackend( @@ -56,7 +56,7 @@ def test_quantinuum_offline(language: Language) -> None: "name": "test 1", "count": 4, "machine": "H1-1", - "language": language.value, + "language": "OPENQASM 2.0" if language == Language.QASM else "QIR 1.0", "program": "...", # not checked "priority": "normal", "options": {"simulator": "state-vector", "error-model": True, "tket": {}}, @@ -95,7 +95,7 @@ def test_max_classical_register_ii() -> None: backend._check_all_circuits([c]) -@pytest.mark.parametrize("language", [Language.QASM, Language.QIR]) +@pytest.mark.parametrize("language", [Language.QASM, Language.QIR, Language.PQIR]) def test_tket_pass_submission(language: Language) -> None: backend = QuantinuumBackend(device_name="H1-1SC", machine_debug=True) @@ -125,6 +125,7 @@ def test_tket_pass_submission(language: Language) -> None: [ Language.QASM, Language.QIR, + Language.PQIR, ], ) def test_shots_bits_edgecases(n_shots, n_bits, language: Language) -> None: