View Javadoc
1   package org.djunits.value.vdouble.vector.data;
2   
3   import java.util.Arrays;
4   
5   import org.djunits.value.ValueRuntimeException;
6   import org.djunits.value.storage.StorageType;
7   import org.djunits.value.vdouble.function.DoubleFunction;
8   import org.djunits.value.vdouble.function.DoubleFunction2;
9   
10  /**
11   * Stores sparse data for a DoubleVector 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="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
15   * </p>
16   * @author <a href="https://www.tudelft.nl/averbraeck">Alexander Verbraeck</a>
17   * @author <a href="https://www.tudelft.nl/staff/p.knoppers/">Peter Knoppers</a>
18   */
19  public class DoubleVectorDataSparse extends DoubleVectorData
20  {
21      /** */
22      private static final long serialVersionUID = 1L;
23  
24      /** The index values of the Vector. */
25      private int[] indices;
26  
27      /** The length of the vector (padded with 0 after highest index in indices). */
28      private final int size;
29  
30      /**
31       * Create a vector with sparse data.
32       * @param vectorSI double[]; the data to store
33       * @param indices int[]; the index values of the Vector
34       * @param size int; the length of the vector (padded with 0 after highest index in indices)
35       */
36      public DoubleVectorDataSparse(final double[] vectorSI, final int[] indices, final int size)
37      {
38          super(StorageType.SPARSE);
39          this.vectorSI = vectorSI;
40          this.indices = indices;
41          this.size = size;
42      }
43  
44      /** {@inheritDoc} */
45      @Override
46      public final int cardinality()
47      {
48          return this.indices.length;
49      }
50  
51      /** {@inheritDoc} */
52      @Override
53      public DoubleVectorData assign(final DoubleFunction doubleFunction)
54      {
55          if (doubleFunction.apply(0d) != 0d)
56          {
57              // It is most unlikely that the result AND the left and right operands are efficiently stored in Sparse format
58              DoubleVectorDataSparse result = toDense().assign(doubleFunction).toSparse();
59              this.indices = result.indices;
60              this.vectorSI = result.vectorSI;
61              return this;
62          }
63          // The code below relies on the fact that doubleFunction.apply(0d) yields 0d
64          int currentSize = size();
65          if (currentSize > 16)
66          {
67              currentSize = 16;
68          }
69          int[] newIndices = new int[currentSize];
70          double[] newValues = new double[currentSize];
71          int nonZeroValues = 0;
72          int ownIndex = 0;
73          while (ownIndex < this.indices.length)
74          {
75              int index = this.indices[ownIndex];
76              double value = doubleFunction.apply(this.vectorSI[ownIndex]);
77              ownIndex++;
78              if (value != 0d)
79              {
80                  if (nonZeroValues >= currentSize)
81                  {
82                      // increase size of arrays
83                      currentSize *= 2;
84                      if (currentSize > size())
85                      {
86                          currentSize = size();
87                      }
88                      int[] newNewIndices = new int[currentSize];
89                      System.arraycopy(newIndices, 0, newNewIndices, 0, newIndices.length);
90                      newIndices = newNewIndices;
91                      double[] newNewValues = new double[currentSize];
92                      System.arraycopy(newValues, 0, newNewValues, 0, newValues.length);
93                      newValues = newNewValues;
94                  }
95                  newIndices[nonZeroValues] = index;
96                  newValues[nonZeroValues] = value;
97                  nonZeroValues++;
98              }
99          }
100         if (nonZeroValues < currentSize)
101         {
102             // reduce size of arrays
103             int[] newNewIndices = new int[nonZeroValues];
104             System.arraycopy(newIndices, 0, newNewIndices, 0, nonZeroValues);
105             newIndices = newNewIndices;
106             double[] newNewValues = new double[nonZeroValues];
107             System.arraycopy(newValues, 0, newNewValues, 0, nonZeroValues);
108             newValues = newNewValues;
109         }
110         this.indices = newIndices;
111         this.vectorSI = newValues;
112         return this;
113     }
114 
115     /** {@inheritDoc} */
116     @Override
117     public final DoubleVectorDataSparse assign(final DoubleFunction2 doubleFunction, final DoubleVectorData right)
118     {
119         if (doubleFunction.apply(0d, 0d) != 0d)
120         {
121             // It is most unlikely that the result AND the left and right operands are efficiently stored in Sparse format
122             DoubleVectorDataSparse result = toDense().assign(doubleFunction, right).toSparse();
123             this.indices = result.indices;
124             this.vectorSI = result.vectorSI;
125             return this;
126         }
127         // The code below relies on the fact that doubleFunction.apply(0d, 0d) yields 0d
128         checkSizes(right);
129         int currentSize = size();
130         if (currentSize > 16)
131         {
132             currentSize = 16;
133         }
134         int[] newIndices = new int[currentSize];
135         double[] newValues = new double[currentSize];
136         int nonZeroValues = 0;
137         int ownIndex = 0;
138         int otherIndex = 0;
139         if (right.isSparse())
140         { // both are sparse, result must be sparse
141             DoubleVectorDataSparse../org/djunits/value/vdouble/vector/data/DoubleVectorDataSparse.html#DoubleVectorDataSparse">DoubleVectorDataSparse other = (DoubleVectorDataSparse) right;
142             while (ownIndex < this.indices.length || otherIndex < other.indices.length)
143             {
144                 double value;
145                 int index;
146                 if (ownIndex < this.indices.length && otherIndex < other.indices.length)
147                 { // neither we nor right has run out of values
148                     if (this.indices[ownIndex] == other.indices[otherIndex])
149                     {
150                         value = doubleFunction.apply(this.vectorSI[ownIndex], other.vectorSI[otherIndex]);
151                         index = this.indices[ownIndex];
152                         ownIndex++;
153                         otherIndex++;
154                     }
155                     else if (this.indices[ownIndex] < other.indices[otherIndex])
156                     {
157                         // we have a non-zero; right has a zero
158                         value = doubleFunction.apply(this.vectorSI[ownIndex], 0d);
159                         index = this.indices[ownIndex];
160                         ownIndex++;
161                     }
162                     else
163                     {
164                         // we have a zero; right has a non-zero
165                         value = doubleFunction.apply(0d, other.vectorSI[otherIndex]);
166                         index = other.indices[otherIndex];
167                         otherIndex++;
168                     }
169                 }
170                 else if (ownIndex < this.indices.length)
171                 { // right has run out of values; we have not
172                     value = doubleFunction.apply(this.vectorSI[ownIndex], 0d);
173                     index = this.indices[ownIndex];
174                     ownIndex++;
175                 }
176                 else
177                 { // we have run out of values; right has not
178                     value = doubleFunction.apply(0d, other.vectorSI[otherIndex]);
179                     index = other.indices[otherIndex];
180                     otherIndex++;
181                 }
182                 if (value != 0d)
183                 {
184                     if (nonZeroValues >= currentSize)
185                     {
186                         // increase size of arrays
187                         currentSize *= 2;
188                         if (currentSize > size())
189                         {
190                             currentSize = size();
191                         }
192                         int[] newNewIndices = new int[currentSize];
193                         System.arraycopy(newIndices, 0, newNewIndices, 0, newIndices.length);
194                         newIndices = newNewIndices;
195                         double[] newNewValues = new double[currentSize];
196                         System.arraycopy(newValues, 0, newNewValues, 0, newValues.length);
197                         newValues = newNewValues;
198                     }
199                     newIndices[nonZeroValues] = index;
200                     newValues[nonZeroValues] = value;
201                     nonZeroValues++;
202                 }
203             }
204         }
205         else
206         { // we are sparse; other is dense, result must be sparse
207             DoubleVectorDataDense/../org/djunits/value/vdouble/vector/data/DoubleVectorDataDense.html#DoubleVectorDataDense">DoubleVectorDataDense other = (DoubleVectorDataDense) right;
208             while (otherIndex < right.vectorSI.length)
209             {
210                 double value;
211                 int index = otherIndex;
212                 if (ownIndex < this.indices.length)
213                 { // neither we nor right has run out of values
214                     if (this.indices[ownIndex] == otherIndex)
215                     {
216                         value = doubleFunction.apply(this.vectorSI[ownIndex], other.vectorSI[otherIndex]);
217                         ownIndex++;
218                     }
219                     else
220                     {
221                         // we have a zero; other has a value
222                         value = doubleFunction.apply(0d, other.vectorSI[otherIndex]);
223                     }
224                     otherIndex++;
225                 }
226                 else
227                 { // we have run out of values; right has not
228                     value = doubleFunction.apply(0d, other.vectorSI[otherIndex]);
229                     otherIndex++;
230                 }
231                 if (value != 0d)
232                 {
233                     if (nonZeroValues >= currentSize)
234                     {
235                         // increase size of arrays
236                         currentSize *= 2;
237                         if (currentSize > size())
238                         {
239                             currentSize = size();
240                         }
241                         int[] newNewIndices = new int[currentSize];
242                         System.arraycopy(newIndices, 0, newNewIndices, 0, newIndices.length);
243                         newIndices = newNewIndices;
244                         double[] newNewValues = new double[currentSize];
245                         System.arraycopy(newValues, 0, newNewValues, 0, newValues.length);
246                         newValues = newNewValues;
247                     }
248                     newIndices[nonZeroValues] = index;
249                     newValues[nonZeroValues] = value;
250                     nonZeroValues++;
251                 }
252             }
253         }
254         if (nonZeroValues < currentSize)
255         {
256             // reduce size of arrays
257             int[] newNewIndices = new int[nonZeroValues];
258             System.arraycopy(newIndices, 0, newNewIndices, 0, nonZeroValues);
259             newIndices = newNewIndices;
260             double[] newNewValues = new double[nonZeroValues];
261             System.arraycopy(newValues, 0, newNewValues, 0, nonZeroValues);
262             newValues = newNewValues;
263         }
264         this.indices = newIndices;
265         this.vectorSI = newValues;
266         return this;
267     }
268 
269     /** {@inheritDoc} */
270     @Override
271     public final DoubleVectorDataDense toDense()
272     {
273         double[] denseSI = new double[this.size];
274         for (int index = 0; index < this.vectorSI.length; index++)
275         {
276             denseSI[this.indices[index]] = this.vectorSI[index];
277         }
278         return new DoubleVectorDataDense(denseSI);
279     }
280 
281     /** {@inheritDoc} */
282     @Override
283     public final DoubleVectorDataSparse toSparse()
284     {
285         return this;
286     }
287 
288     /** {@inheritDoc} */
289     @Override
290     public final int size()
291     {
292         return this.size;
293     }
294 
295     /** {@inheritDoc} */
296     @Override
297     public final double getSI(final int index)
298     {
299         int internalIndex = Arrays.binarySearch(this.indices, index);
300         return internalIndex < 0 ? 0.0 : this.vectorSI[internalIndex];
301     }
302 
303     /** {@inheritDoc} */
304     @Override
305     public final void setSI(final int index, final double valueSI)
306     {
307         int internalIndex = Arrays.binarySearch(this.indices, index);
308         if (internalIndex >= 0)
309         {
310             if (valueSI == 0f)
311             {
312                 // remove room
313                 int[] indicesNew = new int[this.indices.length - 1];
314                 double[] vectorSINew = new double[this.vectorSI.length - 1];
315                 System.arraycopy(this.indices, 0, indicesNew, 0, internalIndex);
316                 System.arraycopy(this.vectorSI, 0, vectorSINew, 0, internalIndex);
317                 System.arraycopy(this.indices, internalIndex + 1, indicesNew, internalIndex,
318                         this.indices.length - internalIndex - 1);
319                 System.arraycopy(this.vectorSI, internalIndex + 1, vectorSINew, internalIndex,
320                         this.indices.length - internalIndex - 1);
321                 this.indices = indicesNew;
322                 this.vectorSI = vectorSINew;
323             }
324             else
325             {
326                 this.vectorSI[internalIndex] = valueSI;
327             }
328             return;
329         }
330 
331         // make room. TODO increase size in chunks
332         internalIndex = -internalIndex - 1;
333         int[] indicesNew = new int[this.indices.length + 1];
334         double[] vectorSINew = new double[this.vectorSI.length + 1];
335         System.arraycopy(this.indices, 0, indicesNew, 0, internalIndex);
336         System.arraycopy(this.vectorSI, 0, vectorSINew, 0, internalIndex);
337         // System.out.println("arraycopy1 current size is " + this.indices.length + " srcPos=" + internalIndex +
338         // ", new size is "
339         // + indicesNew.length + " dstPos=" + (internalIndex + 1) + " length=" + (this.indices.length - internalIndex));
340         System.arraycopy(this.indices, internalIndex, indicesNew, internalIndex + 1, this.indices.length - internalIndex);
341         System.arraycopy(this.vectorSI, internalIndex, vectorSINew, internalIndex + 1, this.indices.length - internalIndex);
342         // System.arraycopy(this.indices, internalIndex, indicesNew, internalIndex - 1, this.indices.length - internalIndex);
343         // System.arraycopy(this.vectorSI, internalIndex, vectorSINew, internalIndex - 1, this.indices.length - internalIndex);
344         indicesNew[internalIndex] = index;
345         vectorSINew[internalIndex] = valueSI;
346         this.indices = indicesNew;
347         this.vectorSI = vectorSINew;
348     }
349 
350     /** {@inheritDoc} */
351     @Override
352     public final double[] getDenseVectorSI()
353     {
354         return toDense().vectorSI;
355     }
356 
357     /** {@inheritDoc} */
358     @Override
359     public final DoubleVectorDataSparse copy()
360     {
361         double[] vCopy = new double[this.vectorSI.length];
362         System.arraycopy(this.vectorSI, 0, vCopy, 0, this.vectorSI.length);
363         int[] iCopy = new int[this.indices.length];
364         System.arraycopy(this.indices, 0, iCopy, 0, this.indices.length);
365         return new DoubleVectorDataSparse(vCopy, iCopy, this.size);
366     }
367 
368     /**
369      * Instantiate a DoubleVectorDataSparse from an array.
370      * @param valuesSI double[]; the (SI) values to store
371      * @return the DoubleVectorDataSparse
372      */
373     public static DoubleVectorDataSparse instantiate(final double[] valuesSI)
374     {
375         // determine number of non-null cells
376         int length = (int) Arrays.stream(valuesSI).parallel().filter(d -> d != 0.0).count();
377         double[] sparseSI = new double[length];
378         int[] indices = new int[length];
379 
380         // fill the sparse data structures. Cannot be parallelized because of stateful and sequence-sensitive count
381         int count = 0;
382         for (int i = 0; i < valuesSI.length; i++)
383         {
384             if (valuesSI[i] != 0.0)
385             {
386                 sparseSI[count] = valuesSI[i];
387                 indices[count] = i;
388                 count++;
389             }
390         }
391         return new DoubleVectorDataSparse(sparseSI, indices, valuesSI.length);
392     }
393 
394     /** {@inheritDoc} */
395     @Override
396     public final DoubleVectorDataor/data/DoubleVectorData.html#DoubleVectorData">DoubleVectorData plus(final DoubleVectorData right)
397     {
398         if (right.isDense())
399         {
400             DoubleVectorData result = right.plus(this);
401             return result;
402         }
403         checkSizes(right);
404         return this.copy().incrementBy(right);
405     }
406 
407     /** {@inheritDoc} */
408     @Override
409     public final DoubleVectorDatar/data/DoubleVectorData.html#DoubleVectorData">DoubleVectorData minus(final DoubleVectorData right)
410     {
411         return this.copy().decrementBy(right);
412     }
413 
414     /** {@inheritDoc} */
415     @Override
416     public final DoubleVectorDatar/data/DoubleVectorData.html#DoubleVectorData">DoubleVectorData times(final DoubleVectorData right)
417     {
418         return this.copy().multiplyBy(right);
419     }
420 
421     /** {@inheritDoc} */
422     @Override
423     public final DoubleVectorData/data/DoubleVectorData.html#DoubleVectorData">DoubleVectorData divide(final DoubleVectorData right) throws ValueRuntimeException
424     {
425         if (right.isSparse())
426         {
427             // Sparse divided by sparse makes a dense
428             return this.toDense().divideBy(right);
429         }
430         // Sparse divided by dense makes a sparse
431         return this.copy().divideBy(right);
432     }
433 
434     /** {@inheritDoc} */
435     @Override
436     public int hashCode()
437     {
438         return super.hashCode();
439     }
440 
441     /** {@inheritDoc} */
442     @Override
443     @SuppressWarnings({"checkstyle:needbraces", "checkstyle:designforextension"})
444     public boolean equals(final Object obj)
445     {
446         if (this == obj)
447             return true;
448         if (obj == null)
449             return false;
450         if (!(obj instanceof DoubleVectorData))
451             return false;
452         DoubleVectorData../../../org/djunits/value/vdouble/vector/data/DoubleVectorData.html#DoubleVectorData">DoubleVectorData other = (DoubleVectorData) obj;
453         if (this.size() != other.size())
454             return false;
455         if (other instanceof DoubleVectorDataDense)
456             return super.equals(other);
457         if (getClass() != obj.getClass())
458             return false;
459         // Both are sparse
460         if (!Arrays.equals(this.indices, ((DoubleVectorDataSparse) other).indices))
461             return false;
462         return Arrays.equals(this.vectorSI, ((DoubleVectorDataSparse) other).vectorSI);
463     }
464 
465 }