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

Qiskit v0.23 updates #112

Merged
merged 10 commits into from
Oct 20, 2020
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
5 changes: 5 additions & 0 deletions pennylane_qiskit/converter.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,11 @@ def _function(params: dict = None, wires: list = None):

operation_wires = [wire_map[(qubit.register.name, qubit.index)] for qubit in op[1]]

# New Qiskit gates that are not natively supported by PL (identical
# gates exist with a different name)
# TODO: remove the following when gates have been renamed in PennyLane
instruction_name = "U3Gate" if instruction_name == "UGate" else instruction_name
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 I'll convert this into an issue so that we don't forget

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


if instruction_name in inv_map and inv_map[instruction_name] in pennylane_ops.ops:
# Extract the bound parameters from the operation. If the bound parameters are a
# Qiskit ParameterExpression, then replace it with the corresponding PennyLane
Expand Down
65 changes: 49 additions & 16 deletions pennylane_qiskit/qiskit_device.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,25 @@
from ._version import __version__


# Auxiliary functions for gates subject to deprecation
def U1Gate(theta):
"""Auxiliary function for the ``U1Gate``."""
return ex.PhaseGate(theta)


def U2Gate(phi, lam):
"""Auxiliary function for the ``U2Gate``.

Uses the equation ``u2(phi, lam) = u(pi/2, phi, lam)``.
"""
return ex.U(np.pi / 2, phi, lam)


def U3Gate(theta, phi, lam):
"""Auxiliary function for the ``U3Gate``."""
return ex.U(theta, phi, lam)
Comment on lines +36 to +52
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very nice solution!



QISKIT_OPERATION_MAP = {
# native PennyLane operations also native to qiskit
"PauliX": ex.XGate,
Expand All @@ -52,12 +71,16 @@
"CRX": ex.CRXGate,
"CRY": ex.CRYGate,
"CRZ": ex.CRZGate,
"PhaseShift": ex.U1Gate,
"PhaseShift": ex.PhaseGate,
"QubitStateVector": ex.Initialize,
"U2": ex.U2Gate,
"U3": ex.U3Gate,
"Toffoli": ex.CCXGate,
"QubitUnitary": ex.UnitaryGate,
"U": ex.UGate,
# Qiskit gates subject to deprecation (using custom definitions that depend on
# the latest recommended gates)
"U1": U1Gate,
"U2": U2Gate,
"U3": U3Gate,
}

# Separate dictionary for the inverses as the operations dictionary needs
Expand Down Expand Up @@ -147,22 +170,28 @@ def __init__(self, wires, provider, backend, shots=1024, **kwargs):
# Initialize inner state
self.reset()

# determine if backend supports backend options and noise models,
# and properly put together backend run arguments
s = inspect.signature(b.run)
self.run_args = {}
self.compile_backend = None

if "compile_backend" in kwargs:
self.compile_backend = kwargs.pop("compile_backend")

aer_provider = str(provider) == "AerProvider"
self.noise_model = None
if "noise_model" in kwargs:
if "noise_model" in s.parameters:
self.run_args["noise_model"] = kwargs.pop("noise_model")
else:
if not aer_provider or backend != "qasm_simulator":
raise ValueError("Backend {} does not support noisy simulations".format(backend))

if "backend_options" in s.parameters:
self.noise_model = kwargs.pop("noise_model")

# Get further arguments for run
s = inspect.signature(b.run)
self.run_args = {}

if aer_provider:
# Consider the remaining kwargs as keyword arguments to run
self.run_args.update(kwargs)
Comment on lines +189 to +191
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

backend_options seems to be deprecated for the run method of Aer backends (e.g. for QasmSimulator)


elif "backend_options" in s.parameters:
# BasicAer
Comment on lines +193 to +194
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This does not seem to be the case for BasicAer though (e.g. QasmSimulatorPy).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that the entirety of BasicAer is deprecated, so might be removed in future updates. This is why the new Aer features are likely not backported

self.run_args["backend_options"] = kwargs

@property
Expand All @@ -177,6 +206,7 @@ def reset(self):

self._current_job = None
self._state = None # statevector of a simulator backend
self.noise_model = None

def apply(self, operations, **kwargs):
rotations = kwargs.get("rotations", [])
Expand Down Expand Up @@ -276,14 +306,17 @@ def compile(self):
memory = str(compile_backend) not in self._state_backends

return assemble(
experiments=compiled_circuits,
backend=compile_backend,
shots=self.shots,
memory=memory,
experiments=compiled_circuits, backend=compile_backend, shots=self.shots, memory=memory
)

def run(self, qobj):
"""Run the compiled circuit, and query the result."""
backend = self.backend

if self.noise_model:
# Set the noise model before execution
backend.set_options(noise_model=self.noise_model)

self._current_job = self.backend.run(qobj, **self.run_args)
result = self._current_job.result()

Expand Down
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
qiskit>=0.20
qiskit>=0.23
pennylane>=0.11.0
numpy
networkx>=2.2;python_version>'3.5'
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
long_description = fh.read()

requirements = [
"qiskit>=0.20",
"qiskit>=0.23",
"pennylane>=0.11.0",
"numpy",
"networkx>=2.2;python_version>'3.5'",
Expand Down
72 changes: 45 additions & 27 deletions tests/test_converter.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ def test_quantum_circuit_with_gate_requiring_multiple_parameters(self, recorder)
theta = Parameter('θ')

qc = QuantumCircuit(3, 1)
qc.u3(phi, lam, theta, [0])
qc.u(phi, lam, theta, [0])

quantum_circuit = load(qc)

Expand Down Expand Up @@ -333,12 +333,11 @@ def test_one_qubit_parametrized_operations_supported_by_pennylane(self, recorder
q_reg = QuantumRegister(1)
qc = QuantumCircuit(q_reg)

qc.u1(angle, single_wire)
qc.p(angle, single_wire)
qc.rx(angle, single_wire)
qc.ry(angle, single_wire)
qc.rz(angle, single_wire)
qc.u2(phi, lam, [0])
qc.u3(phi, lam, theta, [0])
qc.u(phi, lam, theta, [0])

quantum_circuit = load(qc)
with recorder:
Expand All @@ -360,16 +359,11 @@ def test_one_qubit_parametrized_operations_supported_by_pennylane(self, recorder
assert recorder.queue[3].parameters == [angle]
assert recorder.queue[3].wires == Wires(single_wire)

assert recorder.queue[4].name == 'U2'
assert len(recorder.queue[4].parameters) == 2
assert recorder.queue[4].parameters == [0.3, 0.4]
assert recorder.queue[4].name == 'U3'
assert len(recorder.queue[4].parameters) == 3
assert recorder.queue[4].parameters == [0.3, 0.4, 0.2]
assert recorder.queue[4].wires == Wires([0])

assert recorder.queue[5].name == 'U3'
assert len(recorder.queue[5].parameters) == 3
assert recorder.queue[5].parameters == [0.3, 0.4, 0.2]
assert recorder.queue[5].wires == Wires([0])

def test_two_qubit_operations_supported_by_pennylane(self, recorder):
"""Tests loading a circuit with the two-qubit operations supported by PennyLane."""

Expand Down Expand Up @@ -551,36 +545,60 @@ def test_operation_transformed_into_qubit_unitary(self, recorder):
assert np.array_equal(recorder.queue[0].parameters[0], ex.CHGate().to_matrix())
assert recorder.queue[0].wires == Wires([0, 1])

def test_quantum_circuit_error_by_passing_wrong_parameters(self, recorder):
"""Tests the load method for a QuantumCircuit raises a QiskitError,
if the wrong type of arguments were passed."""
def test_qiskit_gates_to_be_deprecated(self, recorder):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a good idea. This way, we will know when the gates have been removed due to this test failing :)

"""Tests the Qiskit gates that will be deprecated in an upcoming Qiskit version.

This test case can be removed once the gates are finally deprecated.
"""
qc = QuantumCircuit(1, 1)

theta = Parameter('θ')
angle = 'some_string_instead_of_an_angle'
single_wire = [0]

qc = QuantumCircuit(3, 1)
qc.rz(theta, [0])
with pytest.warns(DeprecationWarning) as record:
with recorder:
qc.u1(0.1, single_wire)
qc.u2(0.1, 0.2, single_wire)
qc.u3(0.1, 0.2, 0.3, single_wire)

# check that warnings were raised
assert len(record) == 3
# check that the message matches
deprecation_substring = "method is deprecated"
assert deprecation_substring in record[0].message.args[0]
assert deprecation_substring in record[1].message.args[0]
assert deprecation_substring in record[2].message.args[0]

quantum_circuit = load(qc)
with recorder:
quantum_circuit()

with pytest.raises(QiskitError):
with recorder:
quantum_circuit(params={theta: angle})
assert recorder.queue[0].name == 'U1'
assert recorder.queue[0].parameters == [0.1]
assert recorder.queue[0].wires == Wires(single_wire)

assert recorder.queue[1].name == 'U2'
assert recorder.queue[1].parameters == [0.1, 0.2]
assert recorder.queue[1].wires == Wires(single_wire)

assert recorder.queue[2].name == 'U3'
assert recorder.queue[2].parameters == [0.1, 0.2, 0.3]
assert recorder.queue[2].wires == Wires(single_wire)

def test_quantum_circuit_error_by_calling_wrong_parameters(self, recorder):
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Weird diff from GitHub, this test was removed.

It doesn't make too much sense as parameter validation has been added to Qiskit so a string input would not work. Arrays will soon be deprecated too.

"""Tests that the load method for a QuantumCircuit raises a TypeError,
def test_quantum_circuit_error_by_passing_wrong_parameters(self, recorder):
"""Tests the load method for a QuantumCircuit raises a QiskitError,
if the wrong type of arguments were passed."""

theta = Parameter('θ')
angle = 'some_string_instead_of_an_angle'

qc = QuantumCircuit(3, 1)
qc.rz(angle, [0])
qc.rz(theta, [0])

quantum_circuit = load(qc)

with pytest.raises(TypeError, match="parameter expected, got <class 'str'>"):
with pytest.raises(QiskitError):
with recorder:
quantum_circuit()
quantum_circuit(params={theta: angle})

def test_quantum_circuit_error_passing_parameters_not_required(self, recorder):
"""Tests the load method raises a QiskitError if arguments
Expand Down
13 changes: 6 additions & 7 deletions tests/test_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,20 +131,19 @@ def test_noise_model(self):
"""Test that the noise model argument is properly
extracted if the backend supports it"""
dev = qml.device("qiskit.aer", wires=2, noise_model="test value")
assert dev.run_args["noise_model"] == "test value"
assert dev.noise_model == "test value"

def test_invalid_noise_model(self):
"""Test that the noise model argument causes an exception to be raised
if the backend does not support it"""
with pytest.raises(ValueError, match="does not support noisy simulations"):
dev = qml.device("qiskit.basicaer", wires=2, noise_model="test value")

@pytest.mark.parametrize("d", pldevices)
def test_overflow_backend_options(self, d):
"""Test all overflow backend options are extracted"""
dev = qml.device(d[0], wires=2, k1="v1", k2="v2")
assert dev.run_args["backend_options"]["k1"] == "v1"
assert dev.run_args["backend_options"]["k2"] == "v2"
def test_overflow_kwargs(self):
"""Test all overflow kwargs are extracted for the AerDevice"""
dev = qml.device('qiskit.aer', wires=2, k1="v1", k2="v2")
assert dev.run_args["k1"] == "v1"
assert dev.run_args["k2"] == "v2"


class TestLoadIntegration:
Expand Down