We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Add simple SVD tool to kit
I find myself doing simple SVDs a lot. We should add a tool to `kit` and then plan to add multidimensional/data support later.

example of what this method might look like:
```
def SVD_doer(arr, i=None):
    u, s, v = np.linalg.svd(arr , full_matrices=False, compute_uv=True)
    u = u.T
    if i == None:
        return u, v, s
    else:
        return u[i], v[i], s[i]
```

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `WrightTools/kit/_array.py`
Content:
```
1 """Array interaction tools."""
2 
3 
4 # --- import --------------------------------------------------------------------------------------
5 
6 
7 import numpy as np
8 
9 from .. import exceptions as wt_exceptions
10 
11 
12 # --- define --------------------------------------------------------------------------------------
13 
14 
15 __all__ = [
16     "closest_pair",
17     "diff",
18     "fft",
19     "joint_shape",
20     "orthogonal",
21     "remove_nans_1D",
22     "share_nans",
23     "smooth_1D",
24     "unique",
25     "valid_index",
26 ]
27 
28 
29 # --- functions -----------------------------------------------------------------------------------
30 
31 
32 def closest_pair(arr, give="indicies"):
33     """Find the pair of indices corresponding to the closest elements in an array.
34 
35     If multiple pairs are equally close, both pairs of indicies are returned.
36     Optionally returns the closest distance itself.
37 
38     I am sure that this could be written as a cheaper operation. I
39     wrote this as a quick and dirty method because I need it now to use on some
40     relatively small arrays. Feel free to refactor if you need this operation
41     done as fast as possible. - Blaise 2016-02-07
42 
43     Parameters
44     ----------
45     arr : numpy.ndarray
46         The array to search.
47     give : {'indicies', 'distance'} (optional)
48         Toggle return behavior. If 'distance', returns a single float - the
49         closest distance itself. Default is indicies.
50 
51     Returns
52     -------
53     list of lists of two tuples
54         List containing lists of two tuples: indicies the nearest pair in the
55         array.
56 
57         >>> arr = np.array([0, 1, 2, 3, 3, 4, 5, 6, 1])
58         >>> closest_pair(arr)
59         [[(1,), (8,)], [(3,), (4,)]]
60 
61     """
62     idxs = [idx for idx in np.ndindex(arr.shape)]
63     outs = []
64     min_dist = arr.max() - arr.min()
65     for idxa in idxs:
66         for idxb in idxs:
67             if idxa == idxb:
68                 continue
69             dist = abs(arr[idxa] - arr[idxb])
70             if dist == min_dist:
71                 if not [idxb, idxa] in outs:
72                     outs.append([idxa, idxb])
73             elif dist < min_dist:
74                 min_dist = dist
75                 outs = [[idxa, idxb]]
76     if give == "indicies":
77         return outs
78     elif give == "distance":
79         return min_dist
80     else:
81         raise KeyError("give not recognized in closest_pair")
82 
83 
84 def diff(xi, yi, order=1):
85     """Take the numerical derivative of a 1D array.
86 
87     Output is mapped onto the original coordinates  using linear interpolation.
88     Expects monotonic xi values.
89 
90     Parameters
91     ----------
92     xi : 1D array-like
93         Coordinates.
94     yi : 1D array-like
95         Values.
96     order : positive integer (optional)
97         Order of differentiation.
98 
99     Returns
100     -------
101     1D numpy array
102         Numerical derivative. Has the same shape as the input arrays.
103     """
104     yi = np.array(yi).copy()
105     flip = False
106     if xi[-1] < xi[0]:
107         xi = np.flipud(xi.copy())
108         yi = np.flipud(yi)
109         flip = True
110     midpoints = (xi[1:] + xi[:-1]) / 2
111     for _ in range(order):
112         d = np.diff(yi)
113         d /= np.diff(xi)
114         yi = np.interp(xi, midpoints, d)
115     if flip:
116         yi = np.flipud(yi)
117     return yi
118 
119 
120 def fft(xi, yi, axis=0):
121     """Take the 1D FFT of an N-dimensional array and return "sensible" properly shifted arrays.
122 
123     Parameters
124     ----------
125     xi : numpy.ndarray
126         1D array over which the points to be FFT'ed are defined
127     yi : numpy.ndarray
128         ND array with values to FFT
129     axis : int
130         axis of yi to perform FFT over
131 
132     Returns
133     -------
134     xi : 1D numpy.ndarray
135         1D array. Conjugate to input xi. Example: if input xi is in the time
136         domain, output xi is in frequency domain.
137     yi : ND numpy.ndarray
138         FFT. Has the same shape as the input array (yi).
139     """
140     # xi must be 1D
141     if xi.ndim != 1:
142         raise wt_exceptions.DimensionalityError(1, xi.ndim)
143     # xi must be evenly spaced
144     spacing = np.diff(xi)
145     if not np.allclose(spacing, spacing.mean()):
146         raise RuntimeError("WrightTools.kit.fft: argument xi must be evenly spaced")
147     # fft
148     yi = np.fft.fft(yi, axis=axis)
149     d = (xi.max() - xi.min()) / (xi.size - 1)
150     xi = np.fft.fftfreq(xi.size, d=d)
151     # shift
152     xi = np.fft.fftshift(xi)
153     yi = np.fft.fftshift(yi, axes=axis)
154     return xi, yi
155 
156 
157 def joint_shape(*args):
158     """Given a set of arrays, return the joint shape.
159 
160     Parameters
161     ----------
162     args : array-likes
163 
164     Returns
165     -------
166     tuple of int
167         Joint shape.
168     """
169     if len(args) == 0:
170         return ()
171     shape = []
172     shapes = [a.shape for a in args]
173     ndim = args[0].ndim
174     for i in range(ndim):
175         shape.append(max([s[i] for s in shapes]))
176     return tuple(shape)
177 
178 
179 def orthogonal(*args):
180     """Determine if a set of arrays are orthogonal.
181 
182     Parameters
183     ----------
184     args : array-likes or array shapes
185 
186     Returns
187     -------
188     bool
189         Array orthogonality condition.
190     """
191     for i, arg in enumerate(args):
192         if hasattr(arg, "shape"):
193             args[i] = arg.shape
194     for s in zip(*args):
195         if np.product(s) != max(s):
196             return False
197     return True
198 
199 
200 def remove_nans_1D(*args):
201     """Remove nans in a set of 1D arrays.
202 
203     Removes indicies in all arrays if any array is nan at that index.
204     All input arrays must have the same size.
205 
206     Parameters
207     ----------
208     args : 1D arrays
209 
210     Returns
211     -------
212     tuple
213         Tuple of 1D arrays in same order as given, with nan indicies removed.
214     """
215     vals = np.isnan(args[0])
216     for a in args:
217         vals |= np.isnan(a)
218     return tuple(np.array(a)[vals == False] for a in args)
219 
220 
221 def share_nans(*arrs):
222     """Take a list of nD arrays and return a new list of nD arrays.
223 
224     The new list is in the same order as the old list.
225     If one indexed element in an old array is nan then every element for that
226     index in all new arrays in the list is then nan.
227 
228     Parameters
229     ----------
230     *arrs : nD arrays.
231 
232     Returns
233     -------
234     list
235         List of nD arrays in same order as given, with nan indicies syncronized.
236     """
237     nans = np.zeros(joint_shape(*arrs))
238     for arr in arrs:
239         nans *= arr
240     return tuple([a + nans for a in arrs])
241 
242 
243 def smooth_1D(arr, n=10):
244     """Smooth 1D data by 'running average'.
245 
246     Parameters
247     ----------
248     n : int
249         number of points to average
250     """
251     for i in range(n, len(arr) - n):
252         window = arr[i - n : i + n].copy()
253         arr[i] = window.mean()
254     return arr
255 
256 
257 def unique(arr, tolerance=1e-6):
258     """Return unique elements in 1D array, within tolerance.
259 
260     Parameters
261     ----------
262     arr : array_like
263         Input array. This will be flattened if it is not already 1D.
264     tolerance : number (optional)
265         The tolerance for uniqueness.
266 
267     Returns
268     -------
269     array
270         The sorted unique values.
271     """
272     arr = sorted(arr.flatten())
273     unique = []
274     while len(arr) > 0:
275         current = arr[0]
276         lis = [xi for xi in arr if np.abs(current - xi) < tolerance]
277         arr = [xi for xi in arr if not np.abs(lis[0] - xi) < tolerance]
278         xi_lis_average = sum(lis) / len(lis)
279         unique.append(xi_lis_average)
280     return np.array(unique)
281 
282 
283 def valid_index(index, shape):
284     """Get a valid index for a broadcastable shape.
285 
286     Parameters
287     ----------
288     index : tuple
289         Given index.
290     shape : tuple of int
291         Shape.
292 
293     Returns
294     -------
295     tuple
296         Valid index.
297     """
298     # append slices to index
299     index = list(index)
300     while len(index) < len(shape):
301         index.append(slice(None))
302     # fill out, in reverse
303     out = []
304     for i, s in zip(index[::-1], shape[::-1]):
305         if s == 1:
306             if isinstance(i, slice):
307                 out.append(slice(None))
308             else:
309                 out.append(0)
310         else:
311             out.append(i)
312     return tuple(out[::-1])
313
```
--- 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/WrightTools/kit/_array.py b/WrightTools/kit/_array.py
--- a/WrightTools/kit/_array.py
+++ b/WrightTools/kit/_array.py
@@ -21,6 +21,7 @@
     "remove_nans_1D",
     "share_nans",
     "smooth_1D",
+    "svd",
     "unique",
     "valid_index",
 ]
@@ -215,7 +216,7 @@
     vals = np.isnan(args[0])
     for a in args:
         vals |= np.isnan(a)
-    return tuple(np.array(a)[vals == False] for a in args)
+    return tuple(np.array(a)[~vals] for a in args)
 
 
 def share_nans(*arrs):
@@ -254,6 +255,33 @@
     return arr
 
 
+def svd(a, i=None):
+    """Singular Value Decomposition.
+
+    Factors the matrix `a` as ``u * np.diag(s) * v``, where `u` and `v`
+    are unitary and `s` is a 1D array of `a`'s singular values.
+
+    Parameters
+    ----------
+    a : array_like
+        Input array.
+    i : int or slice (optional)
+        What singular value "slice" to return.
+        Default is None which returns unitary 2D arrays.
+
+    Returns
+    -------
+    tuple
+        Decomposed arrays in order `u`, `v`, `s`
+    """
+    u, s, v = np.linalg.svd(a, full_matrices=False, compute_uv=True)
+    u = u.T
+    if i is None:
+        return u, v, s
+    else:
+        return u[i], v[i], s[i]
+
+
 def unique(arr, tolerance=1e-6):
     """Return unique elements in 1D array, within tolerance.

{"golden_diff": "diff --git a/WrightTools/kit/_array.py b/WrightTools/kit/_array.py\n--- a/WrightTools/kit/_array.py\n+++ b/WrightTools/kit/_array.py\n@@ -21,6 +21,7 @@\n     \"remove_nans_1D\",\n     \"share_nans\",\n     \"smooth_1D\",\n+    \"svd\",\n     \"unique\",\n     \"valid_index\",\n ]\n@@ -215,7 +216,7 @@\n     vals = np.isnan(args[0])\n     for a in args:\n         vals |= np.isnan(a)\n-    return tuple(np.array(a)[vals == False] for a in args)\n+    return tuple(np.array(a)[~vals] for a in args)\n \n \n def share_nans(*arrs):\n@@ -254,6 +255,33 @@\n     return arr\n \n \n+def svd(a, i=None):\n+    \"\"\"Singular Value Decomposition.\n+\n+    Factors the matrix `a` as ``u * np.diag(s) * v``, where `u` and `v`\n+    are unitary and `s` is a 1D array of `a`'s singular values.\n+\n+    Parameters\n+    ----------\n+    a : array_like\n+        Input array.\n+    i : int or slice (optional)\n+        What singular value \"slice\" to return.\n+        Default is None which returns unitary 2D arrays.\n+\n+    Returns\n+    -------\n+    tuple\n+        Decomposed arrays in order `u`, `v`, `s`\n+    \"\"\"\n+    u, s, v = np.linalg.svd(a, full_matrices=False, compute_uv=True)\n+    u = u.T\n+    if i is None:\n+        return u, v, s\n+    else:\n+        return u[i], v[i], s[i]\n+\n+\n def unique(arr, tolerance=1e-6):\n     \"\"\"Return unique elements in 1D array, within tolerance.\n", "issue": "Add simple SVD tool to kit\nI find myself doing simple SVDs a lot. We should add a tool to `kit` and then plan to add multidimensional/data support later.\r\n\r\nexample of what this method might look like:\r\n```\r\ndef SVD_doer(arr, i=None):\r\n    u, s, v = np.linalg.svd(arr , full_matrices=False, compute_uv=True)\r\n    u = u.T\r\n    if i == None:\r\n        return u, v, s\r\n    else:\r\n        return u[i], v[i], s[i]\r\n```\n", "before_files": [{"content": "\"\"\"Array interaction tools.\"\"\"\n\n\n# --- import --------------------------------------------------------------------------------------\n\n\nimport numpy as np\n\nfrom .. import exceptions as wt_exceptions\n\n\n# --- define --------------------------------------------------------------------------------------\n\n\n__all__ = [\n    \"closest_pair\",\n    \"diff\",\n    \"fft\",\n    \"joint_shape\",\n    \"orthogonal\",\n    \"remove_nans_1D\",\n    \"share_nans\",\n    \"smooth_1D\",\n    \"unique\",\n    \"valid_index\",\n]\n\n\n# --- functions -----------------------------------------------------------------------------------\n\n\ndef closest_pair(arr, give=\"indicies\"):\n    \"\"\"Find the pair of indices corresponding to the closest elements in an array.\n\n    If multiple pairs are equally close, both pairs of indicies are returned.\n    Optionally returns the closest distance itself.\n\n    I am sure that this could be written as a cheaper operation. I\n    wrote this as a quick and dirty method because I need it now to use on some\n    relatively small arrays. Feel free to refactor if you need this operation\n    done as fast as possible. - Blaise 2016-02-07\n\n    Parameters\n    ----------\n    arr : numpy.ndarray\n        The array to search.\n    give : {'indicies', 'distance'} (optional)\n        Toggle return behavior. If 'distance', returns a single float - the\n        closest distance itself. Default is indicies.\n\n    Returns\n    -------\n    list of lists of two tuples\n        List containing lists of two tuples: indicies the nearest pair in the\n        array.\n\n        >>> arr = np.array([0, 1, 2, 3, 3, 4, 5, 6, 1])\n        >>> closest_pair(arr)\n        [[(1,), (8,)], [(3,), (4,)]]\n\n    \"\"\"\n    idxs = [idx for idx in np.ndindex(arr.shape)]\n    outs = []\n    min_dist = arr.max() - arr.min()\n    for idxa in idxs:\n        for idxb in idxs:\n            if idxa == idxb:\n                continue\n            dist = abs(arr[idxa] - arr[idxb])\n            if dist == min_dist:\n                if not [idxb, idxa] in outs:\n                    outs.append([idxa, idxb])\n            elif dist < min_dist:\n                min_dist = dist\n                outs = [[idxa, idxb]]\n    if give == \"indicies\":\n        return outs\n    elif give == \"distance\":\n        return min_dist\n    else:\n        raise KeyError(\"give not recognized in closest_pair\")\n\n\ndef diff(xi, yi, order=1):\n    \"\"\"Take the numerical derivative of a 1D array.\n\n    Output is mapped onto the original coordinates  using linear interpolation.\n    Expects monotonic xi values.\n\n    Parameters\n    ----------\n    xi : 1D array-like\n        Coordinates.\n    yi : 1D array-like\n        Values.\n    order : positive integer (optional)\n        Order of differentiation.\n\n    Returns\n    -------\n    1D numpy array\n        Numerical derivative. Has the same shape as the input arrays.\n    \"\"\"\n    yi = np.array(yi).copy()\n    flip = False\n    if xi[-1] < xi[0]:\n        xi = np.flipud(xi.copy())\n        yi = np.flipud(yi)\n        flip = True\n    midpoints = (xi[1:] + xi[:-1]) / 2\n    for _ in range(order):\n        d = np.diff(yi)\n        d /= np.diff(xi)\n        yi = np.interp(xi, midpoints, d)\n    if flip:\n        yi = np.flipud(yi)\n    return yi\n\n\ndef fft(xi, yi, axis=0):\n    \"\"\"Take the 1D FFT of an N-dimensional array and return \"sensible\" properly shifted arrays.\n\n    Parameters\n    ----------\n    xi : numpy.ndarray\n        1D array over which the points to be FFT'ed are defined\n    yi : numpy.ndarray\n        ND array with values to FFT\n    axis : int\n        axis of yi to perform FFT over\n\n    Returns\n    -------\n    xi : 1D numpy.ndarray\n        1D array. Conjugate to input xi. Example: if input xi is in the time\n        domain, output xi is in frequency domain.\n    yi : ND numpy.ndarray\n        FFT. Has the same shape as the input array (yi).\n    \"\"\"\n    # xi must be 1D\n    if xi.ndim != 1:\n        raise wt_exceptions.DimensionalityError(1, xi.ndim)\n    # xi must be evenly spaced\n    spacing = np.diff(xi)\n    if not np.allclose(spacing, spacing.mean()):\n        raise RuntimeError(\"WrightTools.kit.fft: argument xi must be evenly spaced\")\n    # fft\n    yi = np.fft.fft(yi, axis=axis)\n    d = (xi.max() - xi.min()) / (xi.size - 1)\n    xi = np.fft.fftfreq(xi.size, d=d)\n    # shift\n    xi = np.fft.fftshift(xi)\n    yi = np.fft.fftshift(yi, axes=axis)\n    return xi, yi\n\n\ndef joint_shape(*args):\n    \"\"\"Given a set of arrays, return the joint shape.\n\n    Parameters\n    ----------\n    args : array-likes\n\n    Returns\n    -------\n    tuple of int\n        Joint shape.\n    \"\"\"\n    if len(args) == 0:\n        return ()\n    shape = []\n    shapes = [a.shape for a in args]\n    ndim = args[0].ndim\n    for i in range(ndim):\n        shape.append(max([s[i] for s in shapes]))\n    return tuple(shape)\n\n\ndef orthogonal(*args):\n    \"\"\"Determine if a set of arrays are orthogonal.\n\n    Parameters\n    ----------\n    args : array-likes or array shapes\n\n    Returns\n    -------\n    bool\n        Array orthogonality condition.\n    \"\"\"\n    for i, arg in enumerate(args):\n        if hasattr(arg, \"shape\"):\n            args[i] = arg.shape\n    for s in zip(*args):\n        if np.product(s) != max(s):\n            return False\n    return True\n\n\ndef remove_nans_1D(*args):\n    \"\"\"Remove nans in a set of 1D arrays.\n\n    Removes indicies in all arrays if any array is nan at that index.\n    All input arrays must have the same size.\n\n    Parameters\n    ----------\n    args : 1D arrays\n\n    Returns\n    -------\n    tuple\n        Tuple of 1D arrays in same order as given, with nan indicies removed.\n    \"\"\"\n    vals = np.isnan(args[0])\n    for a in args:\n        vals |= np.isnan(a)\n    return tuple(np.array(a)[vals == False] for a in args)\n\n\ndef share_nans(*arrs):\n    \"\"\"Take a list of nD arrays and return a new list of nD arrays.\n\n    The new list is in the same order as the old list.\n    If one indexed element in an old array is nan then every element for that\n    index in all new arrays in the list is then nan.\n\n    Parameters\n    ----------\n    *arrs : nD arrays.\n\n    Returns\n    -------\n    list\n        List of nD arrays in same order as given, with nan indicies syncronized.\n    \"\"\"\n    nans = np.zeros(joint_shape(*arrs))\n    for arr in arrs:\n        nans *= arr\n    return tuple([a + nans for a in arrs])\n\n\ndef smooth_1D(arr, n=10):\n    \"\"\"Smooth 1D data by 'running average'.\n\n    Parameters\n    ----------\n    n : int\n        number of points to average\n    \"\"\"\n    for i in range(n, len(arr) - n):\n        window = arr[i - n : i + n].copy()\n        arr[i] = window.mean()\n    return arr\n\n\ndef unique(arr, tolerance=1e-6):\n    \"\"\"Return unique elements in 1D array, within tolerance.\n\n    Parameters\n    ----------\n    arr : array_like\n        Input array. This will be flattened if it is not already 1D.\n    tolerance : number (optional)\n        The tolerance for uniqueness.\n\n    Returns\n    -------\n    array\n        The sorted unique values.\n    \"\"\"\n    arr = sorted(arr.flatten())\n    unique = []\n    while len(arr) > 0:\n        current = arr[0]\n        lis = [xi for xi in arr if np.abs(current - xi) < tolerance]\n        arr = [xi for xi in arr if not np.abs(lis[0] - xi) < tolerance]\n        xi_lis_average = sum(lis) / len(lis)\n        unique.append(xi_lis_average)\n    return np.array(unique)\n\n\ndef valid_index(index, shape):\n    \"\"\"Get a valid index for a broadcastable shape.\n\n    Parameters\n    ----------\n    index : tuple\n        Given index.\n    shape : tuple of int\n        Shape.\n\n    Returns\n    -------\n    tuple\n        Valid index.\n    \"\"\"\n    # append slices to index\n    index = list(index)\n    while len(index) < len(shape):\n        index.append(slice(None))\n    # fill out, in reverse\n    out = []\n    for i, s in zip(index[::-1], shape[::-1]):\n        if s == 1:\n            if isinstance(i, slice):\n                out.append(slice(None))\n            else:\n                out.append(0)\n        else:\n            out.append(i)\n    return tuple(out[::-1])\n", "path": "WrightTools/kit/_array.py"}], "after_files": [{"content": "\"\"\"Array interaction tools.\"\"\"\n\n\n# --- import --------------------------------------------------------------------------------------\n\n\nimport numpy as np\n\nfrom .. import exceptions as wt_exceptions\n\n\n# --- define --------------------------------------------------------------------------------------\n\n\n__all__ = [\n    \"closest_pair\",\n    \"diff\",\n    \"fft\",\n    \"joint_shape\",\n    \"orthogonal\",\n    \"remove_nans_1D\",\n    \"share_nans\",\n    \"smooth_1D\",\n    \"svd\",\n    \"unique\",\n    \"valid_index\",\n]\n\n\n# --- functions -----------------------------------------------------------------------------------\n\n\ndef closest_pair(arr, give=\"indicies\"):\n    \"\"\"Find the pair of indices corresponding to the closest elements in an array.\n\n    If multiple pairs are equally close, both pairs of indicies are returned.\n    Optionally returns the closest distance itself.\n\n    I am sure that this could be written as a cheaper operation. I\n    wrote this as a quick and dirty method because I need it now to use on some\n    relatively small arrays. Feel free to refactor if you need this operation\n    done as fast as possible. - Blaise 2016-02-07\n\n    Parameters\n    ----------\n    arr : numpy.ndarray\n        The array to search.\n    give : {'indicies', 'distance'} (optional)\n        Toggle return behavior. If 'distance', returns a single float - the\n        closest distance itself. Default is indicies.\n\n    Returns\n    -------\n    list of lists of two tuples\n        List containing lists of two tuples: indicies the nearest pair in the\n        array.\n\n        >>> arr = np.array([0, 1, 2, 3, 3, 4, 5, 6, 1])\n        >>> closest_pair(arr)\n        [[(1,), (8,)], [(3,), (4,)]]\n\n    \"\"\"\n    idxs = [idx for idx in np.ndindex(arr.shape)]\n    outs = []\n    min_dist = arr.max() - arr.min()\n    for idxa in idxs:\n        for idxb in idxs:\n            if idxa == idxb:\n                continue\n            dist = abs(arr[idxa] - arr[idxb])\n            if dist == min_dist:\n                if not [idxb, idxa] in outs:\n                    outs.append([idxa, idxb])\n            elif dist < min_dist:\n                min_dist = dist\n                outs = [[idxa, idxb]]\n    if give == \"indicies\":\n        return outs\n    elif give == \"distance\":\n        return min_dist\n    else:\n        raise KeyError(\"give not recognized in closest_pair\")\n\n\ndef diff(xi, yi, order=1):\n    \"\"\"Take the numerical derivative of a 1D array.\n\n    Output is mapped onto the original coordinates  using linear interpolation.\n    Expects monotonic xi values.\n\n    Parameters\n    ----------\n    xi : 1D array-like\n        Coordinates.\n    yi : 1D array-like\n        Values.\n    order : positive integer (optional)\n        Order of differentiation.\n\n    Returns\n    -------\n    1D numpy array\n        Numerical derivative. Has the same shape as the input arrays.\n    \"\"\"\n    yi = np.array(yi).copy()\n    flip = False\n    if xi[-1] < xi[0]:\n        xi = np.flipud(xi.copy())\n        yi = np.flipud(yi)\n        flip = True\n    midpoints = (xi[1:] + xi[:-1]) / 2\n    for _ in range(order):\n        d = np.diff(yi)\n        d /= np.diff(xi)\n        yi = np.interp(xi, midpoints, d)\n    if flip:\n        yi = np.flipud(yi)\n    return yi\n\n\ndef fft(xi, yi, axis=0):\n    \"\"\"Take the 1D FFT of an N-dimensional array and return \"sensible\" properly shifted arrays.\n\n    Parameters\n    ----------\n    xi : numpy.ndarray\n        1D array over which the points to be FFT'ed are defined\n    yi : numpy.ndarray\n        ND array with values to FFT\n    axis : int\n        axis of yi to perform FFT over\n\n    Returns\n    -------\n    xi : 1D numpy.ndarray\n        1D array. Conjugate to input xi. Example: if input xi is in the time\n        domain, output xi is in frequency domain.\n    yi : ND numpy.ndarray\n        FFT. Has the same shape as the input array (yi).\n    \"\"\"\n    # xi must be 1D\n    if xi.ndim != 1:\n        raise wt_exceptions.DimensionalityError(1, xi.ndim)\n    # xi must be evenly spaced\n    spacing = np.diff(xi)\n    if not np.allclose(spacing, spacing.mean()):\n        raise RuntimeError(\"WrightTools.kit.fft: argument xi must be evenly spaced\")\n    # fft\n    yi = np.fft.fft(yi, axis=axis)\n    d = (xi.max() - xi.min()) / (xi.size - 1)\n    xi = np.fft.fftfreq(xi.size, d=d)\n    # shift\n    xi = np.fft.fftshift(xi)\n    yi = np.fft.fftshift(yi, axes=axis)\n    return xi, yi\n\n\ndef joint_shape(*args):\n    \"\"\"Given a set of arrays, return the joint shape.\n\n    Parameters\n    ----------\n    args : array-likes\n\n    Returns\n    -------\n    tuple of int\n        Joint shape.\n    \"\"\"\n    if len(args) == 0:\n        return ()\n    shape = []\n    shapes = [a.shape for a in args]\n    ndim = args[0].ndim\n    for i in range(ndim):\n        shape.append(max([s[i] for s in shapes]))\n    return tuple(shape)\n\n\ndef orthogonal(*args):\n    \"\"\"Determine if a set of arrays are orthogonal.\n\n    Parameters\n    ----------\n    args : array-likes or array shapes\n\n    Returns\n    -------\n    bool\n        Array orthogonality condition.\n    \"\"\"\n    for i, arg in enumerate(args):\n        if hasattr(arg, \"shape\"):\n            args[i] = arg.shape\n    for s in zip(*args):\n        if np.product(s) != max(s):\n            return False\n    return True\n\n\ndef remove_nans_1D(*args):\n    \"\"\"Remove nans in a set of 1D arrays.\n\n    Removes indicies in all arrays if any array is nan at that index.\n    All input arrays must have the same size.\n\n    Parameters\n    ----------\n    args : 1D arrays\n\n    Returns\n    -------\n    tuple\n        Tuple of 1D arrays in same order as given, with nan indicies removed.\n    \"\"\"\n    vals = np.isnan(args[0])\n    for a in args:\n        vals |= np.isnan(a)\n    return tuple(np.array(a)[~vals] for a in args)\n\n\ndef share_nans(*arrs):\n    \"\"\"Take a list of nD arrays and return a new list of nD arrays.\n\n    The new list is in the same order as the old list.\n    If one indexed element in an old array is nan then every element for that\n    index in all new arrays in the list is then nan.\n\n    Parameters\n    ----------\n    *arrs : nD arrays.\n\n    Returns\n    -------\n    list\n        List of nD arrays in same order as given, with nan indicies syncronized.\n    \"\"\"\n    nans = np.zeros(joint_shape(*arrs))\n    for arr in arrs:\n        nans *= arr\n    return tuple([a + nans for a in arrs])\n\n\ndef smooth_1D(arr, n=10):\n    \"\"\"Smooth 1D data by 'running average'.\n\n    Parameters\n    ----------\n    n : int\n        number of points to average\n    \"\"\"\n    for i in range(n, len(arr) - n):\n        window = arr[i - n : i + n].copy()\n        arr[i] = window.mean()\n    return arr\n\n\ndef svd(a, i=None):\n    \"\"\"Singular Value Decomposition.\n\n    Factors the matrix `a` as ``u * np.diag(s) * v``, where `u` and `v`\n    are unitary and `s` is a 1D array of `a`'s singular values.\n\n    Parameters\n    ----------\n    a : array_like\n        Input array.\n    i : int or slice (optional)\n        What singular value \"slice\" to return.\n        Default is None which returns unitary 2D arrays.\n\n    Returns\n    -------\n    tuple\n        Decomposed arrays in order `u`, `v`, `s`\n    \"\"\"\n    u, s, v = np.linalg.svd(a, full_matrices=False, compute_uv=True)\n    u = u.T\n    if i is None:\n        return u, v, s\n    else:\n        return u[i], v[i], s[i]\n\n\ndef unique(arr, tolerance=1e-6):\n    \"\"\"Return unique elements in 1D array, within tolerance.\n\n    Parameters\n    ----------\n    arr : array_like\n        Input array. This will be flattened if it is not already 1D.\n    tolerance : number (optional)\n        The tolerance for uniqueness.\n\n    Returns\n    -------\n    array\n        The sorted unique values.\n    \"\"\"\n    arr = sorted(arr.flatten())\n    unique = []\n    while len(arr) > 0:\n        current = arr[0]\n        lis = [xi for xi in arr if np.abs(current - xi) < tolerance]\n        arr = [xi for xi in arr if not np.abs(lis[0] - xi) < tolerance]\n        xi_lis_average = sum(lis) / len(lis)\n        unique.append(xi_lis_average)\n    return np.array(unique)\n\n\ndef valid_index(index, shape):\n    \"\"\"Get a valid index for a broadcastable shape.\n\n    Parameters\n    ----------\n    index : tuple\n        Given index.\n    shape : tuple of int\n        Shape.\n\n    Returns\n    -------\n    tuple\n        Valid index.\n    \"\"\"\n    # append slices to index\n    index = list(index)\n    while len(index) < len(shape):\n        index.append(slice(None))\n    # fill out, in reverse\n    out = []\n    for i, s in zip(index[::-1], shape[::-1]):\n        if s == 1:\n            if isinstance(i, slice):\n                out.append(slice(None))\n            else:\n                out.append(0)\n        else:\n            out.append(i)\n    return tuple(out[::-1])\n", "path": "WrightTools/kit/_array.py"}]}