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
20 changes: 10 additions & 10 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,12 @@ jobs:
- name: Generate requirement file (Unix)
if: runner.os != 'Windows'
run: |
python setup.py gen_reqfile --include-extras=test,braket,revkit
python setup.py gen_reqfile --include-extras=test,azure-quantum,braket,revkit

- name: Generate requirement file (Windows)
if: runner.os == 'Windows'
run: |
python setup.py gen_reqfile --include-extras=test,braket
python setup.py gen_reqfile --include-extras=test,azure-quantum,braket

- name: Prepare env
run: |
Expand All @@ -73,11 +73,11 @@ jobs:

- name: Build and install package (Unix)
if: runner.os != 'Windows'
run: python -m pip install -ve .[braket,revkit,test]
run: python -m pip install -ve .[azure-quantum,braket,revkit,test]

- name: Build and install package (Windows)
if: runner.os == 'Windows'
run: python -m pip install -ve .[braket,test]
run: python -m pip install -ve .[azure-quantum,braket,test]

- name: Pytest
run: |
Expand Down Expand Up @@ -142,14 +142,14 @@ jobs:

- name: Prepare Python env
run: |
python3 setup.py gen_reqfile --include-extras=test,braket
python3 setup.py gen_reqfile --include-extras=test,azure-quantum,braket
python3 -m pip install -r requirements.txt --prefer-binary

- name: Upgrade pybind11 and flaky
run: python3 -m pip install --upgrade pybind11 flaky --prefer-binary

- name: Build and install package
run: python3 -m pip install -ve .[braket,test]
run: python3 -m pip install -ve .[azure-quantum,braket,test]

- name: Pytest
run: |
Expand Down Expand Up @@ -187,14 +187,14 @@ jobs:

- name: Prepare Python env
run: |
python3 setup.py gen_reqfile --include-extras=test,braket
python3 setup.py gen_reqfile --include-extras=test,azure-quantum,braket
python3 -m pip install -r requirements.txt --prefer-binary

- name: Upgrade pybind11 and flaky
run: python3 -m pip install --upgrade pybind11 flaky --prefer-binary

- name: Build and install package
run: python3 -m pip install -ve .[braket,test]
run: python3 -m pip install -ve .[azure-quantum,braket,test]

- name: Pytest
run: |
Expand Down Expand Up @@ -269,11 +269,11 @@ jobs:

- name: Install dependencies
run: |
python3 setup.py gen_reqfile --include-extras=test,braket
python3 setup.py gen_reqfile --include-extras=test,azure-quantum,braket
python3 -m pip install -r requirements.txt --prefer-binary

- name: Build and install package
run: python3 -m pip install -ve .[braket,test]
run: python3 -m pip install -ve .[azure-quantum,braket,test]

- name: Pytest
run: |
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added

- New backend for the Azure Quantum platform

### Repository

- Update `docker/setup-qemu-action` GitHub action to v2
Expand Down
12 changes: 11 additions & 1 deletion projectq/backends/_azure/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,14 @@

"""ProjectQ module for supporting the Azure Quantum platform"""

from ._azure_quantum import AzureQuantumBackend
try:
from ._azure_quantum import AzureQuantumBackend
except ImportError: # pragma: no cover

class AzureQuantumBackend:
"""Dummy class"""

def __init__(self, *args, **kwargs):
raise ImportError(
"Missing optional 'azure-quantum' dependencies. To install run: pip install projectq[azure-quantum]"
)
89 changes: 35 additions & 54 deletions projectq/backends/_azure/_azure_quantum.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,49 +15,41 @@

"""Back-end to run quantum programs using Azure Quantum."""

import numpy as np
from collections import Counter

from projectq.types import WeakQubitRef
import numpy as np

from projectq.cengines import BasicEngine
from projectq.meta import LogicalQubitIDTag
from projectq.ops import (
AllocateQubitGate,
DeallocateQubitGate,
FlushGate,
MeasureGate
)
from projectq.ops import AllocateQubitGate, DeallocateQubitGate, FlushGate, MeasureGate
from projectq.types import WeakQubitRef

from ._azure_quantum_client import send, retrieve
from ._azure_quantum_client import retrieve, send
from ._exceptions import AzureQuantumTargetNotFoundError
from ._util import (
IONQ_PROVIDER_ID,
QUANTINUUM_PROVIDER_ID,
is_available_ionq,
is_available_quantinuum,
rearrange_result,
to_json,
to_qasm,
rearrange_result
)

from ._exceptions import AzureQuantumTargetNotFoundError

try:
from azure.quantum import Workspace
from azure.quantum.target import Target, IonQ, Quantinuum
from azure.quantum.target import IonQ, Quantinuum, Target
from azure.quantum.target.target_factory import TargetFactory
except ImportError: # pragma: no cover
raise ImportError(
raise ImportError( # pylint: disable=raise-missing-from
"Missing optional 'azure-quantum' dependencies. To install run: pip install projectq[azure-quantum]"
)


class AzureQuantumBackend(BasicEngine): # pylint: disable=too-many-instance-attributes
"""Backend for building circuits and submitting them to the Azure Quantum."""

DEFAULT_TARGETS = {
IONQ_PROVIDER_ID: IonQ,
QUANTINUUM_PROVIDER_ID: Quantinuum
}
DEFAULT_TARGETS = {IONQ_PROVIDER_ID: IonQ, QUANTINUUM_PROVIDER_ID: Quantinuum}

def __init__(
self,
Expand All @@ -69,7 +61,7 @@ def __init__(
num_retries=100,
interval=1,
retrieve_execution=None,
**kwargs
**kwargs,
): # pylint: disable=too-many-arguments
"""
Initialize an Azure Quantum Backend object.
Expand Down Expand Up @@ -146,7 +138,6 @@ def _store(self, cmd):
Args:
cmd: Command to store
"""

if self._clear:
self._probabilities = {}
self._clear = False
Expand Down Expand Up @@ -200,44 +191,39 @@ def is_available(self, cmd):
"""
if self._provider_id == IONQ_PROVIDER_ID:
return is_available_ionq(cmd)
elif self._provider_id == QUANTINUUM_PROVIDER_ID:

if self._provider_id == QUANTINUUM_PROVIDER_ID:
return is_available_quantinuum(cmd)

return False

@property
def _target_factory(self):
target_factory = TargetFactory(
base_cls=Target, # noqa
workspace=self._workspace,
default_targets=AzureQuantumBackend.DEFAULT_TARGETS
base_cls=Target, workspace=self._workspace, default_targets=AzureQuantumBackend.DEFAULT_TARGETS
)

return target_factory

@property
def _target(self):
target = self._target_factory.get_targets(
name=self._target_name,
provider_id=self._provider_id
)
target = self._target_factory.get_targets(name=self._target_name, provider_id=self._provider_id)

if type(target) is list and len(target) == 0: # pragma: no cover
if isinstance(target, list) and len(target) == 0: # pragma: no cover
raise AzureQuantumTargetNotFoundError(
'Target {} is not available on workspace {}.'.format(
self._target_name, self._workspace.name)
'Target {} is not available on workspace {}.'.format(self._target_name, self._workspace.name)
)

return target

@property
def current_availability(self):
"""Current availability for given target."""
"""Get current availability for given target."""
return self._target.current_availability

@property
def average_queue_time(self):
"""Average queue time for given target."""
"""Get average queue time for given target."""
return self._target.average_queue_time

def get_probability(self, state, qureg):
Expand Down Expand Up @@ -296,38 +282,33 @@ def _input_data(self):
qubits = len(qubit_mapping.keys())

if self._provider_id == IONQ_PROVIDER_ID:
return {
"qubits": qubits,
"circuit": self._circuit
}
elif self._provider_id == QUANTINUUM_PROVIDER_ID:
return {"qubits": qubits, "circuit": self._circuit}

if self._provider_id == QUANTINUUM_PROVIDER_ID:
measurement_gates = ""

for measured_id in self._measured_ids:
qb_loc = self.main_engine.mapper.current_mapping[measured_id]
measurement_gates += "measure q[{0}] -> c[{0}];\n".format(qb_loc)

return f"OPENQASM 2.0;\ninclude \"qelib1.inc\";\nqreg q[{qubits}];\ncreg c[{qubits}];" \
f"{self._circuit}\n{measurement_gates}"
return (
f"OPENQASM 2.0;\ninclude \"qelib1.inc\";\nqreg q[{qubits}];\ncreg c[{qubits}];"
f"{self._circuit}\n{measurement_gates}"
)

raise RuntimeError("Invalid Azure Quantum target.")

@property
def _metadata(self):
qubit_mapping = self.main_engine.mapper.current_mapping
num_qubits = len(qubit_mapping.keys())
meas_map = [qubit_mapping[qubit_id] for qubit_id in self._measured_ids]

return {
"num_qubits": num_qubits,
"meas_map": meas_map
}
return {"num_qubits": num_qubits, "meas_map": meas_map}

def estimate_cost(self, **kwargs):
"""Estimate cost for the circuit this object has built during engine execution."""
return self._target.estimate_cost(
circuit=self._input_data, # noqa
num_shots=self._num_runs, # noqa
**kwargs
) # noqa
return self._target.estimate_cost(circuit=self._input_data, num_shots=self._num_runs, **kwargs)

def _run(self): # pylint: disable=too-many-locals
"""Run the circuit this object has built during engine execution."""
Expand All @@ -343,7 +324,7 @@ def _run(self): # pylint: disable=too-many-locals
target=self._target,
num_retries=self._num_retries,
interval=self._interval,
verbose=self._verbose
verbose=self._verbose,
)

if res is None:
Expand All @@ -354,7 +335,7 @@ def _run(self): # pylint: disable=too-many-locals
target=self._target,
num_retries=self._num_retries,
interval=self._interval,
verbose=self._verbose
verbose=self._verbose,
)

if res is None:
Expand All @@ -368,9 +349,9 @@ def _run(self): # pylint: disable=too-many-locals
}
elif self._provider_id == QUANTINUUM_PROVIDER_ID:
histogram = Counter(res["c"])
self._probabilities = {
k: v / self._num_runs for k, v in histogram.items()
}
self._probabilities = {k: v / self._num_runs for k, v in histogram.items()}
else:
raise RuntimeError("Invalid Azure Quantum target.")

# Set a single measurement result
bitstring = np.random.choice(list(self._probabilities.keys()), p=list(self._probabilities.values()))
Expand Down
Loading