We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
`qml.fourier.coefficients` won't  work with more than one expectation value
#### Issue description

When I try to calculate the Fourier coefficients with the new module `qml.fourier` if the circuit has more than one measurement an error appears.

#### Code:
```python
import pennylane as qml
from pennylane import numpy as np
from pennylane.fourier import coefficients

dev = qml.device('default.qubit', wires=2)

@qml.qnode(dev)
def simple_circuit(x):
    qml.RX(x[0], wires=0)
    qml.RY(x[0], wires=1)
    qml.CNOT(wires=[1, 0])
    return qml.expval(qml.PauliZ(0)), qml.expval(qml.PauliZ(1))


coeffs = coefficients(simple_circuit, 1, 2)
print(np.round(coeffs, decimals=4))
```
#### Output:
```
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
TypeError: only size-1 arrays can be converted to Python scalars

The above exception was the direct cause of the following exception:

ValueError                                Traceback (most recent call last)
<ipython-input-111-8c2ae78fdea3> in <module>
     13 
     14 
---> 15 coeffs = coefficients(simple_circuit, 1, 2)
     16 print(np.round(coeffs, decimals=4))

~/.local/lib/python3.8/site-packages/pennylane/fourier/coefficients.py in coefficients(f, n_inputs, degree, lowpass_filter, filter_threshold)
    154     """
    155     if not lowpass_filter:
--> 156         return _coefficients_no_filter(f, n_inputs, degree)
    157 
    158     if filter_threshold is None:

~/.local/lib/python3.8/site-packages/pennylane/fourier/coefficients.py in _coefficients_no_filter(f, n_inputs, degree)
    228 
    229         # fill discretized function array with value of f at inpts
--> 230         f_discrete[tuple(nvec)] = f(sample_points)
    231 
    232     coeffs = np.fft.fftn(f_discrete) / f_discrete.size

ValueError: setting an array element with a sequence.
```

--- 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/fourier/coefficients.py`
Content:
```
1 # Copyright 2018-2021 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 """Contains methods for computing Fourier coefficients and frequency spectra of quantum functions."""
15 from itertools import product
16 import numpy as np
17 
18 
19 def coefficients(f, n_inputs, degree, lowpass_filter=False, filter_threshold=None):
20     r"""Computes the first :math:`2d+1` Fourier coefficients of a :math:`2\pi`
21     periodic function, where :math:`d` is the highest desired frequency (the
22     degree) of the Fourier spectrum.
23 
24     While this function can be used to compute Fourier coefficients in general,
25     the specific use case in PennyLane is to compute coefficients of the
26     functions that result from measuring expectation values of parametrized
27     quantum circuits, as described in `Schuld, Sweke and Meyer (2020)
28     <https://arxiv.org/abs/2008.08605>`__ and `Vidal and Theis, 2019
29     <https://arxiv.org/abs/1901.11434>`__.
30 
31     **Details**
32 
33     Consider a quantum circuit that depends on a
34     parameter vector :math:`x` with
35     length :math:`N`. The circuit involves application of some unitary
36     operations :math:`U(x)`, and then measurement of an observable
37     :math:`\langle \hat{O} \rangle`. Analytically, the expectation value is
38 
39     .. math::
40 
41        \langle \hat{O} \rangle = \langle 0 \vert U^\dagger (x) \hat{O} U(x) \vert 0\rangle = \langle
42        \psi(x) \vert \hat{O} \vert \psi (x)\rangle.
43 
44     This output is simply a function :math:`f(x) = \langle \psi(x) \vert \hat{O} \vert \psi
45     (x)\rangle`. Notably, it is a periodic function of the parameters, and
46     it can thus be expressed as a multidimensional Fourier series:
47 
48     .. math::
49 
50         f(x) = \sum \limits_{n_1\in \Omega_1} \dots \sum \limits_{n_N \in \Omega_N}
51         c_{n_1,\dots, n_N} e^{-i x_1 n_1} \dots e^{-i x_N n_N},
52 
53     where :math:`n_i` are integer-valued frequencies, :math:`\Omega_i` are the set
54     of available values for the integer frequencies, and the
55     :math:`c_{n_1,\ldots,n_N}` are Fourier coefficients.
56 
57     Args:
58         f (callable): Function that takes a 1D tensor of ``n_inputs`` scalar inputs.
59         n_inputs (int): number of function inputs
60         degree (int): max frequency of Fourier coeffs to be computed. For degree :math:`d`,
61             the coefficients from frequencies :math:`-d, -d+1,...0,..., d-1, d` will be computed.
62         lowpass_filter (bool): If ``True``, a simple low-pass filter is applied prior to
63             computing the set of coefficients in order to filter out frequencies above the
64             given degree. See examples below.
65         filter_threshold (None or int): The integer frequency at which to filter. If
66             ``lowpass_filter`` is set to ``True,`` but no value is specified, ``2 * degree`` is used.
67 
68     Returns:
69         array[complex]: The Fourier coefficients of the function ``f`` up to the specified degree.
70 
71     **Example**
72 
73     Suppose we have the following quantum function and wish to compute its Fourier
74     coefficients with respect to the variable ``inpt``, which is an array with 2 values:
75 
76     .. code-block:: python
77 
78         dev = qml.device('default.qubit', wires=['a'])
79 
80         @qml.qnode(dev)
81         def circuit(weights, inpt):
82             qml.RX(inpt[0], wires='a')
83             qml.Rot(*weights[0], wires='a')
84 
85             qml.RY(inpt[1], wires='a')
86             qml.Rot(*weights[1], wires='a')
87 
88             return qml.expval(qml.PauliZ(wires='a'))
89 
90     Unless otherwise specified, the coefficients will be computed for all input
91     values. To compute coefficients with respect to only a subset of the input
92     values, it is necessary to use a wrapper function (e.g.,
93     ``functools.partial``). We do this below, while fixing a value for
94     ``weights``:
95 
96     >>> from functools import partial
97     >>> weights = np.array([[0.1, 0.2, 0.3], [-4.1, 3.2, 1.3]])
98     >>> partial_circuit = partial(circuit, weights)
99 
100     Now we must specify the number of inputs, and the maximum desired
101     degree. Based on the underlying theory, we expect the degree to be 1
102     (frequencies -1, 0, and 1).
103 
104     >>> num_inputs = 2
105     >>> degree = 1
106 
107     Then we can obtain the coefficients:
108 
109     >>> coeffs = coefficients(partial_circuit, num_inputs, degree)
110     >>> print(coeffs)
111     [[ 0.    +0.j     -0.    +0.j     -0.    +0.j    ]
112     [-0.0014-0.022j  -0.3431-0.0408j -0.1493+0.0374j]
113     [-0.0014+0.022j  -0.1493-0.0374j -0.3431+0.0408j]]
114 
115     If the specified degree is lower than the highest frequency of the function,
116     aliasing may occur, and the resultant coefficients will be incorrect as they
117     will include components of the series expansion from higher frequencies. In
118     order to mitigate aliasing, setting ``lowpass_filter=True`` will apply a
119     simple low-pass filter prior to computing the coefficients. Coefficients up
120     to a specified value are computed, and then frequencies higher than the
121     degree are simply removed. This ensures that the coefficients returned will
122     have the correct values, though they may not be the full set of
123     coefficients. If no threshold value is provided, the threshold will be set
124     to ``2 * degree``.
125 
126     Consider the circuit below:
127 
128     .. code-block:: python
129 
130         @qml.qnode(dev)
131         def circuit(inpt):
132             qml.RX(inpt[0], wires=0)
133             qml.RY(inpt[0], wires=1)
134             qml.CNOT(wires=[1, 0])
135             return qml.expval(qml.PauliZ(0))
136 
137     One can work out by hand that the Fourier coefficients are :math:`c_0 = 0.5, c_1 = c_{-1} = 0,`
138     and :math:`c_2 = c_{-2} = 0.25`. Suppose we would like only to obtain the coefficients
139     :math:`c_0` and :math:`c_1, c_{-1}`. If we simply ask for the coefficients of degree 1,
140     we will obtain incorrect values due to aliasing:
141 
142     >>> coefficients(circuit, 1, 1)
143     array([0.5 +0.j, 0.25+0.j, 0.25+0.j])
144 
145     However if we enable the low-pass filter, we can still obtain the correct coefficients:
146 
147     >>> coefficients(circuit, 1, 1, lowpass_filter=True)
148     array([0.5+0.j, 0. +0.j, 0. +0.j])
149 
150     Note that in this case, ``2 * degree`` gives us exactly the maximum coefficient;
151     in other situations it may be desirable to set the threshold value explicitly.
152 
153     The `coefficients` function can handle qnodes from all PennyLane interfaces.
154     """
155     if not lowpass_filter:
156         return _coefficients_no_filter(f, n_inputs, degree)
157 
158     if filter_threshold is None:
159         filter_threshold = 2 * degree
160 
161     # Compute the fft of the function at 2x the specified degree
162     unfiltered_coeffs = _coefficients_no_filter(f, n_inputs, filter_threshold)
163 
164     # Shift the frequencies so that the 0s are at the centre
165     shifted_unfiltered_coeffs = np.fft.fftshift(unfiltered_coeffs)
166 
167     # Next, slice up the array so that we get only the coefficients we care about,
168     # those between -degree and degree
169     range_slices = list(
170         range(
171             filter_threshold - degree,
172             shifted_unfiltered_coeffs.shape[0] - (filter_threshold - degree),
173         )
174     )
175 
176     shifted_filtered_coeffs = shifted_unfiltered_coeffs.copy()
177 
178     # Go axis by axis and take only the central components
179     for axis in range(n_inputs - 1, -1, -1):
180         shifted_filtered_coeffs = np.take(shifted_filtered_coeffs, range_slices, axis=axis)
181 
182     # Shift everything back into "normal" fft ordering
183     filtered_coeffs = np.fft.ifftshift(shifted_filtered_coeffs)
184 
185     # Compute the inverse FFT
186     f_discrete_filtered = np.fft.ifftn(filtered_coeffs)
187 
188     # Now compute the FFT again on the filtered data
189     coeffs = np.fft.fftn(f_discrete_filtered)
190 
191     return coeffs
192 
193 
194 def _coefficients_no_filter(f, n_inputs, degree):
195     r"""Computes the first :math:`2d+1` Fourier coefficients of a :math:`2\pi` periodic
196     function, where :math:`d` is the highest desired frequency in the Fourier spectrum.
197 
198     This function computes the coefficients blindly without any filtering applied, and
199     is thus used as a helper function for the true ``coefficients`` function.
200 
201     Args:
202         f (callable): function that takes a 1D array of ``n_inputs`` scalar inputs
203         n_inputs (int): number of function inputs
204         degree (int): max frequency of Fourier coeffs to be computed. For degree :math:`d`,
205             the coefficients from frequencies :math:`-d, -d+1,...0,..., d-1, d ` will be computed.
206 
207     Returns:
208         array[complex]: The Fourier coefficients of the function f up to the specified degree.
209     """
210 
211     # number of integer values for the indices n_i = -degree,...,0,...,degree
212     k = 2 * degree + 1
213 
214     # create generator for indices nvec = (n1, ..., nN), ranging from (-d,...,-d) to (d,...,d).
215     n_range = np.array(range(-degree, degree + 1))
216     n_ranges = [n_range] * n_inputs
217     nvecs = product(*n_ranges)
218 
219     # here we will collect the discretized values of function f
220     shp = tuple([k] * n_inputs)
221     f_discrete = np.zeros(shape=shp)
222 
223     for nvec in nvecs:
224         nvec = np.array(nvec)
225 
226         # compute the evaluation points for frequencies nvec
227         sample_points = 2 * np.pi / k * nvec
228 
229         # fill discretized function array with value of f at inpts
230         f_discrete[tuple(nvec)] = f(sample_points)
231 
232     coeffs = np.fft.fftn(f_discrete) / f_discrete.size
233 
234     return coeffs
235
```
--- 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/fourier/coefficients.py b/pennylane/fourier/coefficients.py
--- a/pennylane/fourier/coefficients.py
+++ b/pennylane/fourier/coefficients.py
@@ -55,7 +55,8 @@
     :math:`c_{n_1,\ldots,n_N}` are Fourier coefficients.
 
     Args:
-        f (callable): Function that takes a 1D tensor of ``n_inputs`` scalar inputs.
+        f (callable): Function that takes a 1D tensor of ``n_inputs`` scalar inputs. The function can be a QNode, but
+            has to return a real scalar value (such as an expectation).
         n_inputs (int): number of function inputs
         degree (int): max frequency of Fourier coeffs to be computed. For degree :math:`d`,
             the coefficients from frequencies :math:`-d, -d+1,...0,..., d-1, d` will be computed.
@@ -87,6 +88,10 @@
 
             return qml.expval(qml.PauliZ(wires='a'))
 
+    .. note::
+
+        The QNode has to return a scalar value (such as a single expectation).
+
     Unless otherwise specified, the coefficients will be computed for all input
     values. To compute coefficients with respect to only a subset of the input
     values, it is necessary to use a wrapper function (e.g.,

{"golden_diff": "diff --git a/pennylane/fourier/coefficients.py b/pennylane/fourier/coefficients.py\n--- a/pennylane/fourier/coefficients.py\n+++ b/pennylane/fourier/coefficients.py\n@@ -55,7 +55,8 @@\n     :math:`c_{n_1,\\ldots,n_N}` are Fourier coefficients.\n \n     Args:\n-        f (callable): Function that takes a 1D tensor of ``n_inputs`` scalar inputs.\n+        f (callable): Function that takes a 1D tensor of ``n_inputs`` scalar inputs. The function can be a QNode, but\n+            has to return a real scalar value (such as an expectation).\n         n_inputs (int): number of function inputs\n         degree (int): max frequency of Fourier coeffs to be computed. For degree :math:`d`,\n             the coefficients from frequencies :math:`-d, -d+1,...0,..., d-1, d` will be computed.\n@@ -87,6 +88,10 @@\n \n             return qml.expval(qml.PauliZ(wires='a'))\n \n+    .. note::\n+\n+        The QNode has to return a scalar value (such as a single expectation).\n+\n     Unless otherwise specified, the coefficients will be computed for all input\n     values. To compute coefficients with respect to only a subset of the input\n     values, it is necessary to use a wrapper function (e.g.,\n", "issue": "`qml.fourier.coefficients` won't  work with more than one expectation value\n#### Issue description\r\n\r\nWhen I try to calculate the Fourier coefficients with the new module `qml.fourier` if the circuit has more than one measurement an error appears.\r\n\r\n#### Code:\r\n```python\r\nimport pennylane as qml\r\nfrom pennylane import numpy as np\r\nfrom pennylane.fourier import coefficients\r\n\r\ndev = qml.device('default.qubit', wires=2)\r\n\r\n@qml.qnode(dev)\r\ndef simple_circuit(x):\r\n    qml.RX(x[0], wires=0)\r\n    qml.RY(x[0], wires=1)\r\n    qml.CNOT(wires=[1, 0])\r\n    return qml.expval(qml.PauliZ(0)), qml.expval(qml.PauliZ(1))\r\n\r\n\r\ncoeffs = coefficients(simple_circuit, 1, 2)\r\nprint(np.round(coeffs, decimals=4))\r\n```\r\n#### Output:\r\n```\r\n---------------------------------------------------------------------------\r\nTypeError                                 Traceback (most recent call last)\r\nTypeError: only size-1 arrays can be converted to Python scalars\r\n\r\nThe above exception was the direct cause of the following exception:\r\n\r\nValueError                                Traceback (most recent call last)\r\n<ipython-input-111-8c2ae78fdea3> in <module>\r\n     13 \r\n     14 \r\n---> 15 coeffs = coefficients(simple_circuit, 1, 2)\r\n     16 print(np.round(coeffs, decimals=4))\r\n\r\n~/.local/lib/python3.8/site-packages/pennylane/fourier/coefficients.py in coefficients(f, n_inputs, degree, lowpass_filter, filter_threshold)\r\n    154     \"\"\"\r\n    155     if not lowpass_filter:\r\n--> 156         return _coefficients_no_filter(f, n_inputs, degree)\r\n    157 \r\n    158     if filter_threshold is None:\r\n\r\n~/.local/lib/python3.8/site-packages/pennylane/fourier/coefficients.py in _coefficients_no_filter(f, n_inputs, degree)\r\n    228 \r\n    229         # fill discretized function array with value of f at inpts\r\n--> 230         f_discrete[tuple(nvec)] = f(sample_points)\r\n    231 \r\n    232     coeffs = np.fft.fftn(f_discrete) / f_discrete.size\r\n\r\nValueError: setting an array element with a sequence.\r\n```\n", "before_files": [{"content": "# Copyright 2018-2021 Xanadu Quantum Technologies Inc.\n\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n\n#     http://www.apache.org/licenses/LICENSE-2.0\n\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"Contains methods for computing Fourier coefficients and frequency spectra of quantum functions.\"\"\"\nfrom itertools import product\nimport numpy as np\n\n\ndef coefficients(f, n_inputs, degree, lowpass_filter=False, filter_threshold=None):\n    r\"\"\"Computes the first :math:`2d+1` Fourier coefficients of a :math:`2\\pi`\n    periodic function, where :math:`d` is the highest desired frequency (the\n    degree) of the Fourier spectrum.\n\n    While this function can be used to compute Fourier coefficients in general,\n    the specific use case in PennyLane is to compute coefficients of the\n    functions that result from measuring expectation values of parametrized\n    quantum circuits, as described in `Schuld, Sweke and Meyer (2020)\n    <https://arxiv.org/abs/2008.08605>`__ and `Vidal and Theis, 2019\n    <https://arxiv.org/abs/1901.11434>`__.\n\n    **Details**\n\n    Consider a quantum circuit that depends on a\n    parameter vector :math:`x` with\n    length :math:`N`. The circuit involves application of some unitary\n    operations :math:`U(x)`, and then measurement of an observable\n    :math:`\\langle \\hat{O} \\rangle`. Analytically, the expectation value is\n\n    .. math::\n\n       \\langle \\hat{O} \\rangle = \\langle 0 \\vert U^\\dagger (x) \\hat{O} U(x) \\vert 0\\rangle = \\langle\n       \\psi(x) \\vert \\hat{O} \\vert \\psi (x)\\rangle.\n\n    This output is simply a function :math:`f(x) = \\langle \\psi(x) \\vert \\hat{O} \\vert \\psi\n    (x)\\rangle`. Notably, it is a periodic function of the parameters, and\n    it can thus be expressed as a multidimensional Fourier series:\n\n    .. math::\n\n        f(x) = \\sum \\limits_{n_1\\in \\Omega_1} \\dots \\sum \\limits_{n_N \\in \\Omega_N}\n        c_{n_1,\\dots, n_N} e^{-i x_1 n_1} \\dots e^{-i x_N n_N},\n\n    where :math:`n_i` are integer-valued frequencies, :math:`\\Omega_i` are the set\n    of available values for the integer frequencies, and the\n    :math:`c_{n_1,\\ldots,n_N}` are Fourier coefficients.\n\n    Args:\n        f (callable): Function that takes a 1D tensor of ``n_inputs`` scalar inputs.\n        n_inputs (int): number of function inputs\n        degree (int): max frequency of Fourier coeffs to be computed. For degree :math:`d`,\n            the coefficients from frequencies :math:`-d, -d+1,...0,..., d-1, d` will be computed.\n        lowpass_filter (bool): If ``True``, a simple low-pass filter is applied prior to\n            computing the set of coefficients in order to filter out frequencies above the\n            given degree. See examples below.\n        filter_threshold (None or int): The integer frequency at which to filter. If\n            ``lowpass_filter`` is set to ``True,`` but no value is specified, ``2 * degree`` is used.\n\n    Returns:\n        array[complex]: The Fourier coefficients of the function ``f`` up to the specified degree.\n\n    **Example**\n\n    Suppose we have the following quantum function and wish to compute its Fourier\n    coefficients with respect to the variable ``inpt``, which is an array with 2 values:\n\n    .. code-block:: python\n\n        dev = qml.device('default.qubit', wires=['a'])\n\n        @qml.qnode(dev)\n        def circuit(weights, inpt):\n            qml.RX(inpt[0], wires='a')\n            qml.Rot(*weights[0], wires='a')\n\n            qml.RY(inpt[1], wires='a')\n            qml.Rot(*weights[1], wires='a')\n\n            return qml.expval(qml.PauliZ(wires='a'))\n\n    Unless otherwise specified, the coefficients will be computed for all input\n    values. To compute coefficients with respect to only a subset of the input\n    values, it is necessary to use a wrapper function (e.g.,\n    ``functools.partial``). We do this below, while fixing a value for\n    ``weights``:\n\n    >>> from functools import partial\n    >>> weights = np.array([[0.1, 0.2, 0.3], [-4.1, 3.2, 1.3]])\n    >>> partial_circuit = partial(circuit, weights)\n\n    Now we must specify the number of inputs, and the maximum desired\n    degree. Based on the underlying theory, we expect the degree to be 1\n    (frequencies -1, 0, and 1).\n\n    >>> num_inputs = 2\n    >>> degree = 1\n\n    Then we can obtain the coefficients:\n\n    >>> coeffs = coefficients(partial_circuit, num_inputs, degree)\n    >>> print(coeffs)\n    [[ 0.    +0.j     -0.    +0.j     -0.    +0.j    ]\n    [-0.0014-0.022j  -0.3431-0.0408j -0.1493+0.0374j]\n    [-0.0014+0.022j  -0.1493-0.0374j -0.3431+0.0408j]]\n\n    If the specified degree is lower than the highest frequency of the function,\n    aliasing may occur, and the resultant coefficients will be incorrect as they\n    will include components of the series expansion from higher frequencies. In\n    order to mitigate aliasing, setting ``lowpass_filter=True`` will apply a\n    simple low-pass filter prior to computing the coefficients. Coefficients up\n    to a specified value are computed, and then frequencies higher than the\n    degree are simply removed. This ensures that the coefficients returned will\n    have the correct values, though they may not be the full set of\n    coefficients. If no threshold value is provided, the threshold will be set\n    to ``2 * degree``.\n\n    Consider the circuit below:\n\n    .. code-block:: python\n\n        @qml.qnode(dev)\n        def circuit(inpt):\n            qml.RX(inpt[0], wires=0)\n            qml.RY(inpt[0], wires=1)\n            qml.CNOT(wires=[1, 0])\n            return qml.expval(qml.PauliZ(0))\n\n    One can work out by hand that the Fourier coefficients are :math:`c_0 = 0.5, c_1 = c_{-1} = 0,`\n    and :math:`c_2 = c_{-2} = 0.25`. Suppose we would like only to obtain the coefficients\n    :math:`c_0` and :math:`c_1, c_{-1}`. If we simply ask for the coefficients of degree 1,\n    we will obtain incorrect values due to aliasing:\n\n    >>> coefficients(circuit, 1, 1)\n    array([0.5 +0.j, 0.25+0.j, 0.25+0.j])\n\n    However if we enable the low-pass filter, we can still obtain the correct coefficients:\n\n    >>> coefficients(circuit, 1, 1, lowpass_filter=True)\n    array([0.5+0.j, 0. +0.j, 0. +0.j])\n\n    Note that in this case, ``2 * degree`` gives us exactly the maximum coefficient;\n    in other situations it may be desirable to set the threshold value explicitly.\n\n    The `coefficients` function can handle qnodes from all PennyLane interfaces.\n    \"\"\"\n    if not lowpass_filter:\n        return _coefficients_no_filter(f, n_inputs, degree)\n\n    if filter_threshold is None:\n        filter_threshold = 2 * degree\n\n    # Compute the fft of the function at 2x the specified degree\n    unfiltered_coeffs = _coefficients_no_filter(f, n_inputs, filter_threshold)\n\n    # Shift the frequencies so that the 0s are at the centre\n    shifted_unfiltered_coeffs = np.fft.fftshift(unfiltered_coeffs)\n\n    # Next, slice up the array so that we get only the coefficients we care about,\n    # those between -degree and degree\n    range_slices = list(\n        range(\n            filter_threshold - degree,\n            shifted_unfiltered_coeffs.shape[0] - (filter_threshold - degree),\n        )\n    )\n\n    shifted_filtered_coeffs = shifted_unfiltered_coeffs.copy()\n\n    # Go axis by axis and take only the central components\n    for axis in range(n_inputs - 1, -1, -1):\n        shifted_filtered_coeffs = np.take(shifted_filtered_coeffs, range_slices, axis=axis)\n\n    # Shift everything back into \"normal\" fft ordering\n    filtered_coeffs = np.fft.ifftshift(shifted_filtered_coeffs)\n\n    # Compute the inverse FFT\n    f_discrete_filtered = np.fft.ifftn(filtered_coeffs)\n\n    # Now compute the FFT again on the filtered data\n    coeffs = np.fft.fftn(f_discrete_filtered)\n\n    return coeffs\n\n\ndef _coefficients_no_filter(f, n_inputs, degree):\n    r\"\"\"Computes the first :math:`2d+1` Fourier coefficients of a :math:`2\\pi` periodic\n    function, where :math:`d` is the highest desired frequency in the Fourier spectrum.\n\n    This function computes the coefficients blindly without any filtering applied, and\n    is thus used as a helper function for the true ``coefficients`` function.\n\n    Args:\n        f (callable): function that takes a 1D array of ``n_inputs`` scalar inputs\n        n_inputs (int): number of function inputs\n        degree (int): max frequency of Fourier coeffs to be computed. For degree :math:`d`,\n            the coefficients from frequencies :math:`-d, -d+1,...0,..., d-1, d ` will be computed.\n\n    Returns:\n        array[complex]: The Fourier coefficients of the function f up to the specified degree.\n    \"\"\"\n\n    # number of integer values for the indices n_i = -degree,...,0,...,degree\n    k = 2 * degree + 1\n\n    # create generator for indices nvec = (n1, ..., nN), ranging from (-d,...,-d) to (d,...,d).\n    n_range = np.array(range(-degree, degree + 1))\n    n_ranges = [n_range] * n_inputs\n    nvecs = product(*n_ranges)\n\n    # here we will collect the discretized values of function f\n    shp = tuple([k] * n_inputs)\n    f_discrete = np.zeros(shape=shp)\n\n    for nvec in nvecs:\n        nvec = np.array(nvec)\n\n        # compute the evaluation points for frequencies nvec\n        sample_points = 2 * np.pi / k * nvec\n\n        # fill discretized function array with value of f at inpts\n        f_discrete[tuple(nvec)] = f(sample_points)\n\n    coeffs = np.fft.fftn(f_discrete) / f_discrete.size\n\n    return coeffs\n", "path": "pennylane/fourier/coefficients.py"}], "after_files": [{"content": "# Copyright 2018-2021 Xanadu Quantum Technologies Inc.\n\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n\n#     http://www.apache.org/licenses/LICENSE-2.0\n\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"Contains methods for computing Fourier coefficients and frequency spectra of quantum functions.\"\"\"\nfrom itertools import product\nimport numpy as np\n\n\ndef coefficients(f, n_inputs, degree, lowpass_filter=False, filter_threshold=None):\n    r\"\"\"Computes the first :math:`2d+1` Fourier coefficients of a :math:`2\\pi`\n    periodic function, where :math:`d` is the highest desired frequency (the\n    degree) of the Fourier spectrum.\n\n    While this function can be used to compute Fourier coefficients in general,\n    the specific use case in PennyLane is to compute coefficients of the\n    functions that result from measuring expectation values of parametrized\n    quantum circuits, as described in `Schuld, Sweke and Meyer (2020)\n    <https://arxiv.org/abs/2008.08605>`__ and `Vidal and Theis, 2019\n    <https://arxiv.org/abs/1901.11434>`__.\n\n    **Details**\n\n    Consider a quantum circuit that depends on a\n    parameter vector :math:`x` with\n    length :math:`N`. The circuit involves application of some unitary\n    operations :math:`U(x)`, and then measurement of an observable\n    :math:`\\langle \\hat{O} \\rangle`. Analytically, the expectation value is\n\n    .. math::\n\n       \\langle \\hat{O} \\rangle = \\langle 0 \\vert U^\\dagger (x) \\hat{O} U(x) \\vert 0\\rangle = \\langle\n       \\psi(x) \\vert \\hat{O} \\vert \\psi (x)\\rangle.\n\n    This output is simply a function :math:`f(x) = \\langle \\psi(x) \\vert \\hat{O} \\vert \\psi\n    (x)\\rangle`. Notably, it is a periodic function of the parameters, and\n    it can thus be expressed as a multidimensional Fourier series:\n\n    .. math::\n\n        f(x) = \\sum \\limits_{n_1\\in \\Omega_1} \\dots \\sum \\limits_{n_N \\in \\Omega_N}\n        c_{n_1,\\dots, n_N} e^{-i x_1 n_1} \\dots e^{-i x_N n_N},\n\n    where :math:`n_i` are integer-valued frequencies, :math:`\\Omega_i` are the set\n    of available values for the integer frequencies, and the\n    :math:`c_{n_1,\\ldots,n_N}` are Fourier coefficients.\n\n    Args:\n        f (callable): Function that takes a 1D tensor of ``n_inputs`` scalar inputs. The function can be a QNode, but\n            has to return a real scalar value (such as an expectation).\n        n_inputs (int): number of function inputs\n        degree (int): max frequency of Fourier coeffs to be computed. For degree :math:`d`,\n            the coefficients from frequencies :math:`-d, -d+1,...0,..., d-1, d` will be computed.\n        lowpass_filter (bool): If ``True``, a simple low-pass filter is applied prior to\n            computing the set of coefficients in order to filter out frequencies above the\n            given degree. See examples below.\n        filter_threshold (None or int): The integer frequency at which to filter. If\n            ``lowpass_filter`` is set to ``True,`` but no value is specified, ``2 * degree`` is used.\n\n    Returns:\n        array[complex]: The Fourier coefficients of the function ``f`` up to the specified degree.\n\n    **Example**\n\n    Suppose we have the following quantum function and wish to compute its Fourier\n    coefficients with respect to the variable ``inpt``, which is an array with 2 values:\n\n    .. code-block:: python\n\n        dev = qml.device('default.qubit', wires=['a'])\n\n        @qml.qnode(dev)\n        def circuit(weights, inpt):\n            qml.RX(inpt[0], wires='a')\n            qml.Rot(*weights[0], wires='a')\n\n            qml.RY(inpt[1], wires='a')\n            qml.Rot(*weights[1], wires='a')\n\n            return qml.expval(qml.PauliZ(wires='a'))\n\n    .. note::\n\n        The QNode has to return a scalar value (such as a single expectation).\n\n    Unless otherwise specified, the coefficients will be computed for all input\n    values. To compute coefficients with respect to only a subset of the input\n    values, it is necessary to use a wrapper function (e.g.,\n    ``functools.partial``). We do this below, while fixing a value for\n    ``weights``:\n\n    >>> from functools import partial\n    >>> weights = np.array([[0.1, 0.2, 0.3], [-4.1, 3.2, 1.3]])\n    >>> partial_circuit = partial(circuit, weights)\n\n    Now we must specify the number of inputs, and the maximum desired\n    degree. Based on the underlying theory, we expect the degree to be 1\n    (frequencies -1, 0, and 1).\n\n    >>> num_inputs = 2\n    >>> degree = 1\n\n    Then we can obtain the coefficients:\n\n    >>> coeffs = coefficients(partial_circuit, num_inputs, degree)\n    >>> print(coeffs)\n    [[ 0.    +0.j     -0.    +0.j     -0.    +0.j    ]\n    [-0.0014-0.022j  -0.3431-0.0408j -0.1493+0.0374j]\n    [-0.0014+0.022j  -0.1493-0.0374j -0.3431+0.0408j]]\n\n    If the specified degree is lower than the highest frequency of the function,\n    aliasing may occur, and the resultant coefficients will be incorrect as they\n    will include components of the series expansion from higher frequencies. In\n    order to mitigate aliasing, setting ``lowpass_filter=True`` will apply a\n    simple low-pass filter prior to computing the coefficients. Coefficients up\n    to a specified value are computed, and then frequencies higher than the\n    degree are simply removed. This ensures that the coefficients returned will\n    have the correct values, though they may not be the full set of\n    coefficients. If no threshold value is provided, the threshold will be set\n    to ``2 * degree``.\n\n    Consider the circuit below:\n\n    .. code-block:: python\n\n        @qml.qnode(dev)\n        def circuit(inpt):\n            qml.RX(inpt[0], wires=0)\n            qml.RY(inpt[0], wires=1)\n            qml.CNOT(wires=[1, 0])\n            return qml.expval(qml.PauliZ(0))\n\n    One can work out by hand that the Fourier coefficients are :math:`c_0 = 0.5, c_1 = c_{-1} = 0,`\n    and :math:`c_2 = c_{-2} = 0.25`. Suppose we would like only to obtain the coefficients\n    :math:`c_0` and :math:`c_1, c_{-1}`. If we simply ask for the coefficients of degree 1,\n    we will obtain incorrect values due to aliasing:\n\n    >>> coefficients(circuit, 1, 1)\n    array([0.5 +0.j, 0.25+0.j, 0.25+0.j])\n\n    However if we enable the low-pass filter, we can still obtain the correct coefficients:\n\n    >>> coefficients(circuit, 1, 1, lowpass_filter=True)\n    array([0.5+0.j, 0. +0.j, 0. +0.j])\n\n    Note that in this case, ``2 * degree`` gives us exactly the maximum coefficient;\n    in other situations it may be desirable to set the threshold value explicitly.\n\n    The `coefficients` function can handle qnodes from all PennyLane interfaces.\n    \"\"\"\n    if not lowpass_filter:\n        return _coefficients_no_filter(f, n_inputs, degree)\n\n    if filter_threshold is None:\n        filter_threshold = 2 * degree\n\n    # Compute the fft of the function at 2x the specified degree\n    unfiltered_coeffs = _coefficients_no_filter(f, n_inputs, filter_threshold)\n\n    # Shift the frequencies so that the 0s are at the centre\n    shifted_unfiltered_coeffs = np.fft.fftshift(unfiltered_coeffs)\n\n    # Next, slice up the array so that we get only the coefficients we care about,\n    # those between -degree and degree\n    range_slices = list(\n        range(\n            filter_threshold - degree,\n            shifted_unfiltered_coeffs.shape[0] - (filter_threshold - degree),\n        )\n    )\n\n    shifted_filtered_coeffs = shifted_unfiltered_coeffs.copy()\n\n    # Go axis by axis and take only the central components\n    for axis in range(n_inputs - 1, -1, -1):\n        shifted_filtered_coeffs = np.take(shifted_filtered_coeffs, range_slices, axis=axis)\n\n    # Shift everything back into \"normal\" fft ordering\n    filtered_coeffs = np.fft.ifftshift(shifted_filtered_coeffs)\n\n    # Compute the inverse FFT\n    f_discrete_filtered = np.fft.ifftn(filtered_coeffs)\n\n    # Now compute the FFT again on the filtered data\n    coeffs = np.fft.fftn(f_discrete_filtered)\n\n    return coeffs\n\n\ndef _coefficients_no_filter(f, n_inputs, degree):\n    r\"\"\"Computes the first :math:`2d+1` Fourier coefficients of a :math:`2\\pi` periodic\n    function, where :math:`d` is the highest desired frequency in the Fourier spectrum.\n\n    This function computes the coefficients blindly without any filtering applied, and\n    is thus used as a helper function for the true ``coefficients`` function.\n\n    Args:\n        f (callable): function that takes a 1D array of ``n_inputs`` scalar inputs\n        n_inputs (int): number of function inputs\n        degree (int): max frequency of Fourier coeffs to be computed. For degree :math:`d`,\n            the coefficients from frequencies :math:`-d, -d+1,...0,..., d-1, d ` will be computed.\n\n    Returns:\n        array[complex]: The Fourier coefficients of the function f up to the specified degree.\n    \"\"\"\n\n    # number of integer values for the indices n_i = -degree,...,0,...,degree\n    k = 2 * degree + 1\n\n    # create generator for indices nvec = (n1, ..., nN), ranging from (-d,...,-d) to (d,...,d).\n    n_range = np.array(range(-degree, degree + 1))\n    n_ranges = [n_range] * n_inputs\n    nvecs = product(*n_ranges)\n\n    # here we will collect the discretized values of function f\n    shp = tuple([k] * n_inputs)\n    f_discrete = np.zeros(shape=shp)\n\n    for nvec in nvecs:\n        nvec = np.array(nvec)\n\n        # compute the evaluation points for frequencies nvec\n        sample_points = 2 * np.pi / k * nvec\n\n        # fill discretized function array with value of f at inpts\n        f_discrete[tuple(nvec)] = f(sample_points)\n\n    coeffs = np.fft.fftn(f_discrete) / f_discrete.size\n\n    return coeffs\n", "path": "pennylane/fourier/coefficients.py"}]}