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

Aws backend #18

Closed
wants to merge 18 commits into from
Closed
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/unit_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ jobs:
run: poetry install --no-interaction --no-root
if: steps.cache.outputs.cache-hit != 'true'
- name: Install project
run: poetry install --no-interaction --with test
run: poetry install --no-interaction --with test --all-extras
- name: Install task runner
run: pip install poethepoet
- name: Lint
Expand Down
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -157,5 +157,5 @@ cython_debug/
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
.idea/
.devenv
2,359 changes: 941 additions & 1,418 deletions poetry.lock

Large diffs are not rendered by default.

9 changes: 6 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,11 @@ packages = [{ include = "qibo_cloud_backends", from = "src" }]

[tool.poetry.dependencies]
python = ">=3.9,<3.12"
qibo = ">=0.2.4"
qibo_client = ">=0.0.3"
qibo = "^0.2.8"
qibo_client = "^0.0.5"
qiskit_ibm_runtime = ">=0.17"
qiskit_ibm_provider = ">=0.8.0"
amazon-braket-sdk = { version = "^1.77.3", optional = true }

[tool.poetry.group.test.dependencies]
pytest = "^7.4.3"
Expand All @@ -38,11 +39,13 @@ sphinx-copybutton = "^0.5.2"
nbsphinx = "^0.9.3"
furo = "^2023.9.10"


[tool.poetry.group.dev.dependencies]
ipython = "^7"
pdbpp = "^0.10.3"

[tool.poetry.extras]
aws = ["amazon-braket-sdk"]

[tool.poe.tasks]
test = "pytest"
lint = "pylint --errors-only src"
Expand Down
1 change: 1 addition & 0 deletions requirements-aws.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
amazon-braket-sdk
125 changes: 125 additions & 0 deletions src/qibo_cloud_backends/aws_client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
from qibo.backends import NumpyBackend
from qibo.config import raise_error
from qibo.result import MeasurementOutcomes, QuantumState

import re
import importlib.util
import sys

# # Import Qiskit packages for transpiler
# from qiskit import transpile
# from qiskit import QuantumCircuit
# from qiskit.transpiler import CouplingMap
# from qiskit.circuit.random import random_circuit
# from qiskit import qasm2

from qibo import Circuit as QiboCircuit

from braket.aws import AwsDevice, AwsQuantumTask
from braket.circuits import Gate, observables
from braket.circuits import Circuit as BraketCircuit
from braket.devices import Devices, LocalSimulator

_QASM_BRAKET_GATES = {
"id": "i",
"cx": "cnot",
"sx": "v",
"sxdg": "vi",
"sdg": "si",
"tdg": "ti",
}

class BraketClientBackend(NumpyBackend):
"""Backend for the remote execution of AWS circuits on the AWS backends.

Args:
device_arn (str | None): The ARN of the Braket device.
If `None`, instantiates the `LocalSimulator("default")`.
"""

def __init__(self, device=None, verbatim_circuit=False):
"""Initializes BraketBackend.

Args:
device (str): Default device is Braket's statevector LocalSimulator, LocalSimulator("default").
Other devices are Braket's density matrix simulator, LocalSimulator("braket_dm"), or any other
QPUs.
verbatim_circuit (bool): If `True`, wrap the Braket circuit in a verbatim box to run it on the QPU
without any transpilation. Defaults to `False`.
"""

super().__init__()

self.verbatim_circuit = verbatim_circuit

if device is None:
self.device = LocalSimulator("default")
# self.device = LocalSimulator("braket_dm")
else:
self.device = device
self.name = "aws"

def remove_qelib1_inc(self, qasm_string):
"""To remove the 'includes qe1lib.inc' from the OpenQASM string.

Args:
qasm_code (OpenQASM circuit, str): circuit given in the OpenQASM format.

Returns:
qasm_code (OpenQASM circuit, str): circuit given in the OpenQASM format.
"""

# Remove the "include "qelib1.inc";\n" line
modified_code = re.sub(r'include\s+"qelib1.inc";\n', '', qasm_string)
return modified_code

def qasm_convert_gates(self, qasm_code):
"""To replace the notation for certain gates in OpenQASM

Args:
qasm_code (OpenQASM circuit, str): circuit given in the OpenQASM format.

Returns:
qasm_code (OpenQASM circuit, str): circuit given in the OpenQASM format.
"""

lines = qasm_code.split('\n')
modified_code = ""
for line in lines:
for key in _QASM_BRAKET_GATES:
if key in line:
line = line.replace(key, _QASM_BRAKET_GATES[key])
break
modified_code += line + '\n'
return modified_code

def execute_circuit(self,
mho291 marked this conversation as resolved.
Show resolved Hide resolved
circuit,
nshots=1000):
"""Executes a circuit (Qibo or Qibo-transpiled-to-Braket) on an AWS Braket device. The device defaults to the LocalSimulator().

Args:
circuit (quantum circuit): Either qibo.models.Circuit or braket.circuits.Circuit to execute on the Braket device.
nshots (int): Total number of shots.

Returns:
Measurement outcomes (qibo.measurement.MeasurementOutcomes): The outcome of the circuit execution.
"""

measurements = circuit.measurements
if not measurements:
raise_error(RuntimeError, "No measurement found in the provided circuit.")
nqubits = circuit.nqubits
circuit_qasm = circuit.to_qasm()
circuit_qasm = self.remove_qelib1_inc(circuit_qasm)
circuit_qasm = self.qasm_convert_gates(circuit_qasm)
braket_circuit = BraketCircuit.from_ir(circuit_qasm)

# Wrap verbatim box, execute:
if self.verbatim_circuit:
braket_circuit = BraketCircuit().add_verbatim_box(braket_circuit)
result = self.device.run(braket_circuit, shots=nshots).result()
samples = result.measurements
return MeasurementOutcomes(
measurements=measurements, backend=self, samples=samples, nshots=nshots
)
28 changes: 28 additions & 0 deletions tests/test_aws.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
from qibo import Circuit
from qibo import gates
import numpy as np
from qibo.quantum_info import random_clifford
from qibo_cloud_backends import aws_client
from qibo.backends import NumpyBackend

NP_BACKEND = NumpyBackend()


def test_aws_client_backend():
circuit_qibo = random_clifford(3, backend=NP_BACKEND)
circuit_qibo.add(gates.M(0, 2))

# Local simulator test, does not cost money
client = aws_client.BraketClientBackend()
# AWS device tests, cost money
# AWS = aws_client.BraketClientBackend(device = AwsDevice("arn:aws:braket:eu-west-2::device/qpu/oqc/Lucy"))
# AWS = aws_client.BraketClientBackend(device = AwsDevice("arn:aws:braket:::device/quantum-simulator/amazon/sv1"))

local_res = NP_BACKEND.execute_circuit(circuit_qibo)
remote_res = client.execute_circuit(circuit_qibo)

NP_BACKEND.assert_allclose(
local_res.probabilities(qubits=[0, 2]),
remote_res.probabilities(qubits=[0, 2]),
atol=1e-1,
)
41 changes: 30 additions & 11 deletions tests/test_backends.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,17 @@
QIBO_TK = os.environ.get("QIBO_CLIENT_TII_TOKEN")


def test_qiskit_client_backend():
c = random_clifford(3, backend=NP_BACKEND)
c.add(gates.M(0, 2))
client = QiskitClientBackend(
token=QISKIT_TK, provider="ibm-q", platform="ibmq_qasm_simulator"
)
local_res = NP_BACKEND.execute_circuit(c)
remote_res = client.execute_circuit(c)
NP_BACKEND.assert_allclose(
local_res.probabilities(qubits=[0, 2]), remote_res.probabilities(), atol=1e-1
)
# def test_qiskit_client_backend():
# c = random_clifford(3, backend=NP_BACKEND)
# c.add(gates.M(0, 2))
# client = QiskitClientBackend(
# token=QISKIT_TK, provider="ibm-q", platform="ibmq_qasm_simulator"
# )
# local_res = NP_BACKEND.execute_circuit(c)
# remote_res = client.execute_circuit(c)
# NP_BACKEND.assert_allclose(
# local_res.probabilities(qubits=[0, 2]), remote_res.probabilities(), atol=1e-1
# )


def test_qibo_client_backend():
Expand All @@ -35,3 +35,22 @@ def test_qibo_client_backend():
remote_res.probabilities(qubits=[0, 2]),
atol=1e-1,
)

# def test_aws_client_backend():
# circuit_qibo = random_clifford(3, backend=NP_BACKEND)
# circuit_qibo.add(gates.M(0, 2))
#
# # Local simulator test, does not cost money
# client = aws_client.BraketClientBackend()
# # AWS device tests, cost money
# # AWS = aws_client.BraketClientBackend(device = AwsDevice("arn:aws:braket:eu-west-2::device/qpu/oqc/Lucy"))
# # AWS = aws_client.BraketClientBackend(device = AwsDevice("arn:aws:braket:::device/quantum-simulator/amazon/sv1"))
#
# local_res = NP_BACKEND.execute_circuit(circuit_qibo)
# remote_res = client.execute_circuit(circuit_qibo)
#
# NP_BACKEND.assert_allclose(
# local_res.probabilities(qubits=[0, 2]),
# remote_res.probabilities(qubits=[0, 2]),
# atol=1e-1,
# )
Loading