Coverage for /home/martinb/.local/share/virtualenvs/camcops/lib/python3.6/site-packages/scipy/sparse/sputils.py : 17%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1""" Utility functions for sparse matrix module
2"""
4import sys
5import operator
6import warnings
7import numpy as np
8from scipy._lib._util import prod
10__all__ = ['upcast', 'getdtype', 'isscalarlike', 'isintlike',
11 'isshape', 'issequence', 'isdense', 'ismatrix', 'get_sum_dtype']
13supported_dtypes = [np.bool_, np.byte, np.ubyte, np.short, np.ushort, np.intc,
14 np.uintc, np.int_, np.uint, np.longlong, np.ulonglong, np.single, np.double,
15 np.longdouble, np.csingle, np.cdouble, np.clongdouble]
17_upcast_memo = {}
20def upcast(*args):
21 """Returns the nearest supported sparse dtype for the
22 combination of one or more types.
24 upcast(t0, t1, ..., tn) -> T where T is a supported dtype
26 Examples
27 --------
29 >>> upcast('int32')
30 <type 'numpy.int32'>
31 >>> upcast('bool')
32 <type 'numpy.bool_'>
33 >>> upcast('int32','float32')
34 <type 'numpy.float64'>
35 >>> upcast('bool',complex,float)
36 <type 'numpy.complex128'>
38 """
40 t = _upcast_memo.get(hash(args))
41 if t is not None:
42 return t
44 upcast = np.find_common_type(args, [])
46 for t in supported_dtypes:
47 if np.can_cast(upcast, t):
48 _upcast_memo[hash(args)] = t
49 return t
51 raise TypeError('no supported conversion for types: %r' % (args,))
54def upcast_char(*args):
55 """Same as `upcast` but taking dtype.char as input (faster)."""
56 t = _upcast_memo.get(args)
57 if t is not None:
58 return t
59 t = upcast(*map(np.dtype, args))
60 _upcast_memo[args] = t
61 return t
64def upcast_scalar(dtype, scalar):
65 """Determine data type for binary operation between an array of
66 type `dtype` and a scalar.
67 """
68 return (np.array([0], dtype=dtype) * scalar).dtype
71def downcast_intp_index(arr):
72 """
73 Down-cast index array to np.intp dtype if it is of a larger dtype.
75 Raise an error if the array contains a value that is too large for
76 intp.
77 """
78 if arr.dtype.itemsize > np.dtype(np.intp).itemsize:
79 if arr.size == 0:
80 return arr.astype(np.intp)
81 maxval = arr.max()
82 minval = arr.min()
83 if maxval > np.iinfo(np.intp).max or minval < np.iinfo(np.intp).min:
84 raise ValueError("Cannot deal with arrays with indices larger "
85 "than the machine maximum address size "
86 "(e.g. 64-bit indices on 32-bit machine).")
87 return arr.astype(np.intp)
88 return arr
91def to_native(A):
92 return np.asarray(A, dtype=A.dtype.newbyteorder('native'))
95def getdtype(dtype, a=None, default=None):
96 """Function used to simplify argument processing. If 'dtype' is not
97 specified (is None), returns a.dtype; otherwise returns a np.dtype
98 object created from the specified dtype argument. If 'dtype' and 'a'
99 are both None, construct a data type out of the 'default' parameter.
100 Furthermore, 'dtype' must be in 'allowed' set.
101 """
102 # TODO is this really what we want?
103 if dtype is None:
104 try:
105 newdtype = a.dtype
106 except AttributeError:
107 if default is not None:
108 newdtype = np.dtype(default)
109 else:
110 raise TypeError("could not interpret data type")
111 else:
112 newdtype = np.dtype(dtype)
113 if newdtype == np.object_:
114 warnings.warn("object dtype is not supported by sparse matrices")
116 return newdtype
119def get_index_dtype(arrays=(), maxval=None, check_contents=False):
120 """
121 Based on input (integer) arrays `a`, determine a suitable index data
122 type that can hold the data in the arrays.
124 Parameters
125 ----------
126 arrays : tuple of array_like
127 Input arrays whose types/contents to check
128 maxval : float, optional
129 Maximum value needed
130 check_contents : bool, optional
131 Whether to check the values in the arrays and not just their types.
132 Default: False (check only the types)
134 Returns
135 -------
136 dtype : dtype
137 Suitable index data type (int32 or int64)
139 """
141 int32min = np.iinfo(np.int32).min
142 int32max = np.iinfo(np.int32).max
144 dtype = np.intc
145 if maxval is not None:
146 if maxval > int32max:
147 dtype = np.int64
149 if isinstance(arrays, np.ndarray):
150 arrays = (arrays,)
152 for arr in arrays:
153 arr = np.asarray(arr)
154 if not np.can_cast(arr.dtype, np.int32):
155 if check_contents:
156 if arr.size == 0:
157 # a bigger type not needed
158 continue
159 elif np.issubdtype(arr.dtype, np.integer):
160 maxval = arr.max()
161 minval = arr.min()
162 if minval >= int32min and maxval <= int32max:
163 # a bigger type not needed
164 continue
166 dtype = np.int64
167 break
169 return dtype
172def get_sum_dtype(dtype):
173 """Mimic numpy's casting for np.sum"""
174 if dtype.kind == 'u' and np.can_cast(dtype, np.uint):
175 return np.uint
176 if np.can_cast(dtype, np.int_):
177 return np.int_
178 return dtype
181def isscalarlike(x):
182 """Is x either a scalar, an array scalar, or a 0-dim array?"""
183 return np.isscalar(x) or (isdense(x) and x.ndim == 0)
186def isintlike(x):
187 """Is x appropriate as an index into a sparse matrix? Returns True
188 if it can be cast safely to a machine int.
189 """
190 # Fast-path check to eliminate non-scalar values. operator.index would
191 # catch this case too, but the exception catching is slow.
192 if np.ndim(x) != 0:
193 return False
194 try:
195 operator.index(x)
196 except (TypeError, ValueError):
197 try:
198 loose_int = bool(int(x) == x)
199 except (TypeError, ValueError):
200 return False
201 if loose_int:
202 warnings.warn("Inexact indices into sparse matrices are deprecated",
203 DeprecationWarning)
204 return loose_int
205 return True
208def isshape(x, nonneg=False):
209 """Is x a valid 2-tuple of dimensions?
211 If nonneg, also checks that the dimensions are non-negative.
212 """
213 try:
214 # Assume it's a tuple of matrix dimensions (M, N)
215 (M, N) = x
216 except Exception:
217 return False
218 else:
219 if isintlike(M) and isintlike(N):
220 if np.ndim(M) == 0 and np.ndim(N) == 0:
221 if not nonneg or (M >= 0 and N >= 0):
222 return True
223 return False
226def issequence(t):
227 return ((isinstance(t, (list, tuple)) and
228 (len(t) == 0 or np.isscalar(t[0]))) or
229 (isinstance(t, np.ndarray) and (t.ndim == 1)))
232def ismatrix(t):
233 return ((isinstance(t, (list, tuple)) and
234 len(t) > 0 and issequence(t[0])) or
235 (isinstance(t, np.ndarray) and t.ndim == 2))
238def isdense(x):
239 return isinstance(x, np.ndarray)
242def validateaxis(axis):
243 if axis is not None:
244 axis_type = type(axis)
246 # In NumPy, you can pass in tuples for 'axis', but they are
247 # not very useful for sparse matrices given their limited
248 # dimensions, so let's make it explicit that they are not
249 # allowed to be passed in
250 if axis_type == tuple:
251 raise TypeError(("Tuples are not accepted for the 'axis' "
252 "parameter. Please pass in one of the "
253 "following: {-2, -1, 0, 1, None}."))
255 # If not a tuple, check that the provided axis is actually
256 # an integer and raise a TypeError similar to NumPy's
257 if not np.issubdtype(np.dtype(axis_type), np.integer):
258 raise TypeError("axis must be an integer, not {name}"
259 .format(name=axis_type.__name__))
261 if not (-2 <= axis <= 1):
262 raise ValueError("axis out of range")
265def check_shape(args, current_shape=None):
266 """Imitate numpy.matrix handling of shape arguments"""
267 if len(args) == 0:
268 raise TypeError("function missing 1 required positional argument: "
269 "'shape'")
270 elif len(args) == 1:
271 try:
272 shape_iter = iter(args[0])
273 except TypeError:
274 new_shape = (operator.index(args[0]), )
275 else:
276 new_shape = tuple(operator.index(arg) for arg in shape_iter)
277 else:
278 new_shape = tuple(operator.index(arg) for arg in args)
280 if current_shape is None:
281 if len(new_shape) != 2:
282 raise ValueError('shape must be a 2-tuple of positive integers')
283 elif new_shape[0] < 0 or new_shape[1] < 0:
284 raise ValueError("'shape' elements cannot be negative")
286 else:
287 # Check the current size only if needed
288 current_size = prod(current_shape)
290 # Check for negatives
291 negative_indexes = [i for i, x in enumerate(new_shape) if x < 0]
292 if len(negative_indexes) == 0:
293 new_size = prod(new_shape)
294 if new_size != current_size:
295 raise ValueError('cannot reshape array of size {} into shape {}'
296 .format(current_size, new_shape))
297 elif len(negative_indexes) == 1:
298 skip = negative_indexes[0]
299 specified = prod(new_shape[0:skip] + new_shape[skip+1:])
300 unspecified, remainder = divmod(current_size, specified)
301 if remainder != 0:
302 err_shape = tuple('newshape' if x < 0 else x for x in new_shape)
303 raise ValueError('cannot reshape array of size {} into shape {}'
304 ''.format(current_size, err_shape))
305 new_shape = new_shape[0:skip] + (unspecified,) + new_shape[skip+1:]
306 else:
307 raise ValueError('can only specify one unknown dimension')
309 if len(new_shape) != 2:
310 raise ValueError('matrix shape must be two-dimensional')
312 return new_shape
315def check_reshape_kwargs(kwargs):
316 """Unpack keyword arguments for reshape function.
318 This is useful because keyword arguments after star arguments are not
319 allowed in Python 2, but star keyword arguments are. This function unpacks
320 'order' and 'copy' from the star keyword arguments (with defaults) and
321 throws an error for any remaining.
322 """
324 order = kwargs.pop('order', 'C')
325 copy = kwargs.pop('copy', False)
326 if kwargs: # Some unused kwargs remain
327 raise TypeError('reshape() got unexpected keywords arguments: {}'
328 .format(', '.join(kwargs.keys())))
329 return order, copy
332def is_pydata_spmatrix(m):
333 """
334 Check whether object is pydata/sparse matrix, avoiding importing the module.
335 """
336 base_cls = getattr(sys.modules.get('sparse'), 'SparseArray', None)
337 return base_cls is not None and isinstance(m, base_cls)
340###############################################################################
341# Wrappers for NumPy types that are deprecated
343def matrix(*args, **kwargs):
344 with warnings.catch_warnings(record=True):
345 warnings.filterwarnings(
346 'ignore', '.*the matrix subclass is not the recommended way.*')
347 return np.matrix(*args, **kwargs)
350def asmatrix(*args, **kwargs):
351 with warnings.catch_warnings(record=True):
352 warnings.filterwarnings(
353 'ignore', '.*the matrix subclass is not the recommended way.*')
354 return np.asmatrix(*args, **kwargs)
357def bmat(*args, **kwargs):
358 with warnings.catch_warnings(record=True):
359 warnings.filterwarnings(
360 'ignore', '.*the matrix subclass is not the recommended way.*')
361 return np.bmat(*args, **kwargs)