様々なデータをOMMX Artifact形式で共有する#
数理最適化のワークフローでは、多様なデータの生成と管理が不可欠です。これらのデータを適切に管理することで、計算結果の再現性が確保され、チーム内での効率的な共有が可能になります。
OMMXは、これらの多様なデータを効率的かつシンプルに管理する仕組みを提供します。具体的には、OMMX Artifactというデータ形式を定義し、最適化計算に関連する多様なデータの保存・管理・共有をOMMX SDKによって可能にします。
事前準備:共有するデータ#
まず共有するべきデータを用意しましょう。ナップザック問題を表す ommx.v1.Instance を作成し、SCIPによる最適化計算を行います。さらに最適化計算に対する分析結果も共有します。今回はこれらの処理の詳細は本題から離れるので省略します。
変数名 |
説明 |
|---|---|
|
0-1ナップサック問題に対応する |
|
0-1ナップサック問題をSCIPで解いた計算結果が格納されている |
|
0-1ナップサック問題の入力データ |
|
0-1ナップサック問題の最適解表す |
ファイルとしてOMMX Artifactを作成する#
OMMX Artifactはファイルで管理する方法と、コンテナのように名前で管理する方法がありますが、ここではまずファイルを使った方法を紹介します。OMMX SDKを使って、上記のデータをOMMX Artifact形式の新しいファイル my_instance.ommx に保存しましょう。まず ArtifactDraft を用意します。
import os
from ommx.artifact import ArtifactDraft
# OMMX Artifactファイルの名前を指定する
filename = "my_instance.ommx"
# 既にファイルが存在している場合は削除する
if os.path.exists(filename):
os.remove(filename)
# 1. draftを作成 (v3 では全アーティファクトが SQLite Local Registry に
# 入るので、draft は image_name を要求します)。名前を考えるのが
# 面倒なら `new_anonymous()` で自動生成できます。
draft = ArtifactDraft.new_anonymous()
ArtifactDraft は主に 3 つのコンストラクタを持ちます。v3 では commit したアーティファクトは必ず SQLite Local Registry のエントリとして残り、.ommx ファイルとして共有したい場合は commit 後に Artifact.save(path) を呼びます。
コンストラクタ |
説明 |
|---|---|
呼び出し側で image_name を指定する |
|
|
|
GitHub Container Registry に合わせてコンテナの名前を決める |
new_anonymous のホスト名は <レジストリ ID8>.ommx.local 形式で、.local (mDNS) link-local TLD を使っているので誤って push しても実際のリモートレジストリには到達しません。先頭のレジストリ ID は各 LocalRegistry の初回作成時に一度だけ生成されてメタデータに保存される UUID で、同じレジストリで作られた anonymous artifact は同じ prefix を共有します。アーカイブを共有した際にも「どのレジストリで作られたか」が判別できます。蓄積した anonymous エントリは ommx prune-anonymous で確認し、ommx prune-anonymous --delete で一括削除できます (異なるレジストリ ID の prefix も含めて削除されます)。
タイムスタンプの注意: 自動生成タグはdraft のローカルタイム (TZ マーカー無し) です。異なるタイムゾーンの相手にアーカイブを共有すると、受信者は同じ数字を自分のローカルタイムとして読むため、絶対時刻としての意味はマシン間で失われます。タイムゾーンに関係なく安定したタグが必要なら ArtifactDraft.new(...) で明示的に名前を指定してください。
どの方法で初期化しても同じように ommx.v1.Instance や他のデータを保存することが出来ます。上で用意したデータを追加してみましょう。
# ommx.v1.Instance オブジェクトを追加する
desc_instance = draft.add_instance(instance)
# ommx.v1.Solution オブジェクトを追加する
desc_solution = draft.add_solution(solution)
# pandas.DataFrame オブジェクトを追加する
desc_df = draft.add_dataframe(df, title="ナップサック問題の最適解")
# JSONに変換可能なオブジェクトを追加する
desc_json = draft.add_json(data, title="ナップサック問題のデータ")
OMMX Artifactではレイヤーという単位でデータを管理しますが、各レイヤーは中身がどんな種類のデータなのかを表現するためにMedia Typeを保持しており、add_instance などの関数はこれらを適切に設定した上でレイヤーを追加します。この関数は生成したレイヤーの情報を保持した Description オブジェクトを返します。
desc_json.to_dict()
add_json に追加した title="..." という部分はレイヤーのアノテーション(注釈)として保存されます。OMMX Artifactというのは人間のためのデータ形式なので、これは基本的には人間が読むための情報です。ArtifactDraft.add_* 関数はいずれも任意のキーワード引数を受け取り、自動的に org.ommx.user. 以下の名前空間に変換します。
さて最後に commit で SQLite Local Registry に commit し、save で .ommx ファイルにエクスポートします。
# 3. SQLite Local Registry に commit
artifact = draft.commit()
# 4. 共有用に .ommx アーカイブにエクスポート
artifact.save(filename)
ファイルが出来上がったか確認してみましょう:
import os
print(os.path.exists(filename))
あとはこの my_instance.ommx を通常のファイル共有の方法で共有すれば、他の人とデータを共有することができます。
OMMX Artifact形式のファイルを読み取る#
次に保存したOMMX Artifactを読み込みましょう。v3 ではアーカイブ読み込みが 2 つのメソッドに分かれています: Artifact.import_archive は アーカイブを SQLite Local Registry に import してフルハンドルを返します (全レイヤを読める)。Artifact.inspect_archive は registry に書き込まずに manifest / layer descriptors だけを読みます。
from ommx.artifact import Artifact
# ローカルにあるOMMX Artifactファイルを読み込む
artifact = Artifact.import_archive(filename)
OMMX Artifactはレイヤーという単位でデータを管理しますが、このレイヤーのデータはマニフェスト(目録)として内包されており、アーカイブファイル全体を読み込まずに確認することが可能です。Artifact.layers によって含まれるレイヤーの Descriptor を取得できます。これにはそのレイヤーのMediaTypeとアノテーションが含まれています。
import pandas as pd
# 見やすいように pandas.DataFrame に変換する
pd.DataFrame({
"Media Type": desc.media_type,
"Size (Bytes)": desc.size
} | desc.annotations
for desc in artifact.layers
)
例えばレイヤー3に入っているJSONを取得するには Artifact.get_json を使います。この関数はMedia Typeが application/json である事を確認し、バイト列をJSON文字列としてPythonオブジェクトに復元します。
artifact.get_json(artifact.layers[3])