1 package org.djunits.value.vdouble.matrix.data;
2
3 import java.util.Arrays;
4 import java.util.Collection;
5 import java.util.concurrent.atomic.AtomicInteger;
6 import java.util.stream.IntStream;
7
8 import org.djunits.unit.Unit;
9 import org.djunits.unit.scale.Scale;
10 import org.djunits.value.ValueRuntimeException;
11 import org.djunits.value.storage.StorageType;
12 import org.djunits.value.vdouble.function.DoubleFunction;
13 import org.djunits.value.vdouble.function.DoubleFunction2;
14 import org.djunits.value.vdouble.matrix.base.DoubleSparseValue;
15 import org.djunits.value.vdouble.scalar.base.DoubleScalarInterface;
16 import org.djutils.exceptions.Throw;
17
18
19
20
21
22
23
24
25
26
27
28 public class DoubleMatrixDataSparse extends DoubleMatrixData
29 {
30
31 private static final long serialVersionUID = 1L;
32
33
34 private long[] indices;
35
36
37
38
39
40
41
42
43 public DoubleMatrixDataSparse(final double[] matrixSI, final long[] indices, final int rows, final int cols)
44 {
45 super(StorageType.SPARSE);
46 this.matrixSI = matrixSI;
47 this.indices = indices;
48 this.rows = rows;
49 this.cols = cols;
50 }
51
52
53
54
55
56
57
58
59
60
61 public <U extends Unit<U>, S extends DoubleScalarInterface<U, S>> DoubleMatrixDataSparse(
62 final Collection<DoubleSparseValue<U, S>> dataSI, final int rows, final int cols) throws NullPointerException
63 {
64 super(StorageType.SPARSE);
65 Throw.whenNull(dataSI, "matrixSI is null");
66
67 int length = (int) dataSI.stream().parallel().filter(d -> d.getValueSI() != 0.0).count();
68 this.rows = rows;
69 this.cols = cols;
70 this.matrixSI = new double[length];
71 this.indices = new long[length];
72 int index = 0;
73 for (DoubleSparseValue<U, S> data : dataSI)
74 {
75 if (data.getValueSI() != 0.0)
76 {
77 this.indices[index] = data.getRow() * this.cols + data.getColumn();
78 this.matrixSI[index] = data.getValueSI();
79 index++;
80 }
81 }
82 }
83
84
85 @Override
86 public final int cardinality()
87 {
88 return this.indices.length;
89 }
90
91
92
93
94
95
96
97
98
99 @SuppressWarnings("checkstyle:finalparameters")
100 private static void fill(final double[][] data, double[] matrixSI, long[] indices) throws ValueRuntimeException
101 {
102 int rows = data.length;
103 int cols = rows == 0 ? 0 : data[0].length;
104 if (cols == 0)
105 {
106 rows = 0;
107 }
108 int count = 0;
109 for (int r = 0; r < rows; r++)
110 {
111 double[] row = data[r];
112
113 for (int c = 0; c < cols; c++)
114 {
115 int index = r * cols + c;
116 if (row[c] != 0.0)
117 {
118 matrixSI[count] = row[c];
119 indices[count] = index;
120 count++;
121 }
122 }
123 }
124 }
125
126
127
128
129
130
131
132
133
134
135 @SuppressWarnings("checkstyle:finalparameters")
136 private static void fill(final double[][] data, double[] matrixSI, long[] indices, final Scale scale)
137 throws ValueRuntimeException
138 {
139 int rows = data.length;
140 int cols = rows == 0 ? 0 : data[0].length;
141 if (cols == 0)
142 {
143 rows = 0;
144 }
145 int count = 0;
146 for (int r = 0; r < rows; r++)
147 {
148 double[] row = data[r];
149
150 for (int c = 0; c < cols; c++)
151 {
152 int index = r * cols + c;
153 double value = scale.toStandardUnit(row[c]);
154 if (value != 0.0)
155 {
156 matrixSI[count] = value;
157 indices[count] = index;
158 count++;
159 }
160 }
161 }
162 }
163
164
165 @Override
166 public DoubleMatrixData assign(final DoubleFunction doubleFunction)
167 {
168 if (doubleFunction.apply(0d) != 0d)
169 {
170
171 DoubleMatrixDataSparse result = toDense().assign(doubleFunction).toSparse();
172 this.indices = result.indices;
173 this.matrixSI = result.matrixSI;
174 return this;
175 }
176
177 int currentSize = rows() * cols();
178 if (currentSize > 16)
179 {
180 currentSize = 16;
181 }
182 long[] newIndices = new long[currentSize];
183 double[] newValues = new double[currentSize];
184 int nonZeroValues = 0;
185 int ownIndex = 0;
186 while (ownIndex < this.indices.length)
187 {
188 long index = this.indices[ownIndex];
189 double value = doubleFunction.apply(this.matrixSI[ownIndex]);
190 ownIndex++;
191 if (value != 0d)
192 {
193 if (nonZeroValues >= currentSize)
194 {
195
196 currentSize *= 2;
197 if (currentSize > rows() * cols())
198 {
199 currentSize = rows() * cols();
200 }
201 long[] newNewIndices = new long[currentSize];
202 System.arraycopy(newIndices, 0, newNewIndices, 0, newIndices.length);
203 newIndices = newNewIndices;
204 double[] newNewValues = new double[currentSize];
205 System.arraycopy(newValues, 0, newNewValues, 0, newValues.length);
206 newValues = newNewValues;
207 }
208 newIndices[nonZeroValues] = index;
209 newValues[nonZeroValues] = value;
210 nonZeroValues++;
211 }
212 }
213 if (nonZeroValues < currentSize)
214 {
215
216 long[] newNewIndices = new long[nonZeroValues];
217 System.arraycopy(newIndices, 0, newNewIndices, 0, nonZeroValues);
218 newIndices = newNewIndices;
219 double[] newNewValues = new double[nonZeroValues];
220 System.arraycopy(newValues, 0, newNewValues, 0, nonZeroValues);
221 newValues = newNewValues;
222 }
223 this.indices = newIndices;
224 this.matrixSI = newValues;
225 return this;
226 }
227
228
229 @Override
230 public final DoubleMatrixDataSparse assign(final DoubleFunction2 doubleFunction, final DoubleMatrixData right)
231 {
232 if (doubleFunction.apply(0d, 0d) != 0d)
233 {
234
235 DoubleMatrixDataSparse result = toDense().assign(doubleFunction, right).toSparse();
236 this.indices = result.indices;
237 this.matrixSI = result.matrixSI;
238 return this;
239 }
240
241 checkSizes(right);
242 int currentSize = rows() * cols();
243 if (currentSize > 16)
244 {
245 currentSize = 16;
246 }
247 long[] newIndices = new long[currentSize];
248 double[] newValues = new double[currentSize];
249 int nonZeroValues = 0;
250 int ownIndex = 0;
251 int otherIndex = 0;
252 if (right.isSparse())
253 {
254 DoubleMatrixDataSparse other = (DoubleMatrixDataSparse) right;
255 while (ownIndex < this.indices.length || otherIndex < other.indices.length)
256 {
257 double value;
258 long index;
259 if (ownIndex < this.indices.length && otherIndex < other.indices.length)
260 {
261 if (this.indices[ownIndex] == other.indices[otherIndex])
262 {
263 value = doubleFunction.apply(this.matrixSI[ownIndex], other.matrixSI[otherIndex]);
264 index = this.indices[ownIndex];
265 ownIndex++;
266 otherIndex++;
267 }
268 else if (this.indices[ownIndex] < other.indices[otherIndex])
269 {
270
271 value = doubleFunction.apply(this.matrixSI[ownIndex], 0d);
272 index = this.indices[ownIndex];
273 ownIndex++;
274 }
275 else
276 {
277
278 value = doubleFunction.apply(0d, other.matrixSI[otherIndex]);
279 index = other.indices[otherIndex];
280 otherIndex++;
281 }
282 }
283 else if (ownIndex < this.indices.length)
284 {
285 value = doubleFunction.apply(this.matrixSI[ownIndex], 0d);
286 index = this.indices[ownIndex];
287 ownIndex++;
288 }
289 else
290 {
291 value = doubleFunction.apply(0d, other.matrixSI[otherIndex]);
292 index = other.indices[otherIndex];
293 otherIndex++;
294 }
295 if (value != 0d)
296 {
297 if (nonZeroValues >= currentSize)
298 {
299
300 currentSize *= 2;
301 if (currentSize > rows() * cols())
302 {
303 currentSize = rows() * cols();
304 }
305 long[] newNewIndices = new long[currentSize];
306 System.arraycopy(newIndices, 0, newNewIndices, 0, newIndices.length);
307 newIndices = newNewIndices;
308 double[] newNewValues = new double[currentSize];
309 System.arraycopy(newValues, 0, newNewValues, 0, newValues.length);
310 newValues = newNewValues;
311 }
312 newIndices[nonZeroValues] = index;
313 newValues[nonZeroValues] = value;
314 nonZeroValues++;
315 }
316 }
317 }
318 else
319 {
320 DoubleMatrixDataDense other = (DoubleMatrixDataDense) right;
321 while (otherIndex < right.matrixSI.length)
322 {
323 double value;
324 int index = otherIndex;
325 if (ownIndex < this.indices.length)
326 {
327 if (this.indices[ownIndex] == otherIndex)
328 {
329 value = doubleFunction.apply(this.matrixSI[ownIndex], other.matrixSI[otherIndex]);
330 ownIndex++;
331 }
332 else
333 {
334
335 value = doubleFunction.apply(0d, other.matrixSI[otherIndex]);
336 }
337 otherIndex++;
338 }
339 else
340 {
341 value = doubleFunction.apply(0d, other.matrixSI[otherIndex]);
342 otherIndex++;
343 }
344 if (value != 0d)
345 {
346 if (nonZeroValues >= currentSize)
347 {
348
349 currentSize *= 2;
350 if (currentSize > rows() * cols())
351 {
352 currentSize = rows() * cols();
353 }
354 long[] newNewIndices = new long[currentSize];
355 System.arraycopy(newIndices, 0, newNewIndices, 0, newIndices.length);
356 newIndices = newNewIndices;
357 double[] newNewValues = new double[currentSize];
358 System.arraycopy(newValues, 0, newNewValues, 0, newValues.length);
359 newValues = newNewValues;
360 }
361 newIndices[nonZeroValues] = index;
362 newValues[nonZeroValues] = value;
363 nonZeroValues++;
364 }
365 }
366 }
367 if (nonZeroValues < currentSize)
368 {
369
370 long[] newNewIndices = new long[nonZeroValues];
371 System.arraycopy(newIndices, 0, newNewIndices, 0, nonZeroValues);
372 newIndices = newNewIndices;
373 double[] newNewValues = new double[nonZeroValues];
374 System.arraycopy(newValues, 0, newNewValues, 0, nonZeroValues);
375 newValues = newNewValues;
376 }
377 this.indices = newIndices;
378 this.matrixSI = newValues;
379 return this;
380 }
381
382
383 @Override
384 public final DoubleMatrixDataDense toDense()
385 {
386 double[] denseSI = new double[this.rows * this.cols];
387 for (int index = 0; index < this.matrixSI.length; index++)
388 {
389 denseSI[(int) this.indices[index]] = this.matrixSI[index];
390 }
391 try
392 {
393 return new DoubleMatrixDataDense(denseSI, this.rows, this.cols);
394 }
395 catch (ValueRuntimeException exception)
396 {
397 throw new RuntimeException(exception);
398 }
399 }
400
401
402 @Override
403 public final DoubleMatrixDataSparse toSparse()
404 {
405 return this;
406 }
407
408
409 @Override
410 public final double getSI(final int row, final int col)
411 {
412 long index = row * this.cols + col;
413 int internalIndex = Arrays.binarySearch(this.indices, index);
414 return internalIndex < 0 ? 0.0 : this.matrixSI[internalIndex];
415 }
416
417
418 @Override
419 public final void setSI(final int row, final int col, final double valueSI)
420 {
421 long index = row * this.cols + col;
422 int internalIndex = Arrays.binarySearch(this.indices, index);
423 if (internalIndex >= 0)
424 {
425 this.matrixSI[internalIndex] = valueSI;
426 return;
427 }
428
429
430 internalIndex = -internalIndex - 1;
431 long[] indicesNew = new long[this.indices.length + 1];
432 double[] matrixSINew = new double[this.matrixSI.length + 1];
433 System.arraycopy(this.indices, 0, indicesNew, 0, internalIndex);
434 System.arraycopy(this.matrixSI, 0, matrixSINew, 0, internalIndex);
435 System.arraycopy(this.indices, internalIndex, indicesNew, internalIndex + 1, this.indices.length - internalIndex);
436 System.arraycopy(this.matrixSI, internalIndex, matrixSINew, internalIndex + 1, this.indices.length - internalIndex);
437 indicesNew[internalIndex] = index;
438 matrixSINew[internalIndex] = valueSI;
439 this.indices = indicesNew;
440 this.matrixSI = matrixSINew;
441 }
442
443
444 @Override
445 public final double[][] getDenseMatrixSI()
446 {
447 return toDense().getDenseMatrixSI();
448 }
449
450
451 @Override
452 public final DoubleMatrixDataSparse copy()
453 {
454 double[] vCopy = new double[this.matrixSI.length];
455 System.arraycopy(this.matrixSI, 0, vCopy, 0, this.matrixSI.length);
456 long[] iCopy = new long[this.indices.length];
457 System.arraycopy(this.indices, 0, iCopy, 0, this.indices.length);
458 return new DoubleMatrixDataSparse(vCopy, iCopy, this.rows, this.cols);
459 }
460
461
462
463
464
465
466
467 public static DoubleMatrixDataSparse instantiate(final double[][] valuesSI) throws ValueRuntimeException
468 {
469 checkRectangularAndNonNull(valuesSI);
470 int length = nonZero(valuesSI);
471 int rows = valuesSI.length;
472 final int cols = rows == 0 ? 0 : valuesSI[0].length;
473 if (cols == 0)
474 {
475 rows = 0;
476 }
477 double[] sparseSI = new double[length];
478 long[] indices = new long[length];
479 fill(valuesSI, sparseSI, indices);
480 return new DoubleMatrixDataSparse(sparseSI, indices, rows, cols);
481 }
482
483
484
485
486
487
488
489
490 public static DoubleMatrixDataSparse instantiate(final double[][] values, final Scale scale) throws ValueRuntimeException
491 {
492 checkRectangularAndNonNull(values);
493 int length = nonZero(values);
494 int rows = values.length;
495 final int cols = rows == 0 ? 0 : values[0].length;
496 if (cols == 0)
497 {
498 rows = 0;
499 }
500 double[] sparseSI = new double[length];
501 long[] indices = new long[length];
502 fill(values, sparseSI, indices, scale);
503 return new DoubleMatrixDataSparse(sparseSI, indices, rows, cols);
504 }
505
506
507
508
509
510
511 private static int nonZero(final double[][] valuesSI)
512 {
513
514 AtomicInteger atomicLength = new AtomicInteger(0);
515 IntStream.range(0, valuesSI.length).parallel().forEach(r -> IntStream.range(0, valuesSI[0].length).forEach(c ->
516 {
517 if (valuesSI[r][c] != 0.0)
518 {
519 atomicLength.incrementAndGet();
520 }
521 }));
522
523 return atomicLength.get();
524 }
525
526
527 @Override
528 public DoubleMatrixData plus(final DoubleMatrixData right) throws ValueRuntimeException
529 {
530 if (right.isDense())
531 {
532 return right.copy().incrementBy(this);
533 }
534 return this.copy().incrementBy(right);
535 }
536
537
538 @Override
539 public final DoubleMatrixData minus(final DoubleMatrixData right)
540 {
541 if (right.isDense())
542 {
543 return this.toDense().decrementBy(right);
544 }
545 return this.copy().decrementBy(right);
546 }
547
548
549 @Override
550 public DoubleMatrixDataSparse times(final DoubleMatrixData right) throws ValueRuntimeException
551 {
552 checkSizes(right);
553 DoubleMatrixDataSparse result = this.copy();
554 result.multiplyBy(right);
555 return result;
556 }
557
558
559 @Override
560 public DoubleMatrixData divide(final DoubleMatrixData right) throws ValueRuntimeException
561 {
562 if (right.isSparse())
563 {
564
565 return this.toDense().divide(right);
566 }
567
568 checkSizes(right);
569 return this.copy().divideBy(right);
570 }
571
572
573 @Override
574 public int hashCode()
575 {
576 return super.hashCode();
577 }
578
579
580 @Override
581 @SuppressWarnings({"checkstyle:needbraces", "checkstyle:designforextension"})
582 public boolean equals(final Object obj)
583 {
584 if (this == obj)
585 return true;
586 if (obj == null)
587 return false;
588 if (!(obj instanceof DoubleMatrixData))
589 return false;
590 DoubleMatrixData other = (DoubleMatrixData) obj;
591 if (this.rows != other.rows)
592 return false;
593 if (this.cols != other.cols)
594 return false;
595 if (other instanceof DoubleMatrixDataDense)
596 return super.equals(other);
597 if (getClass() != obj.getClass())
598 return false;
599
600 if (!Arrays.equals(this.indices, ((DoubleMatrixDataSparse) other).indices))
601 return false;
602 return Arrays.equals(this.matrixSI, ((DoubleMatrixDataSparse) other).matrixSI);
603 }
604
605
606 @Override
607 public String toString()
608 {
609 return "DoubleMatrixDataSparse [storageType=" + getStorageType() + ", indices=" + Arrays.toString(this.indices)
610 + ", matrixSI=" + Arrays.toString(this.matrixSI) + "]";
611 }
612
613 }