Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add azure option in result recording #189

Merged
merged 9 commits into from
Dec 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion _metadata.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
__extension_version__ = "0.18.0rc0"
__extension_version__ = "0.19.0"
__extension_name__ = "pytket-qir"
2 changes: 1 addition & 1 deletion docs/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ API documentation

.. automodule:: pytket.qir
:special-members:
:members: pytket_to_qir, QIRFormat
:members: pytket_to_qir, QIRFormat, QIRProfile
3 changes: 2 additions & 1 deletion docs/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@
Changelog
~~~~~~~~~

0.18.0rc0 (November 2024)
0.19.0 (December 2024)
----------------------

* Updated pytket version requirement to 1.35.
* Updated pyqir version requirement to 0.10.6.
* Add option to generate QIR for azure target

0.17.0 (October 2024)
---------------------
Expand Down
49 changes: 45 additions & 4 deletions pytket/qir/conversion/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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(
Expand Down Expand Up @@ -107,6 +111,13 @@ def pytket_to_qir(
wasm_int_type=int_type,
qir_int_type=int_type,
)
elif profile == QIRProfile.AZUREBASE:
qir_generator = AzureBaseProfileQirGenerator(
circuit=circ,
module=m,
wasm_int_type=int_type,
qir_int_type=int_type,
)
elif profile == QIRProfile.PYTKET:
qir_generator = PytketQirGenerator(
circuit=circ,
Expand All @@ -122,12 +133,42 @@ 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")

populated_module = qir_generator.circuit_to_module(qir_generator.circuit, True)

if qir_generator.has_wasm:
if profile == QIRProfile.AZUREADAPTIVE:

assert not qir_generator.has_wasm
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My understanding is that we don't want to do wasm for azure.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that's a fair assumption for now at least.


sar_azure_dict: dict[str, str] = qir_generator.get_azure_sar()

initial_result = str(populated_module.module.ir())

for az in sar_azure_dict:
initial_result = initial_result.replace(az, sar_azure_dict[az])

result = initial_result

bitcode = pyqir.Module.from_ir(pyqir.Context(), result).bitcode
cqc-alec marked this conversation as resolved.
Show resolved Hide resolved

if qir_format == QIRFormat.BINARY:
return bitcode
elif qir_format == QIRFormat.STRING:
return result
else:
assert not "unsupported return type" # type: ignore

elif qir_generator.has_wasm:
wasm_sar_dict: dict[str, str] = qir_generator.get_wasm_sar()

initial_result = str(populated_module.module.ir())
Expand Down
89 changes: 89 additions & 0 deletions pytket/qir/conversion/azurebaseprofileqirgenerator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
# 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

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 together

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))
),
],
)
112 changes: 112 additions & 0 deletions pytket/qir/conversion/azureprofileqirgenerator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
# 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)

self.azure_sar_dict["!llvm.module.flags = !{!0, !1, !2, !3}"] = (
"!llvm.module.flags = !{!0, !1, !2, !3, !4, !5, !6, !7, !8, !9, !10}"
)

self.azure_sar_dict[
'!3 = !{i32 1, !"dynamic_result_management", i1 false}'
] = """!3 = !{i32 1, !"dynamic_result_management", i1 false}
!4 = !{i32 1, !"classical_ints", i1 true}
!5 = !{i32 1, !"qubit_resetting", i1 true}
!6 = !{i32 1, !"classical_floats", i1 false}
!7 = !{i32 1, !"backwards_branching", i1 false}
!8 = !{i32 1, !"classical_fixed_points", i1 false}
!9 = !{i32 1, !"user_functions", i1 false}
!10 = !{i32 1, !"multiple_target_branching", i1 false}"""

# 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.c_registers)),
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),
self.reg_const[reg_name],
],
)
4 changes: 4 additions & 0 deletions pytket/qir/conversion/qirgenerator.py
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@ def __init__(

self.has_wasm = False
self.wasm_sar_dict: dict[str, str] = {}
self.azure_sar_dict: dict[str, str] = {}
self.wasm_sar_dict["!llvm.module.flags"] = (
'attributes #1 = { "wasm" }\n\n!llvm.module.flags'
)
Expand Down Expand Up @@ -482,6 +483,9 @@ def _get_ssa_from_cl_bit_op(
def get_wasm_sar(self) -> dict[str, str]:
return self.wasm_sar_dict

def get_azure_sar(self) -> dict[str, str]:
return self.azure_sar_dict

def conv_RangePredicateOp(self, op: RangePredicateOp, args: list[Bit]) -> None:
# special case handling for REG_EQ

Expand Down
Loading
Loading