Skip to content

Commit

Permalink
Merge pull request #36 from qBraid/rh1-dev
Browse files Browse the repository at this point in the history
qir runner image + test fixes
  • Loading branch information
ryanhill1 authored Jan 3, 2024
2 parents dbb5e63 + cc3485a commit 07278d2
Show file tree
Hide file tree
Showing 25 changed files with 495 additions and 136 deletions.
30 changes: 0 additions & 30 deletions .github/workflows/test-publish.yml

This file was deleted.

1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Docs
docs/stubs
.vscode/

# Byte-compiled / optimized / DLL files
__pycache__/
Expand Down
67 changes: 48 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,41 +1,70 @@
# qbraid-qir
<img width="full" alt="qbraid-qir-header" src="https://github.com/qBraid/qbraid-qir/assets/46977852/39f921ae-c4bf-442a-b059-6b21abd2ae50">

<p align="left">
<a href="https://github.com/qBraid/qbraid-qir/actions/workflows/main.yml">
<img src="https://github.com/qBraid/qbraid-qir/actions/workflows/main.yml/badge.svg" alt="CI">
<p align='center'>
<a href='https://github.com/qBraid/qbraid-qir/actions/workflows/main.yml'>
<img src='https://github.com/qBraid/qbraid-qir/actions/workflows/main.yml/badge.svg' alt='CI'>
</a>
<a href="https://www.gnu.org/licenses/gpl-3.0.html">
<img src="https://img.shields.io/github/license/qBraid/qbraid.svg" alt="License"/>
<a href='https://docs.qbraid.com/projects/qir/en/latest/?badge=latest'>
<img src='https://readthedocs.com/projects/qbraid-qbraid-qir/badge/?version=latest&token=7656ee72b7a66dec6d78dda911ce808676dca55c3e86702d5e97191badfdf19c' alt='Documentation Status'/>
</a>
<a href="https://discord.gg/TPBU2sa8Et">
<img src="https://img.shields.io/discord/771898982564626445.svg?color=pink" alt="Discord"/>
<a href="https://pypi.org/project/qbraid-qir/">
<img src="https://img.shields.io/pypi/v/qbraid-qir.svg?color=blue" alt="PyPI version"/>
</a>
<a href="https://pypi.org/project/qbraid-qir/">
<img src="https://img.shields.io/pypi/pyversions/qbraid-qir.svg?color=blue" alt="PyPI version"/>
</a>
<a href='https://www.gnu.org/licenses/gpl-3.0.html'>
<img src='https://img.shields.io/github/license/qBraid/qbraid.svg' alt='License'/>
</a>
<a href='https://discord.gg/TPBU2sa8Et'>
<img src='https://img.shields.io/discord/771898982564626445.svg?color=pink' alt='Discord'/>
</a>
</p>

*Work in progress*

qBraid-SDK extension providing support for QIR conversions
qBraid-SDK extension providing support for QIR conversions.

This project aims to make [QIR](https://www.qir-alliance.org/) representations accessible via the qBraid-SDK [transpiler](#architecture-diagram), and by doing so, open the door to language-specific conversions from any and all high-level quantum languages [supported](https://docs.qbraid.com/en/latest/sdk/overview.html#supported-frontends) by `qbraid`. See QIR Alliance: [why do we need it?](https://www.qir-alliance.org/qir-book/concepts/why-do-we-need.html).

## Planned features
## Getting started

This project aims to make [QIR](https://www.qir-alliance.org/) representations accessible via the qBraid-SDK hub and spokes [model](#architecture-diagram), and by doing so, open the door to language-specific conversions from any and all high-level quantum languages [supported](https://docs.qbraid.com/en/latest/sdk/overview.html#supported-frontends) by `qbraid`.
### Installation

```bash
pip install qbraid-qir
```

- [ ] Cirq $\rightarrow$ QIR
- [ ] Quantum operations
- [ ] Classical operations
- [ ] OpenQASM 3 $\rightarrow$ QIR
### Example

```python
import cirq
from qbraid_qir import cirq_to_qir

q0, q1 = cirq.LineQubit.range(2)

circuit = cirq.Circuit(
cirq.H(q0),
cirq.CNOT(q0, q1),
cirq.measure(q0, q1)
)

module = cirq_to_qir(circuit, name="my-circuit")

ir = str(module)
```

See: https://www.qir-alliance.org/qir-book/concepts/why-do-we-need.html
## Development

## Local install
### Install from source

```bash
git clone https://github.com/qBraid/qbraid-qir.git
cd qbraid-qir
pip install -e .
```

## Run tests
### Run tests

```bash
pip install -r requirements-dev.txt
Expand All @@ -48,7 +77,7 @@ with coverage report
pytest --cov=qbraid_qir --cov-report=term tests/
```

## Build docs
### Build docs

```bash
cd docs
Expand Down
2 changes: 1 addition & 1 deletion docs/api/qbraid_qir.cirq.rst
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
:orphan:

qbraid_qir.cirq
=================
================

.. automodule:: qbraid_qir.cirq
:members:
Expand Down
3 changes: 1 addition & 2 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -84,5 +84,4 @@ Indices and Tables
------------------

* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`
* :ref:`modindex`
5 changes: 2 additions & 3 deletions docs/userguide/cirq_qir.rst
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,10 @@ Convert a ``Cirq`` circuit to ``QIR`` code:
circuit = cirq.Circuit(
cirq.H(q0),
cirq.CNOT(q0, q1),
cirq.measure(q0, key='result0'),
cirq.measure(q1, key='result1')
cirq.measure(q0, q1)
)
qir_code = cirq_to_qir(circuit)
qir_code = cirq_to_qir(circuit, name="Bell")
print(qir_code)
Expand Down
8 changes: 0 additions & 8 deletions qbraid_qir/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,6 @@
.. currentmodule:: qbraid_qir
Functions
-----------
.. autosummary::
:toctree: ../stubs/
cirq_to_qir
Exceptions
-----------
Expand Down
12 changes: 12 additions & 0 deletions qbraid_qir/cirq/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,17 @@
cirq_to_qir
Classes
---------
.. autosummary::
:toctree: ../stubs/
CirqModule
BasicQisVisitor
"""
from .convert import cirq_to_qir
from .elements import CirqModule
from .visitor import BasicQisVisitor
33 changes: 13 additions & 20 deletions qbraid_qir/cirq/convert.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,32 +15,14 @@
from typing import Optional

import cirq
import qbraid.programs.cirq
from pyqir import Context, Module, qir_module

from qbraid_qir.cirq.elements import CirqModule, generate_module_id
from qbraid_qir.cirq.passes import preprocess_circuit
from qbraid_qir.cirq.visitor import BasicQisVisitor
from qbraid_qir.exceptions import QirConversionError


def _preprocess_circuit(circuit: cirq.Circuit) -> cirq.Circuit:
"""
Preprocesses a Cirq circuit to ensure that it is compatible with the QIR conversion.
Args:
circuit (cirq.Circuit): The Cirq circuit to preprocess.
Returns:
cirq.Circuit: The preprocessed Cirq circuit.
"""
# circuit = cirq.contrib.qasm_import.circuit_from_qasm(circuit.to_qasm()) # decompose?
qprogram = qbraid.programs.cirq.CirqCircuit(circuit)
qprogram._convert_to_line_qubits()
cirq_circuit = qprogram.program
return cirq_circuit


def cirq_to_qir(circuit: cirq.Circuit, name: Optional[str] = None, **kwargs) -> Module:
"""
Converts a Cirq circuit to a PyQIR module.
Expand All @@ -49,21 +31,32 @@ def cirq_to_qir(circuit: cirq.Circuit, name: Optional[str] = None, **kwargs) ->
circuit (cirq.Circuit): The Cirq circuit to convert.
name (str, optional): Identifier for created QIR module. Auto-generated if not provided.
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`
Returns:
The QIR ``pyqir.Module`` representation of the input Cirq circuit.
Raises:
TypeError: If the input is not a valid Cirq circuit.
ValueError: If the input circuit is empty.
QirConversionError: If the conversion fails.
"""
if not isinstance(circuit, cirq.Circuit):
raise TypeError("Input quantum program must be of type cirq.Circuit.")

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

if name is None:
name = generate_module_id(circuit)

try:
circuit = _preprocess_circuit(circuit)
circuit = preprocess_circuit(circuit)
except Exception as e: # pylint: disable=broad-exception-caught
raise QirConversionError("Failed to preprocess circuit.") from e

Expand Down
28 changes: 26 additions & 2 deletions qbraid_qir/cirq/elements.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,25 @@ def accept(self, visitor):


class CirqModule:
"""
A module representing a quantum circuit in Cirq using QIR.
This class encapsulates a quantum circuit from Cirq and translates it into QIR format,
maintaining information about quantum operations, qubits, and classical bits. It provides
methods to interact with the underlying QIR module and circuit elements.
Args:
name (str): Name of the module.
module (Module): QIR Module instance.
num_qubits (int): Number of qubits in the circuit.
elements (List[_CircuitElement]): List of circuit elements.
Example:
>>> circuit = cirq.Circuit()
>>> cirq_module = CirqModule.from_circuit(circuit)
>>> print(cirq_module.num_qubits)
"""

def __init__(
self,
name: str,
Expand All @@ -86,25 +105,30 @@ def __init__(

@property
def name(self) -> str:
"""Returns the name of the module."""
return self._name

@property
def module(self) -> Module:
"""Returns the QIR Module instance."""
return self._module

@property
def num_qubits(self) -> int:
"""Returns the number of qubits in the circuit."""
return self._num_qubits

@property
def num_clbits(self) -> int:
"""Returns the number of classical bits in the circuit."""
return self._num_clbits

@classmethod
def from_circuit(
cls, circuit: cirq.Circuit, module: Optional[Module] = None
) -> "CirqModule":
"""Create a new CirqModule from a cirq.Circuit object."""
"""Class method. Constructs a CirqModule from a given cirq.Circuit object
and an optional QIR Module."""
elements: List[_CircuitElement] = []

# Register(s). Tentatively using cirq.Qid as input. Better approaches might exist tbd.
Expand All @@ -117,7 +141,7 @@ def from_circuit(
if module is None:
module = Module(Context(), generate_module_id(circuit))
return cls(
name=module.source_filename,
name="main",
module=module,
num_qubits=len(circuit.all_qubits()),
elements=elements,
Expand Down
3 changes: 2 additions & 1 deletion qbraid_qir/cirq/opsets.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
# Two-Qubit Gates
"SWAP": pyqir._native.swap,
"CNOT": pyqir._native.cx,
"CZ": pyqir._native.cz,
# Three-Qubit Gates
"TOFFOLI": pyqir._native.ccx,
# Classical Gates/Operations
Expand Down Expand Up @@ -69,7 +70,7 @@ def map_cirq_op_to_pyqir_callable(op: cirq.Operation) -> Tuple[Callable, str]:
if isinstance(gate, cirq.ops.MeasurementGate):
op_name = "MEASURE"
elif isinstance(gate, (cirq.ops.Rx, cirq.ops.Ry, cirq.ops.Rz)):
op_name = re.search(r"([A-Za-z]+)\(", str(gate)).group(1)
op_name = re.search(r"([Rx-z]+)\(", str(gate)).group(1)
else:
op_name = str(gate)
else:
Expand Down
Loading

0 comments on commit 07278d2

Please sign in to comment.