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