View Javadoc
1   package org.djunits.value.vfloat.vector;
2   
3   import java.util.Arrays;
4   import java.util.List;
5   import java.util.SortedMap;
6   import java.util.stream.IntStream;
7   
8   import org.djunits.unit.scale.Scale;
9   import org.djunits.value.StorageType;
10  import org.djunits.value.ValueException;
11  import org.djunits.value.vfloat.scalar.FloatScalarInterface;
12  
13  /**
14   * Stores the data for a FloatVector and carries out basic operations.
15   * <p>
16   * Copyright (c) 2013-2018 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
17   * BSD-style license. See <a href="http://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
18   * </p>
19   * $LastChangedDate: 2015-07-24 02:58:59 +0200 (Fri, 24 Jul 2015) $, @version $Revision: 1147 $, by $Author: averbraeck $,
20   * initial version Oct 3, 2015 <br>
21   * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
22   * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
23   */
24  abstract class FloatVectorData
25  {
26      /** the internal storage of the Vector; can be sparse or dense. */
27      @SuppressWarnings("checkstyle:visibilitymodifier")
28      protected float[] vectorSI;
29  
30      /** the data type. */
31      private final StorageType storageType;
32  
33      /**
34       * @param storageType the data type.
35       */
36      FloatVectorData(final StorageType storageType)
37      {
38          super();
39          this.storageType = storageType;
40      }
41  
42      /** ============================================================================================ */
43      /** ====================================== INSTANTIATION ======================================= */
44      /** ============================================================================================ */
45  
46      /**
47       * Instantiate a FloatVectorData with the right data type.
48       * @param values the (SI) values to store
49       * @param scale the scale of the unit to use for conversion to SI
50       * @param storageType the data type to use
51       * @return the FloatVectorData with the right data type
52       * @throws ValueException when values are null, or storageType is null
53       */
54      public static FloatVectorData instantiate(final float[] values, final Scale scale, final StorageType storageType)
55              throws ValueException
56      {
57          if (values == null)
58          {
59              throw new ValueException("FloatVectorData.instantiate: float[] values is null");
60          }
61  
62          float[] valuesSI = new float[values.length];
63          IntStream.range(0, values.length).parallel().forEach(i -> valuesSI[i] = (float) scale.toStandardUnit(values[i]));
64  
65          switch (storageType)
66          {
67              case DENSE:
68                  return new FloatVectorDataDense(valuesSI);
69  
70              case SPARSE:
71                  return FloatVectorDataSparse.instantiate(valuesSI);
72  
73              default:
74                  throw new ValueException("Unknown data type in FloatVectorData.instantiate: " + storageType);
75          }
76      }
77  
78      /**
79       * Instantiate a FloatVectorData with the right data type.
80       * @param values the values to store
81       * @param scale the scale of the unit to use for conversion to SI
82       * @param storageType the data type to use
83       * @return the FloatVectorData with the right data type
84       * @throws ValueException when list is null, or storageType is null
85       */
86      public static FloatVectorData instantiate(final List<Float> values, final Scale scale, final StorageType storageType)
87              throws ValueException
88      {
89          if (values == null)
90          {
91              throw new ValueException("FloatVectorData.instantiate: List<Float> values is null");
92          }
93  
94          float[] valuesSI = new float[values.size()];
95          IntStream.range(0, values.size()).parallel().forEach(i -> valuesSI[i] = (float) scale.toStandardUnit(values.get(i)));
96  
97          switch (storageType)
98          {
99              case DENSE:
100                 return new FloatVectorDataDense(valuesSI);
101 
102             case SPARSE:
103                 return FloatVectorDataSparse.instantiate(valuesSI);
104 
105             default:
106                 throw new ValueException("Unknown data type in FloatVectorData.instantiate: " + storageType);
107         }
108     }
109 
110     /**
111      * Instantiate a FloatVectorData with the right data type.
112      * @param values the values to store
113      * @param storageType the data type to use
114      * @return the FloatVectorData with the right data type
115      * @throws ValueException when values is null, or storageType is null
116      */
117     public static FloatVectorData instantiate(final FloatScalarInterface[] values, final StorageType storageType)
118             throws ValueException
119     {
120         if (values == null)
121         {
122             throw new ValueException("FloatVectorData.instantiate: FloatScalar[] values is null");
123         }
124 
125         float[] valuesSI = new float[values.length];
126         IntStream.range(0, values.length).parallel().forEach(i -> valuesSI[i] = values[i].getSI());
127 
128         switch (storageType)
129         {
130             case DENSE:
131                 return new FloatVectorDataDense(valuesSI);
132 
133             case SPARSE:
134                 return FloatVectorDataSparse.instantiate(valuesSI);
135 
136             default:
137                 throw new ValueException("Unknown data type in FloatVectorData.instantiate: " + storageType);
138         }
139     }
140 
141     /**
142      * Instantiate a FloatVectorData with the right data type.
143      * @param values the FloatScalar values to store
144      * @param storageType the data type to use
145      * @return the FloatVectorData with the right data type
146      * @throws ValueException when values is null, or storageType is null
147      */
148     public static FloatVectorData instantiateLD(final List<? extends FloatScalarInterface> values,
149             final StorageType storageType) throws ValueException
150     {
151         if (values == null)
152         {
153             throw new ValueException("FloatVectorData.instantiate: values list is null");
154         }
155 
156         float[] valuesSI = new float[values.size()];
157         IntStream.range(0, values.size()).parallel().forEach(i -> valuesSI[i] = values.get(i).getSI());
158 
159         switch (storageType)
160         {
161             case DENSE:
162                 return new FloatVectorDataDense(valuesSI);
163 
164             case SPARSE:
165                 return FloatVectorDataSparse.instantiate(valuesSI);
166 
167             default:
168                 throw new ValueException("Unknown data type in FloatVectorData.instantiate: " + storageType);
169         }
170     }
171 
172     /**
173      * Instantiate a FloatVectorData with the right data type.
174      * @param values the FloatScalar values to store
175      * @param length the length of the vector to pad with 0 after last entry in map
176      * @param storageType the data type to use
177      * @param <S> the scalar type to use
178      * @return the FloatVectorData with the right data type
179      * @throws ValueException when values is null, or storageType is null
180      */
181     public static <S extends FloatScalarInterface> FloatVectorData instantiateMD(final SortedMap<Integer, S> values,
182             final int length, final StorageType storageType) throws ValueException
183     {
184         if (values == null)
185         {
186             throw new ValueException("FloatVectorData.instantiate: values map is null");
187         }
188 
189         switch (storageType)
190         {
191             case DENSE:
192             {
193                 float[] valuesSI = new float[length];
194                 values.keySet().parallelStream().forEach(index -> valuesSI[index] = values.get(index).getSI());
195                 return new FloatVectorDataDense(valuesSI);
196             }
197 
198             case SPARSE:
199             {
200                 int[] indices = values.keySet().parallelStream().mapToInt(i -> i).toArray();
201                 float[] valuesSI = new float[values.size()];
202                 IntStream.range(0, values.size()).parallel()
203                         .forEach(index -> valuesSI[index] = values.get(indices[index]).getSI());
204                 return new FloatVectorDataSparse(valuesSI, indices, length);
205             }
206 
207             default:
208                 throw new ValueException("Unknown data type in FloatVectorData.instantiate: " + storageType);
209         }
210     }
211 
212     /**
213      * Instantiate a FloatVectorData with the right data type.
214      * @param values the FloatScalar values to store
215      * @param length the length of the vector to pad with 0 after last entry in map
216      * @param scale the scale of the unit to use for conversion to SI
217      * @param storageType the data type to use
218      * @return the FloatVectorData with the right data type
219      * @throws ValueException when values is null, or storageType is null
220      */
221     public static FloatVectorData instantiate(final SortedMap<Integer, Float> values, final int length, final Scale scale,
222             final StorageType storageType) throws ValueException
223     {
224         if (values == null)
225         {
226             throw new ValueException("FloatVectorData.instantiate: values map is null");
227         }
228 
229         switch (storageType)
230         {
231             case DENSE:
232             {
233                 float[] valuesSI = new float[length];
234                 values.keySet().parallelStream()
235                         .forEach(index -> valuesSI[index] = (float) scale.toStandardUnit(values.get(index)));
236                 return new FloatVectorDataDense(valuesSI);
237             }
238 
239             case SPARSE:
240             {
241                 int[] indices = values.keySet().parallelStream().mapToInt(i -> i).toArray();
242                 float[] valuesSI = new float[values.size()];
243                 IntStream.range(0, values.size()).parallel()
244                         .forEach(index -> valuesSI[index] = (float) scale.toStandardUnit(values.get(indices[index])));
245                 return new FloatVectorDataSparse(valuesSI, indices, length);
246             }
247 
248             default:
249                 throw new ValueException("Unknown data type in FloatVectorData.instantiate: " + storageType);
250         }
251     }
252 
253     /** ============================================================================================ */
254     /** ==================================== UTILITY FUNCTIONS ===================================== */
255     /** ============================================================================================ */
256 
257     /**
258      * Return the StorageType (DENSE, SPARSE, etc.) for the stored Vector.
259      * @return the StorageType (DENSE, SPARSE, etc.) for the stored Vector
260      */
261     public final StorageType getStorageType()
262     {
263         return this.storageType;
264     }
265 
266     /**
267      * @return the size of the vector
268      */
269     public abstract int size();
270 
271     /**
272      * @return whether data type is sparse.
273      */
274     public boolean isSparse()
275     {
276         return this.storageType.equals(StorageType.SPARSE);
277     }
278 
279     /**
280      * @return the sparse transformation of this data
281      */
282     public FloatVectorDataSparse toSparse()
283     {
284         return isSparse() ? (FloatVectorDataSparse) this : ((FloatVectorDataDense) this).toSparse();
285     }
286 
287     /**
288      * @return whether data type is dense.
289      */
290     public boolean isDense()
291     {
292         return this.storageType.equals(StorageType.DENSE);
293     }
294 
295     /**
296      * @return the dense transformation of this data
297      */
298     public FloatVectorDataDense toDense()
299     {
300         return isDense() ? (FloatVectorDataDense) this : ((FloatVectorDataSparse) this).toDense();
301     }
302 
303     /**
304      * @param index the index to get the value for
305      * @return the value at the index
306      */
307     public abstract float getSI(int index);
308 
309     /**
310      * Sets a value at the index in the vector.
311      * @param index the index to set the value for
312      * @param valueSI the value at the index
313      */
314     public abstract void setSI(int index, float valueSI);
315 
316     /**
317      * @return the number of non-zero cells.
318      */
319     public final int cardinality()
320     {
321         // this does not copy the data. See http://stackoverflow.com/questions/23106093/how-to-get-a-stream-from-a-float
322         return (int) IntStream.range(0, this.vectorSI.length).parallel().mapToDouble(i -> this.vectorSI[i])
323                 .filter(d -> d != 0.0).count();
324     }
325 
326     /**
327      * @return the sum of the values of all cells.
328      */
329     public final float zSum()
330     {
331         // this does not copy the data. See http://stackoverflow.com/questions/23106093/how-to-get-a-stream-from-a-float
332         return (float) IntStream.range(0, this.vectorSI.length).parallel().mapToDouble(i -> this.vectorSI[i]).sum();
333     }
334 
335     /**
336      * @return a deep copy of the data.
337      */
338     public abstract FloatVectorData copy();
339 
340     /**
341      * @return a safe copy of VectorSI
342      */
343     public abstract float[] getDenseVectorSI();
344 
345     /**
346      * Check the sizes of this data object and the other data object.
347      * @param other the other data object
348      * @throws ValueException if vectors have different lengths
349      */
350     private void checkSizes(final FloatVectorData other) throws ValueException
351     {
352         if (this.size() != other.size())
353         {
354             throw new ValueException("Two data objects used in a FloatVector operation do not have the same size");
355         }
356     }
357 
358     /** ============================================================================================ */
359     /** ================================== CALCULATION FUNCTIONS =================================== */
360     /** ============================================================================================ */
361 
362     /**
363      * Add two vectors on a cell-by-cell basis. If both vectors are sparse, a sparse vector is returned, otherwise a dense
364      * vector is returned.
365      * @param right the other data object to add
366      * @return the sum of this data object and the other data object
367      * @throws ValueException if vectors have different lengths
368      */
369     public FloatVectorData plus(final FloatVectorData right) throws ValueException
370     {
371         checkSizes(right);
372         float[] dv = new float[size()];
373         IntStream.range(0, size()).parallel().forEach(i -> dv[i] = getSI(i) + right.getSI(i));
374         if (this instanceof FloatVectorDataSparse && right instanceof FloatVectorDataSparse)
375         {
376             return new FloatVectorDataDense(dv).toSparse();
377         }
378         return new FloatVectorDataDense(dv);
379     }
380 
381     /**
382      * Add a vector to this vector on a cell-by-cell basis. The type of vector (sparse, dense) stays the same.
383      * @param right the other data object to add
384      * @throws ValueException if vectors have different lengths
385      */
386     public abstract void incrementBy(FloatVectorData right) throws ValueException;
387 
388     /**
389      * Add a number to this vector on a cell-by-cell basis.
390      * @param valueSI the value to add
391      */
392     public void incrementBy(final float valueSI)
393     {
394         IntStream.range(0, this.vectorSI.length).parallel().forEach(i -> this.vectorSI[i] += valueSI);
395     }
396 
397     /**
398      * Subtract two vectors on a cell-by-cell basis. If both vectors are sparse, a sparse vector is returned, otherwise a dense
399      * vector is returned.
400      * @param right the other data object to subtract
401      * @return the sum of this data object and the other data object
402      * @throws ValueException if vectors have different lengths
403      */
404     public FloatVectorData minus(final FloatVectorData right) throws ValueException
405     {
406         checkSizes(right);
407         float[] dv = new float[size()];
408         IntStream.range(0, size()).parallel().forEach(i -> dv[i] = getSI(i) - right.getSI(i));
409         if (this instanceof FloatVectorDataSparse && right instanceof FloatVectorDataSparse)
410         {
411             return new FloatVectorDataDense(dv).toSparse();
412         }
413         return new FloatVectorDataDense(dv);
414     }
415 
416     /**
417      * Subtract a vector from this vector on a cell-by-cell basis. The type of vector (sparse, dense) stays the same.
418      * @param right the other data object to subtract
419      * @throws ValueException if vectors have different lengths
420      */
421     public abstract void decrementBy(FloatVectorData right) throws ValueException;
422 
423     /**
424      * Subtract a number from this vector on a cell-by-cell basis.
425      * @param valueSI the value to subtract
426      */
427     public void decrementBy(final float valueSI)
428     {
429         IntStream.range(0, this.vectorSI.length).parallel().forEach(i -> this.vectorSI[i] -= valueSI);
430     }
431 
432     /**
433      * Multiply two vector on a cell-by-cell basis. If both vectors are dense, a dense vector is returned, otherwise a sparse
434      * vector is returned.
435      * @param right the other data object to multiply with
436      * @return the sum of this data object and the other data object
437      * @throws ValueException if vectors have different lengths
438      */
439     public FloatVectorData times(final FloatVectorData right) throws ValueException
440     {
441         checkSizes(right);
442         float[] dv = new float[size()];
443         IntStream.range(0, size()).parallel().forEach(i -> dv[i] = getSI(i) * right.getSI(i));
444         if (this instanceof FloatVectorDataDense && right instanceof FloatVectorDataDense)
445         {
446             return new FloatVectorDataDense(dv);
447         }
448         return new FloatVectorDataDense(dv).toSparse();
449     }
450 
451     /**
452      * Multiply a vector with the values of another vector on a cell-by-cell basis. The type of vector (sparse, dense) stays the
453      * same.
454      * @param right the other data object to multiply with
455      * @throws ValueException if vectors have different lengths
456      */
457     public abstract void multiplyBy(FloatVectorData right) throws ValueException;
458 
459     /**
460      * Multiply the values of this vector with a number on a cell-by-cell basis.
461      * @param valueSI the value to multiply with
462      */
463     public void multiplyBy(final float valueSI)
464     {
465         IntStream.range(0, this.vectorSI.length).parallel().forEach(i -> this.vectorSI[i] *= valueSI);
466     }
467 
468     /**
469      * Divide two vectors on a cell-by-cell basis. If both vectors are dense, a dense vector is returned, otherwise a sparse
470      * vector is returned.
471      * @param right the other data object to divide by
472      * @return the sum of this data object and the other data object
473      * @throws ValueException if vectors have different lengths
474      */
475     public FloatVectorData divide(final FloatVectorData right) throws ValueException
476     {
477         checkSizes(right);
478         float[] dv = new float[size()];
479         IntStream.range(0, size()).parallel().forEach(i -> dv[i] = getSI(i) / right.getSI(i));
480         if (this instanceof FloatVectorDataDense && right instanceof FloatVectorDataDense)
481         {
482             return new FloatVectorDataDense(dv);
483         }
484         return new FloatVectorDataDense(dv).toSparse();
485     }
486 
487     /**
488      * Divide the values of a vector by the values of another vector on a cell-by-cell basis. The type of vector (sparse, dense)
489      * stays the same.
490      * @param right the other data object to divide by
491      * @throws ValueException if vectors have different lengths
492      */
493     public abstract void divideBy(FloatVectorData right) throws ValueException;
494 
495     /**
496      * Divide the values of this vector by a number on a cell-by-cell basis.
497      * @param valueSI the value to multiply with
498      */
499     public void divideBy(final float valueSI)
500     {
501         IntStream.range(0, this.vectorSI.length).parallel().forEach(i -> this.vectorSI[i] /= valueSI);
502     }
503 
504     /** ============================================================================================ */
505     /** =============================== EQUALS, HASHCODE, TOSTRING ================================= */
506     /** ============================================================================================ */
507 
508     /** {@inheritDoc} */
509     @Override
510     public int hashCode()
511     {
512         final int prime = 31;
513         int result = 1;
514         result = prime * result + Arrays.hashCode(this.vectorSI);
515         return result;
516     }
517 
518     /** {@inheritDoc} */
519     @Override
520     @SuppressWarnings("checkstyle:needbraces")
521     public boolean equals(final Object obj)
522     {
523         if (this == obj)
524             return true;
525         if (obj == null)
526             return false;
527         if (getClass() != obj.getClass())
528             return false;
529         FloatVectorData other = (FloatVectorData) obj;
530         if (!Arrays.equals(this.vectorSI, other.vectorSI))
531             return false;
532         return true;
533     }
534 
535     /** {@inheritDoc} */
536     @Override
537     public String toString()
538     {
539         return "FloatVectorData [storageType=" + this.storageType + ", vectorSI=" + Arrays.toString(this.vectorSI) + "]";
540     }
541 }