1 package org.djunits.value.vfloat.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.vfloat.function.FloatFunction;
13 import org.djunits.value.vfloat.function.FloatFunction2;
14 import org.djunits.value.vfloat.matrix.base.FloatSparseValue;
15 import org.djunits.value.vfloat.scalar.base.FloatScalarInterface;
16 import org.djutils.exceptions.Throw;
17
18
19
20
21
22
23
24
25
26
27
28 public class FloatMatrixDataSparse extends FloatMatrixData
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 FloatMatrixDataSparse(final float[] 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 FloatScalarInterface<U, S>> FloatMatrixDataSparse(
62 final Collection<FloatSparseValue<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 float[length];
71 this.indices = new long[length];
72 int index = 0;
73 for (FloatSparseValue<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 float[][] data, float[] 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 float[] 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 float[][] data, float[] 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 float[] row = data[r];
149
150 for (int c = 0; c < cols; c++)
151 {
152 int index = r * cols + c;
153 float value = (float) 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 FloatMatrixData assign(final FloatFunction floatFunction)
167 {
168 if (floatFunction.apply(0f) != 0f)
169 {
170
171 FloatMatrixDataSparse result = toDense().assign(floatFunction).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 float[] newValues = new float[currentSize];
184 int nonZeroValues = 0;
185 int ownIndex = 0;
186 while (ownIndex < this.indices.length)
187 {
188 long index = this.indices[ownIndex];
189 float value = floatFunction.apply(this.matrixSI[ownIndex]);
190 ownIndex++;
191 if (value != 0f)
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 float[] newNewValues = new float[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 float[] newNewValues = new float[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 FloatMatrixDataSparse assign(final FloatFunction2 floatFunction, final FloatMatrixData right)
231 {
232 checkSizes(right);
233 int currentSize = rows() * cols();
234 if (currentSize > 16)
235 {
236 currentSize = 16;
237 }
238 long[] newIndices = new long[currentSize];
239 float[] newValues = new float[currentSize];
240 int nonZeroValues = 0;
241 int ownIndex = 0;
242 int otherIndex = 0;
243 if (right.isSparse())
244 {
245 FloatMatrixDataSparse other = (FloatMatrixDataSparse) right;
246 while (ownIndex < this.indices.length || otherIndex < other.indices.length)
247 {
248 float value;
249 long index;
250 if (ownIndex < this.indices.length && otherIndex < other.indices.length)
251 {
252 if (this.indices[ownIndex] == other.indices[otherIndex])
253 {
254 value = floatFunction.apply(this.matrixSI[ownIndex], other.matrixSI[otherIndex]);
255 index = this.indices[ownIndex];
256 ownIndex++;
257 otherIndex++;
258 }
259 else if (this.indices[ownIndex] < other.indices[otherIndex])
260 {
261
262 value = floatFunction.apply(this.matrixSI[ownIndex], 0.0f);
263 index = this.indices[ownIndex];
264 ownIndex++;
265 }
266 else
267 {
268
269 value = floatFunction.apply(0.0f, other.matrixSI[otherIndex]);
270 index = other.indices[otherIndex];
271 otherIndex++;
272 }
273 }
274 else if (ownIndex < this.indices.length)
275 {
276 value = floatFunction.apply(this.matrixSI[ownIndex], 0f);
277 index = this.indices[ownIndex];
278 ownIndex++;
279 }
280 else
281 {
282 value = floatFunction.apply(0.0f, other.matrixSI[otherIndex]);
283 index = other.indices[otherIndex];
284 otherIndex++;
285 }
286 if (value != 0f)
287 {
288 if (nonZeroValues >= currentSize)
289 {
290
291 currentSize *= 2;
292 if (currentSize > rows() * cols())
293 {
294 currentSize = rows() * cols();
295 }
296 long[] newNewIndices = new long[currentSize];
297 System.arraycopy(newIndices, 0, newNewIndices, 0, newIndices.length);
298 newIndices = newNewIndices;
299 float[] newNewValues = new float[currentSize];
300 System.arraycopy(newValues, 0, newNewValues, 0, newValues.length);
301 newValues = newNewValues;
302 }
303 newIndices[nonZeroValues] = index;
304 newValues[nonZeroValues] = value;
305 nonZeroValues++;
306 }
307 }
308 }
309 else
310 {
311 FloatMatrixDataDense other = (FloatMatrixDataDense) right;
312 while (otherIndex < right.matrixSI.length)
313 {
314 float value;
315 int index = otherIndex;
316 if (ownIndex < this.indices.length)
317 {
318 if (this.indices[ownIndex] == otherIndex)
319 {
320 value = floatFunction.apply(this.matrixSI[ownIndex], other.matrixSI[otherIndex]);
321 ownIndex++;
322 }
323 else
324 {
325
326 value = floatFunction.apply(0.0f, other.matrixSI[otherIndex]);
327 }
328 otherIndex++;
329 }
330 else
331 {
332 value = floatFunction.apply(0.0f, other.matrixSI[otherIndex]);
333 otherIndex++;
334 }
335 if (value != 0f)
336 {
337 if (nonZeroValues >= currentSize)
338 {
339
340 currentSize *= 2;
341 if (currentSize > rows() * cols())
342 {
343 currentSize = rows() * cols();
344 }
345 long[] newNewIndices = new long[currentSize];
346 System.arraycopy(newIndices, 0, newNewIndices, 0, newIndices.length);
347 newIndices = newNewIndices;
348 float[] newNewValues = new float[currentSize];
349 System.arraycopy(newValues, 0, newNewValues, 0, newValues.length);
350 newValues = newNewValues;
351 }
352 newIndices[nonZeroValues] = index;
353 newValues[nonZeroValues] = value;
354 nonZeroValues++;
355 }
356 }
357 }
358 if (nonZeroValues < currentSize)
359 {
360
361 long[] newNewIndices = new long[nonZeroValues];
362 System.arraycopy(newIndices, 0, newNewIndices, 0, nonZeroValues);
363 newIndices = newNewIndices;
364 float[] newNewValues = new float[nonZeroValues];
365 System.arraycopy(newValues, 0, newNewValues, 0, nonZeroValues);
366 newValues = newNewValues;
367 }
368 this.indices = newIndices;
369 this.matrixSI = newValues;
370 return this;
371 }
372
373
374 @Override
375 public final FloatMatrixDataDense toDense()
376 {
377 float[] denseSI = new float[this.rows * this.cols];
378 for (int index = 0; index < this.matrixSI.length; index++)
379 {
380 denseSI[(int) this.indices[index]] = this.matrixSI[index];
381 }
382 try
383 {
384 return new FloatMatrixDataDense(denseSI, this.rows, this.cols);
385 }
386 catch (ValueRuntimeException exception)
387 {
388 throw new RuntimeException(exception);
389 }
390 }
391
392
393 @Override
394 public final FloatMatrixDataSparse toSparse()
395 {
396 return this;
397 }
398
399
400 @Override
401 public final float getSI(final int row, final int col)
402 {
403 long index = row * this.cols + col;
404 int internalIndex = Arrays.binarySearch(this.indices, index);
405 return internalIndex < 0 ? 0.0f : this.matrixSI[internalIndex];
406 }
407
408
409 @Override
410 public final void setSI(final int row, final int col, final float valueSI)
411 {
412 long index = row * this.cols + col;
413 int internalIndex = Arrays.binarySearch(this.indices, index);
414 if (internalIndex >= 0)
415 {
416 this.matrixSI[internalIndex] = valueSI;
417 return;
418 }
419
420
421 internalIndex = -internalIndex - 1;
422 long[] indicesNew = new long[this.indices.length + 1];
423 float[] matrixSINew = new float[this.matrixSI.length + 1];
424 System.arraycopy(this.indices, 0, indicesNew, 0, internalIndex);
425 System.arraycopy(this.matrixSI, 0, matrixSINew, 0, internalIndex);
426 System.arraycopy(this.indices, internalIndex, indicesNew, internalIndex + 1, this.indices.length - internalIndex);
427 System.arraycopy(this.matrixSI, internalIndex, matrixSINew, internalIndex + 1, this.indices.length - internalIndex);
428 indicesNew[internalIndex] = index;
429 matrixSINew[internalIndex] = valueSI;
430 this.indices = indicesNew;
431 this.matrixSI = matrixSINew;
432 }
433
434
435 @Override
436 public final float[][] getDenseMatrixSI()
437 {
438 return toDense().getDenseMatrixSI();
439 }
440
441
442 @Override
443 public final double[][] getDoubleDenseMatrixSI()
444 {
445 return toDense().getDoubleDenseMatrixSI();
446 }
447
448
449 @Override
450 public final FloatMatrixDataSparse copy()
451 {
452 float[] vCopy = new float[this.matrixSI.length];
453 System.arraycopy(this.matrixSI, 0, vCopy, 0, this.matrixSI.length);
454 long[] iCopy = new long[this.indices.length];
455 System.arraycopy(this.indices, 0, iCopy, 0, this.indices.length);
456 return new FloatMatrixDataSparse(vCopy, iCopy, this.rows, this.cols);
457 }
458
459
460
461
462
463
464
465 public static FloatMatrixDataSparse instantiate(final float[][] valuesSI) throws ValueRuntimeException
466 {
467 checkRectangularAndNonNull(valuesSI);
468 int length = nonZero(valuesSI);
469 int rows = valuesSI.length;
470 final int cols = rows == 0 ? 0 : valuesSI[0].length;
471 if (cols == 0)
472 {
473 rows = 0;
474 }
475 float[] sparseSI = new float[length];
476 long[] indices = new long[length];
477 fill(valuesSI, sparseSI, indices);
478 return new FloatMatrixDataSparse(sparseSI, indices, rows, cols);
479 }
480
481
482
483
484
485
486
487
488 public static FloatMatrixDataSparse instantiate(final float[][] values, final Scale scale) throws ValueRuntimeException
489 {
490 checkRectangularAndNonNull(values);
491 int length = nonZero(values);
492 int rows = values.length;
493 final int cols = rows == 0 ? 0 : values[0].length;
494 if (cols == 0)
495 {
496 rows = 0;
497 }
498 float[] sparseSI = new float[length];
499 long[] indices = new long[length];
500 fill(values, sparseSI, indices, scale);
501 return new FloatMatrixDataSparse(sparseSI, indices, rows, cols);
502 }
503
504
505
506
507
508
509 private static int nonZero(final float[][] valuesSI)
510 {
511
512 AtomicInteger atomicLength = new AtomicInteger(0);
513 IntStream.range(0, valuesSI.length).parallel().forEach(r -> IntStream.range(0, valuesSI[0].length).forEach(c ->
514 {
515 if (valuesSI[r][c] != 0.0f)
516 {
517 atomicLength.incrementAndGet();
518 }
519 }));
520
521 return atomicLength.get();
522 }
523
524
525 @Override
526 public FloatMatrixData plus(final FloatMatrixData right) throws ValueRuntimeException
527 {
528 if (right.isDense())
529 {
530 return right.copy().incrementBy(this);
531 }
532 return this.copy().incrementBy(right);
533 }
534
535
536 @Override
537 public final FloatMatrixData minus(final FloatMatrixData right)
538 {
539 if (right.isDense())
540 {
541 return this.toDense().decrementBy(right);
542 }
543 return this.copy().decrementBy(right);
544 }
545
546
547 @Override
548 public FloatMatrixData times(final FloatMatrixData right) throws ValueRuntimeException
549 {
550 return this.copy().multiplyBy(right);
551 }
552
553
554 @Override
555 public FloatMatrixData divide(final FloatMatrixData right) throws ValueRuntimeException
556 {
557 if (right.isSparse())
558 {
559
560 return this.toDense().divide(right);
561 }
562
563 return this.copy().divideBy(right);
564 }
565
566
567 @Override
568 public int hashCode()
569 {
570 return super.hashCode();
571 }
572
573
574 @Override
575 @SuppressWarnings({"checkstyle:needbraces", "checkstyle:designforextension"})
576 public boolean equals(final Object obj)
577 {
578 if (this == obj)
579 return true;
580 if (obj == null)
581 return false;
582 if (!(obj instanceof FloatMatrixData))
583 return false;
584 FloatMatrixData other = (FloatMatrixData) obj;
585 if (this.rows != other.rows)
586 return false;
587 if (this.cols != other.cols)
588 return false;
589 if (other instanceof FloatMatrixDataDense)
590 return super.equals(other);
591 if (getClass() != obj.getClass())
592 return false;
593
594 if (!Arrays.equals(this.indices, ((FloatMatrixDataSparse) other).indices))
595 return false;
596 return Arrays.equals(this.matrixSI, ((FloatMatrixDataSparse) other).matrixSI);
597 }
598
599 }