diff --git a/_metadata.py b/_metadata.py index ceaf9133..a7e5aa79 100644 --- a/_metadata.py +++ b/_metadata.py @@ -1,2 +1,2 @@ -__extension_version__ = "0.18.0rc0" +__extension_version__ = "0.18.0rc1" __extension_name__ = "pytket-qir" diff --git a/docs/api.rst b/docs/api.rst index 26f1141b..2c99d64c 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -3,4 +3,4 @@ API documentation .. automodule:: pytket.qir :special-members: - :members: pytket_to_qir, QIRFormat + :members: pytket_to_qir, QIRFormat, QIRProfile diff --git a/pytket/qir/conversion/api.py b/pytket/qir/conversion/api.py index 6d30cc1f..a7addccb 100644 --- a/pytket/qir/conversion/api.py +++ b/pytket/qir/conversion/api.py @@ -26,6 +26,8 @@ scratch_reg_resize_pass, ) +from .azurebaseprofileqirgenerator import AzureBaseProfileQirGenerator +from .azureprofileqirgenerator import AzureAdaptiveProfileQirGenerator from .baseprofileqirgenerator import BaseProfileQirGenerator from .module import tketqirModule from .profileqirgenerator import AdaptiveProfileQirGenerator @@ -48,9 +50,11 @@ class QIRProfile(Enum): """Profile for the QIR generation""" BASE = 0 - ADAPTIVE = 1 - ADAPTIVE_CREGSIZE = 2 - PYTKET = 3 + AZUREBASE = 1 + ADAPTIVE = 2 + AZUREADAPTIVE = 3 + ADAPTIVE_CREGSIZE = 4 + PYTKET = 5 def pytket_to_qir( @@ -107,6 +111,13 @@ def pytket_to_qir( wasm_int_type=int_type, qir_int_type=int_type, ) + elif profile == QIRProfile.AZUREBASE: + qir_generator: AbstractQirGenerator = AzureBaseProfileQirGenerator( + circuit=circ, + module=m, + wasm_int_type=int_type, + qir_int_type=int_type, + ) elif profile == QIRProfile.PYTKET: qir_generator = PytketQirGenerator( circuit=circ, @@ -122,6 +133,14 @@ def pytket_to_qir( qir_int_type=int_type, trunc=trunc, ) + elif profile == QIRProfile.AZUREADAPTIVE: + qir_generator = AzureAdaptiveProfileQirGenerator( + circuit=circ, + module=m, + wasm_int_type=int_type, + qir_int_type=int_type, + trunc=trunc, + ) else: raise NotImplementedError("unexpected profile") diff --git a/pytket/qir/conversion/azurebaseprofileqirgenerator.py b/pytket/qir/conversion/azurebaseprofileqirgenerator.py new file mode 100644 index 00000000..4f7da282 --- /dev/null +++ b/pytket/qir/conversion/azurebaseprofileqirgenerator.py @@ -0,0 +1,91 @@ +# Copyright 2019-2024 Quantinuum +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +import pyqir + +from pytket.circuit import ( + Bit, + Circuit, + Qubit, +) + +from .baseprofileqirgenerator import ( + BaseProfileQirGenerator, +) +from .module import tketqirModule + + +class AzureBaseProfileQirGenerator(BaseProfileQirGenerator): + """Generate QIR from a pytket circuit.""" + + def __init__( + self, + circuit: Circuit, + module: tketqirModule, + wasm_int_type: int, + qir_int_type: int, + ) -> None: + + super().__init__(circuit, module, wasm_int_type, qir_int_type) + + # void @__quantum__rt__array_record_output(result) + self.record_output_array = self.module.module.add_external_function( + "__quantum__rt__array_record_output", + pyqir.FunctionType( + pyqir.Type.void(self.module.module.context), + [ + self.qir_int_type, + pyqir.PointerType(pyqir.IntType(self.module.module.context, 8)), + ], + ), + ) + + def conv_measure(self, bits: list[Bit], qubits: list[Qubit]) -> None: + assert len(bits) == 1 + assert len(qubits) == 1 + + pass + + def record_output(self) -> None: + + # this will measure all qubits at the end of the circuit + # the result of the measurement will be added to an array and recorded togeather + + for i in range(len(self.circuit.qubits)): + self.module.qis.mz( + self.module.module.qubits[i], + self.module.module.results[i], + ) + + self.module.builder.call( + self.record_output_array, + [ + pyqir.const(self.qir_int_type, len(self.circuit.qubits)), + pyqir.Constant.null( + pyqir.PointerType(pyqir.IntType(self.module.module.context, 8)) + ), + ], + ) + + for i in range(len(self.circuit.qubits)): + self.module.builder.call( + self.record_output_res, + [ + self.module.module.results[i], + pyqir.Constant.null( + pyqir.PointerType(pyqir.IntType(self.module.module.context, 8)) + ), + ], + ) diff --git a/pytket/qir/conversion/azureprofileqirgenerator.py b/pytket/qir/conversion/azureprofileqirgenerator.py new file mode 100644 index 00000000..492d46cf --- /dev/null +++ b/pytket/qir/conversion/azureprofileqirgenerator.py @@ -0,0 +1,99 @@ +# Copyright 2019-2024 Quantinuum +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +import pyqir + +from pytket.circuit import ( + Bit, + Circuit, + Qubit, +) + +from .module import tketqirModule +from .profileqirgenerator import ( + AdaptiveProfileQirGenerator, +) + + +class AzureAdaptiveProfileQirGenerator(AdaptiveProfileQirGenerator): + """Generate QIR from a pytket circuit.""" + + def __init__( + self, + circuit: Circuit, + module: tketqirModule, + wasm_int_type: int, + qir_int_type: int, + trunc: bool, + ) -> None: + + super().__init__(circuit, module, wasm_int_type, qir_int_type, trunc) + + # void @__quantum__rt__array_record_output(result) + self.record_output_array = self.module.module.add_external_function( + "__quantum__rt__array_record_output", + pyqir.FunctionType( + pyqir.Type.void(self.module.module.context), + [ + self.qir_int_type, + pyqir.PointerType(pyqir.IntType(self.module.module.context, 8)), + ], + ), + ) + + def conv_measure(self, bits: list[Bit], qubits: list[Qubit]) -> None: + + assert len(bits) == 1 + assert len(qubits) == 1 + + qubit_index = qubits[0].index[0] + + self.module.qis.mz( + self.module.module.qubits[qubit_index], + self.module.module.results[qubit_index], + ) + + ssa_measureresult = self.module.builder.call( + self.read_bit_from_result, + [ + self.module.module.results[qubit_index], + ], + ) + + self._set_bit_in_creg(bits[0].reg_name, bits[0].index[0], ssa_measureresult) + + def record_output(self) -> None: + + self.module.builder.call( + self.record_output_array, + [ + pyqir.const(self.qir_int_type, len(self.circuit.qubits)), + pyqir.Constant.null( + pyqir.PointerType(pyqir.IntType(self.module.module.context, 8)) + ), + ], + ) + + for creg in self.circuit.c_registers: + reg_name = creg[0].reg_name + self.module.builder.call( + self.record_output_i64, + [ + self._get_i64_ssa_reg(reg_name), + pyqir.Constant.null( + pyqir.PointerType(pyqir.IntType(self.module.module.context, 8)) + ), + ], + )