DoubleImproperLinearSystem.java

/*
 * Created on 2008/12/10
 * Copyright (C) 2008 Koga Laboratory. All rights reserved.
 *
 */
package org.mklab.tool.control;

import java.io.PrintWriter;
import java.io.Writer;
import java.util.List;

import org.mklab.nfc.matrix.DoubleMatrix;
import org.mklab.nfc.matrix.DoubleRationalPolynomialMatrix;
import org.mklab.nfc.matrix.IntMatrix;
import org.mklab.nfc.scalar.DoubleRationalPolynomial;


/**
 * 非プロパーな線形システムを表すクラスです.
 * 
 * @author Anan
 */
public class DoubleImproperLinearSystem extends DoubleAbstractLinearSystem {
  /** シリアル番号 */
  private static final long serialVersionUID = -7791135374432440376L;

  /**
   * 1入力1出力システムの伝達関数から線形システムを生成します。
   * 
   * @param transferFunction 1入力1出力システムの伝達関数(有理多項式)
   */
  DoubleImproperLinearSystem(final DoubleRationalPolynomial transferFunction) {
    if (transferFunction.getNumeratorDegree() <= transferFunction.getDenominatorDegree()) {
      throw new IllegalArgumentException("Not a improper system"); //$NON-NLS-1$
    }

    this.proper = false;
    this.strictlyProper = false;
    this.G = new DoubleRationalPolynomialMatrix(new DoubleRationalPolynomial[] {transferFunction});
    this.stateSize = transferFunction.getNumeratorDegree() + 1;
    this.inputSize = 1;
    this.outputSize = 1;

    final List<DoubleMatrix> abcde = Tf2des.tf2des(transferFunction);
    this.a = abcde.get(0);
    this.b = abcde.get(1);
    this.c = abcde.get(2);
    this.d = abcde.get(3);
    this.e = abcde.get(4);
    this.index = new IntMatrix(this.stateSize, 1);
  }

  /**
   * ディスクリプタシステム表現の係数行列から線形システムを生成します。
   * 
   * @param a 状態行列
   * @param b ゲイン行列
   * @param c 出力行列
   * @param d ゲイン行列
   * @param e ディスクリプター行列
   */
  DoubleImproperLinearSystem(final DoubleMatrix a, final DoubleMatrix b, final DoubleMatrix c, final DoubleMatrix d, final DoubleMatrix e) {
    this.proper = false;
    this.strictlyProper = false;
    this.stateSize = a.getRowSize();
    this.inputSize = b.getColumnSize();
    this.outputSize = c.getRowSize();
    this.a = a;
    this.b = b;
    this.c = c;
    this.d = d;
    this.e = e;
    this.index = new IntMatrix(this.stateSize, 1);
  }
  
  /**
   * ディスクリプタシステム表現の係数行列から線形システムを生成します。
   * 
   * @param a 状態行列
   * @param b ゲイン行列
   * @param c 出力行列
   * @param d ゲイン行列
   * @param e ディスクリプター行列
   * @param index 指数
   */
  DoubleImproperLinearSystem(final DoubleMatrix a, final DoubleMatrix b, final DoubleMatrix c, final DoubleMatrix d, final DoubleMatrix e, final IntMatrix index) {
    this.proper = false;
    this.strictlyProper = false;
    this.stateSize = a.getRowSize();
    this.inputSize = b.getColumnSize();
    this.outputSize = c.getRowSize();
    this.a = a;
    this.b = b;
    this.c = c;
    this.d = d;
    this.e = e;
    this.index = index;
  }

  /**
   * 伝達関数行列から線形システムを生成します。(一次的な実装)
   * 
   * @param transferFunctionMatrix 伝達関数行列(有理多項式行列)
   */
  DoubleImproperLinearSystem(DoubleRationalPolynomialMatrix transferFunctionMatrix) {
    this(transferFunctionMatrix.getElement(1));
    if (transferFunctionMatrix.getRowSize() != 1 || transferFunctionMatrix.getColumnSize() != 1) {
      throw new RuntimeException("現在、SISOシステムにのみ対応しています."); //$NON-NLS-1$
    }

  }

  /**
   * {@inheritDoc}
   */
  public DoubleRationalPolynomialMatrix getTransferFunctionMatrix(boolean simplify, double tolerance) {
    final DoubleRationalPolynomialMatrix g = Des2tfm.des2tfm(this.getA(), this.getB(), this.getC(), this.getD(), this.getE(), tolerance, simplify);
    if (isContinuous()) {
      g.setVariable("s"); //$NON-NLS-1$
    } else if (isDiscrete()) {
      g.setVariable("z"); //$NON-NLS-1$
    }
    return g;
  }

  /**
   * {@inheritDoc}
   */
  public DoubleRationalPolynomialMatrix getTransferFunctionMatrix(boolean simplify) {
    final DoubleRationalPolynomialMatrix g = Des2tfm.des2tfm(this.getA(), this.getB(), this.getC(), this.getD(), this.getE(), simplify);
    if (isContinuous()) {
      g.setVariable("s"); //$NON-NLS-1$
    } else if (isDiscrete()) {
      g.setVariable("z"); //$NON-NLS-1$
    }
    return g;
  }

  /**
   * @see org.mklab.tool.control.ProperLinearSystem#clone()
   */
  @Override
  public Object clone() {
    final DoubleImproperLinearSystem inst = new DoubleImproperLinearSystem(this.getA(), this.getB(), this.getC(), this.getD(), this.getE());

    inst.proper = this.proper;
    inst.strictlyProper = this.strictlyProper;
    inst.inputSize = this.inputSize;
    inst.stateSize = this.stateSize;
    inst.outputSize = this.outputSize;
    inst.timeDomainType = this.timeDomainType;
    inst.inputPortTags = this.inputPortTags;
    inst.outputPortTags = this.outputPortTags;
    inst.a = this.a == null ? null : (DoubleMatrix)this.a.clone();
    inst.b = this.b == null ? null : (DoubleMatrix)this.b.clone();
    inst.c = this.c == null ? null : (DoubleMatrix)this.c.clone();
    inst.d = this.d == null ? null : (DoubleMatrix)this.d.clone();
    inst.e = this.e == null ? null : (DoubleMatrix)this.e.clone();
    inst.index = this.index == null ? null : (IntMatrix)this.index.clone();
    inst.G = this.G == null ? null : (DoubleRationalPolynomialMatrix)this.G.clone();
    return inst;
  }

  /**
   * @see org.mklab.tool.control.LinearSystem#print(java.io.Writer)
   */
  public void print(Writer output) {
    printABCD(output);
    printE(output);
  }

  /**
   * EをWriterへ出力します。
   * 
   * @param output 出力先
   */
  private void printE(final Writer output) {
    final String[] eString = this.e.getPrintingElementsString(Integer.MAX_VALUE).split("[\\r\\n]+"); //$NON-NLS-1$
    final PrintWriter pw = new PrintWriter(output);

    pw.print("E(" + this.getStateSize() + "x" + this.getStateSize() + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
    pw.println();

    for (int i = 0; i < this.getStateSize() + 1; i++) {
      pw.print(eString[i]);
      pw.println();
    }

    pw.flush();
  }

  /**
   * A,B,C,DをWriterへ出力します。
   * 
   * @param output 出力先
   */
  private void printABCD(final Writer output) {
    final DoubleMatrix ac = this.getA().appendDown(this.getC());
    final DoubleMatrix bd = this.getB().appendDown(this.getD());
    ac.setElementFormat(this.getA().getElementFormat());
    bd.setElementFormat(this.getB().getElementFormat());

    final String[] acString = ac.getPrintingElementsString(Integer.MAX_VALUE).split("[\\r\\n]+"); //$NON-NLS-1$
    final String[] bdString = bd.getPrintingElementsString(Integer.MAX_VALUE).split("[\\r\\n]+"); //$NON-NLS-1$

    final String[] aString = new String[this.getStateSize() + 1];
    if (this.getStateSize() != 0) {
      System.arraycopy(acString, 0, aString, 0, this.getStateSize() + 1);
    }

    final String[] cString = new String[this.getOutputSize() + 1];
    if (this.getStateSize() != 0) {
      cString[0] = aString[0];
      System.arraycopy(acString, this.getStateSize() + 1, cString, 1, this.getOutputSize());
      resetRowNumber(cString);
    }

    final String[] bString = new String[this.getStateSize() + 1];
    System.arraycopy(bdString, 0, bString, 0, this.getStateSize() + 1);

    final String[] dString = new String[this.getOutputSize() + 1];
    dString[0] = bString[0];
    System.arraycopy(bdString, this.getStateSize() + 1, dString, 1, this.getOutputSize());

    resetRowNumber(dString);

    final PrintWriter pw = new PrintWriter(output);

    if (this.getStateSize() == 0) {
      for (int i = 0; i < this.getOutputSize() + 1; i++) {
        pw.println(dString[i]);
      }
      return;
    }

    for (int i = 0; i < this.getStateSize() + 1; i++) {
      pw.print(aString[i]);
      pw.print("|"); //$NON-NLS-1$
      pw.println(bString[i]);
    }

    for (int i = 0; i < aString[0].length(); i++) {
      pw.print("-"); //$NON-NLS-1$
    }

    pw.print("+"); //$NON-NLS-1$

    for (int i = 0; i < bString[0].length(); i++) {
      pw.print("-"); //$NON-NLS-1$
    }

    pw.println(""); //$NON-NLS-1$

    for (int i = 0; i < this.getOutputSize() + 1; i++) {
      pw.print(cString[i]);
      pw.print("|"); //$NON-NLS-1$
      pw.println(dString[i]);
    }

    pw.println(""); //$NON-NLS-1$

    pw.flush();
  }

  /**
   * @see org.mklab.tool.control.LinearSystem#getSymbolicStateSpaceRepresentation(boolean)
   */
  public String getSymbolicStateSpaceRepresentation(final boolean withInputOutput) {
    final StringBuffer string = new StringBuffer();

    if (0 < getStateSize()) {
      final int[] columnLengthes = getColumnLengthesOfABCD();
      final String[] symbolicState = getSymbolicState();
      string.append(getSymbolicStateEquation(withInputOutput));

      int eLength = getSymbolicELength();

      for (int i = 0; i < eLength; i++) {
        string.append(" "); //$NON-NLS-1$
      }

      if (withInputOutput) {
        if (isContinuous()) {
          if (getSubSystemSize() == 1) {
            string.append("       "); //$NON-NLS-1$
          } else {
            string.append("     "); //$NON-NLS-1$
          }
        }

        if (isDiscrete()) {
          string.append("     "); //$NON-NLS-1$
        }

        final String format = "%" + (symbolicState[0].length() + 1 + 1 + 1) + "s"; //$NON-NLS-1$ //$NON-NLS-2$
        string.append(String.format(format, "")); //$NON-NLS-1$
      }

      string.append("["); //$NON-NLS-1$
      drawHorizontalLine(string, columnLengthes);
      string.append("]" + System.getProperty("line.separator")); //$NON-NLS-1$ //$NON-NLS-2$
    }

    string.append(getSymbolicOutputEquation(withInputOutput));

    return string.toString();
  }

  /**
   * E行列のシンボルの長さを返します。
   * 
   * @return E行列のシンボルの長さ
   */
  private int getSymbolicELength() {
    int eLength = 0;
    for (int i : getELength()) {
      eLength += i;
    }
    eLength += 2;
    if (getELength().length != 1) {
      eLength += (getSubSystemSize() - 1) * 2;
    }
    return eLength;
  }

  /**
   * @see org.mklab.tool.control.LinearSystem#getSymbolicOutputEquation(boolean)
   */
  public String getSymbolicOutputEquation(final boolean withInputOutput) {
    final StringBuffer string = new StringBuffer();

    final String[] symbolicInput = getSymbolicInput();
    final String[] symbolicOutput = getSymbolicOutput();
    final int[] columnLengthes = getColumnLengthesOfABCD();
    final String lineSeparator = System.getProperty("line.separator"); //$NON-NLS-1$

    for (int row = 0; row < getOutputPortSize(); row++) {
      for (int i = 0; i < getSymbolicELength(); i++) {
        string.append(" "); //$NON-NLS-1$
      }
      if (withInputOutput) {
        if (isContinuous()) {
          if (getSubSystemSize() == 1) {
            string.append("    "); //$NON-NLS-1$
          } else {
            string.append("  "); //$NON-NLS-1$
          }
        }

        string.append("[" + symbolicOutput[row] + "]"); //$NON-NLS-1$ //$NON-NLS-2$
        if (row == getOutputPortSize() / 2) {
          if (isContinuous()) {
            string.append("(t)="); //$NON-NLS-1$
          } else {
            string.append("(k)  ="); //$NON-NLS-1$
          }
        } else {
          if (isContinuous()) {
            string.append("    "); //$NON-NLS-1$
          } else {
            string.append("  "); //$NON-NLS-1$
          }
        }
      }

      string.append("["); //$NON-NLS-1$
      for (int column = 0; column < getSubSystemSize(); column++) {
        final String format = "%" + columnLengthes[column] + "s"; //$NON-NLS-1$ //$NON-NLS-2$
        string.append(String.format(format, this.getSymbolicCMatrix()[row][column]));
        if (column != getSubSystemSize() - 1) {
          string.append("  "); //$NON-NLS-1$
        }
      }

      if (0 < getStateSize()) {
        string.append("|"); //$NON-NLS-1$
      }

      for (int column = 0; column < getInputPortSize(); column++) {
        final String format = "%" + columnLengthes[column + getSubSystemSize()] + "s"; //$NON-NLS-1$ //$NON-NLS-2$
        string.append(String.format(format, this.getSymbolicDMatrix()[row][column]));
        if (column != getInputPortSize() - 1) {
          string.append("  "); //$NON-NLS-1$
        }
      }
      string.append("]"); //$NON-NLS-1$

      if (withInputOutput && row < symbolicInput.length) {
        string.append("[" + symbolicInput[row] + "]"); //$NON-NLS-1$ //$NON-NLS-2$
        if (row == getInputPortSize() / 2) {
          if (isContinuous()) {
            string.append("(t)"); //$NON-NLS-1$
          } else {
            string.append("(k)"); //$NON-NLS-1$
          }
        }
      }

      string.append(lineSeparator);
    }

    if (withInputOutput && symbolicOutput.length < symbolicInput.length) {
      final int lengthTolastLineSeparator = string.lastIndexOf(lineSeparator, string.length() - lineSeparator.length() - 1) + lineSeparator.length();
      final int widthOfInputVector = (1 + symbolicInput[0].length() + 1);
      int spaceLength = (string.length() - lengthTolastLineSeparator) - 2 - widthOfInputVector;
      if (getOutputPortSize() - 1 == getInputPortSize() / 2) {
        spaceLength -= 3;
      }
      final String format = "%" + spaceLength + "s"; //$NON-NLS-1$ //$NON-NLS-2$

      for (int i = 0; i < symbolicInput.length - symbolicOutput.length; i++) {
        string.append(String.format(format, "")); //$NON-NLS-1$
        final int row = symbolicOutput.length + i;
        string.append("[" + symbolicInput[row] + "]"); //$NON-NLS-1$ //$NON-NLS-2$

        if (row == getInputPortSize() / 2) {
          if (isContinuous()) {
            string.append("(t)"); //$NON-NLS-1$
          } else {
            string.append("(k)"); //$NON-NLS-1$
          }
        }
        string.append(lineSeparator);
      }
    }

    return string.toString();
  }

  /**
   * Eの長さを返します。
   * 
   * @return Eの長さ
   */
  private int[] getELength() {
    int[] eLengthes = new int[getSubSystemSize()];
    for (int column = 0; column < getSubSystemSize(); column++) {
      eLengthes[column] = getMaxLengthColumnWise(this.getSymbolicEMatrix(), column);
    }
    return eLengthes;
  }

  /**
   * @see org.mklab.tool.control.LinearSystem#getSymbolicStateEquation(boolean)
   */
  public String getSymbolicStateEquation(final boolean withInputOutput) {
    final StringBuffer string = new StringBuffer();

    final String[] symbolicState = getSymbolicState();
    final int[] columnLengthes = getColumnLengthesOfABCD();
    final String lineSeparator = System.getProperty("line.separator"); //$NON-NLS-1$

    for (int row = 0; row < getSubSystemSize(); row++) {

      string.append("["); //$NON-NLS-1$
      for (int column = 0; column < getSubSystemSize(); column++) {
        final String format = "%" + getELength()[column] + "s"; //$NON-NLS-1$ //$NON-NLS-2$
        string.append(String.format(format, this.getSymbolicEMatrix()[row][column]));
        if (column != getSubSystemSize() - 1) {
          string.append("  "); //$NON-NLS-1$
        }
      }
      string.append("]"); //$NON-NLS-1$

      if (withInputOutput) {
        if (isContinuous()) {
          if (getSubSystemSize() == 1) {
            string.append("d/dt"); //$NON-NLS-1$
          } else if (getSubSystemSize() == 2) {
            if (row == 0) {
              string.append("d/"); //$NON-NLS-1$
            } else {
              string.append("dt"); //$NON-NLS-1$
            }
          } else {
            if (row == getSubSystemSize() / 2 - 1) {
              string.append("d "); //$NON-NLS-1$
            } else if (row == getSubSystemSize() / 2) {
              string.append("--"); //$NON-NLS-1$
            } else if (row == getSubSystemSize() / 2 + 1) {
              string.append("dt"); //$NON-NLS-1$
            } else {
              string.append("  "); //$NON-NLS-1$
            }
          }
        }

        string.append("[" + symbolicState[row] + "]"); //$NON-NLS-1$ //$NON-NLS-2$
        if (row == getSubSystemSize() / 2) {
          if (isContinuous()) {
            string.append("(t)="); //$NON-NLS-1$
          } else {
            string.append("(k+1)="); //$NON-NLS-1$
          }
        } else {
          if (isContinuous()) {
            string.append("    "); //$NON-NLS-1$
          } else {
            string.append("        "); //$NON-NLS-1$
          }
        }
      }

      string.append("["); //$NON-NLS-1$
      for (int column = 0; column < getSubSystemSize(); column++) {
        final String format = "%" + columnLengthes[column] + "s"; //$NON-NLS-1$ //$NON-NLS-2$
        string.append(String.format(format, this.getSymbolicAMatrix()[row][column]));
        if (column != getSubSystemSize() - 1) {
          string.append("  "); //$NON-NLS-1$
        }
      }
      string.append("|"); //$NON-NLS-1$

      for (int column = 0; column < getInputPortSize(); column++) {
        final String format = "%" + columnLengthes[column + getSubSystemSize()] + "s"; //$NON-NLS-1$ //$NON-NLS-2$
        string.append(String.format(format, this.getSymbolicBMatrix()[row][column]));
        if (column != getInputPortSize() - 1) {
          string.append("  "); //$NON-NLS-1$
        }
      }
      string.append("]"); //$NON-NLS-1$

      if (withInputOutput) {
        string.append("[" + symbolicState[row] + "]"); //$NON-NLS-1$ //$NON-NLS-2$
        if (row == getSubSystemSize() / 2) {
          if (isContinuous()) {
            string.append("(t)"); //$NON-NLS-1$
          } else {
            string.append("(k)"); //$NON-NLS-1$
          }
        }
      }

      string.append(lineSeparator);
    }

    return string.toString();
  }

  /**
   * 三対(入力数, 出力数, 状態数)の文字列に変換します。
   * 
   * @see java.lang.Object#toString()
   */
  @Override
  public String toString() {
    return "NonProperLinearSystem(" + this.inputSize + " inputs, " + this.outputSize + " outputs, " + this.stateSize + " states)"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
  }

}