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

[WIP] Redesign capabilities dictionary #781

Merged
merged 53 commits into from
Sep 14, 2020
Merged

Conversation

mariaschuld
Copy link
Contributor

@mariaschuld mariaschuld commented Aug 27, 2020

This PR implements two goals:

  1. Redesign of the capabilities dictionary
  • The dictionary is now inherited from the parent class
  • The keys have more expressive names, indicating the value type (i.e., supports_XYZ instead of XYZ)
  • All keys, plus a few more that will be handy in future, are defined in the base device class
  • The developer documentation is updated
  1. Test capabilities (plus supported operations) of a device in the shared device tests
  • The supported operations and observables of a device are now tested
  • Most of the capabilities of a device are tested

In the process, a few bugs were uncovered, and the default.tensor device got the attribute analytic to allow some more tests to run.

'performs_noisy_computation': False,
}
"""The capabilities dictionary stores the properties of a device. Devices can add their
own custom properties and overwrite existing ones by overwriting the ``capabilities`` class method."""
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not sure if one would use docstrings for class attributes?

pennylane/_device.py Outdated Show resolved Hide resolved
pennylane/_device.py Outdated Show resolved Hide resolved
_capabilities = {} #: dict[str->*]: plugin capabilities
_capabilities = {
'model': None,
'passthru_interface': 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.

In two minds about this one: we could only define it in the devices that actually have passthru functionality (so that absence of the key signifies that the device does not)...

def capabilities(cls):
capabilities = super().capabilities().copy()
capabilities.update(
supports_reversible_diff=True,
Copy link
Contributor

Choose a reason for hiding this comment

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

How come this would not be defined among the capabilities in Device?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It is set to False in Device...

Copy link
Contributor

Choose a reason for hiding this comment

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

Oh not sure how I missed that, my bad!

Copy link
Contributor

Choose a reason for hiding this comment

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

for my understanding, how do we know if something supports reversible diff?

ops[operation]
return qml.expval(qml.Identity(wires=0))

assert isinstance(circuit(), (float, np.ndarray))
Copy link
Contributor

Choose a reason for hiding this comment

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

Just curious, what would be a specific case for this assertion to fail?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good question, the assert statement is more of a dummy here. It's one of those tests where I just want to know "does the execution throw an error". Is there a generic way to do this with pytest? One could also not have an assert statement at all, as in some other test, but I thought this is bad practice...

Copy link
Contributor

@antalszava antalszava left a comment

Choose a reason for hiding this comment

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

Looks good to me, great on @mariaschuld! 💯 Had some questions, mostly for my own understanding

@mariaschuld
Copy link
Contributor Author

After double approval I implemented quite a few more changes:

  • The base device class now only defines the model:None capability, everything else is up to the device
  • Added keys returns_probs and returns_state, and use the former to exclude devices from tests
  • Improved the capabilities tests to complain if a capability is misreported (before it only complained if it was illegally claimed to be True)

@trbromley , since the second change is partly requested by you, would you mind having a look at the overall PR too?

@mariaschuld
Copy link
Contributor Author

After double approval I implemented quite a few more changes:

  • The base device class now only defines the model:None capability, everything else is up to the device
  • Added keys returns_probs and returns_state, and use the former to exclude devices from tests
  • Improved the capabilities tests to complain if a capability is misreported (before it only complained if it was illegally claimed to be True)

@trbromley , since the second change is partly requested by you, would you mind having a look at the overall PR too?

Copy link
Contributor

@trbromley trbromley left a comment

Choose a reason for hiding this comment

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

Thanks @mariaschuld! Just left minor comments. One thing I was wondering is whether we should set return_state=False in the base Device?

@@ -101,15 +101,31 @@ as well as potential further capabilities, by providing the following class attr
conversion between the two conventions takes place automatically
by the plugin device.

* :attr:`.Device._capabilities`: a dictionary containing information about the capabilities of
the device. Keys currently supported include:
* :func:`.Device.capabilities`: A class method which returns the dictionary of capabilities of a device. A
Copy link
Contributor

Choose a reason for hiding this comment

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

image

Just wondering if it would make sense to update the subsection title to make it more capability focused, e.g., "Defining capabilities"

pennylane/beta/devices/default_tensor.py Show resolved Hide resolved
supports_analytic_computation=True,
supports_finite_shots=False,
supports_tensor_observables=True,
returns_state=False,
Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks for this! 👍

def capabilities(cls):
capabilities = super().capabilities().copy()
capabilities.update(
supports_reversible_diff=True,
Copy link
Contributor

Choose a reason for hiding this comment

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

for my understanding, how do we know if something supports reversible diff?

Comment on lines 209 to 210
with pytest.raises(AttributeError):
dev.state # pylint: disable = pointless-statement
Copy link
Contributor

Choose a reason for hiding this comment

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

Some devices (e.g. qiskit.aer) have the dev.state attribute available, but it is equal to None. I would say that for such devices, cap["returns_state"] = False should be the case. However, I think this test would fail for such a device since it wouldn't raise an AttributeError 🤔

If I understand the layout of the test suite correctly, this test would be applied to qiskit.aer in https://github.com/PennyLaneAI/plugin-test-matrix (I guess it would skip for now, until we define the capabilities for plugins).

Maybe we should have

if cap["returns_state"]:
    assert dev.state is not None
else:
    try:
        state = dev.state
    except AttributeError:
        state = None

    assert state is None

tests/beta/test_default_tensor.py Outdated Show resolved Hide resolved
supports_analytic_computation=True,
supports_finite_shots=True,
returns_probs=False,
returns_state=False,
Copy link
Contributor

Choose a reason for hiding this comment

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

Perhaps we could set returns_state=False in the base Device, and then only turn it on when needed.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The reason why I reduced the default values in the base device is that not having the value is better than misreporting. Also, it would set this value for all devices, and it would be better to go through them one by one...

@@ -488,6 +500,7 @@ def test_two_qubit_no_parameters(self, device, init_state, op, mat, tol, skip_if
n_wires = 2
dev = device(n_wires)
skip_if(dev, {"supports_inverse_operations": False})
skip_if(dev, {"returns_probs": False})
Copy link
Contributor

Choose a reason for hiding this comment

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

seems to have complaints for theta in this file too 🤔

Copy link
Contributor

@antalszava antalszava left a comment

Choose a reason for hiding this comment

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

💯 (no particular suggestions, good to go from my side once Tom's major suggestions are also addressed)

@mariaschuld
Copy link
Contributor Author

About #781 (comment):

I test it by checking if one can use the device in a reversible qnode and can compute a gradient...but maybe there is a better option. Luckily, the device test are due for an upgrade.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants