Tf2des.java

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

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.mklab.nfc.matrix.ComplexNumericalMatrix;
import org.mklab.nfc.matrix.DoubleMatrix;
import org.mklab.nfc.matrix.DoubleMatrixUtil;
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.DoubleRationalPolynomial;
import org.mklab.nfc.scalar.RealNumericalScalar;


/**
 * 伝達関数(係数からなる行列)から状態空間表現に変換するクラスです.
 * 
 * <p>Transfer function to descriptor-state-space conversion
 * 
 * @author Anan
 */
public class Tf2des {

  /**
   * 1入力多出力システム(非プロパー)
   * 
   * <pre><code>
   * 
   * NUM(s) G(s) = -------- den(s)
   * 
   * </code></pre>
   * 
   * の状態空間表現
   * 
   * <pre><code>
   * 
   * . Ex = Ax + Bu y = Cx + Du
   * 
   * </code></pre>
   * 
   * を求めます。 係数行列 <code>A,B,C,D</code> は、可制御正準形で返されます。
   * 
   * @param transferFunction 伝達関数
   * @return Descriptor DoubleMatrix List{A,B,C,D,E} ディスクリプター表現(descriptor state space representation)
   */
  public static List<DoubleMatrix> tf2des(DoubleRationalPolynomial transferFunction) {
    return tf2des(transferFunction.getNumerator().getCoefficients(), transferFunction.getDenominator().getCoefficients());
  }

  /**
   * 1入力多出力システム(非プロパー)
   * 
   * <pre><code>
   * 
   * NUM(s) G(s) = -------- den(s)
   * 
   * </code></pre>
   * 
   * の状態空間表現
   * 
   * <pre><code>
   * 
   * . Ex = Ax + Bu y = Cx + Du
   * 
   * </code></pre>
   * 
   * を求めます。 係数行列 <code>A,B,C,D</code> は、可制御正準形で返されます。
   * 
   * @param numeratorMatrix 伝達関数の分子多項式の係数
   * @param denominatorMatrix 伝達関数の分母多項式の係数
   * @return Descriptor DoubleMatrix List{A,B,C,D,E} ディスクリプター表現(descriptor state space representation)
   */
  public static List<DoubleMatrix> tf2des(final DoubleMatrix numeratorMatrix, final DoubleMatrix denominatorMatrix) {
    // Strip leading zeros;
    IntMatrix denIdx = denominatorMatrix.flipLeftRight().compareElementWise(".!=", 0).find(); //$NON-NLS-1$
    DoubleMatrix denominator;
    if (denIdx.length() > 0) {
      denominator = denominatorMatrix.flipLeftRight().getSubVector(denIdx.getIntElement(1), denominatorMatrix.length());
    } else {
      denominator = denominatorMatrix.createOnes(1);
    }

    // Strip leading zeros;
    IntMatrix numIdx = numeratorMatrix.flipLeftRight().compareElementWise(".!=", 0).find(); //$NON-NLS-1$
    DoubleMatrix numerator;
    if (numIdx.length() > 0) {
      numerator = numeratorMatrix.flipLeftRight().getSubVector(numIdx.getIntElement(1), numeratorMatrix.length());
    } else {
      numerator = numeratorMatrix.createZero(1);
      throw new RuntimeException(Messages.getString("Tf2dss.0")); //$NON-NLS-1$
    }

    //    if (numerator.length() < denominator.length()) throw new RuntimeException(Messages.getString("Tf2dss.1")); //$NON-NLS-1$
    //    if (numerator.length() == denominator.length()) throw new RuntimeException(Messages.getString("Tf2dss.2")); //$NON-NLS-1$

    final DoubleMatrix E = createE(numerator.length());
    final DoubleMatrix A = createA(numerator, denominator);
    final DoubleMatrix B = createB(numerator.length());
    final DoubleMatrix C = createC(numerator);
    final DoubleMatrix D = createD();

    return new ArrayList<>(Arrays.asList(new DoubleMatrix[] {A, B, C, D, E}));
  }

  /**
   * E行列を作成します.
   * 
   * @param degree 次数
   * @return E(Matrix)
   */
  private static DoubleMatrix createE(int degree) {
    final DoubleMatrix subMatrix = new DoubleMatrix(DoubleMatrixUtil.createUnit(degree - 1, degree - 1));
    final DoubleMatrix e = new DoubleMatrix(degree, degree);
    e.setSubMatrix(1, 1, subMatrix, subMatrix);
    //    e.appendDown(new DoubleMatrix(1, degree + 1));
    //    e.appendRight(new DoubleMatrix(degree + 1, 1));
    return e;
  }

  /**
   * A行列を作成します.
   * 
   * @param numeratorMatrix 分子行列
   * @param denominatorMatrix 分母行列
   * @return A(Matrix)
   */
  private static DoubleMatrix createA(DoubleMatrix numeratorMatrix, DoubleMatrix denominatorMatrix) {
    final int nDegree = numeratorMatrix.length() - 1;
    final int dDegree = denominatorMatrix.length() - 1;
    final DoubleMatrix unitMatrix = new DoubleMatrix(DoubleMatrixUtil.createUnit(nDegree, nDegree));
    final DoubleMatrix upperMatrix = new DoubleMatrix(nDegree, 1).appendRight(unitMatrix);
    final DoubleMatrix coefficientsMatrix = denominatorMatrix.flipLeftRight().unaryMinus();
    final DoubleMatrix lowerMatrix = coefficientsMatrix.appendRight(new DoubleMatrix(1, nDegree - dDegree));
    final DoubleMatrix a = upperMatrix.appendDown(lowerMatrix);
    return a;
  }

  /**
   * B行列を作成します.
   * 
   * @param degree 次数
   * @return B(Matrix)
   */
  private static DoubleMatrix createB(int degree) {
    final DoubleMatrix subMatrix = new DoubleMatrix(degree - 1, 1);
    final DoubleMatrix b = subMatrix.appendDown(new DoubleMatrix(new double[] {1}));
    return b;
  }

  /**
   * C行列を作成します.
   * 
   * @param numeratorMatrix 分子行列
   * @return C(Matrix)
   */
  private static DoubleMatrix createC(DoubleMatrix numeratorMatrix) {
    return numeratorMatrix.flipLeftRight();
  }

  /**
   * D行列を作成します.
   * 
   * @return D(Matrix)
   */
  private static DoubleMatrix createD() {
    return new DoubleMatrix(1, 1);
  }

  /**
   * 1入力多出力システム(非プロパー)
   * 
   * <pre><code>
   * 
   * NUM(s) G(s) = -------- den(s)
   * 
   * </code></pre>
   * 
   * の状態空間表現
   * 
   * <pre><code>
   * 
   * . Ex = Ax + Bu y = Cx + Du
   * 
   * </code></pre>
   * 
   * を求めます。 係数行列 <code>A,B,C,D</code> は、可制御正準形で返されます。
   * 
   * @param numeratorMatrix 伝達関数の分子多項式の係数
   * @param denominatorMatrix 伝達関数の分母多項式の係数
   * @return Descriptor DoubleMatrix List{A,B,C,D,E} ディスクリプター表現(descriptor state space representation)
   * @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 static <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>> List<RM> tf2des(
      final RM numeratorMatrix, final RM denominatorMatrix) {
    // Strip leading zeros;
    IntMatrix denIdx = denominatorMatrix.flipLeftRight().compareElementWise(".!=", 0).find(); //$NON-NLS-1$
    RM denominator;
    if (denIdx.length() > 0) {
      denominator = denominatorMatrix.flipLeftRight().getSubVector(denIdx.getIntElement(1), denominatorMatrix.length());
    } else {
      denominator = denominatorMatrix.createOnes(1);
    }

    // Strip leading zeros;
    IntMatrix numIdx = numeratorMatrix.flipLeftRight().compareElementWise(".!=", 0).find(); //$NON-NLS-1$
    RM numerator;
    if (numIdx.length() > 0) {
      numerator = numeratorMatrix.flipLeftRight().getSubVector(numIdx.getIntElement(1), numeratorMatrix.length());
    } else {
      numerator = numeratorMatrix.createZero(1);
      throw new RuntimeException(Messages.getString("Tf2dss.0")); //$NON-NLS-1$
    }

    //    if (numerator.length() < denominator.length()) throw new RuntimeException(Messages.getString("Tf2dss.1")); //$NON-NLS-1$
    //    if (numerator.length() == denominator.length()) throw new RuntimeException(Messages.getString("Tf2dss.2")); //$NON-NLS-1$

    RS sunit = numerator.getElement(1).createUnit();

    final RM E = createE(numerator.length(), sunit);
    final RM A = createA(numerator, denominator, sunit);
    final RM B = createB(numerator.length(), sunit);
    final RM C = createC(numerator, sunit);
    final RM D = createD(sunit);

    List<RM> abcde = new ArrayList<>();
    abcde.add(A);
    abcde.add(B);
    abcde.add(C);
    abcde.add(D);
    abcde.add(E);

    return abcde;
  }

  /**
   * A行列を作成します.
   * 
   * @param numeratorMatrix 分子行列
   * @param denominatorMatrix 分母行列
   * @return A(Matrix)
   * @param <RS> type of real scalar
   * @param <RM> type of real matrix
   * @param <CS> type of complex scalar
   * @param <CM> type of complex matrix
   * @param sunit unit of scalar
   */
  private static <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>> RM createA(
      RM numeratorMatrix, RM denominatorMatrix, RS sunit) {
    final int nDegree = numeratorMatrix.length() - 1;
    final int dDegree = denominatorMatrix.length() - 1;
    final RM unitMatrix = sunit.createUnitGrid(nDegree, nDegree);
    final RM upperMatrix = sunit.createZeroGrid(nDegree, 1).appendRight(unitMatrix);
    final RM coefficientsMatrix = denominatorMatrix.flipLeftRight().unaryMinus();
    final RM lowerMatrix = coefficientsMatrix.appendRight(sunit.createZeroGrid(1, nDegree - dDegree));
    final RM a = upperMatrix.appendDown(lowerMatrix);
    return a;
  }

  /**
   * B行列を作成します.
   * 
   * @param degree 次数
   * @return B(Matrix)
   * @param <RS> type of real scalar
   * @param <RM> type of real matrix
   * @param <CS> type of complex scalar
   * @param <CM> type of complex matrix
   * @param sunit unit of scalar
   */
  private static <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>> RM createB(
      int degree, RS sunit) {
    final RM subMatrix = sunit.createZeroGrid(degree - 1, 1);
    final RM b = subMatrix.appendDown(sunit.createUnitGrid(1));
    return b;
  }

  /**
   * C行列を作成します.
   * 
   * @param numeratorMatrix 分子行列
   * @return C(Matrix)
   * @param <RS> type of real scalar
   * @param <RM> type of real matrix
   * @param <CS> type of complex scalar
   * @param <CM> type of complex matrix
   * @param sunit unit of scalar
   */
  private static <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>> RM createC(
      RM numeratorMatrix, RS sunit) {
    return numeratorMatrix.flipLeftRight();
  }

  /**
   * D行列を作成します.
   * 
   * @return D(Matrix)
   * @param <RS> type of real scalar
   * @param <RM> type of real matrix
   * @param <CS> type of complex scalar
   * @param <CM> type of complex matrix
   * @param sunit unit of scalar
   */
  private static <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>> RM createD(
      RS sunit) {
    return sunit.createZeroGrid(1, 1);
  }

  /**
   * E行列を作成します.
   * 
   * @param degree 次数
   * @return E(Matrix)
   * @param <RS> type of real scalar
   * @param <RM> type of real matrix
   * @param <CS> type of complex scalar
   * @param <CM> type of complex matrix
   * @param sunit unit of scalar
   */
  private static <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>> RM createE(
      int degree, RS sunit) {
    final RM subMatrix = sunit.createUnitGrid(degree - 1, degree - 1);
    final RM e = sunit.createZeroGrid(degree, degree);
    e.setSubMatrix(1, 1, subMatrix, subMatrix);
    return e;
  }

  /**
   * 1入力多出力システム(非プロパー)
   * 
   * <pre><code>
   * 
   * NUM(s) G(s) = -------- den(s)
   * 
   * </code></pre>
   * 
   * の状態空間表現
   * 
   * <pre><code>
   * 
   * . Ex = Ax + Bu y = Cx + Du
   * 
   * </code></pre>
   * 
   * を求めます。 係数行列 <code>A,B,C,D</code> は、可制御正準形で返されます。
   * 
   * @param transferFunction 伝達関数
   * @return Descriptor DoubleMatrix List{A,B,C,D,E} ディスクリプター表現(descriptor state space representation)
   * @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 static <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>> List<RM> tf2des(
      AnyRealRationalPolynomial<RS, RM, CS, CM> transferFunction) {
    return tf2des(transferFunction.getNumerator().getCoefficients(), transferFunction.getDenominator().getCoefficients());
  }

}