matlab の話
以前、下記の記事にてpython と matlab の接続について書かせていただきました。
この記事はその続きです、接続の方法や手順などはこちらの記事か公式のリファレンス(Python からの MATLAB の呼び出し)をお読みください。
今回は実際にpython用のmatlab ラッパー📯を作る際に引っかかったことについてまとめました
1. matlab eng の起動と時間とquit
matlab を python 上で扱う際には matlab.eng.start()し、matlab 内での処理を eng に渡すことで行う必要があります。
注意すべきなのは、その起動時間。
1 起動当たり数秒時間がかかります
- 確認のためのコード (※matlabroot にはお使いの matlab のエンジンの場所を入力してください、詳しくは参照記事をご覧ください)
import time
import sys
sys.path.append('matlabroot')
import matlab
startTime = time.time()
eng=matlab.engine.start_matlab()
endTime = time.time()
print(f"time:{endTime-startTime}[sec]")
- 結果
time:1.810612440109253[sec]
eng は一度起動すれば他の関数を呼び出す際にも使いまわすことができます。
これによって起動時間を抑えることができます。
公式ではエンジンの停止について以下のように記載しています。
エンジンの実行中に Python を終了すると、Python はエンジンとその MATLAB プロセスを自動的に停止します。
MathWorks, Python 用 MATLAB エンジンの起動と停止
なので python が正常終了するのであれば明示的なエンジンの停止は必ずしも必要というわけではありません。
しかし、コード上明示的に停止したり、コードの実行中に matlab engine を停止したい場合は以下のように行います。
eng.quit()
※使い始めた当初は勘違いして毎度 起動--> 関数呼び出し --> 直後に quit を繰り返していました
2. ワークスペース変数と実行階層
matlab 関数の python ラッパーを実装するにあたり引っかかりやすいのはその処理の実行階層だと思います。
たとえば以下のような階層構成で main.py から matlab の wrapper 関数を扱うと仮定しましょう
.
├── myFuncs/
│ ├── myMatFunction.m
│ └── myPyWrapper.py
└── main.py
fuction output = myFunction(a, b)
output = a + b
end
import sys
sys.path.append('matlabroot')
import matlab
def myPyWrapper(
eng : matlab.engine.MatlabEngine,
a : int,
b: int
) -> int :
return eng.myFunction(a, b)
- main.py
import sys
sys.path.append('matlabroot')
import matlab
from myFuncs.myPyWrapper import myPyWrapper
def main():
print(myPyWrapper(a = 1,b = 2))
if __name__ == '__main__':
main()
main.py から myFunc 内の matlab wrapper 関数を扱おうとしています。
このままだと実は呼び出しても使うことができません。
実は実行階層が異なる場合、階層内のワークスペースに変数や関数が定義されていないため関数を呼び出しても代入する先がわからなくなってしまうのです。
公式のベース ワークスペースと関数ワークスペースには以下のようにあります。
入れ子関数またはそれを含む関数内の変数は、すべて明示的に定義しなければなりません。したがって、変数に値を代入する関数やスクリプトを呼び出すには、関数ワークスペースにその変数が既に存在していなければなりません。
したがって、実行時には.mat とワークスペースが共有できる階層まで降りる必要があります。 このままだと面倒ですね。
そんな時に便利なのが os.chdir です。下記のように使います。
import os
os.chdir(os.path.dirname(os.path.abspath(__file__)))
呼び出し wrapper 上部でこれをするだけで実行階層を呼び出した関数と同じ階層で実行できるので、matlab の関数ワークスペースにアクセスして変数を認識できるようになります。
まとめ
以上、python wrapper を作るときの引っかかりポイントを幾つかあげました。
本当はこのような wrapper を量産するのではなく、そもそも書き直す必要があると思うのですが、ぱっとテストがしたい場合や動作確認をしたいときには有用なのかなと思っています。
よかったら参考にしてみてください
今後もしかすると配列関係のコツもこっそり追記するかもしれません。文量多かったので後出しになると思います、ごめんさい。