-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathsparse_array.py
More file actions
203 lines (169 loc) · 6.48 KB
/
Copy pathsparse_array.py
File metadata and controls
203 lines (169 loc) · 6.48 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
import warnings
import numpy as np
from .array import Array
from .core import AnyWrappedArray, _SparseMixin
from .utils import _spcopy_if_needed, sparse
if sparse:
class WrappedSparseArray(sparse.spmatrix, AnyWrappedArray):
"""Base class for sparse arrays."""
def to_dense(self) -> "Array":
return Array.from_any(self.todense())
class SparseArray(sparse.csc_matrix, _SparseMixin, WrappedSparseArray):
"""
Matlab sparse matrices (scipy.sparse backend).
```python
# Instantiate from size
SparseArray(N, M)
SparseArray([N, M])
SparseArray.from_shape([N, M])
# Instantiate from existing sparse or dense array
SparseArray(other_array)
SparseArray.from_any(other_array)
# Other options
SparseArray(..., dtype=None, *, copy=None)
```
!!! warning
Lists or vectors of integers can be interpreted as shapes
or as dense arrays to copy. They are interpreted as shapes
by the `SparseArray` constructor. To ensure that they are
interpreted as dense arrays to copy, usse `SparseArray.from_any`.
"""
def __init__(self, *args, **kwargs) -> None:
mode, arg, kwargs = self._parse_args(*args, **kwargs)
if mode == "shape":
ndim = len(arg)
return super().__init__(([], [[]] * ndim), shape=arg, **kwargs)
else:
if not isinstance(arg, (np.ndarray, sparse.spmatrix)):
arg = np.asanyarray(arg)
return super().__init__(arg, **kwargs)
@classmethod
def from_coo(cls, values, indices, shape=None, **kw) -> "SparseArray":
"""
Build a sparse array from indices and values.
Parameters
----------
values : (N,) ArrayLike
Values to set at each index.
indices : (D, N) ArrayLike
Indices of nonzero elements.
shape : list[int] | None
Shape of the array.
dtype : np.dtype | None
Target data type. Same as `values` by default.
Returns
-------
array : SparseArray
New array.
"""
indices = np.asarray(indices)
coo = sparse.coo_matrix((values, indices), shape=shape, **kw)
return cls.from_any(coo)
@classmethod
def from_shape(cls, shape=tuple(), **kwargs) -> "SparseArray":
"""
Build an array of a given shape.
Parameters
----------
shape : list[int]
Shape of the new array.
Other Parameters
----------------
dtype : np.dtype | None, default='double'
Target data type.
Returns
-------
array : SparseArray
New array.
"""
return cls(list(shape), **kwargs)
@classmethod
def from_any(cls, other, **kwargs) -> "SparseArray":
"""
Convert an array-like object to a numeric array.
Parameters
----------
other : ArrayLike
object to convert.
Other Parameters
----------------
dtype : np.dtype | None, default=None
Target data type. Guessed if `None`.
copy : bool | None, default=None
Whether to copy the underlying data.
* `True` : the object is copied;
* `None` : the the object is copied only if needed;
* `False`: raises a `ValueError` if a copy cannot be avoided.
Returns
-------
array : SparseArray
Converted array.
"""
copy = kwargs.pop("copy", None)
inp = other
if not isinstance(other, sparse.spmatrix):
other = np.asanyarray(other, **kwargs)
other = cls(other, **kwargs)
other = _spcopy_if_needed(other, inp, copy)
return other
else:
warnings.warn(
"Since scipy.sparse is not available, sparse matrices "
"will be implemented as dense matrices, which can lead to "
"unsubstainable memory usage. If this is an issue, install "
"scipy in your python environment."
)
class SparseArray(_SparseMixin, Array):
"""
Matlab sparse arrays (dense backend).
```python
# Instantiate from size
SparseArray(N, M, ...)
SparseArray([N, M, ...])
SparseArray.from_shape([N, M, ...])
# Instantiate from existing sparse or dense array
SparseArray(other_array)
SparseArray.from_any(other_array)
# Other options
SparseArray(..., dtype=None, *, copy=None)
```
!!! warning
Lists or vectors of integers can be interpreted as shapes
or as dense arrays to copy. They are interpreted as shapes
by the `SparseArray` constructor. To ensure that they are
interpreted as dense arrays to copy, usse `SparseArray.from_any`.
!!! note
This is not really a sparse array, but a dense array that gets
converted to a sparse array when passed to matlab.
"""
def to_dense(self) -> "Array":
return np.ndarray.view(self, Array)
@classmethod
def from_coo(cls, values, indices, shape=None, **kw) -> "SparseArray":
"""
Build a sparse array from indices and values.
Parameters
----------
values : (N,) ArrayLike
Values to set at each index.
indices : (D, N) ArrayLike
Indices of nonzero elements.
shape : list[int] | None
Shape of the array.
dtype : np.dtype | None
Target data type. Same as `values` by default.
Returns
-------
array : SparseArray
New array.
"""
dtype = kw.get("dtype", None)
indices = np.asarray(indices)
values = np.asarray(values, dtype=dtype)
if shape is None:
shape = (1 + indices.max(-1)).astype(np.uint64).tolist()
if dtype is None:
dtype = values.dtype
obj = cls.from_shape(shape, dtype=dtype)
obj[tuple(indices)] = values
return obj