View Javadoc
1   package org.djunits.value.vdouble.matrix.base;
2   
3   import java.lang.reflect.Array;
4   
5   import org.djunits.Throw;
6   import org.djunits.unit.Unit;
7   import org.djunits.value.Absolute;
8   import org.djunits.value.AbstractIndexedValue;
9   import org.djunits.value.ValueRuntimeException;
10  import org.djunits.value.formatter.Format;
11  import org.djunits.value.storage.StorageType;
12  import org.djunits.value.util.ValueUtil;
13  import org.djunits.value.vdouble.function.DoubleFunction;
14  import org.djunits.value.vdouble.function.DoubleMathFunctions;
15  import org.djunits.value.vdouble.matrix.data.DoubleMatrixData;
16  import org.djunits.value.vdouble.scalar.base.AbstractDoubleScalar;
17  import org.djunits.value.vdouble.scalar.base.DoubleScalar;
18  import org.djunits.value.vdouble.vector.base.AbstractDoubleVector;
19  import org.djunits.value.vdouble.vector.data.DoubleVectorData;
20  import org.ojalgo.matrix.PrimitiveMatrix;
21  
22  /**
23   * The most basic abstract class for the DoubleMatrix.
24   * <p>
25   * Copyright (c) 2013-2019 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
26   * BSD-style license. See <a href="https://djunits.org/docs/license.html">DJUNITS License</a>.
27   * </p>
28   * @author <a href="https://www.tudelft.nl/averbraeck">Alexander Verbraeck</a>
29   * @author <a href="https://www.tudelft.nl/staff/p.knoppers/">Peter Knoppers</a>
30   * @author <a href="https://www.transport.citg.tudelft.nl">Wouter Schakel</a>
31   * @param <U> the unit
32   * @param <S> the scalar with unit U
33   * @param <V> the vector type belonging to the matrix type
34   * @param <M> the generic matrix type
35   */
36  public abstract class AbstractDoubleMatrix<U extends Unit<U>, S extends AbstractDoubleScalar<U, S>,
37          V extends AbstractDoubleVector<U, S, V>, M extends AbstractDoubleMatrix<U, S, V, M>>
38          extends AbstractIndexedValue<U, S, M, DoubleMatrixData> implements DoubleMatrixInterface<U, S, V, M>
39  {
40      /** */
41      private static final long serialVersionUID = 20161015L;
42  
43      /** The stored data as an object, can be sparse or dense. */
44      @SuppressWarnings("checkstyle:visibilitymodifier")
45      protected DoubleMatrixData data;
46  
47      /**
48       * Construct a new DoubleMatrix.
49       * @param data DoubleMatrixData; an internal data object
50       * @param unit U; the unit
51       */
52      AbstractDoubleMatrix(final DoubleMatrixData data, final U unit)
53      {
54          super(unit);
55          Throw.whenNull(data, "data cannot be null");
56          this.data = data;
57      }
58  
59      /** {@inheritDoc} */
60      @Override
61      protected final DoubleMatrixData getData()
62      {
63          return this.data;
64      }
65  
66      /** {@inheritDoc} */
67      @Override
68      protected void setData(final DoubleMatrixData data)
69      {
70          this.data = data;
71      }
72  
73      /** {@inheritDoc} */
74      @Override
75      public double getSI(final int row, final int column) throws ValueRuntimeException
76      {
77          checkIndex(row, column);
78          return this.data.getSI(row, column);
79      }
80  
81      /** {@inheritDoc} */
82      @Override
83      public double getInUnit(final int row, final int column) throws ValueRuntimeException
84      {
85          checkIndex(row, column);
86          return ValueUtil.expressAsUnit(this.data.getSI(row, column), getDisplayUnit());
87      }
88  
89      /** {@inheritDoc} */
90      @Override
91      public double getInUnit(final int row, final int column, final U targetUnit) throws ValueRuntimeException
92      {
93          checkIndex(row, column);
94          return ValueUtil.expressAsUnit(this.data.getSI(row, column), targetUnit);
95      }
96  
97      /** {@inheritDoc} */
98      @Override
99      public void setSI(final int row, final int column, final double valueSI) throws ValueRuntimeException
100     {
101         checkIndex(row, column);
102         checkCopyOnWrite();
103         this.data.setSI(row, column, valueSI);
104     }
105 
106     /** {@inheritDoc} */
107     @Override
108     public void setInUnit(final int row, final int column, final double valueInUnit) throws ValueRuntimeException
109     {
110         setSI(row, column, ValueUtil.expressAsSIUnit(valueInUnit, getDisplayUnit()));
111     }
112 
113     /** {@inheritDoc} */
114     @Override
115     public void setInUnit(final int row, final int column, final double valueInUnit, final U valueUnit)
116             throws ValueRuntimeException
117     {
118         setSI(row, column, ValueUtil.expressAsSIUnit(valueInUnit, valueUnit));
119     }
120 
121     /** {@inheritDoc} */
122     @Override
123     public void set(final int row, final int column, final S value) throws ValueRuntimeException
124     {
125         setSI(row, column, value.si);
126     }
127 
128     /** {@inheritDoc} */
129     @Override
130     public double[] getRowSI(final int row) throws ValueRuntimeException
131     {
132         checkRowIndex(row);
133         double[] result = new double[this.data.cols()];
134         for (int col = 0; col < result.length; col++)
135         {
136             result[col] = this.data.getSI(row, col);
137         }
138         return result;
139     }
140 
141     /** {@inheritDoc} */
142     @Override
143     public double[] getColumnSI(final int column) throws ValueRuntimeException
144     {
145         checkColumnIndex(column);
146         double[] result = new double[this.data.rows()];
147         for (int row = 0; row < result.length; row++)
148         {
149             result[row] = this.data.getSI(row, column);
150         }
151         return result;
152     }
153 
154     /** {@inheritDoc} */
155     @Override
156     public double[] getDiagonalSI() throws ValueRuntimeException
157     {
158         checkSquare();
159         double[] result = new double[this.data.rows()];
160         for (int row = 0; row < result.length; row++)
161         {
162             result[row] = this.data.getSI(row, row);
163         }
164         return result;
165     }
166 
167     /** {@inheritDoc} */
168     @Override
169     public final double[][] getValuesSI()
170     {
171         return this.data.getDenseMatrixSI();
172     }
173 
174     /** {@inheritDoc} */
175     @Override
176     public final double[][] getValuesInUnit()
177     {
178         return getValuesInUnit(getDisplayUnit());
179     }
180 
181     /** {@inheritDoc} */
182     @Override
183     public final double[][] getValuesInUnit(final U targetUnit)
184     {
185         double[][] values = getValuesSI();
186         for (int i = values.length; --i >= 0;)
187         {
188             for (int j = values[i].length; --j >= 0;)
189             {
190                 values[i][j] = ValueUtil.expressAsUnit(values[i][j], targetUnit);
191             }
192         }
193         return values;
194     }
195 
196     /** {@inheritDoc} */
197     @Override
198     public int rows()
199     {
200         return this.data.rows();
201     }
202 
203     /** {@inheritDoc} */
204     @Override
205     public int cols()
206     {
207         return this.data.cols();
208     }
209 
210     /** {@inheritDoc} */
211     @SuppressWarnings("unchecked")
212     @Override
213     public S[][] getScalars()
214     {
215         S[][] array = (S[][]) Array.newInstance(getScalarClass(), rows(), cols());
216         for (int i = 0; i < rows(); i++)
217         {
218             S[] row = (S[]) Array.newInstance(getScalarClass(), cols());
219             array[i] = row;
220             for (int j = 0; j < cols(); j++)
221             {
222                 row[j] = get(i, j);
223             }
224         }
225         return array;
226     }
227 
228     /** {@inheritDoc} */
229     @Override
230     public S get(final int row, final int column) throws ValueRuntimeException
231     {
232         checkIndex(row, column);
233         return DoubleScalar.instantiateSI(getSI(row, column), getDisplayUnit());
234     }
235 
236     /** {@inheritDoc} */
237     @Override
238     public V getRow(final int row) throws ValueRuntimeException
239     {
240         checkRowIndex(row);
241         DoubleVectorData dvd =
242                 DoubleVectorData.instantiate(getRowSI(row), getDisplayUnit().getStandardUnit().getScale(), getStorageType());
243         return instantiateVector(dvd, getDisplayUnit());
244     }
245 
246     /** {@inheritDoc} */
247     @Override
248     public V getColumn(final int column) throws ValueRuntimeException
249     {
250         checkColumnIndex(column);
251         DoubleVectorData dvd = DoubleVectorData.instantiate(getColumnSI(column), getDisplayUnit().getStandardUnit().getScale(),
252                 getStorageType());
253         return instantiateVector(dvd, getDisplayUnit());
254     }
255 
256     /** {@inheritDoc} */
257     @Override
258     public V getDiagonal() throws ValueRuntimeException
259     {
260         checkSquare();
261         DoubleVectorData dvd =
262                 DoubleVectorData.instantiate(getDiagonalSI(), getDisplayUnit().getStandardUnit().getScale(), getStorageType());
263         return instantiateVector(dvd, getDisplayUnit());
264     }
265 
266     /** {@inheritDoc} */
267     @SuppressWarnings("unchecked")
268     @Override
269     public S[] getRowScalars(final int row) throws ValueRuntimeException
270     {
271         checkRowIndex(row);
272         S[] array = (S[]) Array.newInstance(getScalarClass(), cols());
273         for (int col = 0; col < cols(); col++)
274         {
275             array[col] = get(row, col);
276         }
277         return array;
278     }
279 
280     /** {@inheritDoc} */
281     @SuppressWarnings("unchecked")
282     @Override
283     public S[] getColumnScalars(final int col) throws ValueRuntimeException
284     {
285         checkColumnIndex(col);
286         S[] array = (S[]) Array.newInstance(getScalarClass(), rows());
287         for (int row = 0; row < rows(); row++)
288         {
289             array[row] = get(row, col);
290         }
291         return array;
292     }
293 
294     /** {@inheritDoc} */
295     @SuppressWarnings("unchecked")
296     @Override
297     public S[] getDiagonalScalars() throws ValueRuntimeException
298     {
299         checkSquare();
300         S[] array = (S[]) Array.newInstance(getScalarClass(), rows());
301         for (int row = 0; row < rows(); row++)
302         {
303             array[row] = get(row, row);
304         }
305         return array;
306     }
307 
308     /** {@inheritDoc} */
309     @SuppressWarnings("unchecked")
310     @Override
311     public M toSparse()
312     {
313         M result;
314         if (getStorageType().equals(StorageType.SPARSE))
315         {
316             result = (M) this;
317             result.setDisplayUnit(getDisplayUnit());
318         }
319         else
320         {
321             result = instantiateMatrix(this.data.toSparse(), getDisplayUnit());
322         }
323         result.setDisplayUnit(getDisplayUnit());
324         return result;
325     }
326 
327     /** {@inheritDoc} */
328     @SuppressWarnings("unchecked")
329     @Override
330     public M toDense()
331     {
332         M result;
333         if (getStorageType().equals(StorageType.DENSE))
334         {
335             result = (M) this;
336             result.setDisplayUnit(getDisplayUnit());
337         }
338         else
339         {
340             result = instantiateMatrix(this.data.toDense(), getDisplayUnit());
341         }
342         return result;
343     }
344 
345     /** {@inheritDoc} */
346     @SuppressWarnings("unchecked")
347     @Override
348     public final M assign(final DoubleFunction doubleFunction)
349     {
350         checkCopyOnWrite();
351         this.data.assign(doubleFunction);
352         return (M) this;
353     }
354 
355     /** {@inheritDoc} */
356     @Override
357     public final M abs()
358     {
359         return assign(DoubleMathFunctions.ABS);
360     }
361 
362     /** {@inheritDoc} */
363     @Override
364     public final M ceil()
365     {
366         return assign(DoubleMathFunctions.CEIL);
367     }
368 
369     /** {@inheritDoc} */
370     @Override
371     public final M floor()
372     {
373         return assign(DoubleMathFunctions.FLOOR);
374     }
375 
376     /** {@inheritDoc} */
377     @Override
378     public final M neg()
379     {
380         return assign(DoubleMathFunctions.NEG);
381     }
382 
383     /** {@inheritDoc} */
384     @Override
385     public final M rint()
386     {
387         return assign(DoubleMathFunctions.RINT);
388     }
389 
390     /** {@inheritDoc} */
391     @Override
392     public String toString()
393     {
394         return toString(getDisplayUnit(), false, true);
395     }
396 
397     /** {@inheritDoc} */
398     @Override
399     public String toString(final U displayUnit)
400     {
401         return toString(displayUnit, false, true);
402     }
403 
404     /** {@inheritDoc} */
405     @Override
406     public String toString(final boolean verbose, final boolean withUnit)
407     {
408         return toString(getDisplayUnit(), verbose, withUnit);
409     }
410 
411     /** {@inheritDoc} */
412     @Override
413     public String toString(final U displayUnit, final boolean verbose, final boolean withUnit)
414     {
415         StringBuffer buf = new StringBuffer();
416         if (verbose)
417         {
418             String ab = this instanceof Absolute ? "Abs " : "Rel ";
419             String ds = this.data.isDense() ? "Dense  " : this.data.isSparse() ? "Sparse " : "?????? ";
420             if (isMutable())
421             {
422                 buf.append("Mutable   " + ab + ds);
423             }
424             else
425             {
426                 buf.append("Immutable " + ab + ds);
427             }
428         }
429         for (int row = 0; row < rows(); row++)
430         {
431             buf.append("\r\n\t");
432             for (int col = 0; col < cols(); col++)
433             {
434                 try
435                 {
436                     double d = ValueUtil.expressAsUnit(getSI(row, col), displayUnit);
437                     buf.append(" " + Format.format(d));
438                 }
439                 catch (ValueRuntimeException ve)
440                 {
441                     buf.append(" " + "********************".substring(0, Format.DEFAULTSIZE));
442                 }
443             }
444         }
445         buf.append("\n");
446         if (withUnit)
447         {
448             buf.append(displayUnit.getDefaultDisplayAbbreviation());
449         }
450         return buf.toString();
451     }
452 
453     /**
454      * Check that provided row and column indices are valid.
455      * @param row int; the row value to check
456      * @param col int; the column value to check
457      * @throws ValueRuntimeException when row or column is invalid
458      */
459     protected final void checkIndex(final int row, final int col) throws ValueRuntimeException
460     {
461         if (row < 0 || row >= rows() || col < 0 || col >= cols())
462         {
463             throw new ValueRuntimeException("index out of range (valid range is 0.." + (rows() - 1) + ", 0.." + (cols() - 1)
464                     + ", got " + row + ", " + col + ")");
465         }
466     }
467 
468     /**
469      * Check that provided row index is valid.
470      * @param row int; the row value to check
471      * @throws ValueRuntimeException when row is invalid
472      */
473     protected final void checkRowIndex(final int row) throws ValueRuntimeException
474     {
475         if (row < 0 || row >= rows())
476         {
477             throw new ValueRuntimeException("row index out of range (valid range is 0.." + (rows() - 1) + ", got " + row + ")");
478         }
479     }
480 
481     /**
482      * Check that provided column index is valid.
483      * @param col int; the column value to check
484      * @throws ValueRuntimeException when row is invalid
485      */
486     protected final void checkColumnIndex(final int col) throws ValueRuntimeException
487     {
488         if (col < 0 || col >= cols())
489         {
490             throw new ValueRuntimeException(
491                     "column index out of range (valid range is 0.." + (cols() - 1) + ", got " + col + ")");
492         }
493     }
494 
495     /**
496      * Check that the matrix is square.
497      * @throws ValueRuntimeException when matrix is not square
498      */
499     protected final void checkSquare() throws ValueRuntimeException
500     {
501         Throw.when(rows() != cols(), ValueRuntimeException.class, "Matrix is not square, rows=%d, cols=%d", rows(), cols());
502     }
503 
504     /** {@inheritDoc} */
505     @Override
506     public final double determinant() throws ValueRuntimeException
507     {
508         try
509         {
510             final PrimitiveMatrix.Factory matrixFactory = PrimitiveMatrix.FACTORY;
511             final PrimitiveMatrix m = matrixFactory.rows(this.data.getDenseMatrixSI());
512             if (!m.isSquare())
513             {
514                 throw new IllegalArgumentException("Matrix is not square -- determinant cannot be calculated.");
515             }
516             return m.getDeterminant().doubleValue();
517         }
518         catch (IllegalArgumentException exception)
519         {
520             throw new ValueRuntimeException(exception); // Matrix must be square
521         }
522     }
523 
524     /** {@inheritDoc} */
525     @Override
526     @SuppressWarnings("checkstyle:designforextension")
527     public int hashCode()
528     {
529         final int prime = 31;
530         int result = getDisplayUnit().getStandardUnit().hashCode();
531         result = prime * result + ((this.data == null) ? 0 : this.data.hashCode());
532         return result;
533     }
534 
535     /** {@inheritDoc} */
536     @Override
537     @SuppressWarnings({"checkstyle:designforextension", "checkstyle:needbraces"})
538     public boolean equals(final Object obj)
539     {
540         if (this == obj)
541             return true;
542         if (obj == null)
543             return false;
544         if (getClass() != obj.getClass())
545             return false;
546         AbstractDoubleMatrix<?, ?, ?, ?> other = (AbstractDoubleMatrix<?, ?, ?, ?>) obj;
547         if (!getDisplayUnit().getStandardUnit().equals(other.getDisplayUnit().getStandardUnit()))
548             return false;
549         if (this.data == null)
550         {
551             if (other.data != null)
552                 return false;
553         }
554         else if (!this.data.equals(other.data))
555             return false;
556         return true;
557     }
558 }