DenseMatrix.java

package org.mklab.sdpj.datastructure;

import java.io.PrintStream;
import java.nio.channels.UnsupportedAddressTypeException;

import org.mklab.nfc.matrix.ComplexNumericalMatrix;
import org.mklab.nfc.matrix.RealNumericalMatrix;
import org.mklab.nfc.scalar.ComplexNumericalScalar;
import org.mklab.nfc.scalar.RealNumericalScalar;
import org.mklab.sdpj.gpack.atlas.ATLAS;
import org.mklab.sdpj.gpack.blaswrap.BLAS;


/**
 * Class <code>DenseMarix</code> is a data structure of dense matrix, 
 * which is saving the data of dense-matrix data type.  
 * <br>密行列を表すクラスです。
 * 
 * @author takafumi
 * @param <RS> type of real scalar
 * @param <RM> type of real matrix
 * @param <CS> type of complex scalar
 * @param <CM> type of complex Matrix
 */
public class DenseMatrix<RS extends RealNumericalScalar<RS, RM, CS, CM>, RM extends RealNumericalMatrix<RS, RM, CS, CM>, CS extends ComplexNumericalScalar<RS, RM, CS, CM>, CM extends ComplexNumericalMatrix<RS, RM, CS, CM>> {

  /** 行数 */
  private int rowSize;
  /** 列数 */
  private int columnSize;
  /** enum data type, if matrix is dense, value is DENSE; else DIAGONAL*/
  private DenseDiagonal denseOrDiagonal;
  /** vector for saving data of a dense matrix*/
  public RS[] denseElements;
  /** vector for saving data of a diagonal matrix*/
  public RS[] diagonalElements;
  
  /** */
  private RS unit;

  /**
   * 新しく生成された{@link DenseMatrix}オブジェクトを初期化します。
   * @param unit unit
   */
  private DenseMatrix(RS unit) {
    this.unit = unit;
    this.columnSize = 0;
    this.rowSize = 0;
    this.denseOrDiagonal = DenseDiagonal.DENSE;
  }

  /**
   * 新しく生成された<code>DenseMatrix</code>オブジェクトを初期化します。
   * 
   * @param rowSize 行数
   * @param columnSize 列数
   * @param denseOrDiagonal 密行列または対角行列
   * @param unit unit
   */
  public DenseMatrix(int rowSize, int columnSize, DenseDiagonal denseOrDiagonal, RS unit) {
    this.unit = unit;
    if (rowSize < 0 || columnSize < 0) {
      throw new RuntimeException("rDenseMatrix:: Dimensions are nonpositive"); //$NON-NLS-1$
    }

    //final RS unit = Tools.getUnitNumber();

    this.rowSize = rowSize;
    this.columnSize = columnSize;
    this.denseOrDiagonal = denseOrDiagonal;

    switch (denseOrDiagonal) {
      case DENSE:
        final int length = rowSize * columnSize;
        this.denseElements = unit.createArray(length);
        ATLAS.setValue(length, unit.createZero(), this.denseElements, 1);
        break;
      case DIAGONAL:
        if (rowSize != columnSize) {
          throw new RuntimeException("rDenseMatrix:: Diagonal must be Square matrix"); //$NON-NLS-1$
        }
        this.diagonalElements = unit.createArray(columnSize);
        ATLAS.setValue(columnSize, unit.createZero(), this.diagonalElements, 1);
        break;
      default:
      throw new UnsupportedOperationException();  
    }
  }

  /**
   * This function is to compute the condition number, minimum singular, and
   * maximum singular value of matrix.
   * 
   * @param out Print result of condition number
   * @param min Print minimum singular value of matrix
   * @param max Print maximum singular value of matrix
   */
  public void getConditionNumber(PrintStream out, PrintStream min, PrintStream max) {
    //RS unit = getElementUnit();
    RS[][] elements = this.unit.createArray(this.rowSize, this.columnSize);

    switch (this.denseOrDiagonal) {
      case DENSE:
        for (int i = 0; i < this.rowSize; ++i) {
          for (int j = 0; j < this.columnSize; ++j) {
            elements[i][j] = this.denseElements[i + this.columnSize * j];
          }
        }
        break;
      case DIAGONAL:
        //RS zero = 
        for (int i = 0; i < this.rowSize; i++) {
          elements[i][i] = this.diagonalElements[i];
          for (int j = 0; j < this.columnSize; ++j) {
            if (i != j) {
              elements[i][j] = this.unit.createZero();
            }
          }
        }
        break;
      default:
        throw new UnsupportedOperationException();
    }

    //NumericalMatrix<RS> cond = new BaseNumericalMatrix<>(this.rowSize, this.columnSize, elements);
    RM cond = elements[0][0].createGrid(elements);
    out.printf("%s\n", cond.conditionNumber().toString("%4.3e")); //$NON-NLS-1$//$NON-NLS-2$
    min.printf("%s\n", cond.minSingularValue().toString("%4.3e")); //$NON-NLS-1$//$NON-NLS-2$
    max.printf("%s\n", cond.maxSingularValue().toString("%4.3e")); //$NON-NLS-1$//$NON-NLS-2$
  }

  /**
   * @see java.lang.Object#toString()
   */
  @Override
  public String toString() {
    final StringBuffer buff = new StringBuffer();

    switch (this.denseOrDiagonal) {
      case DENSE:
        buff.append("{"); //$NON-NLS-1$
        for (int i = 0; i < this.rowSize - 1; ++i) {
          if (i == 0) {
            buff.append(" "); //$NON-NLS-1$
          } else {
            buff.append("  "); //$NON-NLS-1$
          }
          buff.append("{"); //$NON-NLS-1$
          for (int j = 0; j < this.columnSize - 1; ++j) {
            buff.append(this.denseElements[i + this.columnSize * j] + ","); //$NON-NLS-1$
          }
          buff.append(this.denseElements[i + this.columnSize * (getColumnSize() - 1)] + " },\n"); //$NON-NLS-1$
        }
        if (getRowSize() > 1) {
          buff.append("  {"); //$NON-NLS-1$
        }
        for (int j = 0; j < this.columnSize - 1; ++j) {
          buff.append(this.denseElements[(getRowSize() - 1) + this.columnSize * j] + ","); //$NON-NLS-1$
        }
        buff.append(this.denseElements[(getRowSize() - 1) + this.columnSize * (getColumnSize() - 1)] + " }"); //$NON-NLS-1$

        if (getRowSize() > 1) {
          buff.append("   }\n"); //$NON-NLS-1$
        } else {
          buff.append("\n"); //$NON-NLS-1$
        }
        break;
      case DIAGONAL:
        buff.append("{"); //$NON-NLS-1$
        for (int j = 0; j < this.columnSize - 1; ++j) {
          buff.append(this.diagonalElements[j] + ","); //$NON-NLS-1$
        }
        if (getColumnSize() > 0) {
          buff.append(this.diagonalElements[getColumnSize() - 1] + "}\n"); //$NON-NLS-1$
        }
        break;
      default:
        throw new UnsupportedOperationException();
    }
    return buff.toString();
  }

  /**
   * コピー元の行列をコピーします。
   * 
   * @param source コピー元
   */
  public void copyFrom(DenseMatrix<RS,RM,CS,CM> source) {
    if (this == source) {
      return;
    }

    //final RS unit = source.getElementUnit();

    switch (source.denseOrDiagonal) {
      case DENSE:
        this.denseOrDiagonal = DenseDiagonal.DENSE;
        this.rowSize = source.getRowSize();
        this.columnSize = source.getColumnSize();
        int length = this.rowSize * this.columnSize;
        this.denseElements = this.unit.createArray(length);
        this.diagonalElements = null;
        BLAS.dcopy(length, source.denseElements, 1, this.denseElements, 1);
        break;
      case DIAGONAL:
        this.denseOrDiagonal = DenseDiagonal.DIAGONAL;
        this.rowSize = source.getRowSize();
        this.columnSize = source.getColumnSize();
        this.denseElements = null;
        this.diagonalElements = this.unit.createArray(getColumnSize());
        BLAS.dcopy(getColumnSize(), source.diagonalElements, 1, this.diagonalElements, 1);
        break;
      default:
        throw new UnsupportedAddressTypeException();
    }
  }

  /**
   * ゼロ行列にします。
   */
  public void setZero() {
    //final RS unit = getElementUnit();

    switch (this.denseOrDiagonal) {
      case DENSE:
        ATLAS.setValue(getRowSize() * this.columnSize, this.unit.createZero(), this.denseElements, 1);
        break;
      case DIAGONAL:
        ATLAS.setValue(getColumnSize(), this.unit.createZero(), this.diagonalElements, 1);
        break;
      default:
        throw new UnsupportedOperationException();
    }
  }

  /**
   * 単位行列にします。
   * 
   * @param value スカラー
   */
  public void setIdentity(RS value) {
    //final RS unit = value.createUnit();

    if (getRowSize() != this.columnSize) {
      throw new RuntimeException("rSparseMatrix:: Identity matrix must be square matrix"); //$NON-NLS-1$
    }

    switch (this.denseOrDiagonal) {
      case DENSE:
        final int length = this.rowSize * this.columnSize;
        ATLAS.setValue(length, this.unit.createZero(), this.denseElements, 1);
        final int step = this.columnSize + 1;
        ATLAS.setValue(getColumnSize(), value, this.denseElements, step);
        break;
      case DIAGONAL:
        ATLAS.setValue(getColumnSize(), value, this.diagonalElements, 1);
        break;
      default:
        throw new UnsupportedOperationException();
    }
  }

  /**
   * 密行列であるか判定します。
   * 
   * @return 密行列ならばtrue
   */
  public boolean isDense() {
    return this.denseOrDiagonal == DenseDiagonal.DENSE;
  }

  /**
   * 対角行列であるか判定します。
   * 
   * @return 対角行列ならばtrue
   */
  public boolean isDiagonal() {
    return this.denseOrDiagonal == DenseDiagonal.DIAGONAL;
  }

  /**
   * 密行列また対角行列の種類を返します。
   * 
   * @return 密行列また対角行列の種類
   */
  public DenseDiagonal getDenseOrDiagonal() {
    return this.denseOrDiagonal;
  }

  /**
   * 密行列の成分を返します。
   * 
   * @param index 成分の指数
   * @return 密行列の成分を返します。
   */
  public RS getDenseElement(final int index) {
    return this.denseElements[index];
  }

  /**
   * 密行列の成分を設定します。
   * 
   * @param index 成分の指数
   * @param value 値
   */
  public void setDenseElement(final int index, final RS value) {
    this.denseElements[index] = value;
  }

  /**
   * 対角行列の成分を返します。
   * 
   * @param index 成分の指数
   * @return 対角行列の成分を返します。
   */
  public RS getDiagonalElement(final int index) {
    return this.diagonalElements[index];
  }

  /**
   * 成分の単位を返します。
   * 
   * @return 成分の単位
   */
  public RS getElementUnit() {
    if (isDense()) {
      return getDenseElement(0).createUnit();
    }
    return getDiagonalElement(0).createUnit();
  }

  /**
   * 行数を設定します。
   * 
   * @param rowSize 行数
   */
  public void setRowSize(int rowSize) {
    this.rowSize = rowSize;
  }

  /**
   * 行数を返します。
   * 
   * @return 行数
   */
  public int getRowSize() {
    return this.rowSize;
  }

  /**
   * 列数を設定します。
   * 
   * @param columnSize 列数
   */
  public void setColunSize(int columnSize) {
    this.columnSize = columnSize;
  }

  /**
   * 列数を返します。
   * 
   * @return 列数
   */
  public int getColumnSize() {
    return this.columnSize;
  }

  /**
   * Returns clone.
   * 
   * @return clone
   */
  public DenseMatrix<RS,RM,CS,CM> createClone() {
    final DenseMatrix<RS,RM,CS,CM> inst = new DenseMatrix<>(this.unit);
    inst.rowSize = this.rowSize;
    inst.columnSize = this.columnSize;
    inst.denseOrDiagonal = this.denseOrDiagonal == null ? null : this.denseOrDiagonal;

    if (this.denseElements != null) {
      inst.denseElements = this.denseElements[0].createArray(this.denseElements.length);
      System.arraycopy(this.denseElements, 0, inst.denseElements, 0, this.denseElements.length);
    } else {
      inst.denseElements = null;
    }

    if (this.diagonalElements != null) {
      // inst.denseElements = this.diagonalElements[0].createArray(this.diagonalElements.length);
      inst.diagonalElements = this.diagonalElements[0].createArray(this.diagonalElements.length);
      System.arraycopy(this.diagonalElements, 0, inst.diagonalElements, 0, this.diagonalElements.length);
    } else {
      inst.diagonalElements = null;
    }
    return inst;
  }

  /**
   * Return an array of elements.
   * 
   * @return array of elements
   */
  public RS[][] getArrayOfElements() {
    //final RS unit = getElementUnit();
    final RS[][] data = this.unit.createArray(this.rowSize, this.columnSize);

    switch (getDenseOrDiagonal()) {
      case DENSE:
        for (int i = 0; i < this.rowSize; ++i) {
          for (int j = 0; j < this.columnSize; ++j) {
            data[i][j] = this.denseElements[i + this.columnSize * j];
          }
        }
        break;
      case DIAGONAL:
        //final RS zero = 
        for (int i = 0; i < this.rowSize; i++) {
          data[i][i] = this.diagonalElements[i];
          for (int j = 0; j < this.columnSize; j++) {
            if (i != j) {
              data[i][j] = this.unit.createZero();
            }
          }
        }
        break;
      default:
        throw new UnsupportedOperationException();
    }

    return data;
  }

  /**
   * Set elements
   * 
   * @param a matrix
   */
  public void setElements(RM a) {
    if (this.rowSize != a.getRowSize() || this.columnSize != a.getColumnSize()) {
      throw new IllegalArgumentException();
    }

    switch (getDenseOrDiagonal()) {
      case DENSE:
        for (int i = 0; i < this.rowSize; ++i) {
          for (int j = 0; j < this.columnSize; ++j) {
            this.denseElements[i + this.columnSize * j] = a.getElement(i + 1, j + 1);
          }
        }
        break;
      case DIAGONAL:
        for (int i = 0; i < this.columnSize; ++i) {
          this.diagonalElements[i] = a.getElement(i + 1, i + 1);
        }
        break;
      default:
        throw new UnsupportedOperationException();
    }
  }
}