Skip to content
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 .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ jobs:
pip install --upgrade pip
pip install setuptools
pip install -r requirements-test.txt
bin/install_wheel_extras.sh dist --extra qasm3 --extra cirq --extra squin
bin/install_wheel_extras.sh dist --extra qasm3 --extra cirq --extra qiskit --extra squin
- name: Run tests with pyqir 0.11.x (typed pointers)
run: |
pip install "pyqir>=0.10.0,<0.12"
Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ Types of changes:

### ➕ New Features

- Added `qbraid_qir.qiskit` module for Qiskit to QIR conversion, ported from the archived [microsoft/qiskit-qir](https://github.com/microsoft/qiskit-qir) repository (MIT License). The module has been updated for compatibility with Qiskit 2.x and follows qbraid-qir conventions. Main entry point is `qiskit_to_qir(circuit, name=None, **kwargs)` which converts a Qiskit `QuantumCircuit` to a PyQIR `Module`. ([#272](https://github.com/qBraid/qbraid-qir/issues/272), [#271](https://github.com/qBraid/qbraid-qir/pull/271))

### 🌟 Improvements

### 📜 Documentation
Expand Down
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,12 @@ For Cirq to QIR conversions, install the `cirq` extra:
pip install 'qbraid-qir[cirq]'
```

For Qiskit to QIR conversions, install the `qiskit` extra:

```shell
pip install 'qbraid-qir[qiskit]'
```

For QIR to SQUIN conversions, install the `squin` extra:

```shell
Expand Down Expand Up @@ -146,6 +152,22 @@ module = cirq_to_qir(circuit, name="my-circuit")
ir = str(module)
```

### Qiskit conversions

```python
from qiskit import QuantumCircuit
from qbraid_qir import qiskit_to_qir

circuit = QuantumCircuit(2, 2)
circuit.h(0)
circuit.cx(0, 1)
circuit.measure([0, 1], [0, 1])

module = qiskit_to_qir(circuit, name="bell")

ir = str(module)
```

### SQUIN conversions

```python
Expand Down
8 changes: 8 additions & 0 deletions docs/api/qbraid_qir.qiskit.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
:orphan:

qbraid_qir.qiskit
==================

.. automodule:: qbraid_qir.qiskit
:undoc-members:
:show-inheritance:
Comment thread
TheGupta2012 marked this conversation as resolved.
2 changes: 1 addition & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
# set_type_checking_flag = True
autodoc_member_order = "bysource"
autoclass_content = "both"
autodoc_mock_imports = ["cirq", "openqasm3", "pyqasm", "numpy", "numpy.typing", "kirin", "bloqade"]
autodoc_mock_imports = ["cirq", "openqasm3", "pyqasm", "numpy", "numpy.typing", "kirin", "bloqade", "qiskit"]
napoleon_numpy_docstring = False
todo_include_todos = True
mathjax_path = "https://cdn.jsdelivr.net/npm/mathjax@2/MathJax.js?config=TeX-AMS-MML_HTMLorMML"
Expand Down
5 changes: 3 additions & 2 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -81,11 +81,11 @@ qBraid-QIR requires Python 3.10 or greater. The base package can be installed wi
pip install qbraid-qir


To enable specific conversions such as OpenQASM 3 to QIR or Cirq to QIR, you can install one or both extras:
To enable specific conversions such as OpenQASM 3 to QIR, Cirq to QIR, or Qiskit to QIR, you can install the extras:

.. code-block:: bash

pip install 'qbraid-qir[qasm3,cirq]'
pip install 'qbraid-qir[qasm3,cirq,qiskit]'


Resources
Expand Down Expand Up @@ -117,6 +117,7 @@ Resources
api/qbraid_qir
api/qbraid_qir.cirq
api/qbraid_qir.qasm3
api/qbraid_qir.qiskit
api/qbraid_qir.squin

.. toctree::
Expand Down
5 changes: 3 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ description = "qBraid-SDK extension providing support for QIR conversions."
readme = "README.md"
authors = [{name = "qBraid Development Team"}, {email = "contact@qbraid.com"}]
license = "Apache-2.0"
keywords = ["qbraid", "quantum", "qir", "llvm", "cirq", "openqasm", "squin"]
keywords = ["qbraid", "quantum", "qir", "llvm", "cirq", "openqasm", "qiskit", "squin"]
classifiers = [
"Development Status :: 5 - Production/Stable",
"Intended Audience :: Developers",
Expand Down Expand Up @@ -40,10 +40,11 @@ Discord = "https://discord.gg/TPBU2sa8Et"
[project.optional-dependencies]
cirq = ["cirq-core>=1.3.0,<1.6.0"]
qasm3 = ["pyqasm>=0.4.0,<1.1.0", "numpy"]
qiskit = ["qiskit>=2.0,<3.0"]
squin = ["kirin-toolchain>=0.17.33", "bloqade-circuit>=0.9.1"]

[tool.setuptools]
packages = ["qbraid_qir", "qbraid_qir.cirq", "qbraid_qir.qasm3", "qbraid_qir.squin"]
packages = ["qbraid_qir", "qbraid_qir.cirq", "qbraid_qir.qasm3", "qbraid_qir.qiskit", "qbraid_qir.squin"]

[tool.setuptools.dynamic]
version = {attr = "qbraid_qir.__version__"}
Expand Down
4 changes: 3 additions & 1 deletion qbraid_qir/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,15 @@
"dumps",
"qasm3_to_qir",
"cirq_to_qir",
"qiskit_to_qir",
]

_lazy = {"cirq": "cirq_to_qir", "qasm3": "qasm3_to_qir"}
_lazy = {"cirq": ("cirq_to_qir",), "qasm3": ("qasm3_to_qir",), "qiskit": ("qiskit_to_qir",)}
Comment thread
TheGupta2012 marked this conversation as resolved.

if TYPE_CHECKING:
from .cirq import cirq_to_qir
from .qasm3 import qasm3_to_qir
from .qiskit import qiskit_to_qir


def __getattr__(name):
Expand Down
37 changes: 37 additions & 0 deletions qbraid_qir/qiskit/NOTICE.md
Comment thread
TheGupta2012 marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Third Party Notices

This software includes code derived from third party sources.
Comment thread
TheGupta2012 marked this conversation as resolved.

## qiskit-qir

The `qbraid_qir.qiskit` module contains code derived from [microsoft/qiskit-qir](https://github.com/microsoft/qiskit-qir).

```
MIT License

Copyright (c) Microsoft Corporation.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE
```

The qiskit-qir codebase has been adapted to:
- Work with Qiskit 2.x (updated for new QuantumCircuit API)
- Follow qbraid-qir conventions and coding style
- Support both pyqir 0.10.x and 0.12+ (typed and opaque pointers)
- Integrate with the qbraid-qir module structure
Comment thread
TheGupta2012 marked this conversation as resolved.
59 changes: 59 additions & 0 deletions qbraid_qir/qiskit/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# Copyright 2026 qBraid
#
# 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.

# pylint: disable=line-too-long
# Portions of this module are adapted from microsoft/qiskit-qir
# (https://github.com/microsoft/qiskit-qir), with modifications by qBraid.
# The original MIT license notice is reproduced in NOTICE.md.
# pylint: enable=line-too-long

"""
Module containing Qiskit QIR functionality.

.. currentmodule:: qbraid_qir.qiskit

Functions
-----------

.. autosummary::
:toctree: ../stubs/

qiskit_to_qir


Classes
---------

.. autosummary::
:toctree: ../stubs/

QiskitModule
BasicQiskitVisitor

Exceptions
-----------

.. autosummary::
:toctree: ../stubs/

QiskitConversionError

"""

from .convert import qiskit_to_qir
from .elements import QiskitModule
from .exceptions import QiskitConversionError
from .visitor import BasicQiskitVisitor

__all__ = ["qiskit_to_qir", "QiskitModule", "QiskitConversionError", "BasicQiskitVisitor"]
Comment thread
TheGupta2012 marked this conversation as resolved.
102 changes: 102 additions & 0 deletions qbraid_qir/qiskit/convert.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
# Copyright 2026 qBraid
#
# 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.

# pylint: disable=line-too-long
# Portions of this module are adapted from microsoft/qiskit-qir
# (https://github.com/microsoft/qiskit-qir), with modifications by qBraid.
# The original MIT license notice is reproduced in NOTICE.md.
# pylint: enable=line-too-long

"""
Module containing Qiskit to QIR conversion functions.

"""

from typing import Optional

from pyqir import Context, Module, qir_module
from qiskit.circuit import QuantumCircuit
from qiskit.compiler import transpile as qiskit_transpile

from .elements import QiskitModule, generate_module_id
from .exceptions import QiskitConversionError
from .maps import QISKIT_BASIS_GATES
from .visitor import BasicQiskitVisitor


def qiskit_to_qir(
circuit: QuantumCircuit,
name: Optional[str] = None,
transpile: bool = False,
**kwargs,
) -> Module:
"""
Converts a Qiskit QuantumCircuit to a PyQIR module.

Args:
circuit: The Qiskit QuantumCircuit to convert.
name: Identifier for created QIR module. Auto-generated if not provided.
transpile: If True, transpile the circuit to the supported basis gate set
before conversion. This enables conversion of circuits containing
gates not directly supported in QIR. Defaults to False.

Keyword Args:
initialize_runtime (bool): Whether to perform quantum runtime environment initialization,
default `True`.
record_output (bool): Whether to record output calls for registers, default `True`.
emit_barrier_calls (bool): Whether to emit barrier calls in the QIR, default `False`.

Returns:
The QIR ``pyqir.Module`` representation of the input Qiskit circuit.

Raises:
TypeError: If the input is not a valid Qiskit QuantumCircuit.
ValueError: If the input circuit is empty.
QiskitConversionError: If the conversion fails.

Example:
>>> from qiskit import QuantumCircuit
>>> from qbraid_qir.qiskit import qiskit_to_qir
>>>
>>> qc = QuantumCircuit(2, 2)
>>> qc.h(0)
>>> qc.cx(0, 1)
>>> qc.measure([0, 1], [0, 1])
>>>
>>> module = qiskit_to_qir(qc, name="bell")
>>> ir = str(module)
"""
if not isinstance(circuit, QuantumCircuit):
raise TypeError("Input quantum program must be of type qiskit.QuantumCircuit.")

if len(circuit.data) == 0:
raise ValueError("Input quantum circuit must consist of at least one operation.")

if transpile:
circuit = qiskit_transpile(circuit, basis_gates=QISKIT_BASIS_GATES)

if name is None:
name = generate_module_id(circuit)

llvm_module = qir_module(Context(), name)
module = QiskitModule.from_circuit(circuit, llvm_module)

visitor = BasicQiskitVisitor(**kwargs)
module.accept(visitor)

error = llvm_module.verify()
if error is not None:
raise QiskitConversionError(error)

return llvm_module
Loading