DoubleSystemBuilder.java

/*
 * $Id: ControlSystem.java,v 1.234 2008/07/15 15:27:15 koga Exp $
 *
 * Copyright (C) 2004 Koga Laboratory. All rights reserved.
 *
 */

package org.mklab.tool.control.system;

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

import org.mklab.nfc.matrix.DoubleMatrix;
import org.mklab.nfc.ode.DoubleContinuousAlgebraicSystem;
import org.mklab.nfc.ode.DoubleContinuousDiscreteAlgebraicSystem;
import org.mklab.nfc.ode.DoubleDifferenceSystem;
import org.mklab.nfc.ode.DoubleDifferentialDifferenceSystem;
import org.mklab.nfc.ode.DoubleDiscreteAlgebraicSystem;
import org.mklab.nfc.ode.DoubleExplicitDifferentialSystem;
import org.mklab.nfc.rpn.ReversePolishNotationProcessor;
import org.mklab.nfc.scalar.DoubleNumber;
import org.mklab.tool.control.DoubleLinearSystem;
import org.mklab.tool.control.system.continuous.DoubleBlockContinuousSystem;
import org.mklab.tool.control.system.continuous.DoubleContinuousDynamicSystem;
import org.mklab.tool.control.system.discrete.DoubleBlockDiscreteSystem;
import org.mklab.tool.control.system.discrete.DoubleDiscreteDynamicSystem;
import org.mklab.tool.control.system.math.DoubleConstantSystem;
import org.mklab.tool.control.system.math.DoubleDeMultiplexer;
import org.mklab.tool.control.system.math.DoubleDeMultiplexerGroup;
import org.mklab.tool.control.system.math.DoubleMultiplexer;
import org.mklab.tool.control.system.math.DoubleMultiplexerGroup;
import org.mklab.tool.control.system.math.DoubleNegativeUnitSystem;
import org.mklab.tool.control.system.math.DoubleUnitSystem;
import org.mklab.tool.control.system.sampled.DoubleBlockSampledDataSystem;
import org.mklab.tool.control.system.sampled.DoubleBlockSamplingSystem;
import org.mklab.tool.control.system.sampled.DoubleSampledDataDynamicSystem;
import org.mklab.tool.control.system.sink.DoubleOutputPort;
import org.mklab.tool.control.system.sink.Exporter;
import org.mklab.tool.control.system.source.DoubleInputPort;
import org.mklab.tool.control.system.source.Importer;


/**
 * システムを作成するビルダーです。
 * 
 * @author Koga Laboratory
 * @version $Revision: 1.234 $, 2004/11/10
 */
public class DoubleSystemBuilder {

  /** 隣接行列 */
  private DoubleAdjacencyMatrix adjacencyMatrix;

  /** 隣接行列が表わすブロックシステム */
  private DoubleBlockSystem blockSystem;

  /**
   * 新しく生成された<code>SystemBuilder</code>オブジェクトを初期化します。
   */
  private DoubleSystemBuilder() {
    // nothing;
  }

  /**
   * 新しく生成された<code>SystemBuilder</code>オブジェクトを初期化します。
   * 
   * @param system システムオペレータ
   */
  @SuppressWarnings("boxing")
  public DoubleSystemBuilder(final DoubleSystemOperator system) {
    final DoubleSystemOperator zero = DoubleZeroSystem.getInstance();
    final DoubleSystemOperator[][] matrix = new DoubleSystemOperator[][] { {zero, system}, {zero, zero}};

    final DoubleAdjacencyMatrix adjMatrix = new DoubleAdjacencyMatrix(matrix);
    adjMatrix.setInputNodes(new ArrayList<>(Arrays.asList(1)));
    adjMatrix.setOutputNodes(new ArrayList<>(Arrays.asList(2)));

    final Map<Integer, String> inputPortTags = new TreeMap<>();
    inputPortTags.put(1, "u_1"); //$NON-NLS-1$

    final Map<Integer, String> outputPortTags = new TreeMap<>();
    outputPortTags.put(2, "y_1"); //$NON-NLS-1$

    adjMatrix.setInputPortTags(inputPortTags);
    adjMatrix.setOutputPortTags(outputPortTags);

    this.adjacencyMatrix = adjMatrix;
    this.blockSystem = this.adjacencyMatrix.getBlockSystem();
  }

  /**
   * 新しく生成された<code>SystemBuilder</code>オブジェクトを初期化します。
   * 
   * <p>入力ノードの番号は1、出力ノードの番号は行列の次数となります。
   * 
   * @param matrix 隣接行列
   */
  @SuppressWarnings("boxing")
  public DoubleSystemBuilder(final DoubleAdjacencyMatrix matrix) {
    this(matrix, new ArrayList<>(Arrays.asList(1)), new ArrayList<>(Arrays.asList(matrix.getRowSize())), new SystemBuilderOption());
  }

  /**
   * 新しく生成された<code>SystemBuilder</code>オブジェクトを初期化します。
   * 
   * @param matrix 隣接行列
   * @param inputNodes 入力ポートのノード番号のリスト(番号は1から始まります)
   * @param outputNodes 出力ポートのノード番号のリスト(番号は1から始まります)
   */
  public DoubleSystemBuilder(final DoubleAdjacencyMatrix matrix, final List<Integer> inputNodes, final List<Integer> outputNodes) {
    this(matrix, inputNodes, outputNodes, new ArrayList<Integer>(), new ArrayList<Integer>(), new SystemBuilderOption());
  }

  /**
   * 新しく生成された<code>SystemBuilder</code>オブジェクトを初期化します。
   * 
   * <p>入力ノードの番号は1、出力ノードの番号は行列の次数となります。
   * 
   * @param matrix 隣接行列
   * @param option オプション
   */
  @SuppressWarnings("boxing")
  public DoubleSystemBuilder(final DoubleAdjacencyMatrix matrix, SystemBuilderOption option) {
    this(matrix, new ArrayList<>(Arrays.asList(1)), new ArrayList<>(Arrays.asList(matrix.getRowSize())), option);
  }

  /**
   * 新しく生成された<code>SystemBuilder</code>オブジェクトを初期化します。
   * 
   * @param matrix 隣接行列
   * @param inputNodes 入力ポートのノード番号のリスト(番号は1から始まります)
   * @param outputNodes 出力ポートのノード番号のリスト(番号は1から始まります)
   * @param option オプション
   */
  public DoubleSystemBuilder(final DoubleAdjacencyMatrix matrix, final List<Integer> inputNodes, final List<Integer> outputNodes, SystemBuilderOption option) {
    this(matrix, inputNodes, outputNodes, new ArrayList<Integer>(), new ArrayList<Integer>(), option);
  }

  /**
   * 新しく生成された<code>SystemBuilder</code>オブジェクトを初期化します。
   * 
   * @param matrix 隣接行列
   * @param inputNodes 入力ポートのノード番号のリスト(番号は1から始まります)
   * @param outputNodes 出力ポートのノード番号のリスト(番号は1から始まります)
   * @param sourceNodes Sourceのノード番号のリスト(番号は1から始まります)
   * @param sinkNodes Sinkのノード番号のリスト(番号は1から始まります)
   * @param option オプション
   */
  public DoubleSystemBuilder(final DoubleAdjacencyMatrix matrix, final List<Integer> inputNodes, final List<Integer> outputNodes, final List<Integer> sourceNodes, final List<Integer> sinkNodes,
      SystemBuilderOption option) {
    final Map<Integer, String> inputPortTags = new TreeMap<>();
    int inputNumber = 1;
    for (final Integer inputNode : inputNodes) {
      inputPortTags.put(inputNode, "u_" + inputNumber++); //$NON-NLS-1$
    }

    final Map<Integer, String> outputPortTags = new TreeMap<>();
    int outputNumber = 1;
    for (final Integer outputNode : outputNodes) {
      outputPortTags.put(outputNode, "y_" + outputNumber++); //$NON-NLS-1$
    }

    matrix.setupSystemIDForElements();

    createSystem(matrix, inputNodes, outputNodes, sourceNodes, sinkNodes, inputPortTags, outputPortTags, option);
  }

  /**
   * 新しく生成された<code>SystemBuilder</code>オブジェクトを初期化します。
   * 
   * @param stringMatrix 隣接関係を保持する文字列行列
   */
  public DoubleSystemBuilder(final DoubleAdjacencyStringMatrix stringMatrix) {
    this(stringMatrix, new SystemBuilderOption());
  }

  /**
   * 新しく生成された<code>SystemBuilder</code>オブジェクトを初期化します。
   * 
   * @param stringMatrix 隣接関係を保持する文字列行列
   * @param option オプション
   */
  public DoubleSystemBuilder(final DoubleAdjacencyStringMatrix stringMatrix, SystemBuilderOption option) {
    final List<Integer> inputSourceNodes = stringMatrix.getSortedInputSourceNodes();
    final List<Integer> outputSinkNodes = stringMatrix.getSortedOutputSinkNodes();

    final int inputSourceSize = inputSourceNodes.size();
    final int outputSinkSize = outputSinkNodes.size();

    final int inputSourceOutputSinkLines = 2;
    final int innerSize = stringMatrix.size() - inputSourceOutputSinkLines;
    final int size = innerSize + inputSourceSize + outputSinkSize;
    final DoubleSystemOperator[][] systemMatrix = new DoubleSystemOperator[size][size];

    setupDeMultiplexer(systemMatrix, stringMatrix, inputSourceSize);
    setupMultiplexer(systemMatrix, stringMatrix, inputSourceSize);
    setupSiSoSystem(systemMatrix, stringMatrix, inputSourceSize);
    setupMiMoSystem(systemMatrix, stringMatrix, inputSourceSize);

    // 接続先の無いポート、信号の数が未定のポートの信号を大きさを決定します。
    setupDeMultiplexer(systemMatrix, stringMatrix, inputSourceSize);
    setupMultiplexer(systemMatrix, stringMatrix, inputSourceSize);

    final Map<Integer, String> inputPortTags = getInputPortTags(stringMatrix, inputSourceNodes);
    final Map<Integer, String> outputPortTags = getOutputPortTags(stringMatrix, outputSinkNodes, inputSourceSize);

    final List<List<Integer>> inputNodesSourceNodes = setupInputSource(systemMatrix, stringMatrix, inputSourceNodes);
    final List<List<Integer>> outputNodesSinkNodes = setupOutputSink(systemMatrix, stringMatrix, outputSinkNodes, inputSourceSize);
    final List<Integer> inputNodes = inputNodesSourceNodes.get(0);
    final List<Integer> sourceNodes = inputNodesSourceNodes.get(1);
    final List<Integer> outputNodes = outputNodesSinkNodes.get(0);
    final List<Integer> sinkNodes = outputNodesSinkNodes.get(1);

    final DoubleAdjacencyMatrix matrix = new DoubleAdjacencyMatrix(systemMatrix);

    createSystem(matrix, inputNodes, outputNodes, sourceNodes, sinkNodes, inputPortTags, outputPortTags, option);
  }

  /**
   * データを設定します。
   * 
   * @param matrix 隣接行列
   * @param inputNodes 入力ポートのノード番号のリスト(番号は1から始まります)
   * @param outputNodes 出力ポートのノード番号のリスト(番号は1から始まります)
   * @param sourceNodes Sourceのノード番号のリスト(番号は1から始まります)
   * @param sinkNodes Sinkのノード番号のリスト(番号は1から始まります)
   * @param inputPortTags 入力ポートのノード番号(番号は1から始まります)とタグのマップ
   * @param outputPortTags 出力ポートのノード番号(番号は1から始まります)とタグのマップ
   * @param option オプション
   */
  private void createSystem(final DoubleAdjacencyMatrix matrix, final List<Integer> inputNodes, final List<Integer> outputNodes, final List<Integer> sourceNodes, final List<Integer> sinkNodes,
      final Map<Integer, String> inputPortTags, final Map<Integer, String> outputPortTags, SystemBuilderOption option) {
    final boolean requiringLinearSystem = option.isRequiringLinearSystem();
    final boolean contractingAllConstantEdges = option.isContractingAllConstantEdges();
    final boolean requiringReachableSubSystem = option.isRequiringReachableSubSystem();
    final boolean requiringDescriptor = option.isRequiringDescriptor();
    final boolean requiringPrimitiveExpression = option.isRequiringPrimitiveExpression();

    matrix.setInputNodes(inputNodes);
    matrix.setOutputNodes(outputNodes);
    matrix.setSourceNodes(sourceNodes);
    matrix.setSinkNodes(sinkNodes);
    matrix.setInputPortTags(inputPortTags);
    matrix.setOutputPortTags(outputPortTags);
    matrix.setRequiringLinearSystem(requiringLinearSystem);
    matrix.setRequiringDescriptor(requiringDescriptor);
    matrix.setRequiringPrimitiveExpression(requiringPrimitiveExpression);

    DoubleAdjacencyMatrix allSystem = matrix.expandAllBlockSystem();

    if (requiringLinearSystem) {
      allSystem = allSystem.getLinearSystemFromInputToOutput(requiringReachableSubSystem);
    }

    DoubleAdjacencyMatrix contractedSystem = allSystem;

    do {
      allSystem = contractedSystem;
      contractedSystem = allSystem.contractConstantEdge(true, true);
    } while (allSystem != contractedSystem);

    do {
      allSystem = contractedSystem;
      contractedSystem = allSystem.contractConstantEdge(false, true);
    } while (allSystem != contractedSystem);

    if (requiringLinearSystem || contractingAllConstantEdges) {
      do {
        allSystem = contractedSystem;
        contractedSystem = allSystem.contractConstantEdge(false, false);
      } while (allSystem != contractedSystem);
    }

    allSystem.setupUndefinedMultiplexer();
    allSystem.setupUndefinedDeMultiplexer();

    this.adjacencyMatrix = allSystem;
    this.blockSystem = this.adjacencyMatrix.getBlockSystem();

    if (requiringLinearSystem || contractingAllConstantEdges) {
      this.blockSystem.setZeroSizeToUnDefinedInputPortOutputPort();
    }
  }

  /**
   * SourceブロックからSinkブロックまでのパスに存在する(シミュレーション計算用)システムを返します。
   * 
   * @return SourceブロックからSinkブロックまでのパスに存在する(シミュレーション計算用)システム
   */
  public DoubleSystemBuilder getSystemForSimulation() {
    final DoubleSystemBuilder selectedSystem = new DoubleSystemBuilder();
    selectedSystem.adjacencyMatrix = this.adjacencyMatrix.getSystemBetweenSourceAndSink();
    selectedSystem.blockSystem = selectedSystem.adjacencyMatrix.getBlockSystem();
    return selectedSystem;
  }

  /**
   * SISOシステムを隣接行列に設定します。
   * 
   * @param systemMatrix 隣接行列
   * @param matrix 隣接関係を保持する文字列行列
   * @param inputPortSize 入力ノードの数
   */
  private void setupSiSoSystem(final DoubleSystemOperator[][] systemMatrix, final DoubleAdjacencyStringMatrix matrix, final int inputPortSize) {
    final int inputPortOutputPortLines = 2;
    final int innerSize = matrix.size() - inputPortOutputPortLines;

    for (int row = 2; row <= innerSize + 1; row++) {
      for (int column = 2; column <= innerSize + 1; column++) {
        final String weight = matrix.getWeightOfEdge(row, column);
        final int systemRow = row - 2 + inputPortSize;
        final int systemColumn = column - 2 + inputPortSize;

        if (weight == null || weight.equals("")) { //$NON-NLS-1$
          systemMatrix[systemRow][systemColumn] = DoubleZeroSystem.getInstance();
        } else if (weight.equals("P")) { //$NON-NLS-1$
          final int degree = Math.max(matrix.getNodeDegree(row), matrix.getNodeDegree(column));
          systemMatrix[systemRow][systemColumn] = new DoubleUnitSystem(degree);
        } else if (weight.equals("N")) { //$NON-NLS-1$
          final int degree = Math.max(matrix.getNodeDegree(row), matrix.getNodeDegree(column));
          systemMatrix[systemRow][systemColumn] = new DoubleNegativeUnitSystem(degree);
        } else if (weight.startsWith("G")) { //$NON-NLS-1$
          systemMatrix[systemRow][systemColumn] = matrix.getControlSystem(weight).getSystemOperator();
        } else {
          continue;
        }
        systemMatrix[systemRow][systemColumn].setID("" + systemRow + "," + systemColumn); //$NON-NLS-1$ //$NON-NLS-2$
      }
    }
  }

  /**
   * MIMOシステムを隣接行列に設定します。
   * 
   * @param systemMatrix 隣接行列
   * @param matrix 隣接関係を保持する文字列行列
   * @param inputPortSize 入力ノードの数
   */
  private void setupMiMoSystem(final DoubleSystemOperator[][] systemMatrix, final DoubleAdjacencyStringMatrix matrix, final int inputPortSize) {
    final int inputPortOutputPortLines = 2;
    final int innerSize = matrix.size() - inputPortOutputPortLines;

    for (int row = 2; row <= innerSize + 1; row++) {
      for (int column = 2; column <= innerSize + 1; column++) {
        final String weight = matrix.getWeightOfEdge(row, column);

        if (weight.startsWith("S") == false) { //$NON-NLS-1$
          continue;
        }

        final int systemRow = row - 2 + inputPortSize;
        final int systemColumn = column - 2 + inputPortSize;

        if (column <= innerSize) {
          final String rightWeight = matrix.getWeightOfEdge(row, column + 1);
          if (weight.equals(rightWeight)) {
            continue;
          }
        }

        if (3 <= row) {
          final String upperWeight = matrix.getWeightOfEdge(row - 1, column);
          if (weight.equals(upperWeight)) {
            continue;
          }
        }

        DoubleSystemOperator system = matrix.getControlSystem(weight).getSystemOperator();
        systemMatrix[systemRow][systemColumn] = system;
        systemMatrix[systemRow][systemColumn].setID("" + systemRow + "," + systemColumn); //$NON-NLS-1$ //$NON-NLS-2$
      }
    }
  }

  /**
   * OutputポートとSinkシステムを隣接行列に設定します。
   * 
   * @param systemMatrix 隣接行列
   * @param matrix 隣接関係を保持する文字列行列
   * @param outputSinkNodes 出力ポートとSinkのノードの番号のリスト(番号は1から始まります)
   * @param inputSourceSize 入力ポートとSourceの合計数
   * @return 出力ノードの(隣接行列内での)番号のリスト(番号は1から始まります)
   */
  @SuppressWarnings("boxing")
  private List<List<Integer>> setupOutputSink(final DoubleSystemOperator[][] systemMatrix, final DoubleAdjacencyStringMatrix matrix, final List<Integer> outputSinkNodes, final int inputSourceSize) {
    final int inputPortOutputPortLines = 2;
    final int innerSize = matrix.size() - inputPortOutputPortLines;

    final List<Integer> outputNodes = new ArrayList<>();
    final List<Integer> sinkNodes = new ArrayList<>();

    int systemColumn2 = innerSize + inputSourceSize;
    for (final int outputSinkNode : outputSinkNodes) {
      final String weight = matrix.getWeightOfEdge(outputSinkNode, matrix.size());
      if (weight == null || weight.equals("")) { //$NON-NLS-1$
        throw new IllegalArgumentException(Messages.getString("ControlSystem.13")); //$NON-NLS-1$
      }

      final int systemRow2 = outputSinkNode - 2 + inputSourceSize;
      final DoubleSystemOperator system = matrix.getControlSystem(weight).getSystemOperator();

      if (system instanceof DoubleOutputPort) {
        outputNodes.add(systemColumn2 + 1);

        final int degree = matrix.getNodeDegree(outputSinkNode);
        if (degree < 0) {
          systemMatrix[systemRow2][systemColumn2] = new DoubleUnitSystem(-1);
        } else {
          systemMatrix[systemRow2][systemColumn2] = new DoubleUnitSystem(degree);
        }
        ((DoubleUnitSystem)systemMatrix[systemRow2][systemColumn2]).setTag(((DoubleOutputPort)system).getTag());
      } else {
        sinkNodes.add(systemColumn2 + 1);

        systemMatrix[systemRow2][systemColumn2] = system;
      }

      systemMatrix[systemRow2][systemColumn2].setID("" + systemRow2 + "," + systemColumn2); //$NON-NLS-1$ //$NON-NLS-2$

      systemColumn2++;
    }

    List<List<Integer>> outputSink = new ArrayList<>();
    outputSink.add(outputNodes);
    outputSink.add(sinkNodes);

    return outputSink;
  }

  /**
   * 出力ポートのノード番号(番号は1から始まります)とタグのマップを返します。
   * 
   * @param matrix 隣接関係を保持する文字列行列
   * @param outputSinkNodes 出力ポートとSinkブロックのノードの番号のリスト(番号は1から始まります)
   * @param inputSourceSize 入力ポートとSourceの合計数
   * @return 出力ポートのノード番号(番号は1から始まります)とタグのマップ
   */
  @SuppressWarnings("boxing")
  private Map<Integer, String> getOutputPortTags(final DoubleAdjacencyStringMatrix matrix, final List<Integer> outputSinkNodes, final int inputSourceSize) {
    final Map<Integer, String> outputPortTags = new TreeMap<>();

    final int inputPortOutputPortLines = 2;
    final int innerSize = matrix.size() - inputPortOutputPortLines;

    int systemColumn2 = innerSize + inputSourceSize;
    for (final int outputSinkNode : outputSinkNodes) {
      final String weight = matrix.getWeightOfEdge(outputSinkNode, matrix.size());

      if (weight == null || weight.equals("")) { //$NON-NLS-1$
        throw new IllegalArgumentException(Messages.getString("ControlSystem.13")); //$NON-NLS-1$
      }

      final DoubleSystemOperator system = matrix.getControlSystem(weight).getSystemOperator();

      if (system instanceof DoubleOutputPort) {
        final String tag = ((DoubleOutputPort)system).getTag();
        outputPortTags.put(systemColumn2 + 1, tag);
      }

      systemColumn2++;
    }
    return outputPortTags;
  }

  /**
   * InputポートとSourceシステムを隣接行列に設定します。
   * 
   * @param systemMatrix 隣接行列
   * @param matrix 隣接関係を保持する文字列行列
   * @param inputSourceNodes 入力ポートとSourceのノード番号のリスト(番号は1から始まります)
   * @return 入力ノードの(隣接行列内での)番号のリスト(番号は1から始まります)
   */
  @SuppressWarnings("boxing")
  private List<List<Integer>> setupInputSource(final DoubleSystemOperator[][] systemMatrix, final DoubleAdjacencyStringMatrix matrix, final List<Integer> inputSourceNodes) {
    final int inputSourceSize = inputSourceNodes.size();
    final List<Integer> inputNodes = new ArrayList<>();
    final List<Integer> sourceNodes = new ArrayList<>();

    int systemRow1 = 0;
    for (final int inputSourceNode : inputSourceNodes) {
      final String weight = matrix.getWeightOfEdge(1, inputSourceNode);
      if (weight == null || weight.equals("")) { //$NON-NLS-1$
        throw new IllegalArgumentException(Messages.getString("ControlSystem.18")); //$NON-NLS-1$
      }

      final int systemColumn1 = inputSourceNode - 2 + inputSourceSize;
      final DoubleSystemOperator system = matrix.getControlSystem(weight).getSystemOperator();

      if (system instanceof DoubleInputPort) {
        inputNodes.add(systemRow1 + 1);
        //inputNodes.add(systemColumn1+1);

        final int degree = matrix.getNodeDegree(inputSourceNode);
        if (degree < 0) {
          systemMatrix[systemRow1][systemColumn1] = new DoubleUnitSystem(-1);
        } else {
          systemMatrix[systemRow1][systemColumn1] = new DoubleUnitSystem(degree);
        }
        ((DoubleUnitSystem)systemMatrix[systemRow1][systemColumn1]).setTag(((DoubleInputPort)system).getTag());
      } else {
        sourceNodes.add(systemRow1 + 1);
        //sourceNodes.add(systemColumn1+1);

        systemMatrix[systemRow1][systemColumn1] = system;
      }

      systemMatrix[systemRow1][systemColumn1].setID("" + systemRow1 + "," + systemColumn1); //$NON-NLS-1$ //$NON-NLS-2$

      systemRow1++;
    }

    List<List<Integer>> inputSource = new ArrayList<>();
    inputSource.add(inputNodes);
    inputSource.add(sourceNodes);

    return inputSource;
  }

  /**
   * 入力ポートのノード番号(番号は1から始まります)とタグのマップを返します。
   * 
   * @param matrix 隣接関係を保持する文字列行列
   * @param inputSourceNodes 入力ノードの番号のリスト(番号は1から始まります)
   * @return 入力ポートのノード番号(番号は1から始まります)とタグのマップ
   */
  @SuppressWarnings("boxing")
  private Map<Integer, String> getInputPortTags(final DoubleAdjacencyStringMatrix matrix, final List<Integer> inputSourceNodes) {
    //final int inputSourceSize = inputSourceNodes.size();
    final Map<Integer, String> inputPortTags = new TreeMap<>();

    int systemRow1 = 0;
    for (final int inputSourceNode : inputSourceNodes) {
      final String weight = matrix.getWeightOfEdge(1, inputSourceNode);

      if (weight == null || weight.equals("")) { //$NON-NLS-1$
        throw new IllegalArgumentException(Messages.getString("ControlSystem.18")); //$NON-NLS-1$
      }

      final DoubleSystemOperator system = matrix.getControlSystem(weight).getSystemOperator();

      if (system instanceof DoubleInputPort) {
        final String tag = ((DoubleInputPort)system).getTag();
        inputPortTags.put(systemRow1 + 1, tag);

        //final int systemColumn1 = inputSourceNode - 2 + inputSourceSize;
        //inputPortTags.put(systemColumn1+1, tag);
      }

      systemRow1++;
    }
    return inputPortTags;
  }

  /**
   * 多重器を隣接行列に設定します。
   * 
   * @param systemMatrix 隣接行列
   * @param matrix 隣接関係を保持する文字列行列
   * @param inputPortSize 入力ノードの数
   */
  @SuppressWarnings("boxing")
  private void setupMultiplexer(final DoubleSystemOperator[][] systemMatrix, final DoubleAdjacencyStringMatrix matrix, final int inputPortSize) {
    final int inputPortOutputPortLines = 2;
    final int innerSize = matrix.size() - inputPortOutputPortLines;

    int multiplexerInputPortSize = 0;

    for (int column = 2; column <= innerSize + 1; column++) {
      final Map<Integer, Integer> muxInputs = new TreeMap<>();
      for (int row = 2; row <= innerSize + 1; row++) {
        // 隣接関係を保持する文字列行列から値(文字列)を取り出す.
        final String weight = matrix.getWeightOfEdge(row, column);

        if (weight.startsWith("M") == false) { //$NON-NLS-1$
          continue;
        }

        muxInputs.put(Integer.parseInt(weight.substring(1, weight.indexOf("/"))), matrix.getNodeDegree(row)); //$NON-NLS-1$
        multiplexerInputPortSize = Integer.parseInt(weight.substring(weight.indexOf("/") + 1)); //$NON-NLS-1$
      }

      if (muxInputs.size() == 0) {
        continue;
      }

      // 接続元のない入力ポートがある場合、信号の大きさを0とする。
      for (int i = 1; i <= multiplexerInputPortSize; i++) {
        if (muxInputs.containsKey(i)) {
          continue;
        }
        muxInputs.put(i, 0);
      }

      // 出力ポートのノードの次数が未定の場合
      if (matrix.getNodeDegree(column) == -1) {
        int outputSize = 0;
        boolean allInputSizeDefined = true;
        for (int inputSize : muxInputs.values()) {
          if (inputSize == -1) {
            allInputSizeDefined = false;
            break;
          }
          outputSize += inputSize;
        }
        if (allInputSizeDefined) {
          matrix.setNodeDegree(column, outputSize);
        }
      }

      setupUnconnectedPort(matrix, muxInputs, multiplexerInputPortSize, column);

      final List<Integer> inputSizes = new ArrayList<>(muxInputs.values());

      DoubleMultiplexerGroup group = null;

      for (int row = 2; row <= innerSize + 1; row++) {
        // 隣接関係を保持する文字列行列から値(文字列)を取り出す.
        final String weight = matrix.getWeightOfEdge(row, column);

        if (weight.startsWith("M") == false) { //$NON-NLS-1$
          continue;
        }

        final int inputNumber = Integer.parseInt(weight.substring(1, weight.indexOf("/"))); //$NON-NLS-1$

        final int systemRow = row - 2 + inputPortSize;
        final int systemColumn = column - 2 + inputPortSize;

        if (group == null) {
          group = new DoubleMultiplexerGroup();
        }

        systemMatrix[systemRow][systemColumn] = new DoubleMultiplexer(inputSizes, inputNumber);
        systemMatrix[systemRow][systemColumn].setID("" + systemRow + "," + systemColumn); //$NON-NLS-1$ //$NON-NLS-2$
        ((DoubleMultiplexer)systemMatrix[systemRow][systemColumn]).setGroup(group);
        group.add((DoubleMultiplexer)systemMatrix[systemRow][systemColumn]);
        if (matrix.getNodeDegree(column) != -1) {
          systemMatrix[systemRow][systemColumn].setOutputSize(matrix.getNodeDegree(column));
        }

        matrix.setNodeDegree(row, inputSizes.get(inputNumber - 1));
      }
    }
  }

  /**
   * 分離器を隣接行列に設定します。
   * 
   * @param systemMatrix 隣接行列
   * @param matrix 隣接関係を保持する文字列行列
   * @param inputPortSize 入力ノードの数
   */
  @SuppressWarnings("boxing")
  private void setupDeMultiplexer(final DoubleSystemOperator[][] systemMatrix, final DoubleAdjacencyStringMatrix matrix, final int inputPortSize) {
    final int inputPortOutputPortLines = 2;
    final int innerSize = matrix.size() - inputPortOutputPortLines;

    int deMultiplexerOutputPortSize = 0;

    for (int row = 2; row <= innerSize + 1; row++) {
      final Map<Integer, Integer> deMuxOutputs = new TreeMap<>();
      for (int column = 2; column <= innerSize + 1; column++) {
        // 隣接関係を保持する文字列行列から値(文字列)を取り出す.
        final String weight = matrix.getWeightOfEdge(row, column);

        if (weight.startsWith("D") == false) { //$NON-NLS-1$
          continue;
        }

        deMuxOutputs.put(Integer.parseInt(weight.substring(1, weight.indexOf("/"))), matrix.getNodeDegree(column)); //$NON-NLS-1$
        deMultiplexerOutputPortSize = Integer.parseInt(weight.substring(weight.indexOf("/") + 1)); //$NON-NLS-1$
      }

      if (deMuxOutputs.size() == 0) {
        continue;
      }

      // 接続先のない出力ポート、信号の大きさを1とする。
      for (int i = 1; i <= deMultiplexerOutputPortSize; i++) {
        if (deMuxOutputs.containsKey(i)) {
          continue;
        }
        deMuxOutputs.put(i, 1);
      }

      // 入力ポートのノードの次数が未定の場合
      if (matrix.getNodeDegree(row) == -1) {
        int inputSize = 0;
        boolean allOutputSizeDefined = true;
        for (int outputSize : deMuxOutputs.values()) {
          if (outputSize == -1) {
            allOutputSizeDefined = false;
            break;
          }
          inputSize += outputSize;
        }
        if (allOutputSizeDefined) {
          matrix.setNodeDegree(row, inputSize);
        }
      }

      setupUnconnectedPort(matrix, deMuxOutputs, deMultiplexerOutputPortSize, row);

      final List<Integer> outputSizes = new ArrayList<>(deMuxOutputs.values());

      DoubleDeMultiplexerGroup group = null;

      for (int column = 2; column <= innerSize + 1; column++) {
        // 隣接関係を保持する文字列行列から値(文字列)を取り出す.
        final String weight = matrix.getWeightOfEdge(row, column);

        if (weight.startsWith("D") == false) { //$NON-NLS-1$
          continue;
        }

        final int outputNumber = Integer.parseInt(weight.substring(1, weight.indexOf("/"))); //$NON-NLS-1$

        final int systemRow = row - 2 + inputPortSize;
        final int systemColumn = column - 2 + inputPortSize;

        if (group == null) {
          group = new DoubleDeMultiplexerGroup();
        }

        systemMatrix[systemRow][systemColumn] = new DoubleDeMultiplexer(outputSizes, outputNumber);
        systemMatrix[systemRow][systemColumn].setID("" + systemRow + "," + systemColumn); //$NON-NLS-1$ //$NON-NLS-2$
        ((DoubleDeMultiplexer)systemMatrix[systemRow][systemColumn]).setGroup(group);
        group.add((DoubleDeMultiplexer)systemMatrix[systemRow][systemColumn]);
        if (matrix.getNodeDegree(row) != -1) {
          systemMatrix[systemRow][systemColumn].setInputSize(matrix.getNodeDegree(row));
        }

        matrix.setNodeDegree(column, outputSizes.get(outputNumber - 1));
      }
    }
  }

  /**
   * 接続先のないポート、信号の数が未定のポートの信号を大きさを決定し、 <code>ports</code>に追加登録します。
   * 
   * @param matrix 隣接文字列行列
   * @param ports ポート番号とポートの大きさのマップ
   * @param portSize ポートの数
   * @param node 対象とする隣接行列のノード番号
   */
  @SuppressWarnings("boxing")
  private void setupUnconnectedPort(final DoubleAdjacencyStringMatrix matrix, final Map<Integer, Integer> ports, int portSize, int node) {
    final int allSize = matrix.getNodeDegree(node);

    // 反対側のポートのノードの次数が未定の場合
    if (allSize == -1) {
      return;
    }

    int tentativeSize = 0;
    for (int size : ports.values()) {
      if (size == -1) {
        continue;
      }
      tentativeSize += size;
    }

    // 接続先のないポート、信号の数が未定のポート
    final List<Integer> undefinedSizePorts = new ArrayList<>();

    int definedPortSize = 0;

    // 接続先のないポート、信号の数が未定のポートがある場合、未決定分の信号の大きさを等分割する。
    for (int i = 1; i <= portSize; i++) {
      if (ports.containsKey(i) && ports.get(i) != -1) {
        definedPortSize += ports.get(i);
        continue;
      }
      undefinedSizePorts.add(i);
    }

    int noConnectionPortSize = 0;
    if (undefinedSizePorts.size() > 0) {
      noConnectionPortSize = (allSize - tentativeSize) / undefinedSizePorts.size();

      for (int port : undefinedSizePorts) {
        ports.put(port, noConnectionPortSize);
      }
    }

    for (int i = 0; i < allSize - definedPortSize - noConnectionPortSize * undefinedSizePorts.size(); i++) {
      ports.put(undefinedSizePorts.get(i), noConnectionPortSize + 1);
    }
  }

  /**
   * @see java.lang.Object#equals(java.lang.Object)
   */
  @Override
  public boolean equals(Object opponent) {
    if (opponent == this) {
      return true;
    }
    if (opponent == null) {
      return false;
    }
    if (opponent.getClass() != getClass()) {
      return false;
    }

    return this.adjacencyMatrix.equals(((DoubleSystemBuilder)opponent).adjacencyMatrix);
  }

  /**
   * @see java.lang.Object#hashCode()
   */
  @Override
  public int hashCode() {
    return this.blockSystem.hashCode();
  }

  /**
   * @see java.lang.Object#toString()
   */
  @Override
  public String toString() {
    if (isDynamic()) {
      return Messages.getString("ControlSystem.27") + getInputSize() + Messages.getString("ControlSystem.28") + getOutputSize() + Messages.getString("ControlSystem.29") + getStateSize() + Messages.getString("ControlSystem.30"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
    }
    return Messages.getString("ControlSystem.31") + getInputSize() + Messages.getString("ControlSystem.32") + getOutputSize() + Messages.getString("ControlSystem.33"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
  }

  /**
   * 入力の数を返します。
   * 
   * @return 入力の数
   */
  public int getInputSize() {
    if (isSingleSystem()) {
      return (this.adjacencyMatrix.getElement(1, 2)).getInputSize();
    }
    return this.blockSystem.getInputSize();
  }

  /**
   * 入力の数を設定します。
   * 
   * @param inputSize 入力の数
   */
  public void setInputSize(final int inputSize) {
    if (isSingleSystem()) {
      (this.adjacencyMatrix.getElement(1, 2)).setInputSize(inputSize);
    }
    this.blockSystem.setInputSize(inputSize);
  }

  /**
   * 出力の数を返します。
   * 
   * @return 出力の数
   */
  public int getOutputSize() {
    if (isSingleSystem()) {
      return (this.adjacencyMatrix.getElement(1, 2)).getOutputSize();
    }
    return this.blockSystem.getOutputSize();
  }

  /**
   * 出力の数を設定します。
   * 
   * @param outputSize 出力の数
   */
  public void setOutputSize(final int outputSize) {
    if (isSingleSystem()) {
      (this.adjacencyMatrix.getElement(1, 2)).setOutputSize(outputSize);
    }
    this.blockSystem.setOutputSize(outputSize);
  }

  /**
   * 状態の数を返します。
   * 
   * @return 状態の数
   */
  public int getStateSize() {
    if (isSingleSystem()) {
      return (this.adjacencyMatrix.getElement(1, 2)).getStateSize();
    }
    return this.blockSystem.getStateSize();
  }

  /**
   * 動的システムであるか判定します。
   * 
   * @return 動的システムならばtrue、そうでなければfalse
   */
  public boolean isDynamic() {
    return this.blockSystem.isDynamic();
  }

  /**
   * 静的システムであるか判定します。
   * 
   * @return 静的システムならばtrue、そうでなければfalse
   */
  public boolean isStatic() {
    return this.blockSystem.isStatic();
  }

  /**
   * 連続時間システムであるか判定します。
   * 
   * @return 連続時間システムならばtrue、そうでなければfalse
   */
  public boolean isContinuous() {
    if (this.blockSystem instanceof DoubleBlockContinuousSystem) {
      return true;
    }
    return false;
  }

  /**
   * 離散時間システムであるか判定します。
   * 
   * @return 離散時間システムならばtrue、そうでなければfalse
   */
  public boolean isDiscrete() {
    if (this.blockSystem instanceof DoubleBlockDiscreteSystem) {
      return true;
    }
    return false;
  }

  /**
   * 全てのシステムのサンプリング周期が一致するシステムであるか判定します。
   * 
   * @return 全てのシステムのサンプリング周期が一致するシステムならばtrue、そうでなければfalse
   */
  public boolean isSingleRate() {
    if (!(this.blockSystem instanceof DoubleBlockSamplingSystem)) {
      return false;
    }

    return ((DoubleBlockSamplingSystem)this.blockSystem).isSingleRate();
  }

  /**
   * 全てのシステムに共通するサンプリング周期を返します。
   * 
   * @return 全てのシステムに共通するサンプリング周期
   */
  public double getSingleSamplingInterval() {
    if (isSingleRate() == false) {
      throw new RuntimeException(Messages.getString("ControlSystem.34")); //$NON-NLS-1$
    }

    return ((DoubleBlockSamplingSystem)this.blockSystem).getSingleSamplingInterval();
  }

  /**
   * サンプル値システムであるか判定します。
   * 
   * @return サンプル値システムならばtrue、そうでなければfalse
   */
  public boolean isSampledData() {
    if (this.blockSystem instanceof DoubleBlockSampledDataSystem) {
      return true;
    }
    return false;
  }

  /**
   * 線形システムであるか判定します。
   * 
   * @return 線形システムならばtrue、そうでなければfalse
   */
  public boolean isLinear() {
    return this.blockSystem.isLinear();
  }

  /**
   * 直達項がある(出力が入力に直接依存する)か判定します。
   * 
   * @return 直達項があれば(出力が入力に直接依存すれば)true、そうでなければfalse
   */
  public boolean hasDirectFeedthrough() {
    return this.blockSystem.hasDirectFeedthrough();
  }

  /**
   * 初期状態を設定します。
   * 
   * @param initialState 初期状態
   */
  public void setInitialState(final DoubleMatrix initialState) {
    if (isStatic()) {
      throw new UnsupportedOperationException(Messages.getString("ControlSystem.35")); //$NON-NLS-1$
    }

    if (this.blockSystem instanceof DoubleContinuousDynamicSystem) {
      ((DoubleContinuousDynamicSystem)this.blockSystem).setInitialState(initialState);
    } else if (this.blockSystem instanceof DoubleDiscreteDynamicSystem) {
      ((DoubleDiscreteDynamicSystem)this.blockSystem).setInitialState(initialState);
    } else {
      throw new UnsupportedOperationException(Messages.getString("ControlSystem.36")); //$NON-NLS-1$
    }
  }

  /**
   * 初期状態を返します。
   * 
   * @return 初期状態
   */
  public DoubleMatrix getInitialState() {
    if (isStatic()) {
      throw new UnsupportedOperationException(Messages.getString("ControlSystem.37")); //$NON-NLS-1$
    }

    if (this.blockSystem instanceof DoubleContinuousDynamicSystem) {
      return ((DoubleContinuousDynamicSystem)this.blockSystem).getInitialState();
    }
    if (this.blockSystem instanceof DoubleDiscreteDynamicSystem) {
      return ((DoubleDiscreteDynamicSystem)this.blockSystem).getInitialState();
    }
    throw new UnsupportedOperationException(Messages.getString("ControlSystem.38")); //$NON-NLS-1$
  }

  /**
   * 連続時間部分システムの初期状態を返します。
   * 
   * @return 連続時間部分システムの初期状態
   */
  public DoubleMatrix getContinuousInitialState() {
    if (isStatic()) {
      throw new UnsupportedOperationException(Messages.getString("ControlSystem.39")); //$NON-NLS-1$
    }

    if (this.blockSystem instanceof DoubleSampledDataDynamicSystem) {
      return ((DoubleSampledDataDynamicSystem)this.blockSystem).getContinuousInitialState();
    }
    if (this.blockSystem instanceof DoubleContinuousDynamicSystem) {
      return ((DoubleContinuousDynamicSystem)this.blockSystem).getInitialState();
    }
    throw new UnsupportedOperationException(Messages.getString("ControlSystem.40")); //$NON-NLS-1$
  }

  /**
   * 離散時間部分システムの初期状態を返します。
   * 
   * @return 離散時間部分システムの初期状態
   */
  public DoubleMatrix getDiscreteInitialState() {
    if (isStatic()) {
      throw new UnsupportedOperationException(Messages.getString("ControlSystem.41")); //$NON-NLS-1$
    }

    if (this.blockSystem instanceof DoubleSampledDataDynamicSystem) {
      return ((DoubleSampledDataDynamicSystem)this.blockSystem).getDiscreteInitialState();
    }
    if (this.blockSystem instanceof DoubleDiscreteDynamicSystem) {
      return ((DoubleDiscreteDynamicSystem)this.blockSystem).getInitialState();
    }
    throw new UnsupportedOperationException(Messages.getString("ControlSystem.42")); //$NON-NLS-1$
  }

  /**
   * 現在の状態を返します。
   * 
   * @return 現在の状態
   */
  public DoubleMatrix getState() {
    if (isStatic()) {
      throw new UnsupportedOperationException(Messages.getString("ControlSystem.43")); //$NON-NLS-1$
    }

    if (this.blockSystem instanceof DoubleContinuousDynamicSystem) {
      return ((DoubleContinuousDynamicSystem)this.blockSystem).getState();
    }
    if (this.blockSystem instanceof DoubleDiscreteDynamicSystem) {
      return ((DoubleDiscreteDynamicSystem)this.blockSystem).getState();
    }
    throw new UnsupportedOperationException(Messages.getString("ControlSystem.44")); //$NON-NLS-1$
  }

  /**
   * 連続時間部分システムの現在の状態を返します。
   * 
   * @return 連続時間部分システムの現在の状態
   */
  public DoubleMatrix getContinuousState() {
    if (isStatic()) {
      throw new UnsupportedOperationException(Messages.getString("ControlSystem.45")); //$NON-NLS-1$
    }

    if (this.blockSystem instanceof DoubleSampledDataDynamicSystem) {
      return ((DoubleSampledDataDynamicSystem)this.blockSystem).getContinuousState();
    }
    if (this.blockSystem instanceof DoubleContinuousDynamicSystem) {
      return ((DoubleContinuousDynamicSystem)this.blockSystem).getState();
    }
    throw new UnsupportedOperationException(Messages.getString("ControlSystem.46")); //$NON-NLS-1$
  }

  /**
   * 離散時間部分システムの現在の状態を返します。
   * 
   * @return 離散時間部分システムの現在の状態
   */
  public DoubleMatrix getDiscreteState() {
    if (isStatic()) {
      throw new UnsupportedOperationException(Messages.getString("ControlSystem.47")); //$NON-NLS-1$
    }

    if (this.blockSystem instanceof DoubleSampledDataDynamicSystem) {
      return ((DoubleSampledDataDynamicSystem)this.blockSystem).getDiscreteState();
    }
    if (this.blockSystem instanceof DoubleDiscreteDynamicSystem) {
      return ((DoubleDiscreteDynamicSystem)this.blockSystem).getState();
    }
    throw new UnsupportedOperationException(Messages.getString("ControlSystem.48")); //$NON-NLS-1$
  }

  /**
   * システム<code>opponent</code>との差(符合が異なる並列結合)でできるシステムを返します。
   * 
   * @param opponent 結合するシステム(引くシステム)
   * @return 結合システム(this - opponent)
   */
  public DoubleSystemBuilder subtract(final DoubleSystemBuilder opponent) {
    // 隣接行列1
    final DoubleAdjacencyMatrix matrix1 = this.adjacencyMatrix;
    final int size1 = matrix1.length();
    final int inputSize1 = this.getInputSize();
    final int outputSize1 = this.getOutputSize();

    // 隣接行列2
    final DoubleAdjacencyMatrix matrix2 = opponent.adjacencyMatrix;
    final int size2 = matrix2.length();
    final int inputSize2 = opponent.getInputSize();
    final int outputSize2 = opponent.getOutputSize();

    // 新しい隣接行列
    final DoubleAdjacencyMatrix matrix = new DoubleAdjacencyMatrix(size1 + size2 + 2, size1 + size2 + 2);

    matrix.setElement(1, 2, new DoubleUnitSystem(inputSize1));
    matrix.setElement(1, size1 + 2, new DoubleUnitSystem(inputSize2));
    matrix.setElement(size1 + 1, size1 + size2 + 2, new DoubleUnitSystem(outputSize1));
    matrix.setElement(size1 + size2 + 1, size1 + size2 + 2, new DoubleConstantSystem(DoubleMatrix.unit(outputSize2, outputSize2).unaryMinus()));

    // 隣接行列1を代入
    matrix.setSubMatrix(2, 2 + size1 - 1, 2, 2 + size1 - 1, matrix1);

    // 隣接行列2を代入
    matrix.setSubMatrix(2 + size1, 2 + size1 + size2 - 1, 2 + size1, 2 + size1 + size2 - 1, matrix2);

    return (new DoubleSystemBuilder(matrix));
  }

  /**
   * システム<code>opponent</code>との積(直列結合)でできるシステムを返します。
   * 
   * <p> opponent --- this ---
   * 
   * @param opponent 結合するシステム(入力側に掛けるシステム)
   * @return 結合システム (this * opponent)
   */
  public DoubleSystemBuilder multiply(final DoubleSystemBuilder opponent) {
    if (this.isAutoSize() && this.getInputSize() == -1) {
      this.setInputSize(opponent.getOutputSize());
    }

    if (opponent.isAutoSize() && opponent.getOutputSize() == -1) {
      opponent.setOutputSize(this.getInputSize());
    }

    // SystemEquationMatrix1
    final DoubleAdjacencyMatrix matrix1 = this.adjacencyMatrix;
    final int size1 = matrix1.length();

    // SystemEquationMatrix2
    final DoubleAdjacencyMatrix matrix2 = opponent.adjacencyMatrix;
    final int size2 = matrix2.length();
    final int outputSize2 = opponent.getOutputSize();

    // 新しく作成されるSystemEquationMatrix
    final DoubleAdjacencyMatrix matrix = new DoubleAdjacencyMatrix(size1 + size2, size1 + size2);

    // 単位行列を代入
    matrix.setElement(size2, size2 + 1, new DoubleUnitSystem(outputSize2));

    // matrix2を代入
    matrix.setSubMatrix(1, size2, 1, size2, matrix2);

    // matrix1を代入
    matrix.setSubMatrix(size2 + 1, size2 + size1, size2 + 1, size2 + size1, matrix1);

    return (new DoubleSystemBuilder(matrix));
  }

  /**
   * 符合を逆にしてできるシステムを返します。
   * 
   * @return 符合を逆にしてできるシステム
   */
  public DoubleSystemBuilder unaryMinus() {
    // SystemEquationMatrix取得
    final DoubleAdjacencyMatrix matrix1 = this.adjacencyMatrix;
    final int size1 = matrix1.length();
    final int outputSize1 = this.getOutputSize();

    final DoubleAdjacencyMatrix newMatrix = new DoubleAdjacencyMatrix(size1 + 2, size1 + 2);

    // 元の隣接行列を代入
    newMatrix.setSubMatrix(1, size1, 1, size1, matrix1);

    // 単位行列を代入
    newMatrix.setElement(size1, size1 + 1, new DoubleUnitSystem(outputSize1));

    // 出力を負にするための単位行列を代入
    newMatrix.setElement(size1 + 1, size1 + 2, new DoubleConstantSystem(DoubleMatrix.unit(outputSize1, outputSize1).unaryMinus()));

    return new DoubleSystemBuilder(newMatrix);
  }

  /**
   * 単一(ネガティブ)フィードバック結合でできるシステムを返します。
   * 
   * @return 閉ループシステム
   */
  public DoubleSystemBuilder unityFeedback() {
    return unityFeedback(true);
  }

  /**
   * 単一フィードバック結合でできるシステムを返します。
   * 
   * @param negative true: ネガティブフィードバック, false: ポジティブフィードバック
   * @return 閉ループシステム
   */
  public DoubleSystemBuilder unityFeedback(final boolean negative) {
    if (this.isAutoSize() && this.getInputSize() == -1) {
      this.setInputSize(getOutputSize());
    }
    if (this.isAutoSize() && this.getOutputSize() == -1) {
      this.setOutputSize(getInputSize());
    }

    // SystemEquationMatrix取得
    final DoubleAdjacencyMatrix matrix1 = this.adjacencyMatrix;
    final int size1 = matrix1.length();
    final int inputSize1 = this.getInputSize();
    final int outputSize1 = this.getOutputSize();

    final DoubleAdjacencyMatrix newMatrix = new DoubleAdjacencyMatrix(size1 + 3, size1 + 3);

    // 元の隣接行列を代入
    newMatrix.setSubMatrix(2, 2 + size1 - 1, 2, 2 + size1 - 1, matrix1);

    // 単位行列を代入
    newMatrix.setElement(1, 2, new DoubleUnitSystem(inputSize1));
    newMatrix.setElement(size1 + 1, size1 + 2, new DoubleUnitSystem(outputSize1));
    newMatrix.setElement(size1 + 1, size1 + 3, new DoubleUnitSystem(outputSize1));

    // 単位行列又は負の単位行列を代入
    if (negative) {
      newMatrix.setElement(2 + size1, 2, new DoubleConstantSystem(DoubleMatrix.unit(inputSize1, inputSize1).unaryMinus()));
    } else {
      newMatrix.setElement(2 + size1, 2, new DoubleConstantSystem(DoubleMatrix.unit(inputSize1, inputSize1)));
    }

    return new DoubleSystemBuilder(newMatrix);
  }

  /**
   * (ネガティブ)フィードバック結合でできるシステムを返します。
   * 
   * @param feedbackElement フィードバック結合するシステム
   * @return 閉ループシステム
   */
  public DoubleSystemBuilder feedback(final DoubleSystemBuilder feedbackElement) {
    return feedback(feedbackElement, true);
  }

  /**
   * フィードバック結合でできるシステムを返します。
   * 
   * @param feedbackElement フィードバック結合するシステム
   * @param negative true: ネガティブフィードバック, false: ポジティブフィードバック
   * @return 閉ループシステム
   */
  public DoubleSystemBuilder feedback(final DoubleSystemBuilder feedbackElement, final boolean negative) {
    if (this.isAutoSize() && this.getInputSize() == -1) {
      this.setInputSize(feedbackElement.getOutputSize());
    }
    if (this.isAutoSize() && this.getOutputSize() == -1) {
      this.setOutputSize(feedbackElement.getInputSize());
    }
    if (feedbackElement.isAutoSize() && feedbackElement.getInputSize() == -1) {
      feedbackElement.setInputSize(this.getOutputSize());
    }
    if (feedbackElement.isAutoSize() && feedbackElement.getOutputSize() == -1) {
      feedbackElement.setOutputSize(this.getInputSize());
    }

    final DoubleAdjacencyMatrix matrix1 = this.adjacencyMatrix;
    final int size1 = matrix1.length();
    final int inputSize1 = this.getInputSize();
    final int outputSize1 = this.getOutputSize();

    final DoubleAdjacencyMatrix matrix2 = feedbackElement.adjacencyMatrix;
    int size2 = matrix2.length();

    // フィードバック結合によって生成される行列
    final DoubleAdjacencyMatrix matrix = new DoubleAdjacencyMatrix(size1 + size2 + 2, size1 + size2 + 2);

    matrix.setElement(1, 2, new DoubleUnitSystem(inputSize1));
    matrix.setElement(size1 + 1, size1 + 2, new DoubleUnitSystem(outputSize1));
    matrix.setElement(size1 + 1, size1 + size2 + 2, new DoubleUnitSystem(outputSize1));

    if (negative) {
      matrix.setElement(size1 + size2 + 1, 2, new DoubleConstantSystem(DoubleMatrix.unit(inputSize1, inputSize1).unaryMinus()));
    } else {
      matrix.setElement(size1 + size2 + 1, 2, new DoubleUnitSystem(inputSize1));
    }

    // フィードフォワード要素の隣接行列を代入
    matrix.setSubMatrix(2, 2 + size1 - 1, 2, 2 + size1 - 1, matrix1);

    // フォードバック要素の隣接行列を代入
    matrix.setSubMatrix(2 + size1, 2 + size1 + size2 - 1, 2 + size1, 2 + size1 + size2 - 1, matrix2);

    return (new DoubleSystemBuilder(matrix));
  }

  /**
   * システムを表す常微分方程式システム(状態方程式、入出力方程式)を返します。
   * 
   * @return システムを表す常微分方程式システム(状態方程式、入出力方程式)
   */
  public DoubleExplicitDifferentialSystem getDifferentialSystem() {
    if (isStatic()) {
      throw new IllegalStateException(Messages.getString("ControlSystem.49")); //$NON-NLS-1$
    }

    if (!isContinuous()) {
      throw new IllegalStateException(Messages.getString("ControlSystem.50")); //$NON-NLS-1$
    }

    return (DoubleExplicitDifferentialSystem)this.blockSystem;
  }

  /**
   * システムを表す連続時間代数方程式システム(入出力方程式)を返します。
   * 
   * @return システムを表す連続時間代数方程式システム(入出力方程式)
   */
  public DoubleContinuousAlgebraicSystem getContinuousAlgebraicEquation() {
    if (isDynamic()) {
      throw new IllegalStateException(Messages.getString("ControlSystem.51")); //$NON-NLS-1$
    }

    if (!isContinuous()) {
      throw new IllegalStateException(Messages.getString("ControlSystem.52")); //$NON-NLS-1$
    }

    return (DoubleContinuousAlgebraicSystem)this.blockSystem;
  }

  /**
   * システムを表す連続・離散時間代数方程式システム(入出力方程式)を返します。
   * 
   * @return システムを表す連続・離散時間代数方程式システム(入出力方程式)
   */
  public DoubleContinuousDiscreteAlgebraicSystem getContinuousDiscreteAlgebraicEquation() {
    if (isDynamic()) {
      throw new IllegalStateException(Messages.getString("ControlSystem.53")); //$NON-NLS-1$
    }

    if (!isSampledData()) {
      throw new IllegalStateException(Messages.getString("ControlSystem.54")); //$NON-NLS-1$
    }

    return (DoubleContinuousDiscreteAlgebraicSystem)this.blockSystem;
  }

  /**
   * システムを表す離散時間代数方程式システム(入出力方程式)を返します。
   * 
   * @return システムを表す離散時間代数方程式システム(入出力方程式)
   */
  public DoubleDiscreteAlgebraicSystem getDiscreteAlgebraicEquation() {
    if (isDynamic()) {
      throw new IllegalStateException(Messages.getString("ControlSystem.55")); //$NON-NLS-1$
    }

    if (!isDiscrete()) {
      throw new IllegalStateException(Messages.getString("ControlSystem.56")); //$NON-NLS-1$
    }

    return (DoubleDiscreteAlgebraicSystem)this.blockSystem;
  }

  /**
   * システムを表す差分方程式システム(状態方程式、入出力方程式)を返します。
   * 
   * @return システムを表す差分方程式システム(状態方程式、入出力方程式)
   */
  public DoubleDifferenceSystem getDifferenceSystem() {
    if (isStatic()) {
      throw new IllegalStateException(Messages.getString("ControlSystem.57")); //$NON-NLS-1$
    }

    if (!isDiscrete()) {
      throw new IllegalStateException(Messages.getString("ControlSystem.58")); //$NON-NLS-1$
    }

    return (DoubleDifferenceSystem)this.blockSystem;
  }

  /**
   * システムを表す微分差分方程式システム(状態方程式、入出力方程式)を返します。
   * 
   * @return システムを表す微分差分方程式システム(状態方程式、入出力方程式)
   */
  public DoubleDifferentialDifferenceSystem getDifferentialDifferenceSystem() {
    if (isStatic()) {
      throw new IllegalStateException(Messages.getString("ControlSystem.59")); //$NON-NLS-1$
    }

    if (!isSampledData()) {
      throw new IllegalStateException(Messages.getString("ControlSystem.60")); //$NON-NLS-1$
    }

    return (DoubleDifferentialDifferenceSystem)this.blockSystem;
  }

  /**
   * 線形システムの式を返します。
   * 
   * @param requiringReachableSubSystem 可到達なサブシステム(入力ノードから出力ノードまでのパスに対応するシステム)を求めるならばtrue
   * @return 線形システムの式
   */
  public DoubleLinearSystem getLinearSystem(final boolean requiringReachableSubSystem) {
    return this.adjacencyMatrix.getLinearSystem(requiringReachableSubSystem);
  }

  /**
   * 線形システムの式を返します。
   * 
   * @param requiringReachableSubSystem 可到達なサブシステム(入力ノードから出力ノードまでのパスに対応するシステム)を求めるならばtrue
   * @return 線形システムの式
   */
  public DoubleAdjacencyConstantMatrix getSymbolicTransferFunction(final boolean requiringReachableSubSystem) {
    return this.adjacencyMatrix.getSymbolicTransferFunction(requiringReachableSubSystem);
  }

  /**
   * 線形システムの式を返します。
   * 
   * @param requiringReachableSubSystem 可到達なサブシステム(入力ノードから出力ノードまでのパスに対応するシステム)を求めるならばtrue
   * @param processor プロセッサ
   * @return 線形システムの式
   */
  public DoubleLinearSystem getLinearSystemForMaxima(final boolean requiringReachableSubSystem, final ReversePolishNotationProcessor<DoubleNumber,DoubleMatrix> processor) {
    return this.adjacencyMatrix.getLinearSystemForMaxima(requiringReachableSubSystem, processor);
  }

  /**
   * 線形システムの式(数式も含む)を返します。
   * 
   * @param requiringReachableSubSystem 可到達なサブシステム(入力ノードから出力ノードまでのパスに対応するシステム)を求めるならばtrue
   * @param processor 解釈するプロセッサー
   * @return 線形システムの式(数式も含む)
   */
  public DoubleLinearSystem getLinearSystemWithExpression(final boolean requiringReachableSubSystem, final ReversePolishNotationProcessor<DoubleNumber,DoubleMatrix> processor) {
    return this.adjacencyMatrix.getLinearSystemByProcessor(requiringReachableSubSystem, processor);
  }

  /**
   * 入力ノードから出力ノードまでの隣接行列を返します。
   * 
   * @param requiringReachableSubSystem 可到達なサブシステムを求めるならばtrue
   * @return 入力ノードから出力ノードまでの隣接行列
   */
  public DoubleAdjacencyMatrix getAllSystem(final boolean requiringReachableSubSystem) {
    return this.adjacencyMatrix.getLinearSystemFromInputToOutput(requiringReachableSubSystem);
  }

  /**
   * システム<code>opponent</code>との和(並列結合)でできるシステムを返します。
   * 
   * @param opponent 結合するシステム(加えるシステム)
   * @return 結合システム
   */
  public DoubleSystemBuilder add(final DoubleSystemBuilder opponent) {
    if (this.isAutoSize() && this.getInputSize() == -1) {
      setInputSize(opponent.getInputSize());
    }
    if (this.isAutoSize() && this.getOutputSize() == -1) {
      setOutputSize(opponent.getOutputSize());
    }
    if (opponent.isAutoSize() && opponent.getInputSize() == -1) {
      opponent.setInputSize(getInputSize());
    }
    if (opponent.isAutoSize() && opponent.getOutputSize() == -1) {
      opponent.setOutputSize(getOutputSize());
    }

    final DoubleAdjacencyMatrix matrix1 = this.adjacencyMatrix;
    final int size1 = matrix1.length();
    int inputSize1 = this.getInputSize();
    int outputSize1 = this.getOutputSize();

    final DoubleAdjacencyMatrix matrix2 = opponent.adjacencyMatrix;
    final int size2 = matrix2.length();
    int inputSize2 = opponent.getInputSize();
    int outputSize2 = opponent.getOutputSize();

    // 新しい隣接行列
    final DoubleAdjacencyMatrix matrix = new DoubleAdjacencyMatrix(size1 + size2 + 2, size1 + size2 + 2);

    matrix.setElement(1, 2, new DoubleUnitSystem(inputSize1));
    matrix.setElement(1, 2 + size1, new DoubleUnitSystem(inputSize2));
    matrix.setElement(1 + size1, 2 + size1 + size2, new DoubleUnitSystem(outputSize1));
    matrix.setElement(1 + size1 + size2, 2 + size1 + size2, new DoubleUnitSystem(outputSize2));

    // 隣接行列1を代入
    matrix.setSubMatrix(2, 2 + size1 - 1, 2, 2 + size1 - 1, matrix1);

    // 隣接行列2を代入
    matrix.setSubMatrix(2 + size1, 2 + size1 + size2 - 1, 2 + size1, 2 + size1 + size2 - 1, matrix2);

    return (new DoubleSystemBuilder(matrix));
  }

  /**
   * システムオペレータを返します。
   * 
   * @return システムオペレータ
   */
  public DoubleSystemOperator getSystemOperator() {
    if (isSingleSystem()) {
      return this.adjacencyMatrix.getElement(1, 2);
    }
    return this.blockSystem;
  }

  /**
   * 入力器のリストを返します。
   * 
   * @return 入力器のリスト
   */
  public List<Importer> getImporters() {
    return this.adjacencyMatrix.getImporters();
  }

  /**
   * 出力器のリストを返します。
   * 
   * @return 出力器のリスト
   */
  public List<Exporter> getExporters() {
    return this.adjacencyMatrix.getExporters();
  }

  /**
   * 単一のシステム(結合システムでない)であるか判定します。
   * 
   * @return 単一システムならばtrue、そうでなければfalse
   */
  public boolean isSingleSystem() {
    return this.blockSystem.isSingleSystem();
  }

  /**
   * システムの隣接行列を返します。
   * 
   * @return システムの隣接行列
   */
  public DoubleAdjacencyMatrix getAdjacencyMatrix() {
    return this.adjacencyMatrix;
  }

  /**
   * 自動的に入出力数を設定するか判定します。
   * 
   * @return 自動的に入出力数を設定するならばtrue、そうでなければfalse
   */
  public boolean isAutoSize() {
    return this.blockSystem.isAutoSize();
  }

  /**
   * 自動的に入出力数を設定するか設定します。
   * 
   * @param autoSize 自動的に入出力数を設定するならばtrue、そうでなければfalse
   */
  public void setAutoSize(boolean autoSize) {
    this.blockSystem.setAutoSize(autoSize);
  }

  /**
   * 自動的に入出力数を設定するシステムの入出力数をリセットします。
   */
  public void resetAutoSize() {
    if (isAutoSize() == false) {
      return;
    }

    this.blockSystem.resetAutoSize();
  }
}