We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Arithmetic operations between pennylane.numpy arrays have inconsistent `requires_grad` output 
**Issue**

The PennyLane wrapper if the numpy library, which is used to optimise circuits with the standard autograd interface, has an inconsistent behaviour when using arithmetic functions such as +, -, * (here using the sum to demonstrate). 

Summing two `pennylane.numpy.array` objects, one having gradients enabled and the other not, returns a differentiable array or not, depending on the order.

``` python
import pennylane as qml
from pennylane import numpy as np

a = np.array(1., requires_grad=True)
b = np.array(0., requires_grad=False)

c = a+b
print(c.requires_grad) # True

d = b+a
print(d.requires_grad) # False
```
**Expected behaviour**
The `requires_grad = True` flag should always be overwriting, and the output of the two print statements should be true in both cases. This is important for example in loss function where target outputs (which do not require gradients) and predictions (which depend on the parameters and require gradients) are compared, i.e. via `np.sum(np.abs((target - prediction))**2)`.

**System information**
Reproducible with PennyLane v0.12 stable release, and with the current master, in tape mode or not in tape mode.


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `pennylane/numpy/tensor.py`
Content:
```
1 # Copyright 2018-2020 Xanadu Quantum Technologies Inc.
2 
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 
7 #     http://www.apache.org/licenses/LICENSE-2.0
8 
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14 """
15 This module provides the PennyLane :class:`~.tensor` class.
16 """
17 import numpy as onp
18 
19 from autograd import numpy as _np
20 
21 from autograd.tracer import Box
22 from autograd.numpy.numpy_boxes import ArrayBox
23 from autograd.numpy.numpy_vspaces import ComplexArrayVSpace, ArrayVSpace
24 from autograd.core import VSpace
25 
26 
27 __doc__ = "NumPy with automatic differentiation support, provided by Autograd and PennyLane."
28 
29 
30 class tensor(_np.ndarray):
31     """Constructs a PennyLane tensor for use with Autograd QNodes.
32 
33     The ``tensor`` class is a subclass of ``numpy.ndarray``,
34     providing the same multidimensional, homogeneous data-structure
35     of fixed-size items, with an additional flag to indicate to PennyLane
36     whether the contained data is differentiable or not.
37 
38     .. warning::
39 
40         PennyLane ``tensor`` objects are only used as part of the Autograd QNode
41         interface. If using another machine learning library such as PyTorch or
42         TensorFlow, use their built-in ``tf.Variable`` and ``torch.tensor`` classes
43         instead.
44 
45     .. warning::
46 
47         Tensors should be constructed using standard array construction functions
48         provided as part of PennyLane's NumPy implementation, including
49         ``np.array``, ``np.zeros`` or ``np.empty``.
50 
51         The parameters given here refer to a low-level class
52         for instantiating tensors.
53 
54 
55     Args:
56         input_array (array_like): Any data structure in any form that can be converted to
57             an array. This includes lists, lists of tuples, tuples, tuples of tuples,
58             tuples of lists and ndarrays.
59         requires_grad (bool): whether the tensor supports differentiation
60 
61     **Example**
62 
63     The trainability of a tensor can be set on construction via the
64     ``requires_grad`` keyword argument,
65 
66     >>> from pennylane import numpy as np
67     >>> x = np.array([0, 1, 2], requires_grad=True)
68     >>> x
69     tensor([0, 1, 2], requires_grad=True)
70 
71     or in-place by modifying the ``requires_grad`` attribute:
72 
73     >>> x.requires_grad = False
74     tensor([0, 1, 2], requires_grad=False)
75 
76     Since tensors are subclasses of ``np.ndarray``, they can be provided as arguments
77     to any PennyLane-wrapped NumPy function:
78 
79     >>> np.sin(x)
80     tensor([0.        , 0.84147098, 0.90929743], requires_grad=True)
81 
82     When composing functions of multiple tensors, if at least one input tensor is differentiable,
83     then the output will also be differentiable:
84 
85     >>> x = np.array([0, 1, 2], requires_grad=False)
86     >>> y = np.zeros([3], requires_grad=True)
87     >>> np.vstack([x, y])
88     tensor([[0., 1., 2.],
89         [0., 0., 0.]], requires_grad=True)
90     """
91 
92     def __new__(cls, input_array, *args, requires_grad=True, **kwargs):
93         obj = _np.array(input_array, *args, **kwargs)
94 
95         if isinstance(obj, _np.ndarray):
96             obj = obj.view(cls)
97             obj.requires_grad = requires_grad
98 
99         return obj
100 
101     def __array_finalize__(self, obj):
102         # pylint: disable=attribute-defined-outside-init
103         if obj is None:  # pragma: no cover
104             return
105 
106         self.requires_grad = getattr(obj, "requires_grad", None)
107 
108     def __repr__(self):
109         string = super().__repr__()
110         return string[:-1] + ", requires_grad={})".format(self.requires_grad)
111 
112     def __array_wrap__(self, obj):
113         out_arr = tensor(obj, requires_grad=self.requires_grad)
114         return super().__array_wrap__(out_arr)
115 
116     def __getitem__(self, *args, **kwargs):
117         item = super().__getitem__(*args, **kwargs)
118 
119         if not isinstance(item, tensor):
120             item = tensor(item, requires_grad=self.requires_grad)
121 
122         return item
123 
124     def __hash__(self):
125         if self.ndim == 0:
126             # Allowing hashing if the tensor is a scalar.
127             # We hash both the scalar value *and* the differentiability information,
128             # to match the behaviour of PyTorch.
129             return hash((self.item(), self.requires_grad))
130 
131         raise TypeError("unhashable type: 'numpy.tensor'")
132 
133     def unwrap(self):
134         """Converts the tensor to a standard, non-differentiable NumPy ndarray or Python scalar if
135         the tensor is 0-dimensional.
136 
137         All information regarding differentiability of the tensor will be lost.
138 
139         .. warning::
140 
141             The returned array is a new view onto the **same data**. That is,
142             the tensor and the returned ``ndarray`` share the same underlying storage.
143             Changes to the tensor object will be reflected within the returned array,
144             and vice versa.
145 
146         **Example**
147 
148         >>> from pennylane import numpy as np
149         >>> x = np.array([1, 2], requires_grad=True)
150         >>> x
151         tensor([1, 2], requires_grad=True)
152         >>> x.unwrap()
153         array([1, 2])
154 
155         Zero dimensional array are converted to Python scalars:
156 
157         >>> x = np.array(1.543, requires_grad=False)
158         >>> x.unwrap()
159         1.543
160         >>> type(x.unwrap())
161         float
162 
163         The underlying data is **not** copied:
164 
165         >>> x = np.array([1, 2], requires_grad=True)
166         >>> y = x.unwrap()
167         >>> x[0] = 5
168         >>> y
169         array([5, 2])
170         >>> y[1] = 7
171         >>> x
172         tensor([5, 7], requires_grad=True)
173 
174 
175         To create a copy, the ``copy()`` method can be used:
176 
177         >>> x = np.array([1, 2], requires_grad=True)
178         >>> y = x.unwrap().copy()
179         >>> x[0] = 5
180         >>> y
181         array([1, 2])
182         """
183         if self.ndim == 0:
184             return self.view(onp.ndarray).item()
185 
186         return self.view(onp.ndarray)
187 
188     def numpy(self):
189         """Converts the tensor to a standard, non-differentiable NumPy ndarray or Python scalar if
190         the tensor is 0-dimensional.
191 
192         This method is an alias for :meth:`~.unwrap`. See :meth:`~.unwrap` for more details.
193         """
194         return self.unwrap()
195 
196 
197 class NonDifferentiableError(Exception):
198     """Exception raised if attempting to differentiate non-trainable
199     :class:`~.tensor` using Autograd."""
200 
201 
202 def tensor_to_arraybox(x, *args):
203     """Convert a :class:`~.tensor` to an Autograd ``ArrayBox``.
204 
205     Args:
206         x (array_like): Any data structure in any form that can be converted to
207             an array. This includes lists, lists of tuples, tuples, tuples of tuples,
208             tuples of lists and ndarrays.
209 
210     Returns:
211         autograd.numpy.numpy_boxes.ArrayBox: Autograd ArrayBox instance of the array
212 
213     Raises:
214         NonDifferentiableError: if the provided tensor is non-differentiable
215     """
216     if isinstance(x, tensor):
217         if x.requires_grad:
218             return ArrayBox(x, *args)
219 
220         raise NonDifferentiableError(
221             "{} is non-differentiable. Set the requires_grad attribute to True.".format(x)
222         )
223 
224     return ArrayBox(x, *args)
225 
226 
227 Box.type_mappings[tensor] = tensor_to_arraybox
228 VSpace.mappings[tensor] = lambda x: ComplexArrayVSpace(x) if onp.iscomplexobj(x) else ArrayVSpace(x)
229
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/pennylane/numpy/tensor.py b/pennylane/numpy/tensor.py
--- a/pennylane/numpy/tensor.py
+++ b/pennylane/numpy/tensor.py
@@ -113,6 +113,55 @@
         out_arr = tensor(obj, requires_grad=self.requires_grad)
         return super().__array_wrap__(out_arr)
 
+    def __array_ufunc__(self, ufunc, method, *inputs, **kwargs):
+        # pylint: disable=no-member,attribute-defined-outside-init
+
+        # unwrap any outputs the ufunc might have
+        outputs = [i.view(onp.ndarray) for i in kwargs.get("out", ())]
+
+        if outputs:
+            # Insert the unwrapped outputs into the keyword
+            # args dictionary, to be passed to ndarray.__array_ufunc__
+            outputs = tuple(outputs)
+            kwargs["out"] = outputs
+        else:
+            # If the ufunc has no ouputs, we simply
+            # create a tuple containing None for all potential outputs.
+            outputs = (None,) * ufunc.nout
+
+        # unwrap the input arguments to the ufunc
+        args = [i.unwrap() if hasattr(i, "unwrap") else i for i in inputs]
+
+        # call the ndarray.__array_ufunc__ method to compute the result
+        # of the vectorized ufunc
+        res = super().__array_ufunc__(ufunc, method, *args, **kwargs)
+
+        if ufunc.nout == 1:
+            res = (res,)
+
+        # construct a list of ufunc outputs to return
+        ufunc_output = [
+            (onp.asarray(result) if output is None else output)
+            for result, output in zip(res, outputs)
+        ]
+
+        # if any of the inputs were trainable, the output is also trainable
+        requires_grad = any(
+            isinstance(x, onp.ndarray) and getattr(x, "requires_grad", True) for x in inputs
+        )
+
+        # Iterate through the ufunc outputs and convert each to a PennyLane tensor.
+        # We also correctly set the requires_grad attribute.
+        for i in range(len(ufunc_output)):  # pylint: disable=consider-using-enumerate
+            ufunc_output[i] = tensor(ufunc_output[i], requires_grad=requires_grad)
+
+        if len(ufunc_output) == 1:
+            # the ufunc has a single output so return a single tensor
+            return ufunc_output[0]
+
+        # otherwise we must return a tuple of tensors
+        return tuple(ufunc_output)
+
     def __getitem__(self, *args, **kwargs):
         item = super().__getitem__(*args, **kwargs)

{"golden_diff": "diff --git a/pennylane/numpy/tensor.py b/pennylane/numpy/tensor.py\n--- a/pennylane/numpy/tensor.py\n+++ b/pennylane/numpy/tensor.py\n@@ -113,6 +113,55 @@\n         out_arr = tensor(obj, requires_grad=self.requires_grad)\r\n         return super().__array_wrap__(out_arr)\r\n \r\n+    def __array_ufunc__(self, ufunc, method, *inputs, **kwargs):\r\n+        # pylint: disable=no-member,attribute-defined-outside-init\r\n+\r\n+        # unwrap any outputs the ufunc might have\r\n+        outputs = [i.view(onp.ndarray) for i in kwargs.get(\"out\", ())]\r\n+\r\n+        if outputs:\r\n+            # Insert the unwrapped outputs into the keyword\r\n+            # args dictionary, to be passed to ndarray.__array_ufunc__\r\n+            outputs = tuple(outputs)\r\n+            kwargs[\"out\"] = outputs\r\n+        else:\r\n+            # If the ufunc has no ouputs, we simply\r\n+            # create a tuple containing None for all potential outputs.\r\n+            outputs = (None,) * ufunc.nout\r\n+\r\n+        # unwrap the input arguments to the ufunc\r\n+        args = [i.unwrap() if hasattr(i, \"unwrap\") else i for i in inputs]\r\n+\r\n+        # call the ndarray.__array_ufunc__ method to compute the result\r\n+        # of the vectorized ufunc\r\n+        res = super().__array_ufunc__(ufunc, method, *args, **kwargs)\r\n+\r\n+        if ufunc.nout == 1:\r\n+            res = (res,)\r\n+\r\n+        # construct a list of ufunc outputs to return\r\n+        ufunc_output = [\r\n+            (onp.asarray(result) if output is None else output)\r\n+            for result, output in zip(res, outputs)\r\n+        ]\r\n+\r\n+        # if any of the inputs were trainable, the output is also trainable\r\n+        requires_grad = any(\r\n+            isinstance(x, onp.ndarray) and getattr(x, \"requires_grad\", True) for x in inputs\r\n+        )\r\n+\r\n+        # Iterate through the ufunc outputs and convert each to a PennyLane tensor.\r\n+        # We also correctly set the requires_grad attribute.\r\n+        for i in range(len(ufunc_output)):  # pylint: disable=consider-using-enumerate\r\n+            ufunc_output[i] = tensor(ufunc_output[i], requires_grad=requires_grad)\r\n+\r\n+        if len(ufunc_output) == 1:\r\n+            # the ufunc has a single output so return a single tensor\r\n+            return ufunc_output[0]\r\n+\r\n+        # otherwise we must return a tuple of tensors\r\n+        return tuple(ufunc_output)\r\n+\r\n     def __getitem__(self, *args, **kwargs):\r\n         item = super().__getitem__(*args, **kwargs)\n", "issue": "Arithmetic operations between pennylane.numpy arrays have inconsistent `requires_grad` output \n**Issue**\r\n\r\nThe PennyLane wrapper if the numpy library, which is used to optimise circuits with the standard autograd interface, has an inconsistent behaviour when using arithmetic functions such as +, -, * (here using the sum to demonstrate). \r\n\r\nSumming two `pennylane.numpy.array` objects, one having gradients enabled and the other not, returns a differentiable array or not, depending on the order.\r\n\r\n``` python\r\nimport pennylane as qml\r\nfrom pennylane import numpy as np\r\n\r\na = np.array(1., requires_grad=True)\r\nb = np.array(0., requires_grad=False)\r\n\r\nc = a+b\r\nprint(c.requires_grad) # True\r\n\r\nd = b+a\r\nprint(d.requires_grad) # False\r\n```\r\n**Expected behaviour**\r\nThe `requires_grad = True` flag should always be overwriting, and the output of the two print statements should be true in both cases. This is important for example in loss function where target outputs (which do not require gradients) and predictions (which depend on the parameters and require gradients) are compared, i.e. via `np.sum(np.abs((target - prediction))**2)`.\r\n\r\n**System information**\r\nReproducible with PennyLane v0.12 stable release, and with the current master, in tape mode or not in tape mode.\r\n\n", "before_files": [{"content": "# Copyright 2018-2020 Xanadu Quantum Technologies Inc.\r\n\r\n# Licensed under the Apache License, Version 2.0 (the \"License\");\r\n# you may not use this file except in compliance with the License.\r\n# You may obtain a copy of the License at\r\n\r\n#     http://www.apache.org/licenses/LICENSE-2.0\r\n\r\n# Unless required by applicable law or agreed to in writing, software\r\n# distributed under the License is distributed on an \"AS IS\" BASIS,\r\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n# See the License for the specific language governing permissions and\r\n# limitations under the License.\r\n\"\"\"\r\nThis module provides the PennyLane :class:`~.tensor` class.\r\n\"\"\"\r\nimport numpy as onp\r\n\r\nfrom autograd import numpy as _np\r\n\r\nfrom autograd.tracer import Box\r\nfrom autograd.numpy.numpy_boxes import ArrayBox\r\nfrom autograd.numpy.numpy_vspaces import ComplexArrayVSpace, ArrayVSpace\r\nfrom autograd.core import VSpace\r\n\r\n\r\n__doc__ = \"NumPy with automatic differentiation support, provided by Autograd and PennyLane.\"\r\n\r\n\r\nclass tensor(_np.ndarray):\r\n    \"\"\"Constructs a PennyLane tensor for use with Autograd QNodes.\r\n\r\n    The ``tensor`` class is a subclass of ``numpy.ndarray``,\r\n    providing the same multidimensional, homogeneous data-structure\r\n    of fixed-size items, with an additional flag to indicate to PennyLane\r\n    whether the contained data is differentiable or not.\r\n\r\n    .. warning::\r\n\r\n        PennyLane ``tensor`` objects are only used as part of the Autograd QNode\r\n        interface. If using another machine learning library such as PyTorch or\r\n        TensorFlow, use their built-in ``tf.Variable`` and ``torch.tensor`` classes\r\n        instead.\r\n\r\n    .. warning::\r\n\r\n        Tensors should be constructed using standard array construction functions\r\n        provided as part of PennyLane's NumPy implementation, including\r\n        ``np.array``, ``np.zeros`` or ``np.empty``.\r\n\r\n        The parameters given here refer to a low-level class\r\n        for instantiating tensors.\r\n\r\n\r\n    Args:\r\n        input_array (array_like): Any data structure in any form that can be converted to\r\n            an array. This includes lists, lists of tuples, tuples, tuples of tuples,\r\n            tuples of lists and ndarrays.\r\n        requires_grad (bool): whether the tensor supports differentiation\r\n\r\n    **Example**\r\n\r\n    The trainability of a tensor can be set on construction via the\r\n    ``requires_grad`` keyword argument,\r\n\r\n    >>> from pennylane import numpy as np\r\n    >>> x = np.array([0, 1, 2], requires_grad=True)\r\n    >>> x\r\n    tensor([0, 1, 2], requires_grad=True)\r\n\r\n    or in-place by modifying the ``requires_grad`` attribute:\r\n\r\n    >>> x.requires_grad = False\r\n    tensor([0, 1, 2], requires_grad=False)\r\n\r\n    Since tensors are subclasses of ``np.ndarray``, they can be provided as arguments\r\n    to any PennyLane-wrapped NumPy function:\r\n\r\n    >>> np.sin(x)\r\n    tensor([0.        , 0.84147098, 0.90929743], requires_grad=True)\r\n\r\n    When composing functions of multiple tensors, if at least one input tensor is differentiable,\r\n    then the output will also be differentiable:\r\n\r\n    >>> x = np.array([0, 1, 2], requires_grad=False)\r\n    >>> y = np.zeros([3], requires_grad=True)\r\n    >>> np.vstack([x, y])\r\n    tensor([[0., 1., 2.],\r\n        [0., 0., 0.]], requires_grad=True)\r\n    \"\"\"\r\n\r\n    def __new__(cls, input_array, *args, requires_grad=True, **kwargs):\r\n        obj = _np.array(input_array, *args, **kwargs)\r\n\r\n        if isinstance(obj, _np.ndarray):\r\n            obj = obj.view(cls)\r\n            obj.requires_grad = requires_grad\r\n\r\n        return obj\r\n\r\n    def __array_finalize__(self, obj):\r\n        # pylint: disable=attribute-defined-outside-init\r\n        if obj is None:  # pragma: no cover\r\n            return\r\n\r\n        self.requires_grad = getattr(obj, \"requires_grad\", None)\r\n\r\n    def __repr__(self):\r\n        string = super().__repr__()\r\n        return string[:-1] + \", requires_grad={})\".format(self.requires_grad)\r\n\r\n    def __array_wrap__(self, obj):\r\n        out_arr = tensor(obj, requires_grad=self.requires_grad)\r\n        return super().__array_wrap__(out_arr)\r\n\r\n    def __getitem__(self, *args, **kwargs):\r\n        item = super().__getitem__(*args, **kwargs)\r\n\r\n        if not isinstance(item, tensor):\r\n            item = tensor(item, requires_grad=self.requires_grad)\r\n\r\n        return item\r\n\r\n    def __hash__(self):\r\n        if self.ndim == 0:\r\n            # Allowing hashing if the tensor is a scalar.\r\n            # We hash both the scalar value *and* the differentiability information,\r\n            # to match the behaviour of PyTorch.\r\n            return hash((self.item(), self.requires_grad))\r\n\r\n        raise TypeError(\"unhashable type: 'numpy.tensor'\")\r\n\r\n    def unwrap(self):\r\n        \"\"\"Converts the tensor to a standard, non-differentiable NumPy ndarray or Python scalar if\r\n        the tensor is 0-dimensional.\r\n\r\n        All information regarding differentiability of the tensor will be lost.\r\n\r\n        .. warning::\r\n\r\n            The returned array is a new view onto the **same data**. That is,\r\n            the tensor and the returned ``ndarray`` share the same underlying storage.\r\n            Changes to the tensor object will be reflected within the returned array,\r\n            and vice versa.\r\n\r\n        **Example**\r\n\r\n        >>> from pennylane import numpy as np\r\n        >>> x = np.array([1, 2], requires_grad=True)\r\n        >>> x\r\n        tensor([1, 2], requires_grad=True)\r\n        >>> x.unwrap()\r\n        array([1, 2])\r\n\r\n        Zero dimensional array are converted to Python scalars:\r\n\r\n        >>> x = np.array(1.543, requires_grad=False)\r\n        >>> x.unwrap()\r\n        1.543\r\n        >>> type(x.unwrap())\r\n        float\r\n\r\n        The underlying data is **not** copied:\r\n\r\n        >>> x = np.array([1, 2], requires_grad=True)\r\n        >>> y = x.unwrap()\r\n        >>> x[0] = 5\r\n        >>> y\r\n        array([5, 2])\r\n        >>> y[1] = 7\r\n        >>> x\r\n        tensor([5, 7], requires_grad=True)\r\n\r\n\r\n        To create a copy, the ``copy()`` method can be used:\r\n\r\n        >>> x = np.array([1, 2], requires_grad=True)\r\n        >>> y = x.unwrap().copy()\r\n        >>> x[0] = 5\r\n        >>> y\r\n        array([1, 2])\r\n        \"\"\"\r\n        if self.ndim == 0:\r\n            return self.view(onp.ndarray).item()\r\n\r\n        return self.view(onp.ndarray)\r\n\r\n    def numpy(self):\r\n        \"\"\"Converts the tensor to a standard, non-differentiable NumPy ndarray or Python scalar if\r\n        the tensor is 0-dimensional.\r\n\r\n        This method is an alias for :meth:`~.unwrap`. See :meth:`~.unwrap` for more details.\r\n        \"\"\"\r\n        return self.unwrap()\r\n\r\n\r\nclass NonDifferentiableError(Exception):\r\n    \"\"\"Exception raised if attempting to differentiate non-trainable\r\n    :class:`~.tensor` using Autograd.\"\"\"\r\n\r\n\r\ndef tensor_to_arraybox(x, *args):\r\n    \"\"\"Convert a :class:`~.tensor` to an Autograd ``ArrayBox``.\r\n\r\n    Args:\r\n        x (array_like): Any data structure in any form that can be converted to\r\n            an array. This includes lists, lists of tuples, tuples, tuples of tuples,\r\n            tuples of lists and ndarrays.\r\n\r\n    Returns:\r\n        autograd.numpy.numpy_boxes.ArrayBox: Autograd ArrayBox instance of the array\r\n\r\n    Raises:\r\n        NonDifferentiableError: if the provided tensor is non-differentiable\r\n    \"\"\"\r\n    if isinstance(x, tensor):\r\n        if x.requires_grad:\r\n            return ArrayBox(x, *args)\r\n\r\n        raise NonDifferentiableError(\r\n            \"{} is non-differentiable. Set the requires_grad attribute to True.\".format(x)\r\n        )\r\n\r\n    return ArrayBox(x, *args)\r\n\r\n\r\nBox.type_mappings[tensor] = tensor_to_arraybox\r\nVSpace.mappings[tensor] = lambda x: ComplexArrayVSpace(x) if onp.iscomplexobj(x) else ArrayVSpace(x)\r\n", "path": "pennylane/numpy/tensor.py"}], "after_files": [{"content": "# Copyright 2018-2020 Xanadu Quantum Technologies Inc.\r\n\r\n# Licensed under the Apache License, Version 2.0 (the \"License\");\r\n# you may not use this file except in compliance with the License.\r\n# You may obtain a copy of the License at\r\n\r\n#     http://www.apache.org/licenses/LICENSE-2.0\r\n\r\n# Unless required by applicable law or agreed to in writing, software\r\n# distributed under the License is distributed on an \"AS IS\" BASIS,\r\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n# See the License for the specific language governing permissions and\r\n# limitations under the License.\r\n\"\"\"\r\nThis module provides the PennyLane :class:`~.tensor` class.\r\n\"\"\"\r\nimport numpy as onp\r\n\r\nfrom autograd import numpy as _np\r\n\r\nfrom autograd.tracer import Box\r\nfrom autograd.numpy.numpy_boxes import ArrayBox\r\nfrom autograd.numpy.numpy_vspaces import ComplexArrayVSpace, ArrayVSpace\r\nfrom autograd.core import VSpace\r\n\r\n\r\n__doc__ = \"NumPy with automatic differentiation support, provided by Autograd and PennyLane.\"\r\n\r\n\r\nclass tensor(_np.ndarray):\r\n    \"\"\"Constructs a PennyLane tensor for use with Autograd QNodes.\r\n\r\n    The ``tensor`` class is a subclass of ``numpy.ndarray``,\r\n    providing the same multidimensional, homogeneous data-structure\r\n    of fixed-size items, with an additional flag to indicate to PennyLane\r\n    whether the contained data is differentiable or not.\r\n\r\n    .. warning::\r\n\r\n        PennyLane ``tensor`` objects are only used as part of the Autograd QNode\r\n        interface. If using another machine learning library such as PyTorch or\r\n        TensorFlow, use their built-in ``tf.Variable`` and ``torch.tensor`` classes\r\n        instead.\r\n\r\n    .. warning::\r\n\r\n        Tensors should be constructed using standard array construction functions\r\n        provided as part of PennyLane's NumPy implementation, including\r\n        ``np.array``, ``np.zeros`` or ``np.empty``.\r\n\r\n        The parameters given here refer to a low-level class\r\n        for instantiating tensors.\r\n\r\n\r\n    Args:\r\n        input_array (array_like): Any data structure in any form that can be converted to\r\n            an array. This includes lists, lists of tuples, tuples, tuples of tuples,\r\n            tuples of lists and ndarrays.\r\n        requires_grad (bool): whether the tensor supports differentiation\r\n\r\n    **Example**\r\n\r\n    The trainability of a tensor can be set on construction via the\r\n    ``requires_grad`` keyword argument,\r\n\r\n    >>> from pennylane import numpy as np\r\n    >>> x = np.array([0, 1, 2], requires_grad=True)\r\n    >>> x\r\n    tensor([0, 1, 2], requires_grad=True)\r\n\r\n    or in-place by modifying the ``requires_grad`` attribute:\r\n\r\n    >>> x.requires_grad = False\r\n    tensor([0, 1, 2], requires_grad=False)\r\n\r\n    Since tensors are subclasses of ``np.ndarray``, they can be provided as arguments\r\n    to any PennyLane-wrapped NumPy function:\r\n\r\n    >>> np.sin(x)\r\n    tensor([0.        , 0.84147098, 0.90929743], requires_grad=True)\r\n\r\n    When composing functions of multiple tensors, if at least one input tensor is differentiable,\r\n    then the output will also be differentiable:\r\n\r\n    >>> x = np.array([0, 1, 2], requires_grad=False)\r\n    >>> y = np.zeros([3], requires_grad=True)\r\n    >>> np.vstack([x, y])\r\n    tensor([[0., 1., 2.],\r\n        [0., 0., 0.]], requires_grad=True)\r\n    \"\"\"\r\n\r\n    def __new__(cls, input_array, *args, requires_grad=True, **kwargs):\r\n        obj = _np.array(input_array, *args, **kwargs)\r\n\r\n        if isinstance(obj, _np.ndarray):\r\n            obj = obj.view(cls)\r\n            obj.requires_grad = requires_grad\r\n\r\n        return obj\r\n\r\n    def __array_finalize__(self, obj):\r\n        # pylint: disable=attribute-defined-outside-init\r\n        if obj is None:  # pragma: no cover\r\n            return\r\n\r\n        self.requires_grad = getattr(obj, \"requires_grad\", None)\r\n\r\n    def __repr__(self):\r\n        string = super().__repr__()\r\n        return string[:-1] + \", requires_grad={})\".format(self.requires_grad)\r\n\r\n    def __array_wrap__(self, obj):\r\n        out_arr = tensor(obj, requires_grad=self.requires_grad)\r\n        return super().__array_wrap__(out_arr)\r\n\r\n    def __array_ufunc__(self, ufunc, method, *inputs, **kwargs):\r\n        # pylint: disable=no-member,attribute-defined-outside-init\r\n\r\n        # unwrap any outputs the ufunc might have\r\n        outputs = [i.view(onp.ndarray) for i in kwargs.get(\"out\", ())]\r\n\r\n        if outputs:\r\n            # Insert the unwrapped outputs into the keyword\r\n            # args dictionary, to be passed to ndarray.__array_ufunc__\r\n            outputs = tuple(outputs)\r\n            kwargs[\"out\"] = outputs\r\n        else:\r\n            # If the ufunc has no ouputs, we simply\r\n            # create a tuple containing None for all potential outputs.\r\n            outputs = (None,) * ufunc.nout\r\n\r\n        # unwrap the input arguments to the ufunc\r\n        args = [i.unwrap() if hasattr(i, \"unwrap\") else i for i in inputs]\r\n\r\n        # call the ndarray.__array_ufunc__ method to compute the result\r\n        # of the vectorized ufunc\r\n        res = super().__array_ufunc__(ufunc, method, *args, **kwargs)\r\n\r\n        if ufunc.nout == 1:\r\n            res = (res,)\r\n\r\n        # construct a list of ufunc outputs to return\r\n        ufunc_output = [\r\n            (onp.asarray(result) if output is None else output)\r\n            for result, output in zip(res, outputs)\r\n        ]\r\n\r\n        # if any of the inputs were trainable, the output is also trainable\r\n        requires_grad = any(\r\n            isinstance(x, onp.ndarray) and getattr(x, \"requires_grad\", True) for x in inputs\r\n        )\r\n\r\n        # Iterate through the ufunc outputs and convert each to a PennyLane tensor.\r\n        # We also correctly set the requires_grad attribute.\r\n        for i in range(len(ufunc_output)):  # pylint: disable=consider-using-enumerate\r\n            ufunc_output[i] = tensor(ufunc_output[i], requires_grad=requires_grad)\r\n\r\n        if len(ufunc_output) == 1:\r\n            # the ufunc has a single output so return a single tensor\r\n            return ufunc_output[0]\r\n\r\n        # otherwise we must return a tuple of tensors\r\n        return tuple(ufunc_output)\r\n\r\n    def __getitem__(self, *args, **kwargs):\r\n        item = super().__getitem__(*args, **kwargs)\r\n\r\n        if not isinstance(item, tensor):\r\n            item = tensor(item, requires_grad=self.requires_grad)\r\n\r\n        return item\r\n\r\n    def __hash__(self):\r\n        if self.ndim == 0:\r\n            # Allowing hashing if the tensor is a scalar.\r\n            # We hash both the scalar value *and* the differentiability information,\r\n            # to match the behaviour of PyTorch.\r\n            return hash((self.item(), self.requires_grad))\r\n\r\n        raise TypeError(\"unhashable type: 'numpy.tensor'\")\r\n\r\n    def unwrap(self):\r\n        \"\"\"Converts the tensor to a standard, non-differentiable NumPy ndarray or Python scalar if\r\n        the tensor is 0-dimensional.\r\n\r\n        All information regarding differentiability of the tensor will be lost.\r\n\r\n        .. warning::\r\n\r\n            The returned array is a new view onto the **same data**. That is,\r\n            the tensor and the returned ``ndarray`` share the same underlying storage.\r\n            Changes to the tensor object will be reflected within the returned array,\r\n            and vice versa.\r\n\r\n        **Example**\r\n\r\n        >>> from pennylane import numpy as np\r\n        >>> x = np.array([1, 2], requires_grad=True)\r\n        >>> x\r\n        tensor([1, 2], requires_grad=True)\r\n        >>> x.unwrap()\r\n        array([1, 2])\r\n\r\n        Zero dimensional array are converted to Python scalars:\r\n\r\n        >>> x = np.array(1.543, requires_grad=False)\r\n        >>> x.unwrap()\r\n        1.543\r\n        >>> type(x.unwrap())\r\n        float\r\n\r\n        The underlying data is **not** copied:\r\n\r\n        >>> x = np.array([1, 2], requires_grad=True)\r\n        >>> y = x.unwrap()\r\n        >>> x[0] = 5\r\n        >>> y\r\n        array([5, 2])\r\n        >>> y[1] = 7\r\n        >>> x\r\n        tensor([5, 7], requires_grad=True)\r\n\r\n\r\n        To create a copy, the ``copy()`` method can be used:\r\n\r\n        >>> x = np.array([1, 2], requires_grad=True)\r\n        >>> y = x.unwrap().copy()\r\n        >>> x[0] = 5\r\n        >>> y\r\n        array([1, 2])\r\n        \"\"\"\r\n        if self.ndim == 0:\r\n            return self.view(onp.ndarray).item()\r\n\r\n        return self.view(onp.ndarray)\r\n\r\n    def numpy(self):\r\n        \"\"\"Converts the tensor to a standard, non-differentiable NumPy ndarray or Python scalar if\r\n        the tensor is 0-dimensional.\r\n\r\n        This method is an alias for :meth:`~.unwrap`. See :meth:`~.unwrap` for more details.\r\n        \"\"\"\r\n        return self.unwrap()\r\n\r\n\r\nclass NonDifferentiableError(Exception):\r\n    \"\"\"Exception raised if attempting to differentiate non-trainable\r\n    :class:`~.tensor` using Autograd.\"\"\"\r\n\r\n\r\ndef tensor_to_arraybox(x, *args):\r\n    \"\"\"Convert a :class:`~.tensor` to an Autograd ``ArrayBox``.\r\n\r\n    Args:\r\n        x (array_like): Any data structure in any form that can be converted to\r\n            an array. This includes lists, lists of tuples, tuples, tuples of tuples,\r\n            tuples of lists and ndarrays.\r\n\r\n    Returns:\r\n        autograd.numpy.numpy_boxes.ArrayBox: Autograd ArrayBox instance of the array\r\n\r\n    Raises:\r\n        NonDifferentiableError: if the provided tensor is non-differentiable\r\n    \"\"\"\r\n    if isinstance(x, tensor):\r\n        if x.requires_grad:\r\n            return ArrayBox(x, *args)\r\n\r\n        raise NonDifferentiableError(\r\n            \"{} is non-differentiable. Set the requires_grad attribute to True.\".format(x)\r\n        )\r\n\r\n    return ArrayBox(x, *args)\r\n\r\n\r\nBox.type_mappings[tensor] = tensor_to_arraybox\r\nVSpace.mappings[tensor] = lambda x: ComplexArrayVSpace(x) if onp.iscomplexobj(x) else ArrayVSpace(x)\r\n", "path": "pennylane/numpy/tensor.py"}]}