Skip to content

Commit

Permalink
[UnitaryHack] Gate Decomposition(#90) added and tested (#111)
Browse files Browse the repository at this point in the history
* implement and test gate decomposition

* Implement and Test Gate Decomposition

* addressed comments in previous PR
  • Loading branch information
Gmontes01 committed Jun 19, 2023
1 parent 9d5150d commit 9dd7e2f
Show file tree
Hide file tree
Showing 4 changed files with 283 additions and 35 deletions.
49 changes: 34 additions & 15 deletions qiskit_braket_provider/providers/adapter.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"""Util function for provider."""
from typing import Callable, Dict, Iterable, List, Optional, Tuple, Union
import warnings

from braket.aws import AwsDevice
from braket.circuits import (
Expand All @@ -24,8 +25,10 @@
GateModelSimulatorParadigmProperties,
)
from braket.devices import LocalSimulator

from numpy import pi
from qiskit import QuantumCircuit

from qiskit import QuantumCircuit, transpile
from qiskit.circuit import Instruction as QiskitInstruction
from qiskit.circuit import Measure, Parameter
from qiskit.circuit.library import (
Expand Down Expand Up @@ -60,8 +63,8 @@
YGate,
ZGate,
)
from qiskit.transpiler import InstructionProperties, Target

from qiskit.transpiler import InstructionProperties, Target
from qiskit_braket_provider.exception import QiskitBraketException

qiskit_to_braket_gate_names_mapping = {
Expand Down Expand Up @@ -97,23 +100,24 @@
"ecr": "ecr",
}

_EPS = 1e-10 # global variable used to chop very small numbers to zero

qiskit_gate_names_to_braket_gates: Dict[str, Callable] = {
"u": lambda theta, phi, lam: [
gates.Rz(lam),
gates.Rx(pi / 2),
gates.Rz(theta),
gates.Rx(-pi / 2),
gates.Rz(phi),
"u1": lambda lam: [gates.PhaseShift(lam)],
"u2": lambda phi, lam: [
gates.PhaseShift(lam),
gates.Ry(pi / 2),
gates.PhaseShift(phi),
],
"u1": lambda lam: [gates.Rz(lam)],
"u2": lambda phi, lam: [gates.Rz(lam), gates.Ry(pi / 2), gates.Rz(phi)],
"u3": lambda theta, phi, lam: [
gates.Rz(lam),
gates.Rx(pi / 2),
gates.Rz(theta),
gates.Rx(-pi / 2),
gates.Rz(phi),
gates.PhaseShift(lam),
gates.Ry(theta),
gates.PhaseShift(phi),
],
"u": lambda theta, phi, lam: [
gates.PhaseShift(lam),
gates.Ry(theta),
gates.PhaseShift(phi),
],
"p": lambda angle: [gates.PhaseShift(angle)],
"cp": lambda angle: [gates.CPhaseShift(angle)],
Expand Down Expand Up @@ -144,6 +148,10 @@
}


translatable_qiskit_gates = set(qiskit_gate_names_to_braket_gates.keys()).union(
{"measure", "barrier", "reset"}
)

qiskit_gate_name_to_braket_gate_mapping: Dict[str, Optional[QiskitInstruction]] = {
"u": UGate(Parameter("theta"), Parameter("phi"), Parameter("lam")),
"u1": U1Gate(Parameter("theta")),
Expand Down Expand Up @@ -409,6 +417,13 @@ def convert_qiskit_to_braket_circuit(circuit: QuantumCircuit) -> Circuit:
Circuit: Braket circuit
"""
quantum_circuit = Circuit()
if not (
{gate.name for gate, _, _ in circuit.data}.issubset(translatable_qiskit_gates)
):
circuit = transpile(circuit, basis_gates=translatable_qiskit_gates)
if circuit.global_phase > _EPS:
warnings.warn("Circuit transpilation resulted in global phase shift")
# handle qiskit to braket conversion
for qiskit_gates in circuit.data:
name = qiskit_gates[0].name
if name == "measure":
Expand All @@ -427,6 +442,10 @@ def convert_qiskit_to_braket_circuit(circuit: QuantumCircuit) -> Circuit:
elif name == "barrier":
# This does not exist
pass
elif name == "reset":
raise NotImplementedError(
"reset operation not supported by qiskit to braket adapter"
)
else:
params = []
if hasattr(qiskit_gates[0], "params"):
Expand Down
2 changes: 1 addition & 1 deletion qiskit_braket_provider/providers/braket_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ def run(
tasks = []
try:
for circuit in circuits:
task: Union[LocalQuantumTask] = self._aws_device.run(
task: LocalQuantumTask = self._aws_device.run(
task_specification=circuit, shots=shots
)
tasks.append(task)
Expand Down
258 changes: 247 additions & 11 deletions tests/providers/test_adapter.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,69 @@
"""Tests for Qiskti to Braket adapter."""
from unittest import TestCase
import pytest

from braket.circuits import Circuit, FreeParameter, observables
from braket.devices import LocalSimulator

import numpy as np
from qiskit import QuantumCircuit

from qiskit import QuantumCircuit, execute, BasicAer
from qiskit.circuit import Parameter
from qiskit.circuit.library import PauliEvolutionGate
from qiskit.opflow import I, Z, X

from qiskit.circuit.library.standard_gates import (
HGate,
CHGate,
IGate,
PhaseGate,
CPhaseGate,
RGate,
RXGate,
CRXGate,
RXXGate,
RYGate,
CRYGate,
RYYGate,
RZGate,
CRZGate,
RZZGate,
RZXGate,
XXMinusYYGate,
XXPlusYYGate,
ECRGate,
SGate,
SdgGate,
CSGate,
CSdgGate,
SwapGate,
CSwapGate,
iSwapGate,
SXGate,
SXdgGate,
CSXGate,
DCXGate,
TGate,
TdgGate,
UGate,
CUGate,
U1Gate,
CU1Gate,
U2Gate,
U3Gate,
CU3Gate,
XGate,
CXGate,
CCXGate,
C3SXGate,
RCCXGate,
RC3XGate,
YGate,
CYGate,
ZGate,
CZGate,
CCZGate,
)

from qiskit_braket_provider.providers.adapter import (
convert_qiskit_to_braket_circuit,
Expand All @@ -13,11 +72,192 @@
qiskit_to_braket_gate_names_mapping,
wrap_circuits_in_verbatim_box,
)
from qiskit_braket_provider.providers.braket_backend import BraketLocalBackend

from tests.providers.test_braket_backend import combine_dicts

_EPS = 1e-10 # global variable used to chop very small numbers to zero

standard_gates = [
IGate(),
SXGate(),
XGate(),
CXGate(),
RZGate(Parameter("λ")),
RGate(Parameter("ϴ"), Parameter("φ")),
C3SXGate(),
CCXGate(),
DCXGate(),
CHGate(),
CPhaseGate(Parameter("ϴ")),
CRXGate(Parameter("ϴ")),
CRYGate(Parameter("ϴ")),
CRZGate(Parameter("ϴ")),
CSwapGate(),
CSXGate(),
CUGate(Parameter("ϴ"), Parameter("φ"), Parameter("λ"), Parameter("γ")),
CU1Gate(Parameter("λ")),
CU3Gate(Parameter("ϴ"), Parameter("φ"), Parameter("λ")),
CYGate(),
CZGate(),
CCZGate(),
HGate(),
PhaseGate(Parameter("ϴ")),
RCCXGate(),
RC3XGate(),
RXGate(Parameter("ϴ")),
RXXGate(Parameter("ϴ")),
RYGate(Parameter("ϴ")),
RYYGate(Parameter("ϴ")),
RZZGate(Parameter("ϴ")),
RZXGate(Parameter("ϴ")),
XXMinusYYGate(Parameter("ϴ")),
XXPlusYYGate(Parameter("ϴ")),
ECRGate(),
SGate(),
SdgGate(),
CSGate(),
CSdgGate(),
SwapGate(),
iSwapGate(),
SXdgGate(),
TGate(),
TdgGate(),
UGate(Parameter("ϴ"), Parameter("φ"), Parameter("λ")),
U1Gate(Parameter("λ")),
U2Gate(Parameter("φ"), Parameter("λ")),
U3Gate(Parameter("ϴ"), Parameter("φ"), Parameter("λ")),
YGate(),
ZGate(),
]


class TestAdapter(TestCase):
"""Tests adapter."""

def test_state_preparation_01(self):
"""Tests state_preparation handling of Adapter"""
input_state_vector = np.array([np.sqrt(3) / 2, np.sqrt(2) * complex(1, 1) / 4])

qiskit_circuit = QuantumCircuit(1)
qiskit_circuit.prepare_state(input_state_vector, 0)

braket_circuit = convert_qiskit_to_braket_circuit(qiskit_circuit)
braket_circuit.state_vector() # pylint: disable=no-member
result = LocalSimulator().run(braket_circuit)
output_state_vector = np.array(result.result().values[0])

self.assertTrue(
(np.linalg.norm(input_state_vector - output_state_vector)) < _EPS
)

def test_state_preparation_00(self):
"""Tests state_preparation handling of Adapter"""
input_state_vector = np.array([1 / np.sqrt(2), -1 / np.sqrt(2)])

qiskit_circuit = QuantumCircuit(1)
qiskit_circuit.prepare_state(input_state_vector, 0)

braket_circuit = convert_qiskit_to_braket_circuit(qiskit_circuit)
braket_circuit.state_vector() # pylint: disable=no-member
result = LocalSimulator().run(braket_circuit)
output_state_vector = np.array(result.result().values[0])

self.assertTrue(
(np.linalg.norm(input_state_vector - output_state_vector)) < _EPS
)

def test_u_gate(self):
"""Tests adapter conversion of u gate"""
qiskit_circuit = QuantumCircuit(1)
backend = BasicAer.get_backend("statevector_simulator")
device = LocalSimulator()
qiskit_circuit.u(np.pi / 2, np.pi / 3, np.pi / 4, 0)

job = execute(qiskit_circuit, backend)

braket_circuit = convert_qiskit_to_braket_circuit(qiskit_circuit)
braket_circuit.state_vector() # pylint: disable=no-member

braket_output = device.run(braket_circuit).result().values[0]
qiskit_output = np.array(job.result().get_statevector(qiskit_circuit))

self.assertTrue(np.linalg.norm(braket_output - qiskit_output) < _EPS)

def test_standard_gate_decomp(self):
"""Tests adapter decomposition of all standard gates to forms that can be translated"""
aer_backend = BasicAer.get_backend("statevector_simulator")
backend = BraketLocalBackend()

for standard_gate in standard_gates:
qiskit_circuit = QuantumCircuit(standard_gate.num_qubits)
qiskit_circuit.append(standard_gate, range(standard_gate.num_qubits))

parameters = standard_gate.params
if parameters:
parameter_values = [
(137 / 61) * np.pi / i for i in range(1, len(parameters) + 1)
]
parameter_bindings = dict(zip(parameters, parameter_values))
qiskit_circuit = qiskit_circuit.assign_parameters(parameter_bindings)

if standard_gate.name != "cu":
with self.subTest(f"Circuit with {standard_gate.name} gate."):
braket_job = backend.run(qiskit_circuit, shots=1000)
braket_result = braket_job.result().get_counts()

qiskit_job = execute(qiskit_circuit, aer_backend, shots=1000)
qiskit_result = qiskit_job.result().get_counts()

combined_results = combine_dicts(
{k: float(v) / 1000.0 for k, v in braket_result.items()},
qiskit_result,
)

for key, values in combined_results.items():
percent_diff = abs(
((float(values[0]) - values[1]) / values[0]) * 100
)
abs_diff = abs(values[0] - values[1])
self.assertTrue(
percent_diff < 10 or abs_diff < 0.05,
f"Key {key} with percent difference {percent_diff} "
f"and absolute difference {abs_diff}. Original values {values}",
)
else:
with pytest.raises(TypeError):
convert_qiskit_to_braket_circuit(qiskit_circuit)

def test_exponential_gate_decomp(self):
"""Tests adapter translation of exponential gates"""
aer_backend = BasicAer.get_backend("statevector_simulator")
backend = BraketLocalBackend()
qiskit_circuit = QuantumCircuit(2)

operator = (Z ^ Z) - 0.1 * (X ^ I)
evo = PauliEvolutionGate(operator, time=2)

qiskit_circuit.append(evo, range(2))

braket_job = backend.run(qiskit_circuit, shots=1000)
braket_result = braket_job.result().get_counts()

qiskit_job = execute(qiskit_circuit, aer_backend, shots=1000)
qiskit_result = qiskit_job.result().get_counts()

combined_results = combine_dicts(
{k: float(v) / 1000.0 for k, v in braket_result.items()}, qiskit_result
)

for key, values in combined_results.items():
percent_diff = abs(((float(values[0]) - values[1]) / values[0]) * 100)
abs_diff = abs(values[0] - values[1])
self.assertTrue(
percent_diff < 10 or abs_diff < 0.05,
f"Key {key} with percent difference {percent_diff} "
f"and absolute difference {abs_diff}. Original values {values}",
)

def test_mappers(self):
"""Tests mappers."""
self.assertEqual(
Expand Down Expand Up @@ -45,16 +285,12 @@ def test_convert_parametric_qiskit_to_braket_circuit(self):
braket_circuit_ans = (
Circuit() # pylint: disable=no-member
.rz(0, FreeParameter("θ"))
.rz(0, FreeParameter("λ"))
.rx(0, np.pi / 2)
.rz(0, FreeParameter("θ"))
.rx(0, -np.pi / 2)
.rz(0, FreeParameter("φ"))
.rz(0, np.pi)
.rx(0, np.pi / 2)
.rz(0, FreeParameter("θ"))
.rx(0, -np.pi / 2)
.rz(0, FreeParameter("φ"))
.phaseshift(0, FreeParameter("λ"))
.ry(0, FreeParameter("θ"))
.phaseshift(0, FreeParameter("φ"))
.phaseshift(0, np.pi)
.ry(0, FreeParameter("θ"))
.phaseshift(0, FreeParameter("φ"))
)

self.assertEqual(braket_circuit, braket_circuit_ans)
Expand Down
Loading

0 comments on commit 9dd7e2f

Please sign in to comment.