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