Skip to content

Commit d4d98e1

Browse files
authored
Merge pull request #408 from bsc-wdc/in_place_magic_methods_array
Added in-place operations
2 parents 0b514b1 + 62031fc commit d4d98e1

File tree

2 files changed

+304
-0
lines changed

2 files changed

+304
-0
lines changed

dislib/data/array.py

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,123 @@ def __sub__(self, other):
224224
return Array(blocks, self._top_left_shape, self._reg_shape,
225225
self.shape, self._sparse)
226226

227+
def __isub__(self, other):
228+
if self.shape[1] != other.shape[1] or (other.shape[0] != 1 and
229+
(other.shape[0] != self.shape[0]
230+
or other._reg_shape[0] !=
231+
self._reg_shape[0])):
232+
raise NotImplementedError("Reverse subtraction not "
233+
"implemented for the "
234+
"given objects")
235+
236+
if other.shape[0] == self.shape[0] and other._reg_shape[0]\
237+
== self._reg_shape[0]:
238+
if other.shape[0] == self.shape[0] and \
239+
other._reg_shape[0] == self._reg_shape[0]:
240+
if self._reg_shape[1] != other._reg_shape[1]:
241+
raise ValueError("incorrect block sizes for the requested "
242+
f"subtraction ("
243+
f"{self._reg_shape[0]}"", "
244+
f"{self._reg_shape[1]} !="
245+
f"{other._reg_shape[0]}"", "
246+
f"{other._reg_shape[1]})")
247+
248+
if self._top_left_shape != other._top_left_shape:
249+
raise ValueError("Incompatible block sizes of the "
250+
"top left block of the matrices"
251+
"b._top_left_shape != b._top_left_shape")
252+
# matrix - matrix
253+
blocks = []
254+
255+
for hblock, others in zip(self._iterator("rows"),
256+
other._iterator("rows")):
257+
out_blocks = [object() for _ in range(hblock._n_blocks[1])]
258+
_combine_blocks(hblock._blocks, others._blocks,
259+
Array._subtract, out_blocks)
260+
blocks.append(out_blocks)
261+
compss_delete_object(self._blocks)
262+
self._blocks = blocks
263+
264+
return self
265+
# matrix - vector
266+
blocks = []
267+
268+
for hblock in self._iterator("rows"):
269+
out_blocks = [object() for _ in range(hblock._n_blocks[1])]
270+
_combine_blocks(hblock._blocks, other._blocks,
271+
Array._subtract, out_blocks)
272+
blocks.append(out_blocks)
273+
274+
compss_delete_object(self._blocks)
275+
self._blocks = blocks
276+
277+
return self
278+
279+
def __add__(self, other):
280+
if self.shape[1] != other.shape[1] or other.shape[0] != 1:
281+
raise NotImplementedError("Addition not implemented for the "
282+
"given objects")
283+
284+
# matrix + vector
285+
blocks = []
286+
287+
for hblock in self._iterator("rows"):
288+
out_blocks = [object() for _ in range(hblock._n_blocks[1])]
289+
_combine_blocks(hblock._blocks, other._blocks,
290+
Array._add, out_blocks)
291+
blocks.append(out_blocks)
292+
293+
return Array(blocks, self._top_left_shape, self._reg_shape,
294+
self.shape, self._sparse)
295+
296+
def __iadd__(self, other):
297+
if self.shape[1] != other.shape[1] or (other.shape[0] != 1 and
298+
(other.shape[0] != self.shape[0]
299+
or other._reg_shape[0] !=
300+
self._reg_shape[0])):
301+
raise NotImplementedError("Self addition not implemented for the "
302+
"given objects")
303+
304+
if other.shape[0] == self.shape[0] and \
305+
other._reg_shape[0] == self._reg_shape[0]:
306+
if self._reg_shape[1] != other._reg_shape[1]:
307+
raise ValueError("incorrect block sizes for the requested "
308+
f"addition ("
309+
f"{self._reg_shape[0]}"", "
310+
f"{self._reg_shape[1]} !="
311+
f"{other._reg_shape[0]}"", "
312+
f"{other._reg_shape[1]})")
313+
314+
if self._top_left_shape != other._top_left_shape:
315+
raise ValueError("Incompatible block sizes of the "
316+
"top left block of the matrices"
317+
"b._top_left_shape != b._top_left_shape")
318+
# matrix + matrix
319+
blocks = []
320+
321+
for hblock, others in zip(self._iterator("rows"),
322+
other._iterator("rows")):
323+
out_blocks = [object() for _ in range(hblock._n_blocks[1])]
324+
_combine_blocks(hblock._blocks, others._blocks,
325+
Array._add, out_blocks)
326+
blocks.append(out_blocks)
327+
compss_delete_object(self._blocks)
328+
self._blocks = blocks
329+
330+
return self
331+
# matrix + vector
332+
blocks = []
333+
334+
for hblock in self._iterator("rows"):
335+
out_blocks = [object() for _ in range(hblock._n_blocks[1])]
336+
_combine_blocks(hblock._blocks, other._blocks,
337+
Array._add, out_blocks)
338+
blocks.append(out_blocks)
339+
compss_delete_object(self._blocks)
340+
self._blocks = blocks
341+
342+
return self
343+
227344
def __truediv__(self, other):
228345
if not np.isscalar(other):
229346
raise NotImplementedError("Non scalar division not supported")
@@ -275,6 +392,22 @@ def _subtract(a, b):
275392
else:
276393
return a - b
277394

395+
@staticmethod
396+
def _add(a, b):
397+
sparse = issparse(a)
398+
399+
# needed because subtract with scipy.sparse does not support
400+
# broadcasting
401+
if sparse:
402+
a = a.toarray()
403+
if issparse(b):
404+
b = b.toarray()
405+
406+
if sparse:
407+
return csr_matrix(a + b)
408+
else:
409+
return a + b
410+
278411
@staticmethod
279412
def _power(x_np, power):
280413
if issparse(x_np):

tests/test_array.py

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1040,6 +1040,177 @@ def test_power(self):
10401040
with self.assertRaises(NotImplementedError):
10411041
x ** x
10421042

1043+
def test_add(self):
1044+
orig = np.array([[1, 2, 3], [4, 5, 6]])
1045+
x = ds.array(orig, block_size=(2, 1))
1046+
orig_vector = np.array([[1, 2, 3]])
1047+
vector = ds.array(orig_vector, block_size=(1, 1))
1048+
1049+
b = x + vector
1050+
self.assertTrue(_validate_array(b))
1051+
expected = np.array([[2, 4, 6], [5, 7, 9]])
1052+
1053+
self.assertTrue(_equal_arrays(expected, b.collect()))
1054+
1055+
orig = sp.csr_matrix([[1, 2, 3], [4, 5, 6]])
1056+
x = ds.array(orig, block_size=(2, 1))
1057+
orig_vector = sp.csr_matrix([[1, 2, 3]])
1058+
vector = ds.array(orig_vector, block_size=(1, 1))
1059+
1060+
b = x + vector
1061+
self.assertTrue(_validate_array(b))
1062+
expected = sp.csr_matrix([[2, 4, 6], [5, 7, 9]])
1063+
1064+
self.assertTrue(_equal_arrays(expected, b.collect()))
1065+
1066+
with self.assertRaises(NotImplementedError):
1067+
orig = np.array([[1, 2, 3], [4, 5, 6]])
1068+
x = ds.array(orig, block_size=(2, 1))
1069+
orig_vector = np.array([[1, 2]])
1070+
vector = ds.array(orig_vector, block_size=(1, 1))
1071+
b = x + vector
1072+
1073+
def test_sub(self):
1074+
orig = np.array([[1, 2, 3], [4, 5, 6]])
1075+
x = ds.array(orig, block_size=(2, 1))
1076+
orig_vector = np.array([[1, 2, 3]])
1077+
vector = ds.array(orig_vector, block_size=(1, 1))
1078+
1079+
b = x - vector
1080+
self.assertTrue(_validate_array(b))
1081+
expected = np.array([[0, 0, 0], [3, 3, 3]])
1082+
1083+
self.assertTrue(_equal_arrays(expected, b.collect()))
1084+
1085+
with self.assertRaises(NotImplementedError):
1086+
orig = np.array([[1, 2, 3], [4, 5, 6]])
1087+
x = ds.array(orig, block_size=(2, 1))
1088+
orig_vector = np.array([[1, 3]])
1089+
vector = ds.array(orig_vector, block_size=(1, 1))
1090+
b = x - vector
1091+
1092+
def test_iadd(self):
1093+
""" Tests ds-array magic method __iadd__ """
1094+
orig = np.array([[1, 2, 3], [4, 5, 6]])
1095+
x = ds.array(orig, block_size=(2, 1))
1096+
orig_vector = np.array([[1, 2, 3]])
1097+
vector = ds.array(orig_vector, block_size=(1, 1))
1098+
1099+
x += vector
1100+
1101+
self.assertTrue(_validate_array(x))
1102+
self.assertTrue(_validate_array(vector))
1103+
1104+
expected = np.array([[2, 4, 6], [5, 7, 9]])
1105+
1106+
self.assertTrue(_equal_arrays(expected, x.collect()))
1107+
self.assertTrue(_equal_arrays(orig_vector, vector.collect()))
1108+
1109+
orig = np.array([[1, 2, 3], [4, 5, 6]])
1110+
x = ds.array(orig, block_size=(2, 1))
1111+
orig_mat = np.array([[1, 2, 3], [4, 5, 6]])
1112+
matrix = ds.array(orig_mat, block_size=(2, 1))
1113+
1114+
x += matrix
1115+
1116+
self.assertTrue(_validate_array(x))
1117+
self.assertTrue(_validate_array(matrix))
1118+
1119+
expected = np.array([[2, 4, 6], [8, 10, 12]])
1120+
1121+
self.assertTrue(_equal_arrays(expected, x.collect()))
1122+
self.assertTrue(_equal_arrays(orig_mat, matrix.collect()))
1123+
1124+
with self.assertRaises(NotImplementedError):
1125+
orig = np.array([[1, 2, 3], [4, 5, 6]])
1126+
x = ds.array(orig, block_size=(2, 1))
1127+
orig_vector = np.array([[1, 2]])
1128+
vector = ds.array(orig_vector, block_size=(1, 1))
1129+
x += vector
1130+
1131+
with self.assertRaises(NotImplementedError):
1132+
orig = np.array([[1, 2, 3], [4, 5, 6]])
1133+
x = ds.array(orig, block_size=(2, 1))
1134+
orig_vector = np.array([[1, 2], [4, 5]])
1135+
vector = ds.array(orig_vector, block_size=(2, 1))
1136+
x += vector
1137+
1138+
with self.assertRaises(ValueError):
1139+
x1 = ds.array([[1, 2, 3], [4, 5, 6]], (2, 3))
1140+
x1.__init__([[1, 2, 3], [4, 5, 6], [7, 8, 9], [1, 2, 3],
1141+
[4, 5, 6]], top_left_shape=(1, 3),
1142+
reg_shape=(2, 3), shape=(5, 3), sparse=False)
1143+
x2 = ds.array([[1, 1, 1], [1, 1, 1],
1144+
[1, 1, 1], [1, 1, 1],
1145+
[1, 1, 1]], (2, 3))
1146+
x1 += x2
1147+
with self.assertRaises(ValueError):
1148+
orig = np.array([[1, 2, 3], [4, 5, 6]])
1149+
x = ds.array(orig, block_size=(2, 1))
1150+
orig_vector = np.array([[1, 2, 3], [4, 5, 6]])
1151+
vector = ds.array(orig_vector, block_size=(2, 2))
1152+
x += vector
1153+
1154+
def test_isub(self):
1155+
""" Tests ds-array magic method __isub__ """
1156+
orig = np.array([[1, 2, 3], [4, 5, 6]])
1157+
x = ds.array(orig, block_size=(2, 1))
1158+
orig_vector = np.array([[1, 2, 3]])
1159+
vector = ds.array(orig_vector, block_size=(1, 1))
1160+
1161+
x -= vector
1162+
1163+
self.assertTrue(_validate_array(x))
1164+
self.assertTrue(_validate_array(vector))
1165+
1166+
expected = np.array([[0, 0, 0], [3, 3, 3]])
1167+
1168+
self.assertTrue(_equal_arrays(expected, x.collect()))
1169+
self.assertTrue(_equal_arrays(orig_vector, vector.collect()))
1170+
1171+
orig = np.array([[1, 2, 3], [4, 5, 6]])
1172+
x = ds.array(orig, block_size=(2, 1))
1173+
orig_mat = np.array([[1, 2, 3], [4, 5, 6]])
1174+
matrix = ds.array(orig_mat, block_size=(2, 1))
1175+
1176+
x -= matrix
1177+
1178+
self.assertTrue(_validate_array(x))
1179+
self.assertTrue(_validate_array(matrix))
1180+
1181+
expected = np.array([[0, 0, 0], [0, 0, 0]])
1182+
1183+
self.assertTrue(_equal_arrays(expected, x.collect()))
1184+
self.assertTrue(_equal_arrays(orig_mat, matrix.collect()))
1185+
with self.assertRaises(NotImplementedError):
1186+
orig = np.array([[1, 2, 3], [4, 5, 6]])
1187+
x = ds.array(orig, block_size=(2, 1))
1188+
orig_vector = np.array([[1, 2]])
1189+
vector = ds.array(orig_vector, block_size=(1, 1))
1190+
x -= vector
1191+
with self.assertRaises(NotImplementedError):
1192+
orig = np.array([[1, 2, 3], [4, 5, 6]])
1193+
x = ds.array(orig, block_size=(2, 1))
1194+
orig_vector = np.array([[1, 2], [4, 5]])
1195+
vector = ds.array(orig_vector, block_size=(2, 1))
1196+
x -= vector
1197+
1198+
with self.assertRaises(ValueError):
1199+
x1 = ds.array([[1, 2, 3], [4, 5, 6]], (2, 3))
1200+
x1.__init__([[1, 2, 3], [4, 5, 6], [7, 8, 9], [1, 2, 3],
1201+
[4, 5, 6]], top_left_shape=(1, 3),
1202+
reg_shape=(2, 3), shape=(5, 3), sparse=False)
1203+
x2 = ds.array([[1, 1, 1], [1, 1, 1],
1204+
[1, 1, 1], [1, 1, 1],
1205+
[1, 1, 1]], (2, 3))
1206+
x1 -= x2
1207+
with self.assertRaises(ValueError):
1208+
orig = np.array([[1, 2, 3], [4, 5, 6]])
1209+
x = ds.array(orig, block_size=(2, 1))
1210+
orig_vector = np.array([[1, 2, 3], [4, 5, 6]])
1211+
vector = ds.array(orig_vector, block_size=(2, 2))
1212+
x -= vector
1213+
10431214
def test_norm(self):
10441215
""" Tests the norm """
10451216
x_np = np.array([[1, 2, 3], [4, 5, 6]])

0 commit comments

Comments
 (0)