Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes15kdownloads
shape_base.py997 linesDownload Raw Back to _core
1__all__ = ['atleast_1d', 'atleast_2d', 'atleast_3d', 'block', 'hstack',
2           'stack', 'unstack', 'vstack']
3
4import functools
5import itertools
6import operator
7
8from . import fromnumeric as _from_nx, numeric as _nx, overrides
9from .multiarray import array, asanyarray, normalize_axis_index
10
11array_function_dispatch = functools.partial(
12    overrides.array_function_dispatch, module='numpy')
13
14
15def _atleast_1d_dispatcher(*arys):
16    return arys
17
18
19@array_function_dispatch(_atleast_1d_dispatcher)
20def atleast_1d(*arys):
21    """
22    Convert inputs to arrays with at least one dimension.
23
24    Scalar inputs are converted to 1-dimensional arrays, whilst
25    higher-dimensional inputs are preserved.
26
27    Parameters
28    ----------
29    arys1, arys2, ... : array_like
30        One or more input arrays.
31
32    Returns
33    -------
34    ret : ndarray
35        An array, or tuple of arrays, each with ``a.ndim >= 1``.
36        Copies are made only if necessary.
37
38    See Also
39    --------
40    atleast_2d, atleast_3d
41
42    Examples
43    --------
44    >>> import numpy as np
45    >>> np.atleast_1d(1.0)
46    array([1.])
47
48    >>> x = np.arange(9.0).reshape(3,3)
49    >>> np.atleast_1d(x)
50    array([[0., 1., 2.],
51           [3., 4., 5.],
52           [6., 7., 8.]])
53    >>> np.atleast_1d(x) is x
54    True
55
56    >>> np.atleast_1d(1, [3, 4])
57    (array([1]), array([3, 4]))
58
59    """
60    if len(arys) == 1:
61        result = asanyarray(arys[0])
62        if result.ndim == 0:
63            result = result.reshape(1)
64        return result
65    res = []
66    for ary in arys:
67        result = asanyarray(ary)
68        if result.ndim == 0:
69            result = result.reshape(1)
70        res.append(result)
71    return tuple(res)
72
73
74def _atleast_2d_dispatcher(*arys):
75    return arys
76
77
78@array_function_dispatch(_atleast_2d_dispatcher)
79def atleast_2d(*arys):
80    """
81    View inputs as arrays with at least two dimensions.
82
83    Parameters
84    ----------
85    arys1, arys2, ... : array_like
86        One or more array-like sequences.  Non-array inputs are converted
87        to arrays.  Arrays that already have two or more dimensions are
88        preserved.
89
90    Returns
91    -------
92    res, res2, ... : ndarray
93        An array, or tuple of arrays, each with ``a.ndim >= 2``.
94        Copies are avoided where possible, and views with two or more
95        dimensions are returned.
96
97    See Also
98    --------
99    atleast_1d, atleast_3d
100
101    Examples
102    --------
103    >>> import numpy as np
104    >>> np.atleast_2d(3.0)
105    array([[3.]])
106
107    >>> x = np.arange(3.0)
108    >>> np.atleast_2d(x)
109    array([[0., 1., 2.]])
110    >>> np.atleast_2d(x).base is x
111    True
112
113    >>> np.atleast_2d(1, [1, 2], [[1, 2]])
114    (array([[1]]), array([[1, 2]]), array([[1, 2]]))
115
116    """
117    res = []
118    for ary in arys:
119        ary = asanyarray(ary)
120        if ary.ndim == 0:
121            result = ary.reshape(1, 1)
122        elif ary.ndim == 1:
123            result = ary[_nx.newaxis, :]
124        else:
125            result = ary
126        res.append(result)
127    if len(res) == 1:
128        return res[0]
129    else:
130        return tuple(res)
131
132
133def _atleast_3d_dispatcher(*arys):
134    return arys
135
136
137@array_function_dispatch(_atleast_3d_dispatcher)
138def atleast_3d(*arys):
139    """
140    View inputs as arrays with at least three dimensions.
141
142    Parameters
143    ----------
144    arys1, arys2, ... : array_like
145        One or more array-like sequences.  Non-array inputs are converted to
146        arrays.  Arrays that already have three or more dimensions are
147        preserved.
148
149    Returns
150    -------
151    res1, res2, ... : ndarray
152        An array, or tuple of arrays, each with ``a.ndim >= 3``.  Copies are
153        avoided where possible, and views with three or more dimensions are
154        returned.  For example, a 1-D array of shape ``(N,)`` becomes a view
155        of shape ``(1, N, 1)``, and a 2-D array of shape ``(M, N)`` becomes a
156        view of shape ``(M, N, 1)``.
157
158    See Also
159    --------
160    atleast_1d, atleast_2d
161
162    Examples
163    --------
164    >>> import numpy as np
165    >>> np.atleast_3d(3.0)
166    array([[[3.]]])
167
168    >>> x = np.arange(3.0)
169    >>> np.atleast_3d(x).shape
170    (1, 3, 1)
171
172    >>> x = np.arange(12.0).reshape(4,3)
173    >>> np.atleast_3d(x).shape
174    (4, 3, 1)
175    >>> np.atleast_3d(x).base is x.base  # x is a reshape, so not base itself
176    True
177
178    >>> for arr in np.atleast_3d([1, 2], [[1, 2]], [[[1, 2]]]):
179    ...     print(arr, arr.shape) # doctest: +SKIP
180    ...
181    [[[1]
182      [2]]] (1, 2, 1)
183    [[[1]
184      [2]]] (1, 2, 1)
185    [[[1 2]]] (1, 1, 2)
186
187    """
188    res = []
189    for ary in arys:
190        ary = asanyarray(ary)
191        if ary.ndim == 0:
192            result = ary.reshape(1, 1, 1)
193        elif ary.ndim == 1:
194            result = ary[_nx.newaxis, :, _nx.newaxis]
195        elif ary.ndim == 2:
196            result = ary[:, :, _nx.newaxis]
197        else:
198            result = ary
199        res.append(result)
200    if len(res) == 1:
201        return res[0]
202    else:
203        return tuple(res)
204
205
206def _arrays_for_stack_dispatcher(arrays):
207    if not hasattr(arrays, "__getitem__"):
208        raise TypeError('arrays to stack must be passed as a "sequence" type '
209                        'such as list or tuple.')
210
211    return tuple(arrays)
212
213
214def _vhstack_dispatcher(tup, *, dtype=None, casting=None):
215    return _arrays_for_stack_dispatcher(tup)
216
217
218@array_function_dispatch(_vhstack_dispatcher)
219def vstack(tup, *, dtype=None, casting="same_kind"):
220    """
221    Stack arrays in sequence vertically (row wise).
222
223    This is equivalent to concatenation along the first axis after 1-D arrays
224    of shape `(N,)` have been reshaped to `(1,N)`. Rebuilds arrays divided by
225    `vsplit`.
226
227    This function makes most sense for arrays with up to 3 dimensions. For
228    instance, for pixel-data with a height (first axis), width (second axis),
229    and r/g/b channels (third axis). The functions `concatenate`, `stack` and
230    `block` provide more general stacking and concatenation operations.
231
232    Parameters
233    ----------
234    tup : sequence of ndarrays
235        The arrays must have the same shape along all but the first axis.
236        1-D arrays must have the same length. In the case of a single
237        array_like input, it will be treated as a sequence of arrays; i.e.,
238        each element along the zeroth axis is treated as a separate array.
239
240    dtype : str or dtype
241        If provided, the destination array will have this dtype. Cannot be
242        provided together with `out`.
243
244        .. versionadded:: 1.24
245
246    casting : {'no', 'equiv', 'safe', 'same_kind', 'unsafe'}, optional
247        Controls what kind of data casting may occur. Defaults to 'same_kind'.
248
249        .. versionadded:: 1.24
250
251    Returns
252    -------
253    stacked : ndarray
254        The array formed by stacking the given arrays, will be at least 2-D.
255
256    See Also
257    --------
258    concatenate : Join a sequence of arrays along an existing axis.
259    stack : Join a sequence of arrays along a new axis.
260    block : Assemble an nd-array from nested lists of blocks.
261    hstack : Stack arrays in sequence horizontally (column wise).
262    dstack : Stack arrays in sequence depth wise (along third axis).
263    column_stack : Stack 1-D arrays as columns into a 2-D array.
264    vsplit : Split an array into multiple sub-arrays vertically (row-wise).
265    unstack : Split an array into a tuple of sub-arrays along an axis.
266
267    Examples
268    --------
269    >>> import numpy as np
270    >>> a = np.array([1, 2, 3])
271    >>> b = np.array([4, 5, 6])
272    >>> np.vstack((a,b))
273    array([[1, 2, 3],
274           [4, 5, 6]])
275
276    >>> a = np.array([[1], [2], [3]])
277    >>> b = np.array([[4], [5], [6]])
278    >>> np.vstack((a,b))
279    array([[1],
280           [2],
281           [3],
282           [4],
283           [5],
284           [6]])
285
286    """
287    arrs = atleast_2d(*tup)
288    if not isinstance(arrs, tuple):
289        arrs = (arrs,)
290    return _nx.concatenate(arrs, 0, dtype=dtype, casting=casting)
291
292
293@array_function_dispatch(_vhstack_dispatcher)
294def hstack(tup, *, dtype=None, casting="same_kind"):
295    """
296    Stack arrays in sequence horizontally (column wise).
297
298    This is equivalent to concatenation along the second axis, except for 1-D
299    arrays where it concatenates along the first axis. Rebuilds arrays divided
300    by `hsplit`.
301
302    This function makes most sense for arrays with up to 3 dimensions. For
303    instance, for pixel-data with a height (first axis), width (second axis),
304    and r/g/b channels (third axis). The functions `concatenate`, `stack` and
305    `block` provide more general stacking and concatenation operations.
306
307    Parameters
308    ----------
309    tup : sequence of ndarrays
310        The arrays must have the same shape along all but the second axis,
311        except 1-D arrays which can be any length. In the case of a single
312        array_like input, it will be treated as a sequence of arrays; i.e.,
313        each element along the zeroth axis is treated as a separate array.
314
315    dtype : str or dtype
316        If provided, the destination array will have this dtype. Cannot be
317        provided together with `out`.
318
319        .. versionadded:: 1.24
320
321    casting : {'no', 'equiv', 'safe', 'same_kind', 'unsafe'}, optional
322        Controls what kind of data casting may occur. Defaults to 'same_kind'.
323
324        .. versionadded:: 1.24
325
326    Returns
327    -------
328    stacked : ndarray
329        The array formed by stacking the given arrays.
330
331    See Also
332    --------
333    concatenate : Join a sequence of arrays along an existing axis.
334    stack : Join a sequence of arrays along a new axis.
335    block : Assemble an nd-array from nested lists of blocks.
336    vstack : Stack arrays in sequence vertically (row wise).
337    dstack : Stack arrays in sequence depth wise (along third axis).
338    column_stack : Stack 1-D arrays as columns into a 2-D array.
339    hsplit : Split an array into multiple sub-arrays
340             horizontally (column-wise).
341    unstack : Split an array into a tuple of sub-arrays along an axis.
342
343    Examples
344    --------
345    >>> import numpy as np
346    >>> a = np.array((1,2,3))
347    >>> b = np.array((4,5,6))
348    >>> np.hstack((a,b))
349    array([1, 2, 3, 4, 5, 6])
350    >>> a = np.array([[1],[2],[3]])
351    >>> b = np.array([[4],[5],[6]])
352    >>> np.hstack((a,b))
353    array([[1, 4],
354           [2, 5],
355           [3, 6]])
356
357    """
358    arrs = atleast_1d(*tup)
359    if not isinstance(arrs, tuple):
360        arrs = (arrs,)
361    # As a special case, dimension 0 of 1-dimensional arrays is "horizontal"
362    if arrs and arrs[0].ndim == 1:
363        return _nx.concatenate(arrs, 0, dtype=dtype, casting=casting)
364    else:
365        return _nx.concatenate(arrs, 1, dtype=dtype, casting=casting)
366
367
368def _stack_dispatcher(arrays, axis=None, out=None, *,
369                      dtype=None, casting=None):
370    arrays = _arrays_for_stack_dispatcher(arrays)
371    if out is not None:
372        # optimize for the typical case where only arrays is provided
373        arrays = list(arrays)
374        arrays.append(out)
375    return arrays
376
377
378@array_function_dispatch(_stack_dispatcher)
379def stack(arrays, axis=0, out=None, *, dtype=None, casting="same_kind"):
380    """
381    Join a sequence of arrays along a new axis.
382
383    The ``axis`` parameter specifies the index of the new axis in the
384    dimensions of the result. For example, if ``axis=0`` it will be the first
385    dimension and if ``axis=-1`` it will be the last dimension.
386
387    Parameters
388    ----------
389    arrays : sequence of ndarrays
390        Each array must have the same shape. In the case of a single ndarray
391        array_like input, it will be treated as a sequence of arrays; i.e.,
392        each element along the zeroth axis is treated as a separate array.
393
394    axis : int, optional
395        The axis in the result array along which the input arrays are stacked.
396
397    out : ndarray, optional
398        If provided, the destination to place the result. The shape must be
399        correct, matching that of what stack would have returned if no
400        out argument were specified.
401
402    dtype : str or dtype
403        If provided, the destination array will have this dtype. Cannot be
404        provided together with `out`.
405
406        .. versionadded:: 1.24
407
408    casting : {'no', 'equiv', 'safe', 'same_kind', 'unsafe'}, optional
409        Controls what kind of data casting may occur. Defaults to 'same_kind'.
410
411        .. versionadded:: 1.24
412
413
414    Returns
415    -------
416    stacked : ndarray
417        The stacked array has one more dimension than the input arrays.
418
419    See Also
420    --------
421    concatenate : Join a sequence of arrays along an existing axis.
422    block : Assemble an nd-array from nested lists of blocks.
423    split : Split array into a list of multiple sub-arrays of equal size.
424    unstack : Split an array into a tuple of sub-arrays along an axis.
425
426    Examples
427    --------
428    >>> import numpy as np
429    >>> rng = np.random.default_rng()
430    >>> arrays = [rng.normal(size=(3,4)) for _ in range(10)]
431    >>> np.stack(arrays, axis=0).shape
432    (10, 3, 4)
433
434    >>> np.stack(arrays, axis=1).shape
435    (3, 10, 4)
436
437    >>> np.stack(arrays, axis=2).shape
438    (3, 4, 10)
439
440    >>> a = np.array([1, 2, 3])
441    >>> b = np.array([4, 5, 6])
442    >>> np.stack((a, b))
443    array([[1, 2, 3],
444           [4, 5, 6]])
445
446    >>> np.stack((a, b), axis=-1)
447    array([[1, 4],
448           [2, 5],
449           [3, 6]])
450
451    """
452    arrays = [asanyarray(arr) for arr in arrays]
453    if not arrays:
454        raise ValueError('need at least one array to stack')
455
456    shapes = {arr.shape for arr in arrays}
457    if len(shapes) != 1:
458        raise ValueError('all input arrays must have the same shape')
459
460    result_ndim = arrays[0].ndim + 1
461    axis = normalize_axis_index(axis, result_ndim)
462
463    sl = (slice(None),) * axis + (_nx.newaxis,)
464    expanded_arrays = [arr[sl] for arr in arrays]
465    return _nx.concatenate(expanded_arrays, axis=axis, out=out,
466                           dtype=dtype, casting=casting)
467
468def _unstack_dispatcher(x, /, *, axis=None):
469    return (x,)
470
471@array_function_dispatch(_unstack_dispatcher)
472def unstack(x, /, *, axis=0):
473    """
474    Split an array into a sequence of arrays along the given axis.
475
476    The ``axis`` parameter specifies the dimension along which the array will
477    be split. For example, if ``axis=0`` (the default) it will be the first
478    dimension and if ``axis=-1`` it will be the last dimension.
479
480    The result is a tuple of arrays split along ``axis``.
481
482    .. versionadded:: 2.1.0
483
484    Parameters
485    ----------
486    x : ndarray
487        The array to be unstacked.
488    axis : int, optional
489        Axis along which the array will be split. Default: ``0``.
490
491    Returns
492    -------
493    unstacked : tuple of ndarrays
494        The unstacked arrays.
495
496    See Also
497    --------
498    stack : Join a sequence of arrays along a new axis.
499    concatenate : Join a sequence of arrays along an existing axis.
500    block : Assemble an nd-array from nested lists of blocks.
501    split : Split array into a list of multiple sub-arrays of equal size.
502
503    Notes
504    -----
505    ``unstack`` serves as the reverse operation of :py:func:`stack`, i.e.,
506    ``stack(unstack(x, axis=axis), axis=axis) == x``.
507
508    This function is equivalent to ``tuple(np.moveaxis(x, axis, 0))``, since
509    iterating on an array iterates along the first axis.
510
511    Examples
512    --------
513    >>> arr = np.arange(24).reshape((2, 3, 4))
514    >>> np.unstack(arr)
515    (array([[ 0,  1,  2,  3],
516            [ 4,  5,  6,  7],
517            [ 8,  9, 10, 11]]),
518     array([[12, 13, 14, 15],
519            [16, 17, 18, 19],
520            [20, 21, 22, 23]]))
521    >>> np.unstack(arr, axis=1)
522    (array([[ 0,  1,  2,  3],
523            [12, 13, 14, 15]]),
524     array([[ 4,  5,  6,  7],
525            [16, 17, 18, 19]]),
526     array([[ 8,  9, 10, 11],
527            [20, 21, 22, 23]]))
528    >>> arr2 = np.stack(np.unstack(arr, axis=1), axis=1)
529    >>> arr2.shape
530    (2, 3, 4)
531    >>> np.all(arr == arr2)
532    np.True_
533
534    """
535    if x.ndim == 0:
536        raise ValueError("Input array must be at least 1-d.")
537    return tuple(_nx.moveaxis(x, axis, 0))
538
539
540# Internal functions to eliminate the overhead of repeated dispatch in one of
541# the two possible paths inside np.block.
542# Use getattr to protect against __array_function__ being disabled.
543_size = getattr(_from_nx.size, '__wrapped__', _from_nx.size)
544_ndim = getattr(_from_nx.ndim, '__wrapped__', _from_nx.ndim)
545_concatenate = getattr(_from_nx.concatenate,
546                       '__wrapped__', _from_nx.concatenate)
547
548
549def _block_format_index(index):
550    """
551    Convert a list of indices ``[0, 1, 2]`` into ``"arrays[0][1][2]"``.
552    """
553    idx_str = ''.join(f'[{i}]' for i in index if i is not None)
554    return 'arrays' + idx_str
555
556
557def _block_check_depths_match(arrays, parent_index=[]):
558    """
559    Recursive function checking that the depths of nested lists in `arrays`
560    all match. Mismatch raises a ValueError as described in the block
561    docstring below.
562
563    The entire index (rather than just the depth) needs to be calculated
564    for each innermost list, in case an error needs to be raised, so that
565    the index of the offending list can be printed as part of the error.
566
567    Parameters
568    ----------
569    arrays : nested list of arrays
570        The arrays to check
571    parent_index : list of int
572        The full index of `arrays` within the nested lists passed to
573        `_block_check_depths_match` at the top of the recursion.
574
575    Returns
576    -------
577    first_index : list of int
578        The full index of an element from the bottom of the nesting in
579        `arrays`. If any element at the bottom is an empty list, this will
580        refer to it, and the last index along the empty axis will be None.
581    max_arr_ndim : int
582        The maximum of the ndims of the arrays nested in `arrays`.
583    final_size: int
584        The number of elements in the final array. This is used the motivate
585        the choice of algorithm used using benchmarking wisdom.
586
587    """
588    if isinstance(arrays, tuple):
589        # not strictly necessary, but saves us from:
590        #  - more than one way to do things - no point treating tuples like
591        #    lists
592        #  - horribly confusing behaviour that results when tuples are
593        #    treated like ndarray
594        raise TypeError(
595            f'{_block_format_index(parent_index)} is a tuple. '
596            'Only lists can be used to arrange blocks, and np.block does '
597            'not allow implicit conversion from tuple to ndarray.'
598        )
599    elif isinstance(arrays, list) and len(arrays) > 0:
600        idxs_ndims = (_block_check_depths_match(arr, parent_index + [i])
601                      for i, arr in enumerate(arrays))
602
603        first_index, max_arr_ndim, final_size = next(idxs_ndims)
604        for index, ndim, size in idxs_ndims:
605            final_size += size
606            if ndim > max_arr_ndim:
607                max_arr_ndim = ndim
608            if len(index) != len(first_index):
609                raise ValueError(
610                    "List depths are mismatched. First element was at "
611                    f"depth {len(first_index)}, but there is an element at "
612                    f"depth {len(index)} ({_block_format_index(index)})"
613                )
614            # propagate our flag that indicates an empty list at the bottom
615            if index[-1] is None:
616                first_index = index
617
618        return first_index, max_arr_ndim, final_size
619    elif isinstance(arrays, list) and len(arrays) == 0:
620        # We've 'bottomed out' on an empty list
621        return parent_index + [None], 0, 0
622    else:
623        # We've 'bottomed out' - arrays is either a scalar or an array
624        size = _size(arrays)
625        return parent_index, _ndim(arrays), size
626
627
628def _atleast_nd(a, ndim):
629    # Ensures `a` has at least `ndim` dimensions by prepending
630    # ones to `a.shape` as necessary
631    return array(a, ndmin=ndim, copy=None, subok=True)
632
633
634def _accumulate(values):
635    return list(itertools.accumulate(values))
636
637
638def _concatenate_shapes(shapes, axis):
639    """Given array shapes, return the resulting shape and slices prefixes.
640
641    These help in nested concatenation.
642
643    Returns
644    -------
645    shape: tuple of int
646        This tuple satisfies::
647
648            shape, _ = _concatenate_shapes([arr.shape for shape in arrs], axis)
649            shape == concatenate(arrs, axis).shape
650
651    slice_prefixes: tuple of (slice(start, end), )
652        For a list of arrays being concatenated, this returns the slice
653        in the larger array at axis that needs to be sliced into.
654
655        For example, the following holds::
656
657            ret = concatenate([a, b, c], axis)
658            _, (sl_a, sl_b, sl_c) = concatenate_slices([a, b, c], axis)
659
660            ret[(slice(None),) * axis + sl_a] == a
661            ret[(slice(None),) * axis + sl_b] == b
662            ret[(slice(None),) * axis + sl_c] == c
663
664        These are called slice prefixes since they are used in the recursive
665        blocking algorithm to compute the left-most slices during the
666        recursion. Therefore, they must be prepended to rest of the slice
667        that was computed deeper in the recursion.
668
669        These are returned as tuples to ensure that they can quickly be added
670        to existing slice tuple without creating a new tuple every time.
671
672    """
673    # Cache a result that will be reused.
674    shape_at_axis = [shape[axis] for shape in shapes]
675
676    # Take a shape, any shape
677    first_shape = shapes[0]
678    first_shape_pre = first_shape[:axis]
679    first_shape_post = first_shape[axis + 1:]
680
681    if any(shape[:axis] != first_shape_pre or
682           shape[axis + 1:] != first_shape_post for shape in shapes):
683        raise ValueError(
684            f'Mismatched array shapes in block along axis {axis}.')
685
686    shape = (first_shape_pre + (sum(shape_at_axis),) + first_shape[axis + 1:])
687
688    offsets_at_axis = _accumulate(shape_at_axis)
689    slice_prefixes = [(slice(start, end),)
690                      for start, end in zip([0] + offsets_at_axis,
691                                            offsets_at_axis)]
692    return shape, slice_prefixes
693
694
695def _block_info_recursion(arrays, max_depth, result_ndim, depth=0):
696    """
697    Returns the shape of the final array, along with a list
698    of slices and a list of arrays that can be used for assignment inside the
699    new array
700
701    Parameters
702    ----------
703    arrays : nested list of arrays
704        The arrays to check
705    max_depth : list of int
706        The number of nested lists
707    result_ndim : int
708        The number of dimensions in thefinal array.
709
710    Returns
711    -------
712    shape : tuple of int
713        The shape that the final array will take on.
714    slices: list of tuple of slices
715        The slices into the full array required for assignment. These are
716        required to be prepended with ``(Ellipsis, )`` to obtain to correct
717        final index.
718    arrays: list of ndarray
719        The data to assign to each slice of the full array
720
721    """
722    if depth < max_depth:
723        shapes, slices, arrays = zip(
724            *[_block_info_recursion(arr, max_depth, result_ndim, depth + 1)
725              for arr in arrays])
726
727        axis = result_ndim - max_depth + depth
728        shape, slice_prefixes = _concatenate_shapes(shapes, axis)
729
730        # Prepend the slice prefix and flatten the slices
731        slices = [slice_prefix + the_slice
732                  for slice_prefix, inner_slices in zip(slice_prefixes, slices)
733                  for the_slice in inner_slices]
734
735        # Flatten the array list
736        arrays = functools.reduce(operator.add, arrays)
737
738        return shape, slices, arrays
739    else:
740        # We've 'bottomed out' - arrays is either a scalar or an array
741        # type(arrays) is not list
742        # Return the slice and the array inside a list to be consistent with
743        # the recursive case.
744        arr = _atleast_nd(arrays, result_ndim)
745        return arr.shape, [()], [arr]
746
747
748def _block(arrays, max_depth, result_ndim, depth=0):
749    """
750    Internal implementation of block based on repeated concatenation.
751    `arrays` is the argument passed to
752    block. `max_depth` is the depth of nested lists within `arrays` and
753    `result_ndim` is the greatest of the dimensions of the arrays in
754    `arrays` and the depth of the lists in `arrays` (see block docstring
755    for details).
756    """
757    if depth < max_depth:
758        arrs = [_block(arr, max_depth, result_ndim, depth + 1)
759                for arr in arrays]
760        return _concatenate(arrs, axis=-(max_depth - depth))
761    else:
762        # We've 'bottomed out' - arrays is either a scalar or an array
763        # type(arrays) is not list
764        return _atleast_nd(arrays, result_ndim)
765
766
767def _block_dispatcher(arrays):
768    # Use type(...) is list to match the behavior of np.block(), which special
769    # cases list specifically rather than allowing for generic iterables or
770    # tuple. Also, we know that list.__array_function__ will never exist.
771    if isinstance(arrays, list):
772        for subarrays in arrays:
773            yield from _block_dispatcher(subarrays)
774    else:
775        yield arrays
776
777
778@array_function_dispatch(_block_dispatcher)
779def block(arrays):
780    """
781    Assemble an nd-array from nested lists of blocks.
782
783    Blocks in the innermost lists are concatenated (see `concatenate`) along
784    the last dimension (-1), then these are concatenated along the
785    second-last dimension (-2), and so on until the outermost list is reached.
786
787    Blocks can be of any dimension, but will not be broadcasted using
788    the normal rules. Instead, leading axes of size 1 are inserted,
789    to make ``block.ndim`` the same for all blocks. This is primarily useful
790    for working with scalars, and means that code like ``np.block([v, 1])``
791    is valid, where ``v.ndim == 1``.
792
793    When the nested list is two levels deep, this allows block matrices to be
794    constructed from their components.
795
796    Parameters
797    ----------
798    arrays : nested list of array_like or scalars (but not tuples)
799        If passed a single ndarray or scalar (a nested list of depth 0), this
800        is returned unmodified (and not copied).
801
802        Elements shapes must match along the appropriate axes (without
803        broadcasting), but leading 1s will be prepended to the shape as
804        necessary to make the dimensions match.
805
806    Returns
807    -------
808    block_array : ndarray
809        The array assembled from the given blocks.
810
811        The dimensionality of the output is equal to the greatest of:
812
813        * the dimensionality of all the inputs
814        * the depth to which the input list is nested
815
816    Raises
817    ------
818    ValueError
819        * If list depths are mismatched - for instance, ``[[a, b], c]`` is
820          illegal, and should be spelt ``[[a, b], [c]]``
821        * If lists are empty - for instance, ``[[a, b], []]``
822
823    See Also
824    --------
825    concatenate : Join a sequence of arrays along an existing axis.
826    stack : Join a sequence of arrays along a new axis.
827    vstack : Stack arrays in sequence vertically (row wise).
828    hstack : Stack arrays in sequence horizontally (column wise).
829    dstack : Stack arrays in sequence depth wise (along third axis).
830    column_stack : Stack 1-D arrays as columns into a 2-D array.
831    vsplit : Split an array into multiple sub-arrays vertically (row-wise).
832    unstack : Split an array into a tuple of sub-arrays along an axis.
833
834    Notes
835    -----
836    When called with only scalars, ``np.block`` is equivalent to an ndarray
837    call. So ``np.block([[1, 2], [3, 4]])`` is equivalent to
838    ``np.array([[1, 2], [3, 4]])``.
839
840    This function does not enforce that the blocks lie on a fixed grid.
841    ``np.block([[a, b], [c, d]])`` is not restricted to arrays of the form::
842
843        AAAbb
844        AAAbb
845        cccDD
846
847    But is also allowed to produce, for some ``a, b, c, d``::
848
849        AAAbb
850        AAAbb
851        cDDDD
852
853    Since concatenation happens along the last axis first, `block` is *not*
854    capable of producing the following directly::
855
856        AAAbb
857        cccbb
858        cccDD
859
860    Matlab's "square bracket stacking", ``[A, B, ...; p, q, ...]``, is
861    equivalent to ``np.block([[A, B, ...], [p, q, ...]])``.
862
863    Examples
864    --------
865    The most common use of this function is to build a block matrix:
866
867    >>> import numpy as np
868    >>> A = np.eye(2) * 2
869    >>> B = np.eye(3) * 3
870    >>> np.block([
871    ...     [A,               np.zeros((2, 3))],
872    ...     [np.ones((3, 2)), B               ]
873    ... ])
874    array([[2., 0., 0., 0., 0.],
875           [0., 2., 0., 0., 0.],
876           [1., 1., 3., 0., 0.],
877           [1., 1., 0., 3., 0.],
878           [1., 1., 0., 0., 3.]])
879
880    With a list of depth 1, `block` can be used as `hstack`:
881
882    >>> np.block([1, 2, 3])              # hstack([1, 2, 3])
883    array([1, 2, 3])
884
885    >>> a = np.array([1, 2, 3])
886    >>> b = np.array([4, 5, 6])
887    >>> np.block([a, b, 10])             # hstack([a, b, 10])
888    array([ 1,  2,  3,  4,  5,  6, 10])
889
890    >>> A = np.ones((2, 2), int)
891    >>> B = 2 * A
892    >>> np.block([A, B])                 # hstack([A, B])
893    array([[1, 1, 2, 2],
894           [1, 1, 2, 2]])
895
896    With a list of depth 2, `block` can be used in place of `vstack`:
897
898    >>> a = np.array([1, 2, 3])
899    >>> b = np.array([4, 5, 6])
900    >>> np.block([[a], [b]])             # vstack([a, b])
901    array([[1, 2, 3],
902           [4, 5, 6]])
903
904    >>> A = np.ones((2, 2), int)
905    >>> B = 2 * A
906    >>> np.block([[A], [B]])             # vstack([A, B])
907    array([[1, 1],
908           [1, 1],
909           [2, 2],
910           [2, 2]])
911
912    It can also be used in place of `atleast_1d` and `atleast_2d`:
913
914    >>> a = np.array(0)
915    >>> b = np.array([1])
916    >>> np.block([a])                    # atleast_1d(a)
917    array([0])
918    >>> np.block([b])                    # atleast_1d(b)
919    array([1])
920
921    >>> np.block([[a]])                  # atleast_2d(a)
922    array([[0]])
923    >>> np.block([[b]])                  # atleast_2d(b)
924    array([[1]])
925
926
927    """
928    arrays, list_ndim, result_ndim, final_size = _block_setup(arrays)
929
930    # It was found through benchmarking that making an array of final size
931    # around 256x256 was faster by straight concatenation on a
932    # i7-7700HQ processor and dual channel ram 2400MHz.
933    # It didn't seem to matter heavily on the dtype used.
934    #
935    # A 2D array using repeated concatenation requires 2 copies of the array.
936    #
937    # The fastest algorithm will depend on the ratio of CPU power to memory
938    # speed.
939    # One can monitor the results of the benchmark
940    # https://pv.github.io/numpy-bench/#bench_shape_base.Block2D.time_block2d
941    # to tune this parameter until a C version of the `_block_info_recursion`
942    # algorithm is implemented which would likely be faster than the python
943    # version.
944    if list_ndim * final_size > (2 * 512 * 512):
945        return _block_slicing(arrays, list_ndim, result_ndim)
946    else:
947        return _block_concatenate(arrays, list_ndim, result_ndim)
948
949
950# These helper functions are mostly used for testing.
951# They allow us to write tests that directly call `_block_slicing`
952# or `_block_concatenate` without blocking large arrays to force the wisdom
953# to trigger the desired path.
954def _block_setup(arrays):
955    """
956    Returns
957    (`arrays`, list_ndim, result_ndim, final_size)
958    """
959    bottom_index, arr_ndim, final_size = _block_check_depths_match(arrays)
960    list_ndim = len(bottom_index)
961    if bottom_index and bottom_index[-1] is None:
962        raise ValueError(
963            f'List at {_block_format_index(bottom_index)} cannot be empty'
964        )
965    result_ndim = max(arr_ndim, list_ndim)
966    return arrays, list_ndim, result_ndim, final_size
967
968
969def _block_slicing(arrays, list_ndim, result_ndim):
970    shape, slices, arrays = _block_info_recursion(
971        arrays, list_ndim, result_ndim)
972    dtype = _nx.result_type(*[arr.dtype for arr in arrays])
973
974    # Test preferring F only in the case that all input arrays are F
975    F_order = all(arr.flags['F_CONTIGUOUS'] for arr in arrays)
976    C_order = all(arr.flags['C_CONTIGUOUS'] for arr in arrays)
977    order = 'F' if F_order and not C_order else 'C'
978    result = _nx.empty(shape=shape, dtype=dtype, order=order)
979    # Note: In a c implementation, the function
980    # PyArray_CreateMultiSortedStridePerm could be used for more advanced
981    # guessing of the desired order.
982
983    for the_slice, arr in zip(slices, arrays):
984        result[(Ellipsis,) + the_slice] = arr
985    return result
986
987
988def _block_concatenate(arrays, list_ndim, result_ndim):
989    result = _block(arrays, list_ndim, result_ndim)
990    if list_ndim == 0:
991        # Catch an edge case where _block returns a view because
992        # `arrays` is a single numpy array and not a list of numpy arrays.
993        # This might copy scalars or lists twice, but this isn't a likely
994        # usecase for those interested in performance
995        result = result.copy()
996    return result
997 
codekingpro/portable-devtools · Team Ai