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

Update plugin by removing analytic argument for device creation #130

Merged
merged 13 commits into from
Mar 28, 2021
8 changes: 4 additions & 4 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -74,10 +74,10 @@ jobs:

- name: Run tests
run: |
pl-device-test --device=qiskit.basicaer --tb=short --skip-ops --analytic=False --shots=20000 --device-kwargs backend=qasm_simulator
pl-device-test --device=qiskit.aer --tb=short --skip-ops --analytic=False --shots=20000 --device-kwargs backend=qasm_simulator
pl-device-test --device=qiskit.aer --tb=short --skip-ops --analytic=True --device-kwargs backend=statevector_simulator
pl-device-test --device=qiskit.aer --tb=short --skip-ops --analytic=True --device-kwargs backend=unitary_simulator
pl-device-test --device=qiskit.basicaer --tb=short --skip-ops --shots=20000 --device-kwargs backend=qasm_simulator
pl-device-test --device=qiskit.aer --tb=short --skip-ops --shots=20000 --device-kwargs backend=qasm_simulator
pl-device-test --device=qiskit.aer --tb=short --skip-ops --shots=None --device-kwargs backend=statevector_simulator
Copy link
Contributor

Choose a reason for hiding this comment

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

Failures in the device test suite originated from the fact that the device keyword arguments were ignored by PennyLane core. Therefore, even for backend=statevector_simulator we got qasm_simulator, though this time with shots=None, which were then changed to 1024 internally (HW warning branch).

pl-device-test --device=qiskit.aer --tb=short --skip-ops --shots=None --device-kwargs backend=unitary_simulator

- name: Upload coverage to Codecov
uses: codecov/codecov-action@v1
Expand Down
3 changes: 2 additions & 1 deletion doc/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -409,8 +409,9 @@
#autodoc_default_flags = ['members']
autosummary_generate = True

from directives import CustomDeviceGalleryItemDirective, CustomDemoGalleryItemDirective
from directives import UsageDetails, CustomDeviceGalleryItemDirective, CustomDemoGalleryItemDirective

def setup(app):
app.add_directive('devicegalleryitem', CustomDeviceGalleryItemDirective)
app.add_directive('demogalleryitem', CustomDemoGalleryItemDirective)
app.add_directive("usagedetails", UsageDetails)
36 changes: 36 additions & 0 deletions doc/directives.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,42 @@

import os

USAGE_DETAILS_TEMPLATE = """
.. raw:: html

<a class="usage-details-header collapse-header" data-toggle="collapse" href="#usageDetails" aria-expanded="false" aria-controls="usageDetails">
<h2 style="font-size: 24px;">
<i class="fas fa-angle-down rotate" style="float: right;"></i> Usage Details
</h2>
</a>
<div class="collapse" id="usageDetails">

{content}

.. raw:: html

</div>
"""


class UsageDetails(Directive):
"""Create a collapsed Usage Details section in the documentation."""

# defines the parameter the directive expects
# directives.unchanged means you get the raw value from RST
required_arguments = 0
optional_arguments = 0
final_argument_whitespace = False
has_content = True

def run(self):
rst = USAGE_DETAILS_TEMPLATE.format(content="\n".join(self.content))
string_list = StringList(rst.split('\n'))
node = nodes.section()
self.state.nested_parse(string_list, self.content_offset, node)
return [node]


GALLERY_TEMPLATE = """
.. raw:: html

Expand Down
8 changes: 3 additions & 5 deletions pennylane_qiskit/aer.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,16 +38,14 @@ class AerDevice(QiskitDevice):
or iterable that contains unique labels for the subsystems as numbers (i.e., ``[-1, 0, 2]``)
or strings (``['ancilla', 'q1', 'q2']``).
backend (str): the desired backend
shots (int): number of circuit evaluations/random samples used
to estimate expectation values and variances of observables
shots (int or None): number of circuit evaluations/random samples used
to estimate expectation values and variances of observables. For statevector backends,
setting to None computes the expectation values and variances analytically.
glassnotes marked this conversation as resolved.
Show resolved Hide resolved

Keyword Args:
name (str): The name of the circuit. Default ``'circuit'``.
compile_backend (BaseBackend): The backend used for compilation. If you wish
to simulate a device compliant circuit, you can specify a backend here.
analytic (bool): For statevector backends, determines if the
expectation values and variances are to be computed analytically.
Default value is ``False``.
noise_model (NoiseModel): NoiseModel Object from ``qiskit.providers.aer.noise``
"""

Expand Down
8 changes: 3 additions & 5 deletions pennylane_qiskit/basic_aer.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,16 +38,14 @@ class BasicAerDevice(QiskitDevice):
or iterable that contains unique labels for the subsystems as numbers (i.e., ``[-1, 0, 2]``)
or strings (``['ancilla', 'q1', 'q2']``).
backend (str): the desired backend
shots (int): number of circuit evaluations/random samples used
to estimate expectation values and variances of observables
shots (int or None): number of circuit evaluations/random samples used
to estimate expectation values and variances of observables. For statevector backends,
setting to None computes the expectation values and variances analytically.
glassnotes marked this conversation as resolved.
Show resolved Hide resolved

Keyword Args:
name (str): The name of the circuit. Default ``'circuit'``.
compile_backend (BaseBackend): The backend used for compilation. If you wish
to simulate a device compliant circuit, you can specify a backend here.
analytic (bool): For statevector backends, determines if the
expectation values and variances are to be computed analytically.
Default value is ``False``.
"""

short_name = "qiskit.basicaer"
Expand Down
20 changes: 7 additions & 13 deletions pennylane_qiskit/qiskit_device.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,16 +75,14 @@ class QiskitDevice(QubitDevice, abc.ABC):
or strings (``['ancilla', 'q1', 'q2']``).
provider (Provider): The Qiskit simulation provider
backend (str): the desired backend
shots (int): Number of circuit evaluations/random samples used
to estimate expectation values of observables.
shots (int or None): number of circuit evaluations/random samples used
to estimate expectation values and variances of observables. For statevector backends,
setting to None computes the expectation values and variances analytically.
glassnotes marked this conversation as resolved.
Show resolved Hide resolved

Keyword Args:
name (str): The name of the circuit. Default ``'circuit'``.
compile_backend (BaseBackend): The backend used for compilation. If you wish
to simulate a device compliant circuit, you can specify a backend here.
analytic (bool): For statevector backends, determines if the
expectation values and variances are to be computed analytically.
Default value is ``False``.
"""
name = "Qiskit PennyLane plugin"
pennylane_requires = ">=0.12.0"
Expand Down Expand Up @@ -113,16 +111,12 @@ def __init__(self, wires, provider, backend, shots=1024, **kwargs):
super().__init__(wires=wires, shots=shots)

# Keep track if the user specified analytic to be True
user_specified_analytic = "analytic" in kwargs and kwargs["analytic"]
self.analytic = kwargs.pop("analytic", False)
if shots is None and backend not in self._state_backends:

if self.analytic and backend not in self._state_backends:
# Raise a warning if no shots were specified for a hardware device
warnings.warn(self.hw_analytic_warning_message.format(backend), UserWarning)

if user_specified_analytic:
# Raise a warning if the analytic attribute was set to True
warnings.warn(self.hw_analytic_warning_message.format(backend), UserWarning)

self.analytic = False
self.shots = 1024

self._backend = None

Expand Down
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
qiskit>=0.23.4
pennylane>=0.14.0
git+https://github.com/PennyLaneAI/pennylane.git
antalszava marked this conversation as resolved.
Show resolved Hide resolved
numpy
networkx>=2.2;python_version>'3.5'
networkx>=2.2,<2.4;python_version=='3.5'
25 changes: 14 additions & 11 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@


@pytest.fixture
def tol(analytic):
if analytic:
def tol(shots):
if shots is None:
return {"atol": 0.01, "rtol": 0}

return {"atol": 0.05, "rtol": 0.1}
Expand All @@ -42,16 +42,19 @@ def _init_state(n):

return _init_state


@pytest.fixture
def skip_unitary(backend):
if backend == "unitary_simulator":
pytest.skip("This test does not support the unitary simulator backend.")


@pytest.fixture
def run_only_for_unitary(backend):
if backend != "unitary_simulator":
pytest.skip("This test only supports the unitary simulator.")


@pytest.fixture(params=state_backends + hw_backends)
def backend(request):
return request.param
Expand All @@ -68,22 +71,22 @@ def hardware_backend(request):


@pytest.fixture(params=[AerDevice, BasicAerDevice])
def device(request, backend, shots, analytic):
if backend not in state_backends and analytic == True:
def device(request, backend, shots):
if backend not in state_backends and shots is None:
Copy link
Contributor Author

Choose a reason for hiding this comment

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

(Changed)

pytest.skip("Hardware simulators do not support analytic mode")

def _device(n, device_options=None):
if device_options is None:
device_options = {}
return request.param(wires=n, backend=backend, shots=shots, analytic=analytic, **device_options)
return request.param(wires=n, backend=backend, shots=shots, **device_options)

return _device


@pytest.fixture(params=[AerDevice, BasicAerDevice])
def state_vector_device(request, statevector_backend, shots, analytic):
def state_vector_device(request, statevector_backend, shots):
def _device(n):
return request.param(wires=n, backend=statevector_backend, shots=shots, analytic=analytic)
return request.param(wires=n, backend=statevector_backend, shots=shots)
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 and the previous two are changed)


return _device

Expand All @@ -94,20 +97,20 @@ def mock_device(monkeypatch):

with monkeypatch.context() as m:
dev = qml.Device
m.setattr(dev, '__abstractmethods__', frozenset())
m.setattr(dev, "__abstractmethods__", frozenset())
yield qml.Device()


@pytest.fixture(scope="function")
def recorder():
return qml._queuing.OperationRecorder()
return qml.tape.OperationRecorder()
Copy link
Contributor Author

Choose a reason for hiding this comment

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

(Changed)



@pytest.fixture(scope="function")
def qubit_device_single_wire():
return qml.device('default.qubit', wires=1)
return qml.device("default.qubit", wires=1)


@pytest.fixture(scope="function")
def qubit_device_2_wires():
return qml.device('default.qubit', wires=2)
return qml.device("default.qubit", wires=2)
29 changes: 12 additions & 17 deletions tests/test_apply.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,25 +42,18 @@
)


single_qubit_operations = [
qml.PauliX,
qml.PauliY,
qml.PauliZ,
qml.Hadamard,
qml.S,
qml.T
]
single_qubit_operations = [qml.PauliX, qml.PauliY, qml.PauliZ, qml.Hadamard, qml.S, qml.T]

single_qubit_operations_param = [qml.PhaseShift, qml.RX, qml.RY, qml.RZ]
two_qubit = [qml.CNOT, qml.SWAP, qml.CZ]
two_qubit_param = [qml.CRZ]
three_qubit = [qml.Toffoli, qml.CSWAP]

@pytest.mark.parametrize("analytic", [True])
@pytest.mark.parametrize("shots", [8192])

@pytest.mark.parametrize("shots", [None])
@pytest.mark.usefixtures("skip_unitary")
class TestAnalyticApply:
"""Test application of PennyLane operations with analytic attribute set to True."""
"""Test application of PennyLane operations with analytic calculation."""

def test_qubit_state_vector(self, init_state, device, tol):
"""Test that the QubitStateVector operation produces the expected
Expand Down Expand Up @@ -149,8 +142,8 @@ def test_three_qubit_operations_no_parameters(self, init_state, device, operatio
expected = np.abs(applied_operation.matrix @ state) ** 2
assert np.allclose(res, expected, **tol)

@pytest.mark.parametrize("analytic", [True])
@pytest.mark.parametrize("shots", [8192])

@pytest.mark.parametrize("shots", [None])
Copy link
Contributor

Choose a reason for hiding this comment

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

Above 3 diffs were changes

@pytest.mark.usefixtures("run_only_for_unitary")
class TestStateApplyUnitarySimulator:
"""Test application of PennyLane operations to the unitary simulator."""
Expand All @@ -161,14 +154,17 @@ def test_invalid_qubit(self, init_state, device):
dev = device(1)
state = init_state(1)

with pytest.raises(qml.DeviceError, match="The QubitStateVector operation is not supported on the unitary simulator backend"):
with pytest.raises(
qml.DeviceError,
match="The QubitStateVector operation is not supported on the unitary simulator backend",
):
dev.apply([qml.QubitStateVector(state, wires=[0])])


@pytest.mark.parametrize("shots", [8192])
@pytest.mark.parametrize("analytic", [False])
@pytest.mark.usefixtures("skip_unitary")
class TestNonAnalyticApply:
"""Test application of PennyLane operations with the analytic attribute set to False."""
"""Test application of PennyLane operations with non-analytic calculation."""
Copy link
Contributor

Choose a reason for hiding this comment

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

(Changed)


def test_qubit_state_vector(self, init_state, device, tol):
"""Test that the QubitStateVector operation produces the expected
Expand Down Expand Up @@ -302,4 +298,3 @@ def test_three_qubit_no_parameters(self, init_state, device, operation, tol):
res = np.fromiter(dev.probability(), dtype=np.float64)
expected = np.abs(applied_operation.matrix @ state) ** 2
assert np.allclose(res, expected, **tol)

Loading