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