View Javadoc
1   package org.djunits.value.vfloat.matrix;
2   
3   import java.util.Arrays;
4   import java.util.concurrent.atomic.AtomicInteger;
5   import java.util.stream.IntStream;
6   
7   import org.djunits.value.StorageType;
8   import org.djunits.value.ValueException;
9   
10  /**
11   * Stores sparse data for a FloatMatrix and carries out basic operations.
12   * <p>
13   * Copyright (c) 2013-2019 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
14   * BSD-style license. See <a href="http://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
15   * </p>
16   * $LastChangedDate: 2015-07-24 02:58:59 +0200 (Fri, 24 Jul 2015) $, @version $Revision: 1147 $, by $Author: averbraeck $,
17   * initial version Oct 3, 2015 <br>
18   * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
19   * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
20   */
21  public class FloatMatrixDataSparse extends FloatMatrixData
22  {
23      /** */
24      private static final long serialVersionUID = 1L;
25  
26      /** the index values of the Matrix. */
27      private long[] indices;
28  
29      /** the length of the vector (padded with 0 after highest index in indices). */
30      private final int length;
31  
32      /**
33       * Create a vector with sparse data.
34       * @param matrixSI float[]; the data to store
35       * @param indices long[]; the index values of the Matrix, with &lt;tt&gt;index = row * cols + col&lt;/tt&gt;
36       * @param length int; the length of the vector (padded with 0 after highest index in indices)
37       * @param rows int; the number of rows
38       * @param cols int; the number of columns
39       */
40      public FloatMatrixDataSparse(final float[] matrixSI, final long[] indices, final int length, final int rows, final int cols)
41      {
42          super(StorageType.SPARSE);
43          this.matrixSI = matrixSI;
44          this.indices = indices;
45          this.length = length;
46          this.rows = rows;
47          this.cols = cols;
48      }
49  
50      /**
51       * Create a vector with sparse data from an internal vector with dense data.
52       * @param denseSI float[]; the dense data to store
53       * @param rows int; the number of rows
54       * @param cols int; the number of columns
55       * @throws ValueException in case size is incorrect
56       */
57      public FloatMatrixDataSparse(final float[] denseSI, final int rows, final int cols) throws ValueException
58      {
59          super(StorageType.SPARSE);
60          if (denseSI == null || denseSI.length == 0)
61          {
62              throw new ValueException("FloatMatrixDataSparse constructor, denseSI == null || denseSI.length == 0");
63          }
64  
65          this.length = nonZero(denseSI);
66          this.rows = rows;
67          this.cols = cols;
68          this.matrixSI = new float[this.length];
69          this.indices = new long[this.length];
70          fill(denseSI, this.matrixSI, this.indices);
71      }
72  
73      /**
74       * Create a vector with sparse data.
75       * @param dataSI float[][]; the data to store
76       * @throws ValueException in case matrix is ragged
77       */
78      public FloatMatrixDataSparse(final float[][] dataSI) throws ValueException
79      {
80          super(StorageType.SPARSE);
81          if (dataSI == null || dataSI.length == 0)
82          {
83              throw new ValueException("FloatMatrixDataSparse constructor, matrixSI == null || matrixSI.length == 0");
84          }
85  
86          this.length = nonZero(dataSI);
87          this.rows = dataSI.length;
88          this.cols = dataSI[0].length;
89          this.matrixSI = new float[this.length];
90          this.indices = new long[this.length];
91          fill(dataSI, this.matrixSI, this.indices);
92      }
93  
94      /**
95       * Fill the sparse data structures matrixSI[] and indices[]. Note: output vectors have to be initialized at the right size.
96       * Cannot be parallelized because of stateful and sequence-sensitive count.
97       * @param data float[][]; the input data
98       * @param matrixSI float[]; the matrix data to write
99       * @param indices long[]; the indices to write
100      * @throws ValueException in case matrix is ragged
101      */
102     @SuppressWarnings("checkstyle:finalparameters")
103     private static void fill(final float[][] data, float[] matrixSI, long[] indices) throws ValueException
104     {
105         int rows = data.length;
106         int cols = data[0].length;
107         int count = 0;
108         for (int r = 0; r < rows; r++)
109         {
110             float[] row = data[r];
111             if (row.length != cols)
112             {
113                 throw new ValueException("Matrix is ragged");
114             }
115             for (int c = 0; c < cols; c++)
116             {
117                 int index = r * cols + c;
118                 if (row[c] != 0.0)
119                 {
120                     matrixSI[count] = row[c];
121                     indices[count] = index;
122                     count++;
123                 }
124             }
125         }
126     }
127 
128     /**
129      * Fill the sparse data structures matrixSI[] and indices[]. Note: output vectors have to be initialized at the right size.
130      * Cannot be parallelized because of stateful and sequence-sensitive count.
131      * @param data float[]; the input data
132      * @param matrixSI float[]; the matrix data to write
133      * @param indices long[]; the indices to write
134      */
135     @SuppressWarnings("checkstyle:finalparameters")
136     private static void fill(final float[] data, float[] matrixSI, long[] indices)
137     {
138         int count = 0;
139         for (int i = 0; i < data.length; i++)
140         {
141             if (data[i] != 0.0)
142             {
143                 matrixSI[count] = data[i];
144                 indices[count] = i;
145                 count++;
146             }
147         }
148     }
149 
150     /** {@inheritDoc} */
151     @Override
152     public final FloatMatrixDataDense toDense()
153     {
154         float[] denseSI = new float[this.rows * this.cols];
155         for (int index = 0; index < this.matrixSI.length; index++)
156         {
157             denseSI[(int) this.indices[index]] = this.matrixSI[index];
158         }
159         try
160         {
161             return new FloatMatrixDataDense(denseSI, this.rows, this.cols);
162         }
163         catch (ValueException exception)
164         {
165             throw new RuntimeException(exception); // cannot happen -- denseSI has the right size
166         }
167     }
168 
169     /** {@inheritDoc} */
170     @Override
171     public final float getSI(final int row, final int col)
172     {
173         long index = row * this.cols + col;
174         int internalIndex = Arrays.binarySearch(this.indices, index);
175         return internalIndex < 0 ? 0.0f : this.matrixSI[internalIndex];
176     }
177 
178     /** {@inheritDoc} */
179     @Override
180     public final void setSI(final int row, final int col, final float valueSI)
181     {
182         long index = row * this.cols + col;
183         int internalIndex = Arrays.binarySearch(this.indices, index);
184         if (internalIndex >= 0)
185         {
186             this.matrixSI[internalIndex] = valueSI;
187             return;
188         }
189 
190         // make room. TODO increase size in chunks
191         internalIndex = -internalIndex - 1;
192         long[] indicesNew = new long[this.indices.length + 1];
193         float[] matrixSINew = new float[this.matrixSI.length + 1];
194         System.arraycopy(this.indices, 0, indicesNew, 0, internalIndex);
195         System.arraycopy(this.matrixSI, 0, matrixSINew, 0, internalIndex);
196         System.arraycopy(this.indices, internalIndex, indicesNew, internalIndex - 1, this.indices.length - internalIndex);
197         System.arraycopy(this.matrixSI, internalIndex, matrixSINew, internalIndex - 1, this.indices.length - internalIndex);
198         indicesNew[internalIndex] = index;
199         matrixSINew[internalIndex] = valueSI;
200         this.indices = indicesNew;
201         this.matrixSI = matrixSINew;
202     }
203 
204     /** {@inheritDoc} */
205     @Override
206     public final float[][] getDenseMatrixSI()
207     {
208         return toDense().getDenseMatrixSI();
209     }
210 
211     /** {@inheritDoc} */
212     @Override
213     public final double[][] getDoubleDenseMatrixSI()
214     {
215         return toDense().getDoubleDenseMatrixSI();
216     }
217 
218     /** {@inheritDoc} */
219     @Override
220     public final FloatMatrixDataSparse copy()
221     {
222         float[] vCopy = new float[this.matrixSI.length];
223         System.arraycopy(this.matrixSI, 0, vCopy, 0, this.matrixSI.length);
224         long[] iCopy = new long[this.indices.length];
225         System.arraycopy(this.indices, 0, iCopy, 0, this.indices.length);
226         return new FloatMatrixDataSparse(vCopy, iCopy, this.length, this.rows, this.cols);
227     }
228 
229     /**
230      * Instantiate a FloatMatrixDataSparse from an array.
231      * @param valuesSI float[][]; the (SI) values to store
232      * @return the FloatMatrixDataSparse
233      * @throws ValueException in case matrix is ragged
234      */
235     public static FloatMatrixDataSparse instantiate(final float[][] valuesSI) throws ValueException
236     {
237         int length = nonZero(valuesSI);
238         final int rows = valuesSI.length;
239         final int cols = valuesSI[0].length;
240         float[] sparseSI = new float[length];
241         long[] indices = new long[length];
242         fill(valuesSI, sparseSI, indices);
243         return new FloatMatrixDataSparse(sparseSI, indices, length, rows, cols);
244     }
245 
246     /**
247      * Calculate the number of non-zero values in this float[][] matrix.
248      * @param valuesSI float[][]; the float[][] matrix
249      * @return the number of non-zero values in this float[][] matrix
250      */
251     private static int nonZero(final float[][] valuesSI)
252     {
253         // determine number of non-null cells
254         AtomicInteger atomicLength = new AtomicInteger(0);
255         IntStream.range(0, valuesSI.length).parallel().forEach(r -> IntStream.range(0, valuesSI[0].length).forEach(c ->
256         {
257             if (valuesSI[r][c] != 0.0)
258             {
259                 atomicLength.incrementAndGet();
260             }
261         }));
262 
263         return atomicLength.get();
264     }
265 
266     /**
267      * Calculate the number of non-zero values in this float[] vector.
268      * @param valuesSI float[]; the float[] vector
269      * @return the number of non-zero values in this float[] vector
270      */
271     private static int nonZero(final float[] valuesSI)
272     {
273         return (int) IntStream.range(0, valuesSI.length).parallel().mapToDouble(i -> valuesSI[i]).filter(d -> d != 0.0).count();
274     }
275 
276     /** {@inheritDoc} */
277     @Override
278     public final void incrementBy(final FloatMatrixData right) throws ValueException
279     {
280         /*-
281             // the number of new cells = the sum of the number of cells of each minus the overlapping cells.
282             int overlap = 0;
283             for (int index = 0; index < this.matrixSI.length; index++)
284             {
285                 int c = (int) this.indices[index] % this.cols;
286                 int r = (int) this.indices[index] / this.cols;
287                 if (right.getSI(r, c) != 0.0)
288                 {
289                     overlap++;
290                 }
291             }
292             int newLength = cardinality() + right.cardinality() - overlap;
293          */
294         int newLength = 0;
295         for (int r = 0; r < rows(); r++)
296         {
297             for (int c = 0; c < cols(); c++)
298             {
299                 if (this.getSI(r, c) + right.getSI(r, c) != 0.0)
300                 {
301                     newLength++;
302                 }
303             }
304         }
305         float[] newMatrixSI = new float[newLength];
306         long[] newIndices = new long[newLength];
307 
308         // fill the sparse data structures. Cannot be parallelized because of stateful and sequence-sensitive count
309         int count = 0;
310         for (int r = 0; r < rows(); r++)
311         {
312             for (int c = 0; c < cols(); c++)
313             {
314                 float value = this.getSI(r, c) + right.getSI(r, c);
315                 if (value != 0.0)
316                 {
317                     int index = r * cols() + c;
318                     newMatrixSI[count] = value;
319                     newIndices[count] = index;
320                     count++;
321                 }
322             }
323         }
324 
325         this.indices = newIndices;
326         this.matrixSI = newMatrixSI;
327     }
328 
329     /** {@inheritDoc} */
330     @Override
331     public final void decrementBy(final FloatMatrixData right) throws ValueException
332     {
333         int newLength = 0;
334         for (int r = 0; r < rows(); r++)
335         {
336             for (int c = 0; c < cols(); c++)
337             {
338                 if (this.getSI(r, c) - right.getSI(r, c) != 0.0)
339                 {
340                     newLength++;
341                 }
342             }
343         }
344         float[] newMatrixSI = new float[newLength];
345         long[] newIndices = new long[newLength];
346 
347         // fill the sparse data structures. Cannot be parallelized because of stateful and sequence-sensitive count
348         int count = 0;
349         for (int r = 0; r < rows(); r++)
350         {
351             for (int c = 0; c < cols(); c++)
352             {
353                 float value = this.getSI(r, c) - right.getSI(r, c);
354                 if (value != 0.0)
355                 {
356                     int index = r * cols() + c;
357                     newMatrixSI[count] = value;
358                     newIndices[count] = index;
359                     count++;
360                 }
361             }
362         }
363 
364         this.indices = newIndices;
365         this.matrixSI = newMatrixSI;
366     }
367 
368     /** {@inheritDoc} */
369     @Override
370     public final void multiplyBy(final FloatMatrixData right) throws ValueException
371     {
372         int newLength = 0;
373         for (int r = 0; r < rows(); r++)
374         {
375             for (int c = 0; c < cols(); c++)
376             {
377                 if (this.getSI(r, c) * right.getSI(r, c) != 0.0)
378                 {
379                     newLength++;
380                 }
381             }
382         }
383         float[] newMatrixSI = new float[newLength];
384         long[] newIndices = new long[newLength];
385 
386         // fill the sparse data structures. Cannot be parallelized because of stateful and sequence-sensitive count
387         int count = 0;
388         for (int r = 0; r < rows(); r++)
389         {
390             for (int c = 0; c < cols(); c++)
391             {
392                 float value = this.getSI(r, c) * right.getSI(r, c);
393                 if (value != 0.0)
394                 {
395                     int index = r * cols() + c;
396                     newMatrixSI[count] = value;
397                     newIndices[count] = index;
398                     count++;
399                 }
400             }
401         }
402 
403         this.indices = newIndices;
404         this.matrixSI = newMatrixSI;
405     }
406 
407     /** {@inheritDoc} */
408     @Override
409     public final void divideBy(final FloatMatrixData right) throws ValueException
410     {
411         int newLength = 0;
412         for (int r = 0; r < rows(); r++)
413         {
414             for (int c = 0; c < cols(); c++)
415             {
416                 if (this.getSI(r, c) / right.getSI(r, c) != 0.0)
417                 {
418                     newLength++;
419                 }
420             }
421         }
422         float[] newMatrixSI = new float[newLength];
423         long[] newIndices = new long[newLength];
424 
425         // fill the sparse data structures. Cannot be parallelized because of stateful and sequence-sensitive count
426         int count = 0;
427         for (int r = 0; r < rows(); r++)
428         {
429             for (int c = 0; c < cols(); c++)
430             {
431                 float value = this.getSI(r, c) / right.getSI(r, c);
432                 if (value != 0.0)
433                 {
434                     int index = r * cols() + c;
435                     newMatrixSI[count] = value;
436                     newIndices[count] = index;
437                     count++;
438                 }
439             }
440         }
441 
442         this.indices = newIndices;
443         this.matrixSI = newMatrixSI;
444     }
445 
446     /** {@inheritDoc} */
447     @Override
448     @SuppressWarnings("checkstyle:designforextension")
449     public int hashCode()
450     {
451         final int prime = 31;
452         int result = super.hashCode();
453         result = prime * result + Arrays.hashCode(this.indices);
454         result = prime * result + this.length;
455         return result;
456     }
457 
458     /** {@inheritDoc} */
459     @Override
460     @SuppressWarnings({"checkstyle:needbraces", "checkstyle:designforextension"})
461     public boolean equals(final Object obj)
462     {
463         if (this == obj)
464             return true;
465         if (!super.equals(obj))
466             return false;
467         if (getClass() != obj.getClass())
468             return false;
469         FloatMatrixDataSparse other = (FloatMatrixDataSparse) obj;
470         if (!Arrays.equals(this.indices, other.indices))
471             return false;
472         if (this.length != other.length)
473             return false;
474         return true;
475     }
476 }