We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
We should use numpy.fft only because it is faster than scipy.fftpack
<!--
Thank you for your contribution!

Please review https://github.com/mars-project/mars/blob/master/CONTRIBUTING.rst before opening an issue.
-->

Maybe at a point in the history, scipy's fftpack is faster than numpy's fft module. Thus we do some optimization in the execution of fft that  if scipy is installed, scipy's fftpack would be used to calculate fft. However, recently, I found that numpy's fft is obviously faster than scipy's fftpack. Sample code is shown below.

```python
In [1]: N = 1600000                                                             

In [2]: import numpy as np                                                      

In [3]: a = np.random.rand(N, 10)                                               

In [4]: %timeit np.fft.fft(a)                                                   
118 ms ± 1.33 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

In [6]: import scipy.fftpack as sfft                                            

In [7]: %timeit sfft.fft(a)                                                     
290 ms ± 3.65 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

In [9]: np.testing.assert_allclose(np.fft.fft(a), sfft.fft(a))
```

Hence, I suggest to use numpy only to calculate fft.


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `mars/tensor/fft/core.py`
Content:
```
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3 # Copyright 1999-2018 Alibaba Group Holding Ltd.
4 #
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
8 #
9 #      http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16 
17 from collections import Iterable
18 
19 from ...compat import izip
20 from ...serialize import ValueType, KeyField, StringField, Int32Field, \
21     Int64Field, ListField
22 from ..utils import validate_axis, decide_chunk_sizes, recursive_tile
23 from ..operands import TensorHasInput, TensorOperandMixin
24 import numpy as np
25 from ..array_utils import get_array_module
26 
27 try:
28     import scipy.fftpack as scifft
29 except ImportError:  # pragma: no cover
30     scifft = None
31 
32 
33 class TensorFFTBaseMixin(TensorOperandMixin):
34     __slots__ = ()
35 
36     @classmethod
37     def _get_shape(cls, op, shape):
38         raise NotImplementedError
39 
40     @classmethod
41     def _tile_fft(cls, op, axes):
42         in_tensor = op.inputs[0]
43         out_tensor = op.outputs[0]
44 
45         if any(in_tensor.chunk_shape[axis] != 1 for axis in axes):
46             # fft requires only 1 chunk for the specified axis, so we do rechunk first
47             chunks = {validate_axis(in_tensor.ndim, axis): in_tensor.shape[axis] for axis in axes}
48             new_chunks = decide_chunk_sizes(in_tensor.shape, chunks, in_tensor.dtype.itemsize)
49             in_tensor = in_tensor.rechunk(new_chunks).single_tiles()
50 
51         out_chunks = []
52         for c in in_tensor.chunks:
53             chunk_op = op.copy().reset_key()
54             chunk_shape = cls._get_shape(op, c.shape)
55             out_chunk = chunk_op.new_chunk([c], shape=chunk_shape,
56                                            index=c.index, order=out_tensor.order)
57             out_chunks.append(out_chunk)
58 
59         nsplits = [tuple(c.shape[i] for c in out_chunks
60                          if all(idx == 0 for j, idx in enumerate(c.index) if j != i))
61                    for i in range(len(out_chunks[0].shape))]
62         new_op = op.copy()
63         return new_op.new_tensors(op.inputs, out_tensor.shape, order=out_tensor.order,
64                                   chunks=out_chunks, nsplits=nsplits)
65 
66     def __call__(self, a, order=None):
67         shape = self._get_shape(self, a.shape)
68         order = a.order if order is None else order
69         return self.new_tensor([a], shape, order=order)
70 
71 
72 class TensorFFTMixin(TensorFFTBaseMixin):
73     __slots__ = ()
74 
75     @classmethod
76     def tile(cls, op):
77         return cls._tile_fft(op, [op.axis])
78 
79 
80 class TensorComplexFFTMixin(TensorFFTMixin):
81     @classmethod
82     def _get_shape(cls, op, shape):
83         new_shape = list(shape)
84         if op.n is not None:
85             new_shape[op.axis] = op.n
86         return tuple(new_shape)
87 
88 
89 def validate_fft(tensor, axis=-1, norm=None):
90     validate_axis(tensor.ndim, axis)
91     if norm is not None and norm not in ('ortho',):
92         raise ValueError('Invalid norm value {0}, should be None or "ortho"'.format(norm))
93 
94 
95 class TensorFFTNMixin(TensorFFTBaseMixin):
96     @classmethod
97     def tile(cls, op):
98         return cls._tile_fft(op, op.axes)
99 
100     @staticmethod
101     def _merge_shape(op, shape):
102         new_shape = list(shape)
103         if op.shape is not None:
104             for ss, axis in izip(op.shape, op.axes):
105                 new_shape[axis] = ss
106         return new_shape
107 
108 
109 class TensorComplexFFTNMixin(TensorFFTNMixin):
110     @classmethod
111     def _get_shape(cls, op, shape):
112         return tuple(cls._merge_shape(op, shape))
113 
114 
115 class TensorRealFFTNMixin(TensorFFTNMixin):
116     @classmethod
117     def _get_shape(cls, op, shape):
118         new_shape = cls._merge_shape(op, shape)
119         new_shape[op.axes[-1]] = new_shape[op.axes[-1]] // 2 + 1
120         return tuple(new_shape)
121 
122 
123 class TensorRealIFFTNMixin(TensorFFTNMixin):
124     @classmethod
125     def _get_shape(cls, op, shape):
126         new_shape = list(shape)
127         new_shape[op.axes[-1]] = 2 * (new_shape[op.axes[-1]] - 1)
128         return tuple(cls._merge_shape(op, new_shape))
129 
130 
131 def validate_fftn(tensor, s=None, axes=None, norm=None):
132     if axes is None:
133         if s is None:
134             axes = tuple(range(tensor.ndim))
135         else:
136             axes = tuple(range(len(s)))
137     else:
138         for axis in axes:
139             validate_axis(tensor.ndim, axis)
140         if len(set(axes)) < len(axes):
141             raise ValueError('Duplicate axes not allowed')
142 
143     if norm is not None and norm not in ('ortho',):
144         raise ValueError('Invalid norm value {0}, should be None or "ortho"'.format(norm))
145 
146     return axes
147 
148 
149 class TensorFFTShiftMixin(TensorOperandMixin):
150     __slots__ = ()
151 
152     @classmethod
153     def _is_inverse(cls):
154         return False
155 
156     @classmethod
157     def _process_axes(cls, x, axes):
158         if axes is None:
159             axes = tuple(range(x.ndim))
160         elif isinstance(axes, Iterable):
161             axes = tuple(axes)
162         else:
163             axes = (axes,)
164 
165         return axes
166 
167     @classmethod
168     def tile(cls, op):
169         from ..merge import concatenate
170 
171         axes = op.axes
172         in_tensor = op.input
173         is_inverse = cls._is_inverse()
174 
175         x = in_tensor
176         for axis in axes:
177             size = in_tensor.shape[axis]
178             slice_on = (size + 1) // 2 if not is_inverse else size // 2
179             slc1 = [slice(None)] * axis + [slice(slice_on)]
180             slc2 = [slice(None)] * axis + [slice(slice_on, None)]
181             x = concatenate([x[slc2], x[slc1]], axis=axis)
182 
183         recursive_tile(x)
184         new_op = op.copy()
185         return new_op.new_tensors(op.inputs, op.outputs[0].shape,
186                                   chunks=x.chunks, nsplits=x.nsplits)
187 
188 
189 class TensorDiscreteFourierTransform(TensorHasInput):
190     __slots__ = ()
191 
192 
193 class TensorBaseFFT(TensorDiscreteFourierTransform):
194     _input = KeyField('input')
195     _norm = StringField('norm')
196 
197     @property
198     def norm(self):
199         return getattr(self, '_norm', None)
200 
201 
202 class TensorBaseSingleDimensionFFT(TensorBaseFFT):
203     _n = Int64Field('n')
204     _axis = Int32Field('axis')
205 
206     @property
207     def n(self):
208         return self._n
209 
210     @property
211     def axis(self):
212         return self._axis
213 
214     @classmethod
215     def execute(cls, ctx, op):
216         a = ctx[op.inputs[0].key]
217         xp = get_array_module(a)
218         fun = _get_fft_func(op, xp)
219         res = fun(a, n=op.n, axis=op.axis, norm=op.norm)
220         if res.dtype != op.dtype:
221             res = res.astype(op.dtype)
222         ctx[op.outputs[0].key] = res
223 
224 
225 class TensorBaseMultipleDimensionFFT(TensorBaseFFT):
226     _shape = ListField('shape', ValueType.int64)
227     _axes = ListField('axes', ValueType.int32)
228 
229     @property
230     def shape(self):
231         return self._shape
232 
233     @property
234     def axes(self):
235         return self._axes
236 
237     @classmethod
238     def execute(cls, ctx, op):
239         a = ctx[op.inputs[0].key]
240         xp = get_array_module(a)
241         fun = _get_fft_func(op, xp)
242         res = fun(a, s=op.shape, axes=op.axes, norm=op.norm)
243         if res.dtype != op.dtype:
244             res = res.astype(op.dtype)
245         ctx[op.outputs[0].key] = res
246 
247 
248 def _get_fft_func(op, xp):
249     from .. import fft as fftop
250 
251     fun_name = type(op).__name__.lower()[6:]  # all op starts with tensor
252     if type(op) in (fftop.TensorFFT, fftop.TensorIFFT, fftop.TensorFFT2, fftop.TensorIFFT2,
253                     fftop.TensorFFTN, fftop.TensorIFFTN):
254         if xp is np and scifft and op.norm is None:
255             def f(*args, **kwargs):
256                 kwargs.pop('norm', None)
257                 if 's' in kwargs:
258                     kwargs['shape'] = kwargs.pop('s', None)
259                 return getattr(scifft, fun_name)(*args, **kwargs)
260 
261             return f
262         else:
263             return getattr(xp.fft, fun_name)
264     else:
265         return getattr(xp.fft, fun_name)
266 
267 
268 class TensorStandardFFT(TensorBaseSingleDimensionFFT):
269     pass
270 
271 
272 class TensorStandardFFTN(TensorBaseMultipleDimensionFFT):
273     pass
274 
275 
276 class TensorFFTShiftBase(TensorHasInput):
277     _input = KeyField('input')
278     _axes = ListField('axes', ValueType.int32)
279 
280     @property
281     def axes(self):
282         return self._axes
283 
284 
285 class TensorRealFFT(TensorBaseSingleDimensionFFT):
286     pass
287 
288 
289 class TensorRealFFTN(TensorBaseMultipleDimensionFFT):
290     pass
291 
292 
293 class TensorHermitianFFT(TensorBaseSingleDimensionFFT):
294     pass
295
```
--- 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/mars/tensor/fft/core.py b/mars/tensor/fft/core.py
--- a/mars/tensor/fft/core.py
+++ b/mars/tensor/fft/core.py
@@ -21,14 +21,8 @@
     Int64Field, ListField
 from ..utils import validate_axis, decide_chunk_sizes, recursive_tile
 from ..operands import TensorHasInput, TensorOperandMixin
-import numpy as np
 from ..array_utils import get_array_module
 
-try:
-    import scipy.fftpack as scifft
-except ImportError:  # pragma: no cover
-    scifft = None
-
 
 class TensorFFTBaseMixin(TensorOperandMixin):
     __slots__ = ()
@@ -246,23 +240,8 @@
 
 
 def _get_fft_func(op, xp):
-    from .. import fft as fftop
-
     fun_name = type(op).__name__.lower()[6:]  # all op starts with tensor
-    if type(op) in (fftop.TensorFFT, fftop.TensorIFFT, fftop.TensorFFT2, fftop.TensorIFFT2,
-                    fftop.TensorFFTN, fftop.TensorIFFTN):
-        if xp is np and scifft and op.norm is None:
-            def f(*args, **kwargs):
-                kwargs.pop('norm', None)
-                if 's' in kwargs:
-                    kwargs['shape'] = kwargs.pop('s', None)
-                return getattr(scifft, fun_name)(*args, **kwargs)
-
-            return f
-        else:
-            return getattr(xp.fft, fun_name)
-    else:
-        return getattr(xp.fft, fun_name)
+    return getattr(xp.fft, fun_name)
 
 
 class TensorStandardFFT(TensorBaseSingleDimensionFFT):

{"golden_diff": "diff --git a/mars/tensor/fft/core.py b/mars/tensor/fft/core.py\n--- a/mars/tensor/fft/core.py\n+++ b/mars/tensor/fft/core.py\n@@ -21,14 +21,8 @@\n     Int64Field, ListField\n from ..utils import validate_axis, decide_chunk_sizes, recursive_tile\n from ..operands import TensorHasInput, TensorOperandMixin\n-import numpy as np\n from ..array_utils import get_array_module\n \n-try:\n-    import scipy.fftpack as scifft\n-except ImportError:  # pragma: no cover\n-    scifft = None\n-\n \n class TensorFFTBaseMixin(TensorOperandMixin):\n     __slots__ = ()\n@@ -246,23 +240,8 @@\n \n \n def _get_fft_func(op, xp):\n-    from .. import fft as fftop\n-\n     fun_name = type(op).__name__.lower()[6:]  # all op starts with tensor\n-    if type(op) in (fftop.TensorFFT, fftop.TensorIFFT, fftop.TensorFFT2, fftop.TensorIFFT2,\n-                    fftop.TensorFFTN, fftop.TensorIFFTN):\n-        if xp is np and scifft and op.norm is None:\n-            def f(*args, **kwargs):\n-                kwargs.pop('norm', None)\n-                if 's' in kwargs:\n-                    kwargs['shape'] = kwargs.pop('s', None)\n-                return getattr(scifft, fun_name)(*args, **kwargs)\n-\n-            return f\n-        else:\n-            return getattr(xp.fft, fun_name)\n-    else:\n-        return getattr(xp.fft, fun_name)\n+    return getattr(xp.fft, fun_name)\n \n \n class TensorStandardFFT(TensorBaseSingleDimensionFFT):\n", "issue": "We should use numpy.fft only because it is faster than scipy.fftpack\n<!--\r\nThank you for your contribution!\r\n\r\nPlease review https://github.com/mars-project/mars/blob/master/CONTRIBUTING.rst before opening an issue.\r\n-->\r\n\r\nMaybe at a point in the history, scipy's fftpack is faster than numpy's fft module. Thus we do some optimization in the execution of fft that  if scipy is installed, scipy's fftpack would be used to calculate fft. However, recently, I found that numpy's fft is obviously faster than scipy's fftpack. Sample code is shown below.\r\n\r\n```python\r\nIn [1]: N = 1600000                                                             \r\n\r\nIn [2]: import numpy as np                                                      \r\n\r\nIn [3]: a = np.random.rand(N, 10)                                               \r\n\r\nIn [4]: %timeit np.fft.fft(a)                                                   \r\n118 ms \u00b1 1.33 ms per loop (mean \u00b1 std. dev. of 7 runs, 10 loops each)\r\n\r\nIn [6]: import scipy.fftpack as sfft                                            \r\n\r\nIn [7]: %timeit sfft.fft(a)                                                     \r\n290 ms \u00b1 3.65 ms per loop (mean \u00b1 std. dev. of 7 runs, 1 loop each)\r\n\r\nIn [9]: np.testing.assert_allclose(np.fft.fft(a), sfft.fft(a))\r\n```\r\n\r\nHence, I suggest to use numpy only to calculate fft.\r\n\n", "before_files": [{"content": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n# Copyright 1999-2018 Alibaba Group Holding Ltd.\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\nfrom collections import Iterable\n\nfrom ...compat import izip\nfrom ...serialize import ValueType, KeyField, StringField, Int32Field, \\\n    Int64Field, ListField\nfrom ..utils import validate_axis, decide_chunk_sizes, recursive_tile\nfrom ..operands import TensorHasInput, TensorOperandMixin\nimport numpy as np\nfrom ..array_utils import get_array_module\n\ntry:\n    import scipy.fftpack as scifft\nexcept ImportError:  # pragma: no cover\n    scifft = None\n\n\nclass TensorFFTBaseMixin(TensorOperandMixin):\n    __slots__ = ()\n\n    @classmethod\n    def _get_shape(cls, op, shape):\n        raise NotImplementedError\n\n    @classmethod\n    def _tile_fft(cls, op, axes):\n        in_tensor = op.inputs[0]\n        out_tensor = op.outputs[0]\n\n        if any(in_tensor.chunk_shape[axis] != 1 for axis in axes):\n            # fft requires only 1 chunk for the specified axis, so we do rechunk first\n            chunks = {validate_axis(in_tensor.ndim, axis): in_tensor.shape[axis] for axis in axes}\n            new_chunks = decide_chunk_sizes(in_tensor.shape, chunks, in_tensor.dtype.itemsize)\n            in_tensor = in_tensor.rechunk(new_chunks).single_tiles()\n\n        out_chunks = []\n        for c in in_tensor.chunks:\n            chunk_op = op.copy().reset_key()\n            chunk_shape = cls._get_shape(op, c.shape)\n            out_chunk = chunk_op.new_chunk([c], shape=chunk_shape,\n                                           index=c.index, order=out_tensor.order)\n            out_chunks.append(out_chunk)\n\n        nsplits = [tuple(c.shape[i] for c in out_chunks\n                         if all(idx == 0 for j, idx in enumerate(c.index) if j != i))\n                   for i in range(len(out_chunks[0].shape))]\n        new_op = op.copy()\n        return new_op.new_tensors(op.inputs, out_tensor.shape, order=out_tensor.order,\n                                  chunks=out_chunks, nsplits=nsplits)\n\n    def __call__(self, a, order=None):\n        shape = self._get_shape(self, a.shape)\n        order = a.order if order is None else order\n        return self.new_tensor([a], shape, order=order)\n\n\nclass TensorFFTMixin(TensorFFTBaseMixin):\n    __slots__ = ()\n\n    @classmethod\n    def tile(cls, op):\n        return cls._tile_fft(op, [op.axis])\n\n\nclass TensorComplexFFTMixin(TensorFFTMixin):\n    @classmethod\n    def _get_shape(cls, op, shape):\n        new_shape = list(shape)\n        if op.n is not None:\n            new_shape[op.axis] = op.n\n        return tuple(new_shape)\n\n\ndef validate_fft(tensor, axis=-1, norm=None):\n    validate_axis(tensor.ndim, axis)\n    if norm is not None and norm not in ('ortho',):\n        raise ValueError('Invalid norm value {0}, should be None or \"ortho\"'.format(norm))\n\n\nclass TensorFFTNMixin(TensorFFTBaseMixin):\n    @classmethod\n    def tile(cls, op):\n        return cls._tile_fft(op, op.axes)\n\n    @staticmethod\n    def _merge_shape(op, shape):\n        new_shape = list(shape)\n        if op.shape is not None:\n            for ss, axis in izip(op.shape, op.axes):\n                new_shape[axis] = ss\n        return new_shape\n\n\nclass TensorComplexFFTNMixin(TensorFFTNMixin):\n    @classmethod\n    def _get_shape(cls, op, shape):\n        return tuple(cls._merge_shape(op, shape))\n\n\nclass TensorRealFFTNMixin(TensorFFTNMixin):\n    @classmethod\n    def _get_shape(cls, op, shape):\n        new_shape = cls._merge_shape(op, shape)\n        new_shape[op.axes[-1]] = new_shape[op.axes[-1]] // 2 + 1\n        return tuple(new_shape)\n\n\nclass TensorRealIFFTNMixin(TensorFFTNMixin):\n    @classmethod\n    def _get_shape(cls, op, shape):\n        new_shape = list(shape)\n        new_shape[op.axes[-1]] = 2 * (new_shape[op.axes[-1]] - 1)\n        return tuple(cls._merge_shape(op, new_shape))\n\n\ndef validate_fftn(tensor, s=None, axes=None, norm=None):\n    if axes is None:\n        if s is None:\n            axes = tuple(range(tensor.ndim))\n        else:\n            axes = tuple(range(len(s)))\n    else:\n        for axis in axes:\n            validate_axis(tensor.ndim, axis)\n        if len(set(axes)) < len(axes):\n            raise ValueError('Duplicate axes not allowed')\n\n    if norm is not None and norm not in ('ortho',):\n        raise ValueError('Invalid norm value {0}, should be None or \"ortho\"'.format(norm))\n\n    return axes\n\n\nclass TensorFFTShiftMixin(TensorOperandMixin):\n    __slots__ = ()\n\n    @classmethod\n    def _is_inverse(cls):\n        return False\n\n    @classmethod\n    def _process_axes(cls, x, axes):\n        if axes is None:\n            axes = tuple(range(x.ndim))\n        elif isinstance(axes, Iterable):\n            axes = tuple(axes)\n        else:\n            axes = (axes,)\n\n        return axes\n\n    @classmethod\n    def tile(cls, op):\n        from ..merge import concatenate\n\n        axes = op.axes\n        in_tensor = op.input\n        is_inverse = cls._is_inverse()\n\n        x = in_tensor\n        for axis in axes:\n            size = in_tensor.shape[axis]\n            slice_on = (size + 1) // 2 if not is_inverse else size // 2\n            slc1 = [slice(None)] * axis + [slice(slice_on)]\n            slc2 = [slice(None)] * axis + [slice(slice_on, None)]\n            x = concatenate([x[slc2], x[slc1]], axis=axis)\n\n        recursive_tile(x)\n        new_op = op.copy()\n        return new_op.new_tensors(op.inputs, op.outputs[0].shape,\n                                  chunks=x.chunks, nsplits=x.nsplits)\n\n\nclass TensorDiscreteFourierTransform(TensorHasInput):\n    __slots__ = ()\n\n\nclass TensorBaseFFT(TensorDiscreteFourierTransform):\n    _input = KeyField('input')\n    _norm = StringField('norm')\n\n    @property\n    def norm(self):\n        return getattr(self, '_norm', None)\n\n\nclass TensorBaseSingleDimensionFFT(TensorBaseFFT):\n    _n = Int64Field('n')\n    _axis = Int32Field('axis')\n\n    @property\n    def n(self):\n        return self._n\n\n    @property\n    def axis(self):\n        return self._axis\n\n    @classmethod\n    def execute(cls, ctx, op):\n        a = ctx[op.inputs[0].key]\n        xp = get_array_module(a)\n        fun = _get_fft_func(op, xp)\n        res = fun(a, n=op.n, axis=op.axis, norm=op.norm)\n        if res.dtype != op.dtype:\n            res = res.astype(op.dtype)\n        ctx[op.outputs[0].key] = res\n\n\nclass TensorBaseMultipleDimensionFFT(TensorBaseFFT):\n    _shape = ListField('shape', ValueType.int64)\n    _axes = ListField('axes', ValueType.int32)\n\n    @property\n    def shape(self):\n        return self._shape\n\n    @property\n    def axes(self):\n        return self._axes\n\n    @classmethod\n    def execute(cls, ctx, op):\n        a = ctx[op.inputs[0].key]\n        xp = get_array_module(a)\n        fun = _get_fft_func(op, xp)\n        res = fun(a, s=op.shape, axes=op.axes, norm=op.norm)\n        if res.dtype != op.dtype:\n            res = res.astype(op.dtype)\n        ctx[op.outputs[0].key] = res\n\n\ndef _get_fft_func(op, xp):\n    from .. import fft as fftop\n\n    fun_name = type(op).__name__.lower()[6:]  # all op starts with tensor\n    if type(op) in (fftop.TensorFFT, fftop.TensorIFFT, fftop.TensorFFT2, fftop.TensorIFFT2,\n                    fftop.TensorFFTN, fftop.TensorIFFTN):\n        if xp is np and scifft and op.norm is None:\n            def f(*args, **kwargs):\n                kwargs.pop('norm', None)\n                if 's' in kwargs:\n                    kwargs['shape'] = kwargs.pop('s', None)\n                return getattr(scifft, fun_name)(*args, **kwargs)\n\n            return f\n        else:\n            return getattr(xp.fft, fun_name)\n    else:\n        return getattr(xp.fft, fun_name)\n\n\nclass TensorStandardFFT(TensorBaseSingleDimensionFFT):\n    pass\n\n\nclass TensorStandardFFTN(TensorBaseMultipleDimensionFFT):\n    pass\n\n\nclass TensorFFTShiftBase(TensorHasInput):\n    _input = KeyField('input')\n    _axes = ListField('axes', ValueType.int32)\n\n    @property\n    def axes(self):\n        return self._axes\n\n\nclass TensorRealFFT(TensorBaseSingleDimensionFFT):\n    pass\n\n\nclass TensorRealFFTN(TensorBaseMultipleDimensionFFT):\n    pass\n\n\nclass TensorHermitianFFT(TensorBaseSingleDimensionFFT):\n    pass\n", "path": "mars/tensor/fft/core.py"}], "after_files": [{"content": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n# Copyright 1999-2018 Alibaba Group Holding Ltd.\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\nfrom collections import Iterable\n\nfrom ...compat import izip\nfrom ...serialize import ValueType, KeyField, StringField, Int32Field, \\\n    Int64Field, ListField\nfrom ..utils import validate_axis, decide_chunk_sizes, recursive_tile\nfrom ..operands import TensorHasInput, TensorOperandMixin\nfrom ..array_utils import get_array_module\n\n\nclass TensorFFTBaseMixin(TensorOperandMixin):\n    __slots__ = ()\n\n    @classmethod\n    def _get_shape(cls, op, shape):\n        raise NotImplementedError\n\n    @classmethod\n    def _tile_fft(cls, op, axes):\n        in_tensor = op.inputs[0]\n        out_tensor = op.outputs[0]\n\n        if any(in_tensor.chunk_shape[axis] != 1 for axis in axes):\n            # fft requires only 1 chunk for the specified axis, so we do rechunk first\n            chunks = {validate_axis(in_tensor.ndim, axis): in_tensor.shape[axis] for axis in axes}\n            new_chunks = decide_chunk_sizes(in_tensor.shape, chunks, in_tensor.dtype.itemsize)\n            in_tensor = in_tensor.rechunk(new_chunks).single_tiles()\n\n        out_chunks = []\n        for c in in_tensor.chunks:\n            chunk_op = op.copy().reset_key()\n            chunk_shape = cls._get_shape(op, c.shape)\n            out_chunk = chunk_op.new_chunk([c], shape=chunk_shape,\n                                           index=c.index, order=out_tensor.order)\n            out_chunks.append(out_chunk)\n\n        nsplits = [tuple(c.shape[i] for c in out_chunks\n                         if all(idx == 0 for j, idx in enumerate(c.index) if j != i))\n                   for i in range(len(out_chunks[0].shape))]\n        new_op = op.copy()\n        return new_op.new_tensors(op.inputs, out_tensor.shape, order=out_tensor.order,\n                                  chunks=out_chunks, nsplits=nsplits)\n\n    def __call__(self, a, order=None):\n        shape = self._get_shape(self, a.shape)\n        order = a.order if order is None else order\n        return self.new_tensor([a], shape, order=order)\n\n\nclass TensorFFTMixin(TensorFFTBaseMixin):\n    __slots__ = ()\n\n    @classmethod\n    def tile(cls, op):\n        return cls._tile_fft(op, [op.axis])\n\n\nclass TensorComplexFFTMixin(TensorFFTMixin):\n    @classmethod\n    def _get_shape(cls, op, shape):\n        new_shape = list(shape)\n        if op.n is not None:\n            new_shape[op.axis] = op.n\n        return tuple(new_shape)\n\n\ndef validate_fft(tensor, axis=-1, norm=None):\n    validate_axis(tensor.ndim, axis)\n    if norm is not None and norm not in ('ortho',):\n        raise ValueError('Invalid norm value {0}, should be None or \"ortho\"'.format(norm))\n\n\nclass TensorFFTNMixin(TensorFFTBaseMixin):\n    @classmethod\n    def tile(cls, op):\n        return cls._tile_fft(op, op.axes)\n\n    @staticmethod\n    def _merge_shape(op, shape):\n        new_shape = list(shape)\n        if op.shape is not None:\n            for ss, axis in izip(op.shape, op.axes):\n                new_shape[axis] = ss\n        return new_shape\n\n\nclass TensorComplexFFTNMixin(TensorFFTNMixin):\n    @classmethod\n    def _get_shape(cls, op, shape):\n        return tuple(cls._merge_shape(op, shape))\n\n\nclass TensorRealFFTNMixin(TensorFFTNMixin):\n    @classmethod\n    def _get_shape(cls, op, shape):\n        new_shape = cls._merge_shape(op, shape)\n        new_shape[op.axes[-1]] = new_shape[op.axes[-1]] // 2 + 1\n        return tuple(new_shape)\n\n\nclass TensorRealIFFTNMixin(TensorFFTNMixin):\n    @classmethod\n    def _get_shape(cls, op, shape):\n        new_shape = list(shape)\n        new_shape[op.axes[-1]] = 2 * (new_shape[op.axes[-1]] - 1)\n        return tuple(cls._merge_shape(op, new_shape))\n\n\ndef validate_fftn(tensor, s=None, axes=None, norm=None):\n    if axes is None:\n        if s is None:\n            axes = tuple(range(tensor.ndim))\n        else:\n            axes = tuple(range(len(s)))\n    else:\n        for axis in axes:\n            validate_axis(tensor.ndim, axis)\n        if len(set(axes)) < len(axes):\n            raise ValueError('Duplicate axes not allowed')\n\n    if norm is not None and norm not in ('ortho',):\n        raise ValueError('Invalid norm value {0}, should be None or \"ortho\"'.format(norm))\n\n    return axes\n\n\nclass TensorFFTShiftMixin(TensorOperandMixin):\n    __slots__ = ()\n\n    @classmethod\n    def _is_inverse(cls):\n        return False\n\n    @classmethod\n    def _process_axes(cls, x, axes):\n        if axes is None:\n            axes = tuple(range(x.ndim))\n        elif isinstance(axes, Iterable):\n            axes = tuple(axes)\n        else:\n            axes = (axes,)\n\n        return axes\n\n    @classmethod\n    def tile(cls, op):\n        from ..merge import concatenate\n\n        axes = op.axes\n        in_tensor = op.input\n        is_inverse = cls._is_inverse()\n\n        x = in_tensor\n        for axis in axes:\n            size = in_tensor.shape[axis]\n            slice_on = (size + 1) // 2 if not is_inverse else size // 2\n            slc1 = [slice(None)] * axis + [slice(slice_on)]\n            slc2 = [slice(None)] * axis + [slice(slice_on, None)]\n            x = concatenate([x[slc2], x[slc1]], axis=axis)\n\n        recursive_tile(x)\n        new_op = op.copy()\n        return new_op.new_tensors(op.inputs, op.outputs[0].shape,\n                                  chunks=x.chunks, nsplits=x.nsplits)\n\n\nclass TensorDiscreteFourierTransform(TensorHasInput):\n    __slots__ = ()\n\n\nclass TensorBaseFFT(TensorDiscreteFourierTransform):\n    _input = KeyField('input')\n    _norm = StringField('norm')\n\n    @property\n    def norm(self):\n        return getattr(self, '_norm', None)\n\n\nclass TensorBaseSingleDimensionFFT(TensorBaseFFT):\n    _n = Int64Field('n')\n    _axis = Int32Field('axis')\n\n    @property\n    def n(self):\n        return self._n\n\n    @property\n    def axis(self):\n        return self._axis\n\n    @classmethod\n    def execute(cls, ctx, op):\n        a = ctx[op.inputs[0].key]\n        xp = get_array_module(a)\n        fun = _get_fft_func(op, xp)\n        res = fun(a, n=op.n, axis=op.axis, norm=op.norm)\n        if res.dtype != op.dtype:\n            res = res.astype(op.dtype)\n        ctx[op.outputs[0].key] = res\n\n\nclass TensorBaseMultipleDimensionFFT(TensorBaseFFT):\n    _shape = ListField('shape', ValueType.int64)\n    _axes = ListField('axes', ValueType.int32)\n\n    @property\n    def shape(self):\n        return self._shape\n\n    @property\n    def axes(self):\n        return self._axes\n\n    @classmethod\n    def execute(cls, ctx, op):\n        a = ctx[op.inputs[0].key]\n        xp = get_array_module(a)\n        fun = _get_fft_func(op, xp)\n        res = fun(a, s=op.shape, axes=op.axes, norm=op.norm)\n        if res.dtype != op.dtype:\n            res = res.astype(op.dtype)\n        ctx[op.outputs[0].key] = res\n\n\ndef _get_fft_func(op, xp):\n    fun_name = type(op).__name__.lower()[6:]  # all op starts with tensor\n    return getattr(xp.fft, fun_name)\n\n\nclass TensorStandardFFT(TensorBaseSingleDimensionFFT):\n    pass\n\n\nclass TensorStandardFFTN(TensorBaseMultipleDimensionFFT):\n    pass\n\n\nclass TensorFFTShiftBase(TensorHasInput):\n    _input = KeyField('input')\n    _axes = ListField('axes', ValueType.int32)\n\n    @property\n    def axes(self):\n        return self._axes\n\n\nclass TensorRealFFT(TensorBaseSingleDimensionFFT):\n    pass\n\n\nclass TensorRealFFTN(TensorBaseMultipleDimensionFFT):\n    pass\n\n\nclass TensorHermitianFFT(TensorBaseSingleDimensionFFT):\n    pass\n", "path": "mars/tensor/fft/core.py"}]}