Skip to content

Commit

Permalink
Analytic true hw simulator warning (#59)
Browse files Browse the repository at this point in the history
* Adding basic test cases and changing character setting (need polishing)

* Polish tests

* Add tests for the variance case

* Linting elif statement

* Correcting import order

* Organize warning message as a class attribute

* Reorganized tests of old non-native PL operations, added test for QubitUnitary transformation (ch gate)

* Revert "Reorganized tests of old non-native PL operations, added test for QubitUnitary transformation (ch gate)"

This reverts commit 7bb7641.
  • Loading branch information
antalszava authored Nov 20, 2019
1 parent 34976bc commit 1bab8ea
Show file tree
Hide file tree
Showing 4 changed files with 150 additions and 48 deletions.
30 changes: 23 additions & 7 deletions pennylane_qiskit/qiskit_device.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,26 +29,26 @@
~~~~~~~~~~~~
"""
# pylint: disable=too-many-instance-attributes

import abc
from collections import OrderedDict
import functools
import inspect
import itertools
import warnings
from collections import OrderedDict

import numpy as np

from qiskit import ClassicalRegister, QuantumCircuit, QuantumRegister
from qiskit import extensions as ex
from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit

from qiskit.compiler import transpile, assemble
from qiskit.circuit.measure import measure
from qiskit.converters import dag_to_circuit, circuit_to_dag
from qiskit.compiler import assemble, transpile
from qiskit.converters import circuit_to_dag, dag_to_circuit

from pennylane import Device, DeviceError
from pennylane.operation import Sample

from .gates import BasisState, Rot
from ._version import __version__
from .gates import BasisState, Rot


@functools.lru_cache()
Expand Down Expand Up @@ -134,6 +134,10 @@ class QiskitDevice(Device, abc.ABC):
operations = set(_operation_map.keys())
observables = {"PauliX", "PauliY", "PauliZ", "Identity", "Hadamard", "Hermitian"}

hw_analytic_warning_message = "The analytic calculation of expectations and variances "\
"is only supported on statevector backends, not on the {}. "\
"The obtained result is based on sampling."

_eigs = {}

def __init__(self, wires, provider, backend, shots=1024, **kwargs):
Expand Down Expand Up @@ -349,6 +353,12 @@ def expval(self, observable, wires, par):
prob = np.fromiter(self.probabilities(wires=wires).values(), dtype=np.float64)
return (eigvals @ prob).real

if self.analytic:
# Raise a warning if backend is a hardware simulator
warnings.warn(self.hw_analytic_warning_message.
format(self.backend),
UserWarning)

# estimate the ev
return np.mean(self.sample(observable, wires, par))

Expand All @@ -359,6 +369,12 @@ def var(self, observable, wires, par):
prob = np.fromiter(self.probabilities(wires=wires).values(), dtype=np.float64)
return (eigvals ** 2) @ prob - (eigvals @ prob).real ** 2

if self.analytic:
# Raise a warning if backend is a hardware simulator
warnings.warn(self.hw_analytic_warning_message.
format(self.backend),
UserWarning)

return np.var(self.sample(observable, wires, par))

def sample(self, observable, wires, par):
Expand Down
10 changes: 10 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,16 @@ def backend(request):
return request.param


@pytest.fixture(params=state_backends)
def statevector_backend(request):
return request.param


@pytest.fixture(params=hw_backends)
def hardware_backend(request):
return request.param


@pytest.fixture(params=[AerDevice, BasicAerDevice])
def device(request, backend, shots, analytic):
if backend not in state_backends and analytic == True:
Expand Down
3 changes: 1 addition & 2 deletions tests/test_converter.py
Original file line number Diff line number Diff line change
Expand Up @@ -691,8 +691,7 @@ class TestConverterWarnings:
"""Tests that the converter.load function emits warnings."""

def test_barrier_not_supported(self, recorder):
"""Tests the load method raises a ValueError, if something that is
not a QuanctumCircuit was passed."""
"""Tests that a warning is raised if an unsupported instruction was reached."""
qc = QuantumCircuit(3, 1)
qc.barrier()

Expand Down
155 changes: 116 additions & 39 deletions tests/test_qiskit_device.py
Original file line number Diff line number Diff line change
@@ -1,39 +1,116 @@
import pytest

import numpy as np

from pennylane_qiskit.qiskit_device import pauli_eigs
from pennylane_qiskit import AerDevice


Z = np.diag([1, -1])


class TestZEigs:
r"""Test that eigenvalues of Z^{\otimes n} are correctly generated"""

def test_one(self):
"""Test that eigs(Z) = [1, -1]"""
assert np.all(pauli_eigs(1) == np.array([1, -1]))

@pytest.mark.parametrize("n", [2, 3, 6])
def test_multiple(self, n):
r"""Test that eigs(Z^{\otimes n}) is correct"""
res = pauli_eigs(n)
Zn = np.kron(Z, Z)

for _ in range(n - 2):
Zn = np.kron(Zn, Z)

expected = np.diag(Zn)
assert np.all(res == expected)


class TestProbabilities:
"""Tests for the probability function"""

def test_probability_no_results(self):
"""Test that the probabilities function returns
None if no job has yet been run."""
dev = AerDevice(backend="statevector_simulator", wires=1, analytic=True)
assert dev.probabilities() is None
import numpy as np
import pytest

import pennylane as qml
from pennylane_qiskit import AerDevice
from pennylane_qiskit.qiskit_device import pauli_eigs

Z = np.diag([1, -1])


class TestZEigs:
r"""Test that eigenvalues of Z^{\otimes n} are correctly generated"""

def test_one(self):
"""Test that eigs(Z) = [1, -1]"""
assert np.all(pauli_eigs(1) == np.array([1, -1]))

@pytest.mark.parametrize("n", [2, 3, 6])
def test_multiple(self, n):
r"""Test that eigs(Z^{\otimes n}) is correct"""
res = pauli_eigs(n)
Zn = np.kron(Z, Z)

for _ in range(n - 2):
Zn = np.kron(Zn, Z)

expected = np.diag(Zn)
assert np.all(res == expected)


class TestProbabilities:
"""Tests for the probability function"""

def test_probability_no_results(self):
"""Test that the probabilities function returns
None if no job has yet been run."""
dev = AerDevice(backend="statevector_simulator", wires=1, analytic=True)
assert dev.probabilities() is None


class TestAnalyticWarningHWSimulator:
"""Tests the warnings for when the analytic attribute of a device is set to true"""

def test_warning_raised_for_hardware_backend_analytic_expval(self, hardware_backend, recorder):
"""Tests that a warning is raised if the analytic attribute is true on
hardware simulators when calculating the expectation"""

dev = qml.device("qiskit.basicaer", backend=hardware_backend, wires=2)

@qml.qnode(dev)
def circuit():
qml.Hadamard(wires=0)
return qml.expval(qml.PauliZ(0))

with pytest.warns(UserWarning) as record:
circuit()

# check that only one warning was raised
assert len(record) == 1
# check that the message matches
assert record[0].message.args[0] == "The analytic calculation of expectations and variances "\
"is only supported on statevector backends, not on the {}. "\
"The obtained result is based on sampling.".format(dev.backend)

def test_no_warning_raised_for_software_backend_analytic_expval(self, statevector_backend, recorder, recwarn):
"""Tests that no warning is raised if the analytic attribute is true on
statevector simulators when calculating the expectation"""

dev = qml.device("qiskit.basicaer", backend=statevector_backend, wires=2)

@qml.qnode(dev)
def circuit():
qml.Hadamard(wires=0)
return qml.expval(qml.PauliZ(0))

circuit()

# check that no warnings were raised
assert len(recwarn) == 0

def test_warning_raised_for_hardware_backend_analytic_var(self, hardware_backend, recorder):
"""Tests that a warning is raised if the analytic attribute is true on
hardware simulators when calculating the variance"""

dev = qml.device("qiskit.basicaer", backend=hardware_backend, wires=2)

@qml.qnode(dev)
def circuit():
qml.Hadamard(wires=0)
return qml.var(qml.PauliZ(0))

with pytest.warns(UserWarning) as record:
circuit()

# check that only one warning was raised
assert len(record) == 1
# check that the message matches
assert record[0].message.args[0] == "The analytic calculation of expectations and variances "\
"is only supported on statevector backends, not on the {}. "\
"The obtained result is based on sampling.".format(dev.backend)

def test_no_warning_raised_for_software_backend_analytic_var(self, statevector_backend, recorder, recwarn):
"""Tests that no warning is raised if the analytic attribute is true on
statevector simulators when calculating the variance"""

dev = qml.device("qiskit.basicaer", backend=statevector_backend, wires=2)

@qml.qnode(dev)
def circuit():
qml.Hadamard(wires=0)
return qml.var(qml.PauliZ(0))

circuit()

# check that no warnings were raised
assert len(recwarn) == 0

0 comments on commit 1bab8ea

Please sign in to comment.