BlockSparseMatrix.java

package org.mklab.sdpj.datastructure;

import org.mklab.nfc.matrix.ComplexNumericalMatrix;
import org.mklab.nfc.matrix.RealNumericalMatrix;
import org.mklab.nfc.scalar.ComplexNumericalScalar;
import org.mklab.nfc.scalar.RealNumericalScalar;


/**
 * ブロック疎行列を表すクラスです。
 * 
 * @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 BlockSparseMatrix<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 blockSize;
  /** ブロック構造 */
  private int[] blockStruct;
  /** 成分 */
  private SparseMatrix<RS,RM,CS,CM>[] blocks;

  /**
   * 新しく生成された{@link BlockSparseMatrix}オブジェクトを初期化します。
   */
  public BlockSparseMatrix() {
    this.blockSize = 0;
    this.blockStruct = null;
    this.blocks = null;
  }

  /**
   * 新しく生成された{@link BlockSparseMatrix}オブジェクトを初期化します。
   * 
   * @param blockSize ブロックの数
   * @param blockStruct ブロックの構造
   */
  public BlockSparseMatrix(int blockSize, int[] blockStruct) {
    if (blockSize < 0) {
      throw new RuntimeException("rBlockSparseMatrix:: nBlock is nonpositive"); //$NON-NLS-1$
    }

    this.blockSize = blockSize;

    this.blockStruct = new int[blockSize];
    for (int i = 0; i < blockSize; i++) {
      this.blockStruct[i] = blockStruct[i];
    }

    this.blocks = new SparseMatrix[blockSize];
    for (int i = 0; i < this.blockSize; i++) {
      this.blocks[i] = new SparseMatrix<>();
    }
  }

  /**
   * @see java.lang.Object#toString()
   */
  @Override
  public String toString() {
    StringBuffer buff = new StringBuffer();
    buff.append("{\n"); //$NON-NLS-1$

    for (int i = 0; i < this.blockSize; i++) {
      buff.append("nonZeroCount:" + this.blocks[i].nonZeroCount + "\n"); //$NON-NLS-1$ //$NON-NLS-2$
      buff.append("nonZeroEffect:" + this.blocks[i].nonZeroEffect + "\n"); //$NON-NLS-1$ //$NON-NLS-2$
      buff.append("nonZeroNumber:" + this.blocks[i].getNonZeroNumber() + "\n"); //$NON-NLS-1$ //$NON-NLS-2$
      buff.append(this.blocks[i].toString());
    }

    buff.append("}\n"); //$NON-NLS-1$
    return buff.toString();
  }

  /**
   * Denseへ変換します。
   */
  public void changeToDense() {
    for (int i = 0; i < this.blockSize; i++) {
      this.blocks[i].changeToDense(false);
    }
  }

  /**
   * ブロック数を返します。
   * 
   * @return ブロック数
   */
  public int getBlockSize() {
    return this.blockSize;
  }

  /**
   * ブロックの構造を返します。
   * 
   * @param index 指数
   * @return ブロックの構造
   */
  public int getTargetBlockStruct(final int index) {
    return this.blockStruct[index];
  }
  
  /**
   * ブロックの構造を返します。
   * 
   * @return ブロックの構造
   */
  public int[] getBlockStruct(){
    return this.blockStruct;
  }
  
  /**
   * ブロック成分を返します。
   * 
   * @param index 指数
   * @return ブロック成分
   */
  public SparseMatrix<RS,RM,CS,CM> getBlock(int index) {
    return this.blocks[index];
  }

  /**
   * ブロック成分を設定します。
   * 
   * @param index 指数
   * @param block ブロック成分
   */
  public void setBlock(int index, SparseMatrix<RS,RM,CS,CM> block) {
    this.blocks[index] = block;
  }

  /**
   * 成分の単位を返します。
   * 
   * @return 成分の単位
   */
  public RS getElementUnit() {
    for (SparseMatrix<RS,RM,CS,CM> matrix : this.blocks) {
      if (matrix.getNonZeroNumber() != 0) {
        return matrix.getElementUnit();
      }
    }

    throw new RuntimeException("null matrix"); //$NON-NLS-1$
  }

  /**
   * Return a copy of instance of class <code>BlockSparseMatrix</code>.
   * 
   * @return a copy of instance of class <code>BlockSparseMatrix</code>
   */
  public BlockSparseMatrix<RS,RM,CS,CM> createClone() {
    BlockSparseMatrix<RS,RM,CS,CM> inst = new BlockSparseMatrix<>();
    inst.blockSize = this.blockSize;

    if (this.blockStruct != null) {
      inst.blockStruct = new int[this.blockStruct.length];
      for (int i = 0; i < this.blockStruct.length; i++) {
        inst.blockStruct[i] = this.blockStruct[i];
      }
    } else {
      inst.blockStruct = null;
    }

    if (this.blocks != null) {
      inst.blocks = new SparseMatrix[this.blocks.length];
      for (int i = 0; i < this.blocks.length; i++) {
        inst.blocks[i] = this.blocks[i] == null ? null : this.blocks[i].createClone();
      }
    } else {
      inst.blocks = null;
    }
    return inst;
  }

  /**
   * Return an array of elements.
   * 
   * @return array of elements
   */
  public RS[][] getArrayOfElements() {
    final RS unit = getElementUnit();
    int size = 0;

    for (int dimension : this.blockStruct) {
      size += Math.abs(dimension);
    }

    final RS[][] data = unit.createArray(size, size);
    RS zero = unit.createZero();
    for (int i = 0; i < size; i++) {
      for (int j = 0; j < size; j++) {
        data[i][j] = zero;
      }
    }

    int shift = 0;
    for (int index = 0; index < this.blockStruct.length; index++) {
      if (index > 0) {
        shift += Math.abs(this.blockStruct[index - 1]);
      }

      switch (this.getBlock(index).getSparseOrDenseOrDiagonal()) {
        case SPARSE:
          RS[] elementsOfSparse = this.getBlock(index).sparseElements;
          int[] row = this.getBlock(index).rowIndex;
          int[] column = this.getBlock(index).columnIndex;
          int lengthOfSparse = elementsOfSparse.length;
          for (int nIndex = 0; nIndex < lengthOfSparse; nIndex++) {
            int nRow = Math.abs(row[nIndex]) + shift;
            int nCol = Math.abs(column[nIndex]) + shift;
            data[nRow][nCol] = elementsOfSparse[nIndex];
            data[nCol][nRow] = data[nRow][nCol];
          }
          break;
        case DENSE:
          RS[] elementsOfDense = this.getBlock(index).denseElements;
          int lengthOfDense = elementsOfDense.length;
          int nSize = this.getBlock(index).getRowSize();

          for (int nIndex = 0; nIndex < lengthOfDense; nIndex++) {
            data[nIndex / nSize + shift][nIndex % nSize + shift] = elementsOfDense[nIndex];
          }
          break;
        case DIAGONAL:
          RS[] elementsOfDiagonal = this.getBlock(index).diagonalElements;
          int lengthOfDiagonal = elementsOfDiagonal.length;
          for (int nIndex = 0; nIndex < lengthOfDiagonal; nIndex++) {
            data[nIndex + shift][nIndex + shift] = elementsOfDiagonal[nIndex];
          }
          break;
        default:
          throw new UnsupportedOperationException();
      }
    }

    return data;
  }

  /**
   * This function is to substitute the data of class <code>BlockSparseMatrix</code> with the data of array.
   * @param arrayMatrix array matrix
   * @param dimensionArray dimension array
   */
  public void useDataOfArray(RS[][] arrayMatrix, int dimensionArray) {
    //final RS unit = getElementUnit();
    int size = 0;

    for (int dimension : this.blockStruct) {
      size += dimension;
    }

    if (size != dimensionArray) {
      System.err.println("Error: Dimension of array is wrong!"); //$NON-NLS-1$
      System.exit(0);
    }

    //    final RS[][] data = unit.createArray(size, size);
    //    RS zero = unit.createZero();
    //    for (int i = 0; i < size; i++) {
    //      for (int j = 0; j < size; j++) {
    //        data[i][j] = zero;
    //      }
    //    }

    int shift = 0;
    for (int index = 0; index < this.blockStruct.length; index++) {
      if (index > 0) {
        shift += this.blockStruct[index - 1];
      }

      switch (this.getBlock(index).getSparseOrDenseOrDiagonal()) {
        case SPARSE:
          RS[] elementsOfSparse = this.getBlock(index).sparseElements;
          int[] row = this.getBlock(index).rowIndex;
          int[] column = this.getBlock(index).columnIndex;
          int lengthOfSparse = elementsOfSparse.length;
          for (int nIndex = 0; nIndex < lengthOfSparse; nIndex++) {
            int nRow = row[nIndex] + shift;
            int nCol = column[nIndex] + shift;
            //            data[nRow][nCol] = elementsOfSparse[nIndex];
            //            data[nCol][nRow] = data[nRow][nCol];
            elementsOfSparse[nIndex] = arrayMatrix[nRow][nCol].unaryMinus();
          }
          this.getBlock(index).sparseElements = elementsOfSparse;

          break;
        case DENSE:
          RS[] elementsOfDense = this.getBlock(index).denseElements;
          int lengthOfDense = elementsOfDense.length;
          int nSize = this.getBlock(index).getRowSize();
          for (int nIndex = 0; nIndex < lengthOfDense; nIndex++) {
            //            data[nIndex / nSize + shift][nIndex % nSize + shift] = elementsOfDense[nIndex];
            elementsOfDense[nIndex] = arrayMatrix[nIndex / nSize + shift][nIndex % nSize + shift].unaryMinus();
          }
          this.getBlock(index).denseElements = elementsOfDense;

          break;
        case DIAGONAL:
          RS[] elementsOfDiagonal = this.getBlock(index).diagonalElements;
          int lengthOfDiagonal = elementsOfDiagonal.length;
          for (int nIndex = 0; nIndex < lengthOfDiagonal; nIndex++) {
            //           data[nIndex + shift][nIndex + shift] = elementsOfDiagonal[nIndex];
            elementsOfDiagonal[nIndex] = arrayMatrix[nIndex + shift][nIndex + shift].unaryMinus();
          }
          this.getBlock(index).diagonalElements = elementsOfDiagonal;

          break;
        default:
          throw new UnsupportedOperationException();
      }
    }

    //  return data;
  }
}