1import functools
2import sys
3import math
4import warnings
5
6import numpy as np
7from .._utils import set_module
8import numpy.core.numeric as _nx
9from numpy.core.numeric import ScalarType, array
10from numpy.core.numerictypes import issubdtype
11
12import numpy.matrixlib as matrixlib
13from .function_base import diff
14from numpy.core.multiarray import ravel_multi_index, unravel_index
15from numpy.core import overrides, linspace
16from numpy.lib.stride_tricks import as_strided
17
18
19array_function_dispatch = functools.partial(
20 overrides.array_function_dispatch, module='numpy')
21
22
23__all__ = [
24 'ravel_multi_index', 'unravel_index', 'mgrid', 'ogrid', 'r_', 'c_',
25 's_', 'index_exp', 'ix_', 'ndenumerate', 'ndindex', 'fill_diagonal',
26 'diag_indices', 'diag_indices_from'
27]
28
29
30def _ix__dispatcher(*args):
31 return args
32
33
34@array_function_dispatch(_ix__dispatcher)
35def ix_(*args):
36 """
37 Construct an open mesh from multiple sequences.
38
39 This function takes N 1-D sequences and returns N outputs with N
40 dimensions each, such that the shape is 1 in all but one dimension
41 and the dimension with the non-unit shape value cycles through all
42 N dimensions.
43
44 Using `ix_` one can quickly construct index arrays that will index
45 the cross product. ``a[np.ix_([1,3],[2,5])]`` returns the array
46 ``[[a[1,2] a[1,5]], [a[3,2] a[3,5]]]``.
47
48 Parameters
49 ----------
50 args : 1-D sequences
51 Each sequence should be of integer or boolean type.
52 Boolean sequences will be interpreted as boolean masks for the
53 corresponding dimension (equivalent to passing in
54 ``np.nonzero(boolean_sequence)``).
55
56 Returns
57 -------
58 out : tuple of ndarrays
59 N arrays with N dimensions each, with N the number of input
60 sequences. Together these arrays form an open mesh.
61
62 See Also
63 --------
64 ogrid, mgrid, meshgrid
65
66 Examples
67 --------
68 >>> a = np.arange(10).reshape(2, 5)
69 >>> a
70 array([[0, 1, 2, 3, 4],
71 [5, 6, 7, 8, 9]])
72 >>> ixgrid = np.ix_([0, 1], [2, 4])
73 >>> ixgrid
74 (array([[0],
75 [1]]), array([[2, 4]]))
76 >>> ixgrid[0].shape, ixgrid[1].shape
77 ((2, 1), (1, 2))
78 >>> a[ixgrid]
79 array([[2, 4],
80 [7, 9]])
81
82 >>> ixgrid = np.ix_([True, True], [2, 4])
83 >>> a[ixgrid]
84 array([[2, 4],
85 [7, 9]])
86 >>> ixgrid = np.ix_([True, True], [False, False, True, False, True])
87 >>> a[ixgrid]
88 array([[2, 4],
89 [7, 9]])
90
91 """
92 out = []
93 nd = len(args)
94 for k, new in enumerate(args):
95 if not isinstance(new, _nx.ndarray):
96 new = np.asarray(new)
97 if new.size == 0:
98 # Explicitly type empty arrays to avoid float default
99 new = new.astype(_nx.intp)
100 if new.ndim != 1:
101 raise ValueError("Cross index must be 1 dimensional")
102 if issubdtype(new.dtype, _nx.bool_):
103 new, = new.nonzero()
104 new = new.reshape((1,)*k + (new.size,) + (1,)*(nd-k-1))
105 out.append(new)
106 return tuple(out)
107
108
109class nd_grid:
110 """
111 Construct a multi-dimensional "meshgrid".
112
113 ``grid = nd_grid()`` creates an instance which will return a mesh-grid
114 when indexed. The dimension and number of the output arrays are equal
115 to the number of indexing dimensions. If the step length is not a
116 complex number, then the stop is not inclusive.
117
118 However, if the step length is a **complex number** (e.g. 5j), then the
119 integer part of its magnitude is interpreted as specifying the
120 number of points to create between the start and stop values, where
121 the stop value **is inclusive**.
122
123 If instantiated with an argument of ``sparse=True``, the mesh-grid is
124 open (or not fleshed out) so that only one-dimension of each returned
125 argument is greater than 1.
126
127 Parameters
128 ----------
129 sparse : bool, optional
130 Whether the grid is sparse or not. Default is False.
131
132 Notes
133 -----
134 Two instances of `nd_grid` are made available in the NumPy namespace,
135 `mgrid` and `ogrid`, approximately defined as::
136
137 mgrid = nd_grid(sparse=False)
138 ogrid = nd_grid(sparse=True)
139
140 Users should use these pre-defined instances instead of using `nd_grid`
141 directly.
142 """
143
144 def __init__(self, sparse=False):
145 self.sparse = sparse
146
147 def __getitem__(self, key):
148 try:
149 size = []
150 # Mimic the behavior of `np.arange` and use a data type
151 # which is at least as large as `np.int_`
152 num_list = [0]
153 for k in range(len(key)):
154 step = key[k].step
155 start = key[k].start
156 stop = key[k].stop
157 if start is None:
158 start = 0
159 if step is None:
160 step = 1
161 if isinstance(step, (_nx.complexfloating, complex)):
162 step = abs(step)
163 size.append(int(step))
164 else:
165 size.append(
166 int(math.ceil((stop - start) / (step*1.0))))
167 num_list += [start, stop, step]
168 typ = _nx.result_type(*num_list)
169 if self.sparse:
170 nn = [_nx.arange(_x, dtype=_t)
171 for _x, _t in zip(size, (typ,)*len(size))]
172 else:
173 nn = _nx.indices(size, typ)
174 for k, kk in enumerate(key):
175 step = kk.step
176 start = kk.start
177 if start is None:
178 start = 0
179 if step is None:
180 step = 1
181 if isinstance(step, (_nx.complexfloating, complex)):
182 step = int(abs(step))
183 if step != 1:
184 step = (kk.stop - start) / float(step - 1)
185 nn[k] = (nn[k]*step+start)
186 if self.sparse:
187 slobj = [_nx.newaxis]*len(size)
188 for k in range(len(size)):
189 slobj[k] = slice(None, None)
190 nn[k] = nn[k][tuple(slobj)]
191 slobj[k] = _nx.newaxis
192 return nn
193 except (IndexError, TypeError):
194 step = key.step
195 stop = key.stop
196 start = key.start
197 if start is None:
198 start = 0
199 if isinstance(step, (_nx.complexfloating, complex)):
200 # Prevent the (potential) creation of integer arrays
201 step_float = abs(step)
202 step = length = int(step_float)
203 if step != 1:
204 step = (key.stop-start)/float(step-1)
205 typ = _nx.result_type(start, stop, step_float)
206 return _nx.arange(0, length, 1, dtype=typ)*step + start
207 else:
208 return _nx.arange(start, stop, step)
209
210
211class MGridClass(nd_grid):
212 """
213 An instance which returns a dense multi-dimensional "meshgrid".
214
215 An instance which returns a dense (or fleshed out) mesh-grid
216 when indexed, so that each returned argument has the same shape.
217 The dimensions and number of the output arrays are equal to the
218 number of indexing dimensions. If the step length is not a complex
219 number, then the stop is not inclusive.
220
221 However, if the step length is a **complex number** (e.g. 5j), then
222 the integer part of its magnitude is interpreted as specifying the
223 number of points to create between the start and stop values, where
224 the stop value **is inclusive**.
225
226 Returns
227 -------
228 mesh-grid `ndarrays` all of the same dimensions
229
230 See Also
231 --------
232 ogrid : like `mgrid` but returns open (not fleshed out) mesh grids
233 meshgrid: return coordinate matrices from coordinate vectors
234 r_ : array concatenator
235 :ref:`how-to-partition`
236
237 Examples
238 --------
239 >>> np.mgrid[0:5, 0:5]
240 array([[[0, 0, 0, 0, 0],
241 [1, 1, 1, 1, 1],
242 [2, 2, 2, 2, 2],
243 [3, 3, 3, 3, 3],
244 [4, 4, 4, 4, 4]],
245 [[0, 1, 2, 3, 4],
246 [0, 1, 2, 3, 4],
247 [0, 1, 2, 3, 4],
248 [0, 1, 2, 3, 4],
249 [0, 1, 2, 3, 4]]])
250 >>> np.mgrid[-1:1:5j]
251 array([-1. , -0.5, 0. , 0.5, 1. ])
252
253 """
254
255 def __init__(self):
256 super().__init__(sparse=False)
257
258
259mgrid = MGridClass()
260
261
262class OGridClass(nd_grid):
263 """
264 An instance which returns an open multi-dimensional "meshgrid".
265
266 An instance which returns an open (i.e. not fleshed out) mesh-grid
267 when indexed, so that only one dimension of each returned array is
268 greater than 1. The dimension and number of the output arrays are
269 equal to the number of indexing dimensions. If the step length is
270 not a complex number, then the stop is not inclusive.
271
272 However, if the step length is a **complex number** (e.g. 5j), then
273 the integer part of its magnitude is interpreted as specifying the
274 number of points to create between the start and stop values, where
275 the stop value **is inclusive**.
276
277 Returns
278 -------
279 mesh-grid
280 `ndarrays` with only one dimension not equal to 1
281
282 See Also
283 --------
284 mgrid : like `ogrid` but returns dense (or fleshed out) mesh grids
285 meshgrid: return coordinate matrices from coordinate vectors
286 r_ : array concatenator
287 :ref:`how-to-partition`
288
289 Examples
290 --------
291 >>> from numpy import ogrid
292 >>> ogrid[-1:1:5j]
293 array([-1. , -0.5, 0. , 0.5, 1. ])
294 >>> ogrid[0:5,0:5]
295 [array([[0],
296 [1],
297 [2],
298 [3],
299 [4]]), array([[0, 1, 2, 3, 4]])]
300
301 """
302
303 def __init__(self):
304 super().__init__(sparse=True)
305
306
307ogrid = OGridClass()
308
309
310class AxisConcatenator:
311 """
312 Translates slice objects to concatenation along an axis.
313
314 For detailed documentation on usage, see `r_`.
315 """
316 # allow ma.mr_ to override this
317 concatenate = staticmethod(_nx.concatenate)
318 makemat = staticmethod(matrixlib.matrix)
319
320 def __init__(self, axis=0, matrix=False, ndmin=1, trans1d=-1):
321 self.axis = axis
322 self.matrix = matrix
323 self.trans1d = trans1d
324 self.ndmin = ndmin
325
326 def __getitem__(self, key):
327 # handle matrix builder syntax
328 if isinstance(key, str):
329 frame = sys._getframe().f_back
330 mymat = matrixlib.bmat(key, frame.f_globals, frame.f_locals)
331 return mymat
332
333 if not isinstance(key, tuple):
334 key = (key,)
335
336 # copy attributes, since they can be overridden in the first argument
337 trans1d = self.trans1d
338 ndmin = self.ndmin
339 matrix = self.matrix
340 axis = self.axis
341
342 objs = []
343 # dtypes or scalars for weak scalar handling in result_type
344 result_type_objs = []
345
346 for k, item in enumerate(key):
347 scalar = False
348 if isinstance(item, slice):
349 step = item.step
350 start = item.start
351 stop = item.stop
352 if start is None:
353 start = 0
354 if step is None:
355 step = 1
356 if isinstance(step, (_nx.complexfloating, complex)):
357 size = int(abs(step))
358 newobj = linspace(start, stop, num=size)
359 else:
360 newobj = _nx.arange(start, stop, step)
361 if ndmin > 1:
362 newobj = array(newobj, copy=False, ndmin=ndmin)
363 if trans1d != -1:
364 newobj = newobj.swapaxes(-1, trans1d)
365 elif isinstance(item, str):
366 if k != 0:
367 raise ValueError("special directives must be the "
368 "first entry.")
369 if item in ('r', 'c'):
370 matrix = True
371 col = (item == 'c')
372 continue
373 if ',' in item:
374 vec = item.split(',')
375 try:
376 axis, ndmin = [int(x) for x in vec[:2]]
377 if len(vec) == 3:
378 trans1d = int(vec[2])
379 continue
380 except Exception as e:
381 raise ValueError(
382 "unknown special directive {!r}".format(item)
383 ) from e
384 try:
385 axis = int(item)
386 continue
387 except (ValueError, TypeError) as e:
388 raise ValueError("unknown special directive") from e
389 elif type(item) in ScalarType:
390 scalar = True
391 newobj = item
392 else:
393 item_ndim = np.ndim(item)
394 newobj = array(item, copy=False, subok=True, ndmin=ndmin)
395 if trans1d != -1 and item_ndim < ndmin:
396 k2 = ndmin - item_ndim
397 k1 = trans1d
398 if k1 < 0:
399 k1 += k2 + 1
400 defaxes = list(range(ndmin))
401 axes = defaxes[:k1] + defaxes[k2:] + defaxes[k1:k2]
402 newobj = newobj.transpose(axes)
403
404 objs.append(newobj)
405 if scalar:
406 result_type_objs.append(item)
407 else:
408 result_type_objs.append(newobj.dtype)
409
410 # Ensure that scalars won't up-cast unless warranted, for 0, drops
411 # through to error in concatenate.
412 if len(result_type_objs) != 0:
413 final_dtype = _nx.result_type(*result_type_objs)
414 # concatenate could do cast, but that can be overriden:
415 objs = [array(obj, copy=False, subok=True,
416 ndmin=ndmin, dtype=final_dtype) for obj in objs]
417
418 res = self.concatenate(tuple(objs), axis=axis)
419
420 if matrix:
421 oldndim = res.ndim
422 res = self.makemat(res)
423 if oldndim == 1 and col:
424 res = res.T
425 return res
426
427 def __len__(self):
428 return 0
429
430# separate classes are used here instead of just making r_ = concatentor(0),
431# etc. because otherwise we couldn't get the doc string to come out right
432# in help(r_)
433
434
435class RClass(AxisConcatenator):
436 """
437 Translates slice objects to concatenation along the first axis.
438
439 This is a simple way to build up arrays quickly. There are two use cases.
440
441 1. If the index expression contains comma separated arrays, then stack
442 them along their first axis.
443 2. If the index expression contains slice notation or scalars then create
444 a 1-D array with a range indicated by the slice notation.
445
446 If slice notation is used, the syntax ``start:stop:step`` is equivalent
447 to ``np.arange(start, stop, step)`` inside of the brackets. However, if
448 ``step`` is an imaginary number (i.e. 100j) then its integer portion is
449 interpreted as a number-of-points desired and the start and stop are
450 inclusive. In other words ``start:stop:stepj`` is interpreted as
451 ``np.linspace(start, stop, step, endpoint=1)`` inside of the brackets.
452 After expansion of slice notation, all comma separated sequences are
453 concatenated together.
454
455 Optional character strings placed as the first element of the index
456 expression can be used to change the output. The strings 'r' or 'c' result
457 in matrix output. If the result is 1-D and 'r' is specified a 1 x N (row)
458 matrix is produced. If the result is 1-D and 'c' is specified, then a N x 1
459 (column) matrix is produced. If the result is 2-D then both provide the
460 same matrix result.
461
462 A string integer specifies which axis to stack multiple comma separated
463 arrays along. A string of two comma-separated integers allows indication
464 of the minimum number of dimensions to force each entry into as the
465 second integer (the axis to concatenate along is still the first integer).
466
467 A string with three comma-separated integers allows specification of the
468 axis to concatenate along, the minimum number of dimensions to force the
469 entries to, and which axis should contain the start of the arrays which
470 are less than the specified number of dimensions. In other words the third
471 integer allows you to specify where the 1's should be placed in the shape
472 of the arrays that have their shapes upgraded. By default, they are placed
473 in the front of the shape tuple. The third argument allows you to specify
474 where the start of the array should be instead. Thus, a third argument of
475 '0' would place the 1's at the end of the array shape. Negative integers
476 specify where in the new shape tuple the last dimension of upgraded arrays
477 should be placed, so the default is '-1'.
478
479 Parameters
480 ----------
481 Not a function, so takes no parameters
482
483
484 Returns
485 -------
486 A concatenated ndarray or matrix.
487
488 See Also
489 --------
490 concatenate : Join a sequence of arrays along an existing axis.
491 c_ : Translates slice objects to concatenation along the second axis.
492
493 Examples
494 --------
495 >>> np.r_[np.array([1,2,3]), 0, 0, np.array([4,5,6])]
496 array([1, 2, 3, ..., 4, 5, 6])
497 >>> np.r_[-1:1:6j, [0]*3, 5, 6]
498 array([-1. , -0.6, -0.2, 0.2, 0.6, 1. , 0. , 0. , 0. , 5. , 6. ])
499
500 String integers specify the axis to concatenate along or the minimum
501 number of dimensions to force entries into.
502
503 >>> a = np.array([[0, 1, 2], [3, 4, 5]])
504 >>> np.r_['-1', a, a] # concatenate along last axis
505 array([[0, 1, 2, 0, 1, 2],
506 [3, 4, 5, 3, 4, 5]])
507 >>> np.r_['0,2', [1,2,3], [4,5,6]] # concatenate along first axis, dim>=2
508 array([[1, 2, 3],
509 [4, 5, 6]])
510
511 >>> np.r_['0,2,0', [1,2,3], [4,5,6]]
512 array([[1],
513 [2],
514 [3],
515 [4],
516 [5],
517 [6]])
518 >>> np.r_['1,2,0', [1,2,3], [4,5,6]]
519 array([[1, 4],
520 [2, 5],
521 [3, 6]])
522
523 Using 'r' or 'c' as a first string argument creates a matrix.
524
525 >>> np.r_['r',[1,2,3], [4,5,6]]
526 matrix([[1, 2, 3, 4, 5, 6]])
527
528 """
529
530 def __init__(self):
531 AxisConcatenator.__init__(self, 0)
532
533
534r_ = RClass()
535
536
537class CClass(AxisConcatenator):
538 """
539 Translates slice objects to concatenation along the second axis.
540
541 This is short-hand for ``np.r_['-1,2,0', index expression]``, which is
542 useful because of its common occurrence. In particular, arrays will be
543 stacked along their last axis after being upgraded to at least 2-D with
544 1's post-pended to the shape (column vectors made out of 1-D arrays).
545
546 See Also
547 --------
548 column_stack : Stack 1-D arrays as columns into a 2-D array.
549 r_ : For more detailed documentation.
550
551 Examples
552 --------
553 >>> np.c_[np.array([1,2,3]), np.array([4,5,6])]
554 array([[1, 4],
555 [2, 5],
556 [3, 6]])
557 >>> np.c_[np.array([[1,2,3]]), 0, 0, np.array([[4,5,6]])]
558 array([[1, 2, 3, ..., 4, 5, 6]])
559
560 """
561
562 def __init__(self):
563 AxisConcatenator.__init__(self, -1, ndmin=2, trans1d=0)
564
565
566c_ = CClass()
567
568
569@set_module('numpy')
570class ndenumerate:
571 """
572 Multidimensional index iterator.
573
574 Return an iterator yielding pairs of array coordinates and values.
575
576 Parameters
577 ----------
578 arr : ndarray
579 Input array.
580
581 See Also
582 --------
583 ndindex, flatiter
584
585 Examples
586 --------
587 >>> a = np.array([[1, 2], [3, 4]])
588 >>> for index, x in np.ndenumerate(a):
589 ... print(index, x)
590 (0, 0) 1
591 (0, 1) 2
592 (1, 0) 3
593 (1, 1) 4
594
595 """
596
597 def __init__(self, arr):
598 self.iter = np.asarray(arr).flat
599
600 def __next__(self):
601 """
602 Standard iterator method, returns the index tuple and array value.
603
604 Returns
605 -------
606 coords : tuple of ints
607 The indices of the current iteration.
608 val : scalar
609 The array element of the current iteration.
610
611 """
612 return self.iter.coords, next(self.iter)
613
614 def __iter__(self):
615 return self
616
617
618@set_module('numpy')
619class ndindex:
620 """
621 An N-dimensional iterator object to index arrays.
622
623 Given the shape of an array, an `ndindex` instance iterates over
624 the N-dimensional index of the array. At each iteration a tuple
625 of indices is returned, the last dimension is iterated over first.
626
627 Parameters
628 ----------
629 shape : ints, or a single tuple of ints
630 The size of each dimension of the array can be passed as
631 individual parameters or as the elements of a tuple.
632
633 See Also
634 --------
635 ndenumerate, flatiter
636
637 Examples
638 --------
639 Dimensions as individual arguments
640
641 >>> for index in np.ndindex(3, 2, 1):
642 ... print(index)
643 (0, 0, 0)
644 (0, 1, 0)
645 (1, 0, 0)
646 (1, 1, 0)
647 (2, 0, 0)
648 (2, 1, 0)
649
650 Same dimensions - but in a tuple ``(3, 2, 1)``
651
652 >>> for index in np.ndindex((3, 2, 1)):
653 ... print(index)
654 (0, 0, 0)
655 (0, 1, 0)
656 (1, 0, 0)
657 (1, 1, 0)
658 (2, 0, 0)
659 (2, 1, 0)
660
661 """
662
663 def __init__(self, *shape):
664 if len(shape) == 1 and isinstance(shape[0], tuple):
665 shape = shape[0]
666 x = as_strided(_nx.zeros(1), shape=shape,
667 strides=_nx.zeros_like(shape))
668 self._it = _nx.nditer(x, flags=['multi_index', 'zerosize_ok'],
669 order='C')
670
671 def __iter__(self):
672 return self
673
674 def ndincr(self):
675 """
676 Increment the multi-dimensional index by one.
677
678 This method is for backward compatibility only: do not use.
679
680 .. deprecated:: 1.20.0
681 This method has been advised against since numpy 1.8.0, but only
682 started emitting DeprecationWarning as of this version.
683 """
684 # NumPy 1.20.0, 2020-09-08
685 warnings.warn(
686 "`ndindex.ndincr()` is deprecated, use `next(ndindex)` instead",
687 DeprecationWarning, stacklevel=2)
688 next(self)
689
690 def __next__(self):
691 """
692 Standard iterator method, updates the index and returns the index
693 tuple.
694
695 Returns
696 -------
697 val : tuple of ints
698 Returns a tuple containing the indices of the current
699 iteration.
700
701 """
702 next(self._it)
703 return self._it.multi_index
704
705
706# You can do all this with slice() plus a few special objects,
707# but there's a lot to remember. This version is simpler because
708# it uses the standard array indexing syntax.
709#
710# Written by Konrad Hinsen <hinsen@cnrs-orleans.fr>
711# last revision: 1999-7-23
712#
713# Cosmetic changes by T. Oliphant 2001
714#
715#
716
717class IndexExpression:
718 """
719 A nicer way to build up index tuples for arrays.
720
721 .. note::
722 Use one of the two predefined instances `index_exp` or `s_`
723 rather than directly using `IndexExpression`.
724
725 For any index combination, including slicing and axis insertion,
726 ``a[indices]`` is the same as ``a[np.index_exp[indices]]`` for any
727 array `a`. However, ``np.index_exp[indices]`` can be used anywhere
728 in Python code and returns a tuple of slice objects that can be
729 used in the construction of complex index expressions.
730
731 Parameters
732 ----------
733 maketuple : bool
734 If True, always returns a tuple.
735
736 See Also
737 --------
738 index_exp : Predefined instance that always returns a tuple:
739 `index_exp = IndexExpression(maketuple=True)`.
740 s_ : Predefined instance without tuple conversion:
741 `s_ = IndexExpression(maketuple=False)`.
742
743 Notes
744 -----
745 You can do all this with `slice()` plus a few special objects,
746 but there's a lot to remember and this version is simpler because
747 it uses the standard array indexing syntax.
748
749 Examples
750 --------
751 >>> np.s_[2::2]
752 slice(2, None, 2)
753 >>> np.index_exp[2::2]
754 (slice(2, None, 2),)
755
756 >>> np.array([0, 1, 2, 3, 4])[np.s_[2::2]]
757 array([2, 4])
758
759 """
760
761 def __init__(self, maketuple):
762 self.maketuple = maketuple
763
764 def __getitem__(self, item):
765 if self.maketuple and not isinstance(item, tuple):
766 return (item,)
767 else:
768 return item
769
770
771index_exp = IndexExpression(maketuple=True)
772s_ = IndexExpression(maketuple=False)
773
774# End contribution from Konrad.
775
776
777# The following functions complement those in twodim_base, but are
778# applicable to N-dimensions.
779
780
781def _fill_diagonal_dispatcher(a, val, wrap=None):
782 return (a,)
783
784
785@array_function_dispatch(_fill_diagonal_dispatcher)
786def fill_diagonal(a, val, wrap=False):
787 """Fill the main diagonal of the given array of any dimensionality.
788
789 For an array `a` with ``a.ndim >= 2``, the diagonal is the list of
790 locations with indices ``a[i, ..., i]`` all identical. This function
791 modifies the input array in-place, it does not return a value.
792
793 Parameters
794 ----------
795 a : array, at least 2-D.
796 Array whose diagonal is to be filled, it gets modified in-place.
797
798 val : scalar or array_like
799 Value(s) to write on the diagonal. If `val` is scalar, the value is
800 written along the diagonal. If array-like, the flattened `val` is
801 written along the diagonal, repeating if necessary to fill all
802 diagonal entries.
803
804 wrap : bool
805 For tall matrices in NumPy version up to 1.6.2, the
806 diagonal "wrapped" after N columns. You can have this behavior
807 with this option. This affects only tall matrices.
808
809 See also
810 --------
811 diag_indices, diag_indices_from
812
813 Notes
814 -----
815 .. versionadded:: 1.4.0
816
817 This functionality can be obtained via `diag_indices`, but internally
818 this version uses a much faster implementation that never constructs the
819 indices and uses simple slicing.
820
821 Examples
822 --------
823 >>> a = np.zeros((3, 3), int)
824 >>> np.fill_diagonal(a, 5)
825 >>> a
826 array([[5, 0, 0],
827 [0, 5, 0],
828 [0, 0, 5]])
829
830 The same function can operate on a 4-D array:
831
832 >>> a = np.zeros((3, 3, 3, 3), int)
833 >>> np.fill_diagonal(a, 4)
834
835 We only show a few blocks for clarity:
836
837 >>> a[0, 0]
838 array([[4, 0, 0],
839 [0, 0, 0],
840 [0, 0, 0]])
841 >>> a[1, 1]
842 array([[0, 0, 0],
843 [0, 4, 0],
844 [0, 0, 0]])
845 >>> a[2, 2]
846 array([[0, 0, 0],
847 [0, 0, 0],
848 [0, 0, 4]])
849
850 The wrap option affects only tall matrices:
851
852 >>> # tall matrices no wrap
853 >>> a = np.zeros((5, 3), int)
854 >>> np.fill_diagonal(a, 4)
855 >>> a
856 array([[4, 0, 0],
857 [0, 4, 0],
858 [0, 0, 4],
859 [0, 0, 0],
860 [0, 0, 0]])
861
862 >>> # tall matrices wrap
863 >>> a = np.zeros((5, 3), int)
864 >>> np.fill_diagonal(a, 4, wrap=True)
865 >>> a
866 array([[4, 0, 0],
867 [0, 4, 0],
868 [0, 0, 4],
869 [0, 0, 0],
870 [4, 0, 0]])
871
872 >>> # wide matrices
873 >>> a = np.zeros((3, 5), int)
874 >>> np.fill_diagonal(a, 4, wrap=True)
875 >>> a
876 array([[4, 0, 0, 0, 0],
877 [0, 4, 0, 0, 0],
878 [0, 0, 4, 0, 0]])
879
880 The anti-diagonal can be filled by reversing the order of elements
881 using either `numpy.flipud` or `numpy.fliplr`.
882
883 >>> a = np.zeros((3, 3), int);
884 >>> np.fill_diagonal(np.fliplr(a), [1,2,3]) # Horizontal flip
885 >>> a
886 array([[0, 0, 1],
887 [0, 2, 0],
888 [3, 0, 0]])
889 >>> np.fill_diagonal(np.flipud(a), [1,2,3]) # Vertical flip
890 >>> a
891 array([[0, 0, 3],
892 [0, 2, 0],
893 [1, 0, 0]])
894
895 Note that the order in which the diagonal is filled varies depending
896 on the flip function.
897 """
898 if a.ndim < 2:
899 raise ValueError("array must be at least 2-d")
900 end = None
901 if a.ndim == 2:
902 # Explicit, fast formula for the common case. For 2-d arrays, we
903 # accept rectangular ones.
904 step = a.shape[1] + 1
905 # This is needed to don't have tall matrix have the diagonal wrap.
906 if not wrap:
907 end = a.shape[1] * a.shape[1]
908 else:
909 # For more than d=2, the strided formula is only valid for arrays with
910 # all dimensions equal, so we check first.
911 if not np.all(diff(a.shape) == 0):
912 raise ValueError("All dimensions of input must be of equal length")
913 step = 1 + (np.cumprod(a.shape[:-1])).sum()
914
915 # Write the value out into the diagonal.
916 a.flat[:end:step] = val
917
918
919@set_module('numpy')
920def diag_indices(n, ndim=2):
921 """
922 Return the indices to access the main diagonal of an array.
923
924 This returns a tuple of indices that can be used to access the main
925 diagonal of an array `a` with ``a.ndim >= 2`` dimensions and shape
926 (n, n, ..., n). For ``a.ndim = 2`` this is the usual diagonal, for
927 ``a.ndim > 2`` this is the set of indices to access ``a[i, i, ..., i]``
928 for ``i = [0..n-1]``.
929
930 Parameters
931 ----------
932 n : int
933 The size, along each dimension, of the arrays for which the returned
934 indices can be used.
935
936 ndim : int, optional
937 The number of dimensions.
938
939 See Also
940 --------
941 diag_indices_from
942
943 Notes
944 -----
945 .. versionadded:: 1.4.0
946
947 Examples
948 --------
949 Create a set of indices to access the diagonal of a (4, 4) array:
950
951 >>> di = np.diag_indices(4)
952 >>> di
953 (array([0, 1, 2, 3]), array([0, 1, 2, 3]))
954 >>> a = np.arange(16).reshape(4, 4)
955 >>> a
956 array([[ 0, 1, 2, 3],
957 [ 4, 5, 6, 7],
958 [ 8, 9, 10, 11],
959 [12, 13, 14, 15]])
960 >>> a[di] = 100
961 >>> a
962 array([[100, 1, 2, 3],
963 [ 4, 100, 6, 7],
964 [ 8, 9, 100, 11],
965 [ 12, 13, 14, 100]])
966
967 Now, we create indices to manipulate a 3-D array:
968
969 >>> d3 = np.diag_indices(2, 3)
970 >>> d3
971 (array([0, 1]), array([0, 1]), array([0, 1]))
972
973 And use it to set the diagonal of an array of zeros to 1:
974
975 >>> a = np.zeros((2, 2, 2), dtype=int)
976 >>> a[d3] = 1
977 >>> a
978 array([[[1, 0],
979 [0, 0]],
980 [[0, 0],
981 [0, 1]]])
982
983 """
984 idx = np.arange(n)
985 return (idx,) * ndim
986
987
988def _diag_indices_from(arr):
989 return (arr,)
990
991
992@array_function_dispatch(_diag_indices_from)
993def diag_indices_from(arr):
994 """
995 Return the indices to access the main diagonal of an n-dimensional array.
996
997 See `diag_indices` for full details.
998
999 Parameters
1000 ----------
1001 arr : array, at least 2-D
1002
1003 See Also
1004 --------
1005 diag_indices
1006
1007 Notes
1008 -----
1009 .. versionadded:: 1.4.0
1010
1011 Examples
1012 --------
1013
1014 Create a 4 by 4 array.
1015
1016 >>> a = np.arange(16).reshape(4, 4)
1017 >>> a
1018 array([[ 0, 1, 2, 3],
1019 [ 4, 5, 6, 7],
1020 [ 8, 9, 10, 11],
1021 [12, 13, 14, 15]])
1022
1023 Get the indices of the diagonal elements.
1024
1025 >>> di = np.diag_indices_from(a)
1026 >>> di
1027 (array([0, 1, 2, 3]), array([0, 1, 2, 3]))
1028
1029 >>> a[di]
1030 array([ 0, 5, 10, 15])
1031
1032 This is simply syntactic sugar for diag_indices.
1033
1034 >>> np.diag_indices(a.shape[0])
1035 (array([0, 1, 2, 3]), array([0, 1, 2, 3]))
1036
1037 """
1038
1039 if not arr.ndim >= 2:
1040 raise ValueError("input array must be at least 2-d")
1041 # For more than d=2, the strided formula is only valid for arrays with
1042 # all dimensions equal, so we check first.
1043 if not np.all(diff(arr.shape) == 0):
1044 raise ValueError("All dimensions of input must be of equal length")
1045
1046 return diag_indices(arr.shape[0], arr.ndim)