4
4
import numpy as np
5
5
from numpy .lib .stride_tricks import as_strided
6
6
7
+
7
8
__all__ = ['toeplitz' , 'circulant' , 'hankel' ,
8
9
'hadamard' , 'leslie' , 'kron' , 'block_diag' , 'companion' ,
9
10
'helmert' , 'hilbert' , 'invhilbert' , 'pascal' , 'invpascal' , 'dft' ,
16
17
17
18
18
19
def toeplitz (c , r = None ):
19
- """
20
+ r """
20
21
Construct a Toeplitz matrix.
21
22
22
23
The Toeplitz matrix has constant diagonals, with c as its first column
@@ -35,6 +36,12 @@ def toeplitz(c, r=None):
35
36
``[c[0], r[1:]]``. Whatever the actual shape of `r`, it will be
36
37
converted to a 1-D array.
37
38
39
+ .. warning::
40
+
41
+ Beginning in SciPy 1.17, multidimensional input will be treated as a batch,
42
+ not ``ravel``\ ed. To preserve the existing behavior, ``ravel`` aruments
43
+ before passing them to `toeplitz`.
44
+
38
45
Returns
39
46
-------
40
47
A : (len(c), len(r)) ndarray
@@ -65,11 +72,19 @@ def toeplitz(c, r=None):
65
72
[ 4.-1.j, 2.+3.j, 1.+0.j]])
66
73
67
74
"""
68
- c = np .asarray (c ). ravel ()
75
+ c = np .asarray (c )
69
76
if r is None :
70
77
r = c .conjugate ()
71
78
else :
72
- r = np .asarray (r ).ravel ()
79
+ r = np .asarray (r )
80
+
81
+ if c .ndim > 1 or r .ndim > 1 :
82
+ msg = ("Beginning in SciPy 1.17, multidimensional input will be treated as a "
83
+ "batch, not `ravel`ed. To preserve the existing behavior and silence "
84
+ "this warning, `ravel` aruments before passing them to `toeplitz`." )
85
+ warnings .warn (msg , FutureWarning , stacklevel = 2 )
86
+
87
+ c , r = c .ravel (), r .ravel ()
73
88
# Form a 1-D array containing a reversed c followed by r[1:] that could be
74
89
# strided to give us toeplitz matrix.
75
90
vals = np .concatenate ((c [::- 1 ], r [1 :]))
@@ -246,18 +261,19 @@ def leslie(f, s):
246
261
247
262
Parameters
248
263
----------
249
- f : (N,) array_like
264
+ f : (..., N,) array_like
250
265
The "fecundity" coefficients.
251
- s : (N-1,) array_like
252
- The "survival" coefficients, has to be 1-D. The length of `s`
253
- must be one less than the length of `f`, and it must be at least 1.
266
+ s : (..., N-1,) array_like
267
+ The "survival" coefficients. The length of each slice of `s` (along the last
268
+ axis) must be one less than the length of `f`, and it must be at least 1.
254
269
255
270
Returns
256
271
-------
257
- L : (N, N) ndarray
272
+ L : (..., N, N) ndarray
258
273
The array is zero except for the first row,
259
274
which is `f`, and the first sub-diagonal, which is `s`.
260
- The data-type of the array will be the data-type of ``f[0]+s[0]``.
275
+ For 1-D input, the data-type of the array will be the data-type of
276
+ ``f[0]+s[0]``.
261
277
262
278
Notes
263
279
-----
@@ -270,6 +286,11 @@ def leslie(f, s):
270
286
class, and the `n` - 1 "survival coefficients", which give the
271
287
per-capita survival rate of each age class.
272
288
289
+ N-dimensional input are treated as a batches of coefficient arrays: each
290
+ slice along the last axis of the input arrays is a 1-D coefficient array,
291
+ and each slice along the last two dimensions of the output is the
292
+ corresponding Leslie matrix.
293
+
273
294
References
274
295
----------
275
296
.. [1] P. H. Leslie, On the use of matrices in certain population
@@ -290,18 +311,21 @@ def leslie(f, s):
290
311
"""
291
312
f = np .atleast_1d (f )
292
313
s = np .atleast_1d (s )
293
- if f .ndim != 1 :
294
- raise ValueError ("Incorrect shape for f. f must be 1D" )
295
- if s .ndim != 1 :
296
- raise ValueError ("Incorrect shape for s. s must be 1D" )
297
- if f .size != s .size + 1 :
298
- raise ValueError ("Incorrect lengths for f and s. The length"
299
- " of s must be one less than the length of f." )
300
- if s .size == 0 :
314
+
315
+ if f .shape [- 1 ] != s .shape [- 1 ] + 1 :
316
+ raise ValueError ("Incorrect lengths for f and s. The length of s along "
317
+ "the last axis must be one less than the length of f." )
318
+ if s .shape [- 1 ] == 0 :
301
319
raise ValueError ("The length of s must be at least 1." )
302
320
321
+ n = f .shape [- 1 ]
322
+
323
+ if f .ndim > 1 or s .ndim > 1 :
324
+ from scipy .stats ._resampling import _vectorize_statistic
325
+ _leslie_nd = _vectorize_statistic (leslie )
326
+ return np .moveaxis (_leslie_nd (f , s , axis = - 1 ), [0 , 1 ], [- 2 , - 1 ])
327
+
303
328
tmp = f [0 ] + s [0 ]
304
- n = f .size
305
329
a = np .zeros ((n , n ), dtype = tmp .dtype )
306
330
a [0 ] = f
307
331
a [list (range (1 , n )), list (range (0 , n - 1 ))] = s
@@ -948,12 +972,16 @@ def fiedler(a):
948
972
949
973
Parameters
950
974
----------
951
- a : (n,) array_like
952
- coefficient array
975
+ a : (..., n,) array_like
976
+ Coefficient array. N-dimensional arrays are treated as a batch:
977
+ each slice along the last axis is a 1-D coefficient array.
953
978
954
979
Returns
955
980
-------
956
- F : (n, n) ndarray
981
+ F : (..., n, n) ndarray
982
+ Fiedler matrix. For batch input, each slice of shape ``(n, n)``
983
+ along the last two dimensions of the output corresponds with a
984
+ slice of shape ``(n,)`` along the last dimension of the input.
957
985
958
986
See Also
959
987
--------
@@ -1003,8 +1031,8 @@ def fiedler(a):
1003
1031
"""
1004
1032
a = np .atleast_1d (a )
1005
1033
1006
- if a .ndim != 1 :
1007
- raise ValueError ( "Input 'a' must be a 1D array." )
1034
+ if a .ndim > 1 :
1035
+ return np . apply_along_axis ( fiedler , - 1 , a )
1008
1036
1009
1037
if a .size == 0 :
1010
1038
return np .array ([], dtype = float )
@@ -1023,23 +1051,28 @@ def fiedler_companion(a):
1023
1051
1024
1052
Parameters
1025
1053
----------
1026
- a : (N, ) array_like
1054
+ a : (..., N ) array_like
1027
1055
1-D array of polynomial coefficients in descending order with a nonzero
1028
1056
leading coefficient. For ``N < 2``, an empty array is returned.
1057
+ N-dimensional arrays are treated as a batch: each slice along the last
1058
+ axis is a 1-D array of polynomial coefficients.
1029
1059
1030
1060
Returns
1031
1061
-------
1032
- c : (N-1, N-1) ndarray
1033
- Resulting companion matrix
1062
+ c : (..., N-1, N-1) ndarray
1063
+ Resulting companion matrix. For batch input, each slice of shape
1064
+ ``(N-1, N-1)`` along the last two dimensions of the output corresponds
1065
+ with a slice of shape ``(N,)`` along the last dimension of the input.
1034
1066
1035
1067
See Also
1036
1068
--------
1037
1069
companion
1038
1070
1039
1071
Notes
1040
1072
-----
1041
- Similar to `companion` the leading coefficient should be nonzero. In the case
1042
- the leading coefficient is not 1, other coefficients are rescaled before
1073
+ Similar to `companion`, each leading coefficient along the last axis of the
1074
+ input should be nonzero.
1075
+ If the leading coefficient is not 1, other coefficients are rescaled before
1043
1076
the array generation. To avoid numerical issues, it is best to provide a
1044
1077
monic polynomial.
1045
1078
@@ -1067,8 +1100,8 @@ def fiedler_companion(a):
1067
1100
"""
1068
1101
a = np .atleast_1d (a )
1069
1102
1070
- if a .ndim != 1 :
1071
- raise ValueError ( "Input 'a' must be a 1-D array." )
1103
+ if a .ndim > 1 :
1104
+ return np . apply_along_axis ( fiedler_companion , - 1 , a )
1072
1105
1073
1106
if a .size <= 2 :
1074
1107
if a .size == 2 :
@@ -1101,8 +1134,9 @@ def convolution_matrix(a, n, mode='full'):
1101
1134
1102
1135
Parameters
1103
1136
----------
1104
- a : (m,) array_like
1105
- The 1-D array to convolve.
1137
+ a : (..., m) array_like
1138
+ The 1-D array to convolve. N-dimensional arrays are treated as a
1139
+ batch: each slice along the last axis is a 1-D array to convolve.
1106
1140
n : int
1107
1141
The number of columns in the resulting matrix. It gives the length
1108
1142
of the input to be convolved with `a`. This is analogous to the
@@ -1114,7 +1148,7 @@ def convolution_matrix(a, n, mode='full'):
1114
1148
1115
1149
Returns
1116
1150
-------
1117
- A : (k, n) ndarray
1151
+ A : (..., k, n) ndarray
1118
1152
The convolution matrix whose row count `k` depends on `mode`::
1119
1153
1120
1154
======= =========================
@@ -1125,6 +1159,10 @@ def convolution_matrix(a, n, mode='full'):
1125
1159
'valid' max(m, n) - min(m, n) + 1
1126
1160
======= =========================
1127
1161
1162
+ For batch input, each slice of shape ``(k, n)`` along the last two
1163
+ dimensions of the output corresponds with a slice of shape ``(m,)``
1164
+ along the last dimension of the input.
1165
+
1128
1166
See Also
1129
1167
--------
1130
1168
toeplitz : Toeplitz matrix
@@ -1244,16 +1282,17 @@ def convolution_matrix(a, n, mode='full'):
1244
1282
raise ValueError ('n must be a positive integer.' )
1245
1283
1246
1284
a = np .asarray (a )
1247
- if a .ndim != 1 :
1248
- raise ValueError ('convolution_matrix expects a one-dimensional '
1249
- 'array as input' )
1285
+
1250
1286
if a .size == 0 :
1251
1287
raise ValueError ('len(a) must be at least 1.' )
1252
1288
1253
1289
if mode not in ('full' , 'valid' , 'same' ):
1254
1290
raise ValueError (
1255
1291
"'mode' argument must be one of ('full', 'valid', 'same')" )
1256
1292
1293
+ if a .ndim > 1 :
1294
+ return np .apply_along_axis (lambda a : convolution_matrix (a , n , mode ), - 1 , a )
1295
+
1257
1296
# create zero padded versions of the array
1258
1297
az = np .pad (a , (0 , n - 1 ), 'constant' )
1259
1298
raz = np .pad (a [::- 1 ], (0 , n - 1 ), 'constant' )
0 commit comments