Skip to content

Commit

Permalink
[BugFix] KeyError when running QNode(qfun, dev)(graph) with graph…
Browse files Browse the repository at this point in the history
… an instance of `networkx` Graph object (#6600)

**Context:** Issue #6585 indicates that we don't currently allow
arguments with types defined in libraries not listed in the supported
interfaces.

In #6225, the convention of treating internally as `interface=None`
parameters of the `numpy` interface has been introduced.
The idea of this PR is to allow non-trainable `qnode` inputs to "just
work" as arguments, without any special consideration or treatment. We
want to rely on the default `numpy` interface for libraries we know
nothing about.

**Description of the Change:** By default, we set `None` as the value of
the interface if the latter is not found in the list of supported
interfaces. According to the convention introduced in #6225, this should
automatically map the interface to `numpy`.

**Benefits:** Now the `qnode` accepts arguments with types defined in
libraries that are not necessarily in the list of supported interfaces,
such as the `Graph` class defined in `networkx`.

**Possible Drawbacks:** None that I can think of.

**Related GitHub Issues:** #6585 

**Related Shortcut Stories:** [sc-78344]

---------

Co-authored-by: Christina Lee <[email protected]>
  • Loading branch information
PietropaoloFrisoni and albi3ro authored Nov 20, 2024
1 parent c9af833 commit eb37a95
Show file tree
Hide file tree
Showing 4 changed files with 52 additions and 8 deletions.
9 changes: 6 additions & 3 deletions doc/releases/changelog-dev.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@

* Added `qml.devices.qubit_mixed` module for mixed-state qubit device support [(#6379)](https://github.com/PennyLaneAI/pennylane/pull/6379). This module introduces an `apply_operation` helper function that features:


* Two density matrix contraction methods using `einsum` and `tensordot`

* Optimized handling of special cases including: Diagonal operators, Identity operators, CX (controlled-X), Multi-controlled X gates, Grover operators
Expand Down Expand Up @@ -132,7 +131,6 @@ following 4 sets of functions have been either moved or removed[(#6588)](https:/
* The `expand_depth` argument for `qml.compile` has been removed.
[(#6531)](https://github.com/PennyLaneAI/pennylane/pull/6531)


* The `qml.shadows.shadow_expval` transform has been removed. Instead, please use the
`qml.shadow_expval` measurement process.
[(#6530)](https://github.com/PennyLaneAI/pennylane/pull/6530)
Expand Down Expand Up @@ -165,14 +163,19 @@ same information.
[(#6549)](https://github.com/PennyLaneAI/pennylane/pull/6549)

<h3>Documentation 📝</h3>

* Add reporting of test warnings as failures.
[(#6217)](https://github.com/PennyLaneAI/pennylane/pull/6217)

* Add a warning message to Gradients and training documentation about ComplexWarnings
* Add a warning message to Gradients and training documentation about ComplexWarnings.
[(#6543)](https://github.com/PennyLaneAI/pennylane/pull/6543)

<h3>Bug fixes 🐛</h3>

* `qml.QNode` now accepts arguments with types defined in libraries that are not necessarily
in the list of supported interfaces, such as the `Graph` class defined in `networkx`.
[(#6600)](https://github.com/PennyLaneAI/pennylane/pull/6600)

* `qml.math.get_deep_interface` now works properly for autograd arrays.
[(#6557)](https://github.com/PennyLaneAI/pennylane/pull/6557)

Expand Down
4 changes: 2 additions & 2 deletions pennylane/workflow/execution.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
# pylint: disable=import-outside-toplevel,too-many-branches,not-callable,unexpected-keyword-arg
# pylint: disable=unused-argument,unnecessary-lambda-assignment,inconsistent-return-statements
# pylint: disable=invalid-unary-operand-type,isinstance-second-argument-not-valid-type
# pylint: disable=too-many-arguments,too-many-statements,function-redefined,too-many-function-args
# pylint: disable=too-many-arguments,too-many-statements,function-redefined,too-many-function-args,too-many-positional-arguments

import inspect
import logging
Expand Down Expand Up @@ -269,7 +269,7 @@ def _get_interface_name(tapes, interface):
params.extend(tape.get_parameters(trainable_only=False))
interface = qml.math.get_interface(*params)
if interface != "numpy":
interface = INTERFACE_MAP[interface]
interface = INTERFACE_MAP.get(interface, None)
if interface == "tf" and _use_tensorflow_autograph():
interface = "tf-autograph"
if interface == "jax":
Expand Down
6 changes: 3 additions & 3 deletions pennylane/workflow/qnode.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"""
This module contains the QNode class and qnode decorator.
"""
# pylint: disable=too-many-instance-attributes,too-many-arguments,protected-access,unnecessary-lambda-assignment, too-many-branches, too-many-statements, unused-argument
# pylint: disable=too-many-instance-attributes,too-many-arguments,protected-access,unnecessary-lambda-assignment, too-many-branches, too-many-statements, unused-argument, too-many-positional-arguments
import copy
import functools
import inspect
Expand Down Expand Up @@ -75,7 +75,7 @@ def _convert_to_interface(res, interface):
"tf-autograph": "tensorflow",
}

interface_name = interface_conversion_map[interface]
interface_name = interface_conversion_map.get(interface, None)

return qml.math.asarray(res, like=interface_name)

Expand Down Expand Up @@ -983,7 +983,7 @@ def _impl_call(self, *args, **kwargs) -> qml.typing.Result:
else qml.math.get_interface(*args, *list(kwargs.values()))
)
if interface != "numpy":
interface = INTERFACE_MAP[interface]
interface = INTERFACE_MAP.get(interface, None)
self._interface = interface

try:
Expand Down
41 changes: 41 additions & 0 deletions tests/test_qnode.py
Original file line number Diff line number Diff line change
Expand Up @@ -1030,6 +1030,47 @@ def circuit(x):
res = circuit(x)
assert qml.math.get_interface(res) == "numpy"

def test_qnode_default_interface(self):
"""Tests that the default interface is set correctly for a QNode."""

# pylint: disable=import-outside-toplevel
import networkx as nx

@qml.qnode(qml.device("default.qubit"))
def circuit(graph: nx.Graph):
for a in graph.nodes:
qml.Hadamard(wires=a)
for a, b in graph.edges:
qml.CZ(wires=[a, b])
return qml.expval(qml.PauliZ(0))

graph = nx.complete_graph(3)
res = circuit(graph)
assert qml.math.get_interface(res) == "numpy"

def test_qscript_default_interface(self):
"""Tests that the default interface is set correctly for a QuantumScript."""

# pylint: disable=import-outside-toplevel
import networkx as nx

dev = qml.device("default.qubit")

# pylint: disable=too-few-public-methods
class DummyCustomGraphOp(qml.operation.Operation):
"""Dummy custom operation for testing purposes."""

def __init__(self, graph: nx.Graph):
super().__init__(graph, wires=graph.nodes)

def decomposition(self) -> list:
return []

graph = nx.complete_graph(3)
tape = qml.tape.QuantumScript([DummyCustomGraphOp(graph)], [qml.expval(qml.PauliZ(0))])
res = qml.execute([tape], dev)
assert qml.math.get_interface(res) == "numpy"


class TestShots:
"""Unit tests for specifying shots per call."""
Expand Down

0 comments on commit eb37a95

Please sign in to comment.