ChatGPTでSimulinkモデルを作ってみた (2)逆ポーランド記法を使って変換精度を高めてみる

# はじめに
[前回記事](https://qiita.com/covao/items/fa728b6a813c79687a6c)では、ChatGPTを使って、Simulinkのブロック図をMATLABコマンドを介して作ることができるのか？という素朴な疑問から、簡単な計算式のモデルを生成してみました。
その後、要件の計算式を変えて試したところ、最新の大規模言語モデルのGPT-4でも、Simulinkモデルの生成にはいろいろ課題があることが分かりました。そこで、まずは四則演算に限定して、変換精度を高めるプロンプトを検討してみます。

# うまく生成できない例

~~~
MATLABコマンドで次の計算のSimulinkモデルを生成するスクリプトを作ってください
入力:x1,x2
出力:y=(x1+x2)*(x1-x2-1)
~~~
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/329764/0fd3aa2b-0073-379e-23be-7f802ef4ac6c.png)

## 問題点

以下のように生成を間違えるケースがあるようです
1. ライブラリブロックのパスを間違える:confounded:
2. Sumブロックの符号リストの設定 '++'や'+-'を間違える:confounded:
3.  Sumブロックで、存在しない入力ポート番号で結線しようとする:confounded:
4. ブロックの結線を間違える:confounded:
などなど...:thinking:

# プロンプトの要件を明確化する
1から3の対策として、プロンプトにブロックの設定の前提条件などを付け加えてみます。
4の信号線の接続は、たまたまうまく結線する場合もあるようです:scream:。しかしながら計算式が複雑化すると大概はうまくいきません。ChatGPTが結線をどのように解釈しているか謎です...（膨大な学習データからパターンを認識しているようですが...）
そこで、まずは四則演算に限定し、逆ポーランド記法に置き換えるプロンプトを追加し、変換精度を高めてみました。
- 逆ポーランド記法※に従って信号線を接続するルールをプロンプトで指示しています。
 ※演算式をパラメータ - 演算子の順で並べ変えます。(例 1 2 +) スタックに引数を積んで関数を実行するのに適した記法です。
- ChatGPTが生成したブロックのレイアウト配置はうまくいかない場合も多いため、Simulinkの自動配置機能を使用するプロンプトを追加します。
- 日本語プロンプトも可能ですが、ここでは解釈ミスを最小限にするため、英語で簡潔に記述するようにしています。

# 作成したプロンプト

次の式の Simulink モデルを生成する MATLAB スクリプトを作成します。
入力: x1、x2
出力: y
y = (x1 * 2 - 123 ) * (x2 / 4 + 456) - x2

試行錯誤の末、たどり着いた呪文...
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/329764/09d38ea5-6e23-6720-8835-6d89ddf9090c.png)


~~~ GPT-4
Set ChatGPT to Temperature=0
Create a MATLAB script that generates a Simulink model for the following formula
Input: x1, x2
output: y
y = (x1*2-123)*(x2/4+456)-x2

The script does the following
Close all models with the bdclose() command.
Convert to Simulink block constants, variables, and operators in Reverse Polish Notation, 
and place them in the model with the add_block() command.
Connect with a signal line using add_line() according to Reverse Polish Notation.
The order of Reverse Polish Notation is as follows (not implemented, use the method)
For constants and variables, push the block name to the stack
For operators, pop the block name to the stack and connect the signal line
Only one signal line can be connected to the input port of the block

After generation, open the model and automatically arrange it with Simulink.BlockDiagram.arrangeSystem().
Do not save the generated model

Please generate under the following conditions
Libraries used; simulink/Commonly Used Blocks
Blocks used: In1, Out1, Constant, Sum, Product

Block setting
Sum block: IconShape=rectangular,
Inputs is '++' for addition and '+-' for subtraction
Inputs' in the Product block is
'**' for multiplication *, '*/' for division /
Therefore, the input port number of add_line() is up to 2 '[Block Name]/1' or '[Blockname]/2'
Block name: Unique name with library block name e.g. Constant1,Constant2

Model settings
Model name: 'myModel'
Solver type: 'Fixed-Step', fixed step size=1
Write the original formula and the reverse Polish formula in the code comments
Write in short code with minimum comments

~~~


## ChatGPT-4で生成したモデル作成スクリプト
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/329764/81d20855-9ca6-b31e-cc44-67d462281dbd.png)

~~~ matlab
% Original formula: y = (x1*2-123)*(x2/4+456)-x2
% Reverse Polish Notation: x1 2 * 123 - x2 4 / 456 + * x2 - y

% Close all models
bdclose('all');

% Create a new model
modelName = 'myModel';
new_system(modelName);
open_system(modelName);

% Set model settings
set_param(modelName, 'SolverType', 'Fixed-Step');
set_param(modelName, 'FixedStep', '1');

% Add blocks to the model
add_block('simulink/Commonly Used Blocks/In1', [modelName '/x1']);
add_block('simulink/Commonly Used Blocks/In1', [modelName '/x2']);
add_block('simulink/Commonly Used Blocks/Constant', [modelName '/Constant1']);
set_param([modelName '/Constant1'], 'Value', '2');
add_block('simulink/Commonly Used Blocks/Constant', [modelName '/Constant2']);
set_param([modelName '/Constant2'], 'Value', '123');
add_block('simulink/Commonly Used Blocks/Constant', [modelName '/Constant3']);
set_param([modelName '/Constant3'], 'Value', '4');
add_block('simulink/Commonly Used Blocks/Constant', [modelName '/Constant4']);
set_param([modelName '/Constant4'], 'Value', '456');
add_block('simulink/Commonly Used Blocks/Product', [modelName '/Product1']);
set_param([modelName '/Product1'], 'Inputs', '**');
add_block('simulink/Commonly Used Blocks/Product', [modelName '/Product2']);
set_param([modelName '/Product2'], 'Inputs', '*/');
add_block('simulink/Commonly Used Blocks/Sum', [modelName '/Sum1']);
set_param([modelName '/Sum1'], 'IconShape', 'rectangular');
set_param([modelName '/Sum1'], 'Inputs', '+-');
add_block('simulink/Commonly Used Blocks/Sum', [modelName '/Sum2']);
set_param([modelName '/Sum2'], 'IconShape', 'rectangular');
set_param([modelName '/Sum2'], 'Inputs', '++');
add_block('simulink/Commonly Used Blocks/Product', [modelName '/Product3']);
set_param([modelName '/Product3'], 'Inputs', '**');
add_block('simulink/Commonly Used Blocks/Sum', [modelName '/Sum3']);
set_param([modelName '/Sum3'], 'IconShape', 'rectangular');
set_param([modelName '/Sum3'], 'Inputs', '+-');
add_block('simulink/Commonly Used Blocks/Out1', [modelName '/y']);

% Connect blocks according to Reverse Polish Notation
add_line(modelName, 'x1/1', 'Product1/1');
add_line(modelName, 'Constant1/1', 'Product1/2');
add_line(modelName, 'Product1/1', 'Sum1/1');
add_line(modelName, 'Constant2/1', 'Sum1/2');
add_line(modelName, 'x2/1', 'Product2/1');
add_line(modelName, 'Constant3/1', 'Product2/2');
add_line(modelName, 'Product2/1', 'Sum2/1');
add_line(modelName, 'Constant4/1', 'Sum2/2');
add_line(modelName, 'Sum1/1', 'Product3/1');
add_line(modelName, 'Sum2/1', 'Product3/2');
add_line(modelName, 'Product3/1', 'Sum3/1');
add_line(modelName, 'x2/1', 'Sum3/2');
add_line(modelName, 'Sum3/1', 'y/1');

% Arrange blocks automatically
Simulink.BlockDiagram.arrangeSystem(modelName);
~~~

## スクリプト実行で自動生成したモデル
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/329764/42d56757-5dc6-b3a0-c072-d54e18b4e925.png)

## 生成したモデルのテストスクリプト生成
生成したモデルとMATLABで計算した結果を比較するテストスクリプトをChat GPT-4で生成してみます。
入力信号に乱数を使います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/329764/9b924db2-b33d-0cd9-2fbc-26749bc0563e.png)

~~~ GPT-4
Create the following test script in MATLAB
Create one m-file that is not a function
1. Write the original formula as a MATLAB anonymous function
2. Run the generated Simulink model
Compare the calculations of 1. and 2. and test with assert()
Tolerance < 1e-6

Please test under the following conditions
Simulation time: 10
Sample time: 0.01
Set time column in TimeSignal
Generate InputSignal with random number
Set output signal to OutputSignal
Simulation results are set in sim_out.yout.signals.values

Set the following properties in the generated Simulink model
Load External Input: on
ExternalInput: '[TimeSignal,InputSignal]'
SaveFormat: StructureWithTime
FixedStep: Sets sample time

~~~

~~~ matlab
% Original formula as an anonymous function
original_formula = @(x1, x2) (x1 .* 2 - 123) .* (x2 ./ 4 + 456) - x2;

% Set simulation time, sample time, and generate input signals
sim_time = 10;
sample_time = 0.01;
time_signal = (0:sample_time:sim_time)';
num_samples = length(time_signal);
x1_signal = rand(num_samples, 1) * 100;
x2_signal = rand(num_samples, 1) * 100;
input_signal = [x1_signal, x2_signal];

% Set model properties
modelName = 'myModel';
set_param(modelName, 'LoadExternalInput', 'on');
set_param(modelName, 'ExternalInput', '[time_signal, input_signal]');
set_param(modelName, 'SaveFormat', 'StructureWithTime');
set_param(modelName, 'FixedStep', num2str(sample_time));

% Run Simulink model
sim_out = sim(modelName, sim_time);
sim_output_signal = sim_out.yout.signals.values;

% Calculate output using the original formula
output_signal = original_formula(x1_signal, x2_signal);

% Compare and test with assert
tolerance = 1e-6;
assert(all(abs(sim_output_signal - output_signal) < tolerance), ...
    'Simulink model output does not match original formula output');

~~~

上記テストの結果、MATLABの関数とChatGPTが生成したモデルの計算が一致しました。:relaxed:

## まとめ
- ChatGPT-4でMATLABコマンドを介してSimulinkモデルの生成を指示すると、ブロックの設定ミス、レイアウト配置、信号線接続など課題があります。
- 四則演算では、逆ポーランド記法に置き換えるという手順を教えることで、Simulink信号線の接続精度は高くなりました。
- 生成するスクリプトはまだ完璧ではなく、ときどき、実行エラーとなる生成ミスや期待と異なるコードパターンが出現する場合があります。
- 生成ミスは、大規模言語モデルの学習に使用したデータセットの影響を受けている可能性もあります。（MATLAB/Simulinkと関係するデータセットの規模やツールの仕様変更による影響など）
- 生成ミスがあった場合、具体的な要件をプロンプトに追加することで変換精度を高めていくことができます。

# さいごに
GPT-4でも、自然言語からこれだけのコードが生成できるのはすごいですが、次のGPT-5にも期待したいです。