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