ProperLinearSystem.java
/*
* $Id: LinearSystem.java,v 1.86 2008/07/17 07:30:03 koga Exp $
*
* Copyright (C) 2004 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.AnyRealRationalPolynomialMatrix;
import org.mklab.nfc.matrix.ComplexNumericalMatrix;
import org.mklab.nfc.matrix.IntMatrix;
import org.mklab.nfc.matrix.RealNumericalMatrix;
import org.mklab.nfc.scalar.AnyRealRationalPolynomial;
import org.mklab.nfc.scalar.ComplexNumericalScalar;
import org.mklab.nfc.scalar.RealNumericalScalar;
/**
* プロパーな線形システムを表すクラスです。
*
* @author koga
* @version $Revision: 1.86 $
* @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 ProperLinearSystem<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>>
extends AbstractLinearSystem<RS, RM, CS, CM> {
/** シリアル番号 */
private static final long serialVersionUID = 1002207140697213466L;
// /**
// * メインメソッド
// *
// * @param args コマンドライン引数
// * @throws IOException ファイルに出力できない場合
// * @throws ClassNotFoundException 読み込んだデータがLinearSystem型でない場合
// */
// public static void main(String[] args) throws IOException,
// ClassNotFoundException {
// final RM a = new DoubleMatrix(new double[][] { {0, 1}, {-3, -2}});
// final RM b = new DoubleMatrix(new double[][] { {1, 0}, {0, 1}});
// final RM c = new DoubleMatrix(new double[][] { {1, 0}, {0, 1}});
// final RM d = new DoubleMatrix(new double[][] { {1, 1}, {1, 1}});
// final ProperLinearSystem system = LinearSystemFactory.createLinearSystem(a,
// b, c, d);
// system.print(new OutputStreamWriter(System.out));
//
// try (final ObjectOutputStream out = new ObjectOutputStream(new
// FileOutputStream("abcd.sys"))) { //$NON-NLS-1$
// out.writeObject(system);
//
// System.out.println(""); //$NON-NLS-1$
//
// try (final ObjectInputStream in = new ObjectInputStream(new
// FileInputStream("abcd.sys"))) { //$NON-NLS-1$
// ProperLinearSystem system2 = (ProperLinearSystem)in.readObject();
// system2.print(new OutputStreamWriter(System.out));
// }
// }
// }
/**
* 新しく生成された<code>ProperLinearSystem</code>オブジェクトを初期化します。
*/
private ProperLinearSystem() {
// nothing to do
}
/**
* 状態空間表現の係数行列から線形システムを生成します。
*
* @param a システム行列
* @param b 入力行列
* @param c 出力行列
* @param d ゲイン行列
*/
ProperLinearSystem(final RM a, final RM b, final RM c, final RM d) {
this(a, b, c, d, a.createUnit());
}
/**
* 状態空間表現の係数行列から線形システムを生成します。
*
* @param a システム行列
* @param b 入力行列
* @param c 出力行列
* @param d ゲイン行列
* @param e ディスクリプタ行列
*
*/
ProperLinearSystem(final RM a, final RM b, final RM c, final RM d, final RM e) {
this.proper = true;
if (d.isZero() == false) {
this.strictlyProper = false;
}
this.a = a;
this.b = b;
this.c = c;
this.d = d;
this.e = e;
this.stateSize = a.getRowSize();
this.inputSize = b.getColumnSize();
this.outputSize = c.getRowSize();
this.index = new IntMatrix(this.stateSize, 1);
}
/**
* 伝達関数行列から線形システムを生成します。
*
* @param transferFunctionMatrix 伝達関数行列(有理多項式行列)
*/
ProperLinearSystem(final AnyRealRationalPolynomialMatrix<RS, RM, CS, CM> transferFunctionMatrix) {
this.proper = true;
final int rowSize = transferFunctionMatrix.getRowSize();
final int columnSize = transferFunctionMatrix.getColumnSize();
for (int i = 1; i <= rowSize; i++) {
for (int j = 1; j <= columnSize; j++) {
final AnyRealRationalPolynomial<RS, RM, CS, CM> g = transferFunctionMatrix.getElement(i, j);
if (g.getNumeratorDegree() > g.getDenominatorDegree()) {
throw new IllegalArgumentException("Not a proper system"); //$NON-NLS-1$
}
if (g.getNumeratorDegree() == g.getDenominatorDegree()) {
this.strictlyProper = false;
}
}
}
final List<RM> abcd = Tfm2ss.tfm2ss(transferFunctionMatrix);
this.a = abcd.get(0);
this.b = abcd.get(1);
this.c = abcd.get(2);
this.d = abcd.get(3);
this.e = this.a.createUnit();
this.G = transferFunctionMatrix.createClone();
this.stateSize = this.a.getRowSize();
this.inputSize = this.b.getColumnSize();
this.outputSize = this.c.getRowSize();
this.index = new IntMatrix(this.stateSize, 1);
}
/**
* 1入力1出力システムの伝達関数から線形システムを生成します。
*
* @param transferFunction 1入力1出力システムの伝達関数(有理多項式)
*/
ProperLinearSystem(final AnyRealRationalPolynomial<RS, RM, CS, CM> transferFunction) {
this.proper = true;
if (transferFunction.getNumeratorDegree() > transferFunction.getDenominatorDegree()) {
throw new IllegalArgumentException("Not a proper system"); //$NON-NLS-1$
}
if (transferFunction.getNumeratorDegree() == transferFunction.getDenominatorDegree()) {
this.strictlyProper = false;
}
final List<RM> abcd = Tfn2ss.tfn2ss(transferFunction);
this.a = abcd.get(0);
this.b = abcd.get(1);
this.c = abcd.get(2);
this.d = abcd.get(3);
this.e = this.a.createUnit();
this.G = new AnyRealRationalPolynomialMatrix<RS, RM, CS, CM>(new AnyRealRationalPolynomial[][] { { transferFunction.clone() } });
this.stateSize = this.a.getRowSize();
this.inputSize = this.b.getColumnSize();
this.outputSize = this.c.getRowSize();
this.index = new IntMatrix(this.stateSize, 1);
}
/**
* 三対(入力数, 出力数, 状態数)の文字列に変換します。
*
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "ProperLinearSystem(" + this.inputSize + " inputs, " + this.outputSize + " outputs, " + this.stateSize //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ " states)"; //$NON-NLS-1$
}
/**
* @see org.mklab.tool.control.LinearSystem#print(java.io.Writer)
*/
public void print(final Writer output) {
printABCD(output);
}
/**
* 出力ストリームにプロパーなシステムの係数行列A,B,C,Dを出力します。
*
* @param output 出力ストリーム
*/
private void printABCD(final Writer output) {
final RM ac = this.a.appendDown(this.c);
final RM bd = this.b.appendDown(this.d);
ac.setElementFormat(this.a.getElementFormat());
bd.setElementFormat(this.a.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.stateSize + 1];
if (this.stateSize != 0) {
System.arraycopy(acString, 0, aString, 0, this.stateSize + 1);
}
final String[] cString = new String[this.outputSize + 1];
if (this.stateSize != 0) {
cString[0] = aString[0];
System.arraycopy(acString, this.stateSize + 1, cString, 1, this.outputSize);
resetRowNumber(cString);
}
final String[] bString = new String[this.stateSize + 1];
System.arraycopy(bdString, 0, bString, 0, this.stateSize + 1);
final String[] dString = new String[this.outputSize + 1];
dString[0] = bString[0];
System.arraycopy(bdString, this.stateSize + 1, dString, 1, this.outputSize);
resetRowNumber(dString);
final PrintWriter pw = new PrintWriter(output);
if (this.stateSize == 0) {
for (int i = 0; i < this.outputSize + 1; i++) {
pw.println(dString[i]);
}
return;
}
for (int i = 0; i < this.stateSize + 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.outputSize + 1; i++) {
pw.print(cString[i]);
pw.print("|"); //$NON-NLS-1$
pw.println(dString[i]);
}
pw.flush();
}
/**
* @see org.mklab.nfc.matrix.GridElement#clone()
*/
@Override
public Object clone() {
final ProperLinearSystem<RS, RM, CS, CM> inst = new ProperLinearSystem<>();
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 : this.a.createClone();
inst.b = this.b == null ? null : this.b.createClone();
inst.c = this.c == null ? null : this.c.createClone();
inst.d = this.d == null ? null : this.d.createClone();
inst.e = this.e == null ? null : this.e.createClone();
inst.G = this.G == null ? null : this.G.createClone();
return inst;
}
/**
* @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));
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();
}
/**
* @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++) {
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 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++) {
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();
}
/**
* @see org.mklab.tool.control.LinearSystem#getTransferFunctionMatrix(boolean)
*/
public AnyRealRationalPolynomialMatrix<RS, RM, CS, CM> getTransferFunctionMatrix(final boolean simplify) {
final AnyRealRationalPolynomialMatrix<RS, RM, CS, CM> g = Ss2tfm.ss2tfm(this.a, this.b, this.c, this.d, simplify);
if (isContinuous()) {
g.setVariable("s"); //$NON-NLS-1$
} else if (isDiscrete()) {
g.setVariable("z"); //$NON-NLS-1$
}
return g;
}
/**
* {@inheritDoc}
*/
public boolean equals(Object opponent, RS tolerance) {
return this.G.equals(((ProperLinearSystem<RS,RM,CS,CM>)opponent).G);
}
}