Re:JMPJSLからのPython並列処理コンテンツエディタがdefs.pyのコンテンツからキャリッジリターンを削除し、次のように表示されていることに注意してください。
def f(x):
return x*x
JMPJSLからのPython並列処理休暇中、私はPythonをいじくり回す時間がありました。 Python / JSLインターフェースからの並列処理で遭遇した問題は、Pythonは通常、PythonではなくJMPの複数のコピーを起動することです。 この理由は、PythonをJMPに埋め込んでいるため、実行可能ファイルはPythonではなくJMPです。 マルチプロセッシングパッケージを使用すると、並列コードの実行時に実行可能ファイルを起動するように設定できることがわかりました。 これはパズルに欠けている部分です。
以下のスクリプトは、Python3.9を搭載したMacとWindowsの両方のJMP15.2.1でテストしました。私のWindowsマシンでは、Python Init()へのPath()引数を使用する必要がありました。 Pythonには複数のバージョンがあり、正しいバージョンが選択されていることを確認したいと思います。 私のMacでは、引数のないPython Init()が私の構成で正常に機能します。 Windowsで遭遇した問題が1つあります。それは、pythonw.exeへのパスをハードコーディングする必要があったことです。 Pythonの場所に一致するように変更する必要があります。 FixMeを検索します。
Python Init()パラメータとWindowsでのpythonw.exeへのパスを除いて、これはMacまたはWindowsのいずれかでJMP15.2.1以降で問題なく実行されるはずです。 Pythonをサポートしている古いバージョンのJMPで実行できますが、テストされていません。
このスクリプトと同じディレクトリにdefs.pyファイルを作成する必要があります。 マルチプロセッシングパッケージでは、並行して実行するコードが、スクリプトに対してローカルではなく、インポート可能なコードからのものである必要があります。 したがって、このスクリプトでのほとんどの作業は、sys.pathが適切に設定されていることを確認して、defs.pyファイルをスクリプトで簡単にインポートできるようにすることでした。
defs.pyファイルの内容全体がファイルのコメントに含まれているため、新しいファイルに簡単に貼り付けることができます。 以下に複製された内容は次のとおりです。
def f(x):
return x*x
スクリプトは数値のリスト[1,2,3]を取り、並行してf(x)を呼び出して値を二乗し、結果[1,4,9]を返します。
pyParallel.jsl
Names Default To Here( 1 );
/* Description: Python mulltiprocessing from JMP JSL
* File: pyParallel.jsl, and defs.py
* Author: Paul Nelson
* JMP Statistical Discovery, LLC
*
* Python multiprocessing from an embedded context adds additional challenges.
* Just attempting to run Python parallel processing modules, typically launches
* multiple copies of JMP, not executing the Python code in parallel.
*
* The multiprocessing module allows setting the executable to launch, since we
* are loading Python into JMP, it's an embedded case thus sys.executable => JMP, not Python.
*
*
* Note: Functions to be run in parallel must come from importable code, not local
* code definitions.
* A multiprocessing 'Feature', not bug, See: https://bugs.python.org/issue25053
*
* Known Bugs:
* On Windows within JMP, sys.exec_prefix is empty, so path to pythonw.exe has to be set explicitly
*
* Create a defs.py file with the contents below. Place in the same directory as this JSL file.
def f(x):
return x*x
*/
// On my windows machine since I have multiple versions of Python, I have to specify the
// full path to the python39.dll, on my Mac it just works.
If( Host Is("Windows"),
Python Init(Path("C:\\Users\\panels\\AppData\\Local\\Programs\\Python\\Python39\\python39.dll")),
Python Init()
);
// send the current working directory where we loaded this script, down to Python so we can find defs.py
script_path = Get Default Directory();
Python Send(script_path);
Python Submit("\[
import sys
import os
import multiprocessing
from multiprocessing import Pool
import platform
# The loaded Python does not have the current working directory initialized, so change it
# to the directory of the script, as sent in from JSL
print('script_path: ' + script_path)
hostPlatform = platform.system()
print(hostPlatform)
try:
if 'Windows' == hostPlatform:
# strip leading / off from path if it exists
if '/' == script_path[0]:
script_path = script_path[1:]
norm_path = os.path.normpath(script_path)
print(norm_path)
os.chdir(norm_path)
else:
os.chdir(script_path)
except:
print("Unable to change current working directory", sys.exc_info())
finally:
print(os.getcwd())
# if '.' is not at the beginning of sys.path, prepend so we can look for modules within CWD
if sys.path[0] != '.':
sys.path.insert(0, '.')
print( sys.path )
# here argv is [''] empty string, doesn't seem to bother us in this use case.
#print( 'argv: ', sys.argv )
# prints the path to JMP, not Python
print('sys.executable: ' + sys.executable)
# build up path prefix to python3 exectable
if 'Darwin' == hostPlatform:
pyPath = os.path.join(sys.exec_prefix, 'bin', 'python3')
elif 'Windows' == hostPlatform:
# Bug - on windows sys.exec_prefix is empty in Python within JMP!
pyPath = None
if sys.exec_prefix:
pyPath = os.path.join(sys.exec_prefix, 'pythonw.exe')
else:
# FixMe: hardcode for now
pyPath = 'C:\\Users\\panels\\AppData\\Local\\Programs\\Python\\Python39\\pythonw.exe'
print ('Python Executable: ' + pyPath)
# Finally down to running something in parallel.
#
# Import the function code that will be executed in parallel.
# JMP is not reporting ImportError or ModuleNotFound errors, the scripting just returns -1
# on an import failure, so wrap it within our own try: except: block
try:
import defs as d
# print( d.f(2) )
# Tell multiprocessing to use the Python executable, not JMP to spawn the workers.
multiprocessing.set_executable(pyPath)
# Run the tasks in parallel - (separate Python instances)
with Pool(5) as p:
print(p.map(d.f, [1,2,3])) # Results in [1,4,9] the square of the numbers
except ModuleNotFoundError as mnf:
print(mnf)
print('Check that the moudule is located on your sys.path:')
print( sys.path )
except ImportError as error:
print(error)
except Exception as ex:
print(ex)
]\");
サードパーティの統合分析アプリケーション開発ユーティリティRe: JMP JSLからのPython並列処理こんにちは@Paul_Nelson 、埋め込み Python を使用して、JMP 18 で同じ結果 (バックグラウンドでの Python スクリプトまたは関数の実行) をどのように達成できますか?
ありがとう!
マテオ
Re: JMP JSLからのPython並列処理上記と同じ defs.py を使用する Parallel.py は、JMP 18 の統合によりはるかに簡単に実行できます。 JMP は、Python 実行可能ファイルの場所を認識し、 jmpインポート パッケージの「定数」として提供されます。また、JMP は、ローカル ファイルをインポートできるように、スクリプトのディレクトリをパッケージ検索パスに追加することを認識します。
# test_multiprocessing.py
# Author: Paul R. Nelson
# JMP Statistical Discovery LLC
#
# Description: Demonstrate using Python's parallel processing capability.
# The file defs.py contains the code that will execute in parallel.
#
# Contents of defs.py:
#def f(x):
# return x*x
#
import jmp
import multiprocessing
from multiprocessing import Pool
# jmp.PYTHON_EXE is the file path to JMP's Python executable.
print(jmp.PYTHON_EXE)
try:
# defs.py is the code to be run in parallel, located in same directory as this file.
import defs as d
# tell multiprocessing to use Python executable, not spawn JMP for the workers
multiprocessing.set_executable(jmp.PYTHON_EXE)
# square each number in parallel - separate Python instances
with Pool(5) as p:
print(p.map(d.f, [1,2,3])) # result is the square of the numbers
except ModuleNotFoundError as mnf:
print(mnf)
print('Check that the module is located on your sys.path:')
print( sys.path )
except ImportError as error:
print(error)
except Exception as ex:
print(ex)Re: JMP JSLからのPython並列処理早速のご返事ありがとうございます。コードはそのままでも問題なく機能しますが、対象のケースに拡張するのに苦労しています。 JMP から cmdstanpy サンプル メソッド (ベイジアン MCMC) を実行しようとしています。基本的な形式で使用するだけで問題なく動作しますが、MCMC チェーンごとに cmd.exe ターミナルを開き、アルゴリズムが完了するまで (かなり長い時間がかかる場合があります) 開いたままにし、対話もログの表示も許可しません。終わり。
これは基本的なコードです:
import cmdstanpy as cs
model = cs.CmdStanModel(stan_file=stan_file)
fit = model.sample(data=datafile, show_progress=True, show_console=True)
fit_table = fit.draws_pd(vars=genVariables+modelParams)
バックグラウンドで実行させて、アルゴリズムの実行中にログ情報を表示したいと考えています。あなたの提案に基づいてこの適応を試しました:
def sample_chain(chain_id):
return model.sample(data=datafile, chains=1, parallel_chains=1, show_progress=True, show_console=True)
try:
multiprocessing.set_executable(jmp.PYTHON_EXE)
with multiprocessing.Pool(4) as pool:
results = pool.map(sample_chain, range(4))4 つの cmd.exe ウィンドウを開きますが、決して終了せず、さらに 4 つのウィンドウが開いている間、メイン プログラムに制御を戻しません。さらに何か提案はありますか?
ありがとう
マテオ
Re: JMP JSLからのPython並列処理このパッケージには詳しくありませんが、パラメーター show_progress=True または show_console=True が問題を引き起こしているのではないかと思います。並行して実行されるコードには、失敗が発生した場合に終了する例外ハンドラーがあることも確認します。戻らないのは窓が開いたままだからかもしれません。並列スクリプト (JMP 内のスクリプトではなく) の最後に exit を追加してみてください。
JMP は Python 共有ライブラリをプロセス スペースにロードするため、Python の実行中は、JMP ではなく Python がイベント ループを制御します。ループ内などで jmp.run_jsl('Wait(0);') を使用すると、定期的に JMP に制御を戻すことができます。これにより、JMP イベント ループを実行するために、一時的に JMP に制御が戻されます。JMP 実行コードでもこれを試すことができるかもしれませんが、ループではなく並列起動であり、起動された並列コードには import jmp 機能へのアクセス権がないため、どこで試すかは正確にはわかりません。コード ビットを試して、何か目立つ点がないか確認します。
Re: JMP JSL からの Python 並列処理小さくシンプルに始めるのが、何かをデバッグする最良の方法だと私は思います。マルチスレッドにジャンプして「実行」しようとする前に、JMP 内から cmdstanpy を実行して「ウォーク」してください。
次のことを試しました
import os
from cmdstanpy import CmdStanModel
stan_file = os.path.join('/Users/_me_/Desktop', 'bernoulli.stan')
print(stan_file)
model = CmdStanModel(stan_file=stan_file)
print(model)
print(model.exe_info())
結果
/*:
//:*/
import os
from cmdstanpy import CmdStanModel
stan_file = os.path.join('/Users/_me_/Desktop', 'bernoulli.stan')
print(stan_file)
model = CmdStanModel(stan_file=stan_file)
print(model)
print(model.exe_info())
/*:
/Users/_me_/Desktop/bernoulli.stan
19:01:04 - cmdstanpy - INFO - No CmdStan installation found.
19:01:04 - cmdstanpy - INFO - Cannot determine whether version is before 2.27.
19:01:04 - cmdstanpy - INFO - No CmdStan installation found.
19:01:04 - cmdstanpy - INFO - Cannot determine whether version is before 2.27.
Traceback (most recent call last):
File ""、8行目、ファイル「/Users/_me_/Library/Application Support/JMP/Python/3.11/lib/python/site-packages/cmdstanpy/model.py」、212 行目、__init__ model_info = self.src_info() ^^^^^ ^^^^^^^^^^ ファイル "/Users/_me_/Library/Application Support/JMP/Python/3.11/lib/python/site-packages/cmdstanpy/model.py"、src_info リターン コンパイルの 318 行目.src_info(str(self.stan_file), self._compiler_options) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ファイル "/Users/_me_/Library/Application Support/JMP/Python/3.11/lib/python /site-packages/cmdstanpy/compilation.py"、src_info の 354 行目 [os.path.join(cmdstan_path(), 'bin', 'stanc' + EXTENSION)] ^^^^^^^^^^^ ^^^ ファイル「/Users/_me_/Library/Application Support/JMP/Python/3.11/lib/python/site-packages/cmdstanpy/utils/cmdstan.py」、行 170、cmdstan_path raise ValueError( ValueError: No CmdStanインストールが見つかりました。コマンド「install_cmdstan」を実行するか、conda 環境を (再) アクティブ化してください。
この例でも JMP に何を要求しているのかを理解してください。 CmdStan はコマンド ライン プログラムで、cmdstanpy パッケージは Python ラッパーです。つまり、Python サブプロセスのようなことを行って、OS を呼び出し、コマンド ライン プログラムを起動し、シェル出力を監視しているようです。 JMP と、JMP jpip ラッパーによってインストールされたパッケージは、JMP のプライベート サイトパッケージ ディレクトリに置かれます。これは Conda ディストリビューションではなく、install_cmdstan パッケージが JMP 仮想環境で実行するのに十分な環境を構成する場合と構成しない場合があるため、Conda init は実行しません。通常、Conda または venv のアクティブ化では、パッケージ パスとディレクトリ パスの環境変数が設定されます。おそらく、install_cmdstan も同じことを行います。ただし、これらはすべて、cmdstan をインポートする Python 実行可能ファイルの実行に、構成したばかりの同じシェル環境が使用されることを前提としています。これらの仮定は当てはまらないため、システム上の他の Python インスタンスから JMP パッケージ環境を分離できるように、JMP 自体がこれらのパスを設定する必要があります。そのため、JMP は cmdstanpy パッケージをインポートして実行できますが、パッケージ自体は cmdstan コマンド ライン実行可能ファイルを検索し、それを見つけません。次に、マルチプロセッシングを追加しようとすると、事実上、JMP の Python に、Python スクリプトを実行する jmp.PYTHON_EXE コピーを起動するよう要求することになります。次に、サブプロセスを起動して、タスクを実行するコマンド ライン プログラムを実行しようとします...
私のオリジナルの例では、複数の Python インスタンスを起動することで並列実行される純粋な Python 関数を作成するユースケースを示しました。実行しようとしているものでは、コマンド ライン実行可能ファイルに Python ラッパーの複雑さが追加され、ネストされたシェル環境で問題が発生します。
あなたが試みているユースケースは現在の形式ではサポートできないと思います。 JMP で試す前に、JMP の外部で同じマルチプロセッシングを試してください。そこで機能させることができれば、JMP 内で成功する可能性があります。 ( jmp.PYTHON_EXE を python.exe へのフルパスに置き換えるだけで、jmp はインポートしません)。
私は基本的な純粋な Python の並列スクリプトが動作することを知っています。共有ライブラリのインポートに依存しないインポートされた Python パッケージでも、並列スクリプトで動作するはずだと思います... (ただし、テストはしていません)。 JMP 環境の site.USER_BASE を並列 jmp.PYTHON_EXE に渡し、その環境を JMP 自体が設定しているのと同じディレクトリ環境に一致するように適切に設定する必要がある場合があります。
探索中に発見したことがあれば、コミュニティに投稿してください。
JMP は、Python が JMP のメイン イベント ループで実行されているという事実によって制限されています。JMP とマルチスレッド Python の可能な解決策の 1 つは、独立した Python サブインタープリターを起動することです。Python のグローバル インタープリター ロック (GIL) の削除に関するすべての問題を解決するには、Python 3.13 が必要です。私はそれを調査中ですが、Python 3.13 は JMP 18 には含まれません。JMP 18 は、ライフサイクル全体を通じて Python 3.11.x に残ります。Python 3.13 のタイムラインと安定性に応じて、そのような機能が JMP 19 または 20 に導入される可能性があります。
Re: JMP JSLからのPython並列処理説明ありがとうございます。さらに勉強して、新たな発見があればまた連絡します。
マテオ