見出し画像

③LogisticsEngineering 準備

toddleの万力で頭を開く。さぁ、活動開始。
こういうのは土日しか出来なくてね。


こういう話題はClaude 3.5 sonnetさんが抜群に強い(笑)
かなり無茶ぶりだが、こういう話をし尽くしていて私の好みは熟知されてるから何とかなろだろ?

ダイナミックグラフの試行錯誤をします。
Beforeの製造計画では
T= 1 2 3
  A→B→C
  D→E→F
で製造が予定をしていて、後工程の発送では
T=1でAとDを混載、BとE、CとFの混載で3台を用意して準備万端でした。
ところが実際には
T= 1 2 3
  B→A→C
  D→E→F
で製造が進んでいて、製造完了時刻が変わってしまいます。
実はアルバイトの問題でこうすると5千円コスト削減できたらしいのです。
発送はAとDを混載する予定だったのにAが出来てないので、
しょうがなくDだけ単体発送しておっかけで追加のAを出します。
Bは前倒しになったので発送の影響はなしとします。
その結果、車の予定台数が1台増えました。
仮に製造利益が2万円として、改善で▲5千円、追加の車代が3万円とすると赤字です。

これをダイナミックグラフで表現したいのです。
Mathematicaで

プロンプト

1秒で、なんか不思議なコードを返してきたな… 
どれどれ。ふむ、さっぱりわからん。

(* 財務計算関数 *)
calculateFinancials[state_, time_] := Module[{
    manufacturingProfit = 20000,
    laborSavings = If[state === "After", 5000, 0],
    shippingCost = If[state === "Before", 
      time * 10000,
      If[time == 1, 10000,
        If[time == 2, 20000,
          If[time == 3, 40000, 0]
        ]]
    ]
  },
  {
    manufacturingProfit,
    laborSavings,
    shippingCost,
    manufacturingProfit + laborSavings - shippingCost
  }
]

(* グラフ生成関数 *)
makeStateGraph[state_, time_] := Module[{
    vertices = If[state === "Before",
      {Subscript["A", 1], Subscript["B", 2], Subscript["C", 3],
       Subscript["D", 1], Subscript["E", 2], Subscript["F", 3]},
      {Subscript["B", 1], Subscript["A", 2], Subscript["C", 3],
       Subscript["D", 1], Subscript["E", 2], Subscript["F", 3]}
    ],
    edges = If[state === "Before",
      {Subscript["A", 1] -> Subscript["B", 2],
       Subscript["B", 2] -> Subscript["C", 3],
       Subscript["D", 1] -> Subscript["E", 2],
       Subscript["E", 2] -> Subscript["F", 3]},
      {Subscript["B", 1] -> Subscript["A", 2],
       Subscript["A", 2] -> Subscript["C", 3],
       Subscript["D", 1] -> Subscript["E", 2],
       Subscript["E", 2] -> Subscript["F", 3]}
    ]
  },
  Graph[vertices, edges,
    VertexLabels -> "Name",
    VertexSize -> 0.3,
    VertexStyle -> (# -> If[StringTake[ToString[#], 1] <= ToString[time], Red, Gray] & /@ vertices),
    EdgeStyle -> Directive[Black, Thick],
    VertexLabelStyle -> Directive[Black, Bold, 12],
    GraphLayout -> "LayeredDrawing"
  ]
]

(* メインの表示 *)
DynamicModule[{time = 1},
  Column[{
    Row[{"Time: ", 
      Manipulator[Dynamic[time], {1, 3, 1}],
      Dynamic[time]}],
    Grid[{
      {"Before Plan", "After Reality"},
      {Dynamic[makeStateGraph["Before", time]], 
       Dynamic[makeStateGraph["After", time]]},
      {
        Dynamic[Column[{
          Text[Style["収支状況:", Bold]],
          Text["製造利益: ¥" <> ToString[calculateFinancials["Before", time][[1]], StandardForm]],
          Text["コスト削減: ¥" <> ToString[calculateFinancials["Before", time][[2]], StandardForm]],
          Text["発送コスト: ¥" <> ToString[calculateFinancials["Before", time][[3]], StandardForm]],
          Text[Style[
            "純利益: ¥" <> ToString[calculateFinancials["Before", time][[4]], StandardForm],
            If[calculateFinancials["Before", time][[4]] >= 0, Green, Red]
          ]]
        }]],
        Dynamic[Column[{
          Text[Style["収支状況:", Bold]],
          Text["製造利益: ¥" <> ToString[calculateFinancials["After", time][[1]], StandardForm]],
          Text["コスト削減: ¥" <> ToString[calculateFinancials["After", time][[2]], StandardForm]],
          Text["発送コスト: ¥" <> ToString[calculateFinancials["After", time][[3]], StandardForm]],
          Text[Style[
            "純利益: ¥" <> ToString[calculateFinancials["After", time][[4]], StandardForm],
            If[calculateFinancials["After", time][[4]] >= 0, Green, Red]
          ]]
        }]]
      }
    }, Spacings -> {2, 1}]
  }]
]

実行。なんじゃこれは!スライダーでシミュレーションも出来るのか!
変更結果が分かりにくいのと、ちょっと数値条件は違うけどそんなのは後で直せばいい!
簡単なシミュレーションなら作れそうだな。


現場向けは良いとして、次はGNNによる解析をしたい。

現場説明用はこれでいいとして、 GNNに食わせる帰納的アプローチ用のデータの作り方はどうしましょう

プロンプト

こりゃ分からんけど、なんとかなりそう。丸投げしよう。

GNN(Graph Neural Network)のためのデータ構造を設計しましょう。製造スケジューリング問題をGNNで学習させるためには、適切な形式でデータを準備する必要があります。

このデータ構造の特徴:
1. ノード特徴量
- 計画時刻と実際の時刻
- 処理時間
- リソース要求量
- 優先度
- 発送グループID

2. エッジ特徴量
- 関係タイプ(製造順序/発送グループ)
- 時間間隔制約
- 段取り替えコスト

3. グローバル特徴量
- 各種コスト(製造/労務/発送)
- 制約条件(最大時間/リソース制限/最小間隔)

このデータ構造を使用することで:
1. 製造順序の最適化
2. コスト最小化
3. 発送グループの最適化
などの学習が可能になります。

学習の方向性として:
1. スケジュール変更による影響の予測
2. コスト最小化を目的とした順序最適化
3. 制約を満たす実行可能解の生成
を検討できます。

追加で必要な情報や、特定の最適化目標がありますか?

回答
import torch
from torch_geometric.data import Data
import numpy as np

def create_manufacturing_graph(
    schedule_data,
    time_windows,
    shipping_groups,
    costs,
    constraints
):
    """
    製造スケジュールをGNN用のグラフデータに変換
    
    Parameters:
    schedule_data: Dict - 各ノードの製造順序情報
    time_windows: Dict - 各工程の時間窓制約
    shipping_groups: List - 発送グループの組み合わせ
    costs: Dict - 各種コスト情報
    constraints: Dict - 各種制約条件
    
    Returns:
    Data: PyTorch Geometric のデータオブジェクト
    """
    # ノード特徴量の設計
    def create_node_features(node_id, schedule_info):
        return np.array([
            schedule_info['planned_time'],  # 計画製造時刻
            schedule_info['actual_time'],   # 実際の製造時刻
            schedule_info['processing_time'],# 処理時間
            schedule_info['resource_req'],  # リソース要求量
            schedule_info['priority'],      # 優先度
            schedule_info['shipping_group'] # 発送グループID
        ])

    # エッジ特徴量の設計
    def create_edge_features(source, target, relation_type):
        return np.array([
            relation_type,                  # 関係タイプ(製造順序/発送グループ)
            time_windows[source][target],   # 時間間隔制約
            costs['transition'][f"{source}-{target}"]  # 段取り替えコスト
        ])

    # ノードとエッジのリスト作成
    nodes = []
    edges = []
    node_features = []
    edge_features = []
    
    # 製造順序からノードとエッジを生成
    for node_id, info in schedule_data.items():
        nodes.append(node_id)
        node_features.append(create_node_features(node_id, info))
        
        # 製造順序の依存関係をエッジとして追加
        for next_node in info['next_processes']:
            edges.append([nodes.index(node_id), nodes.index(next_node)])
            edge_features.append(
                create_edge_features(node_id, next_node, relation_type=0)
            )
    
    # 発送グループの関係をエッジとして追加
    for group in shipping_groups:
        for i in range(len(group)):
            for j in range(i + 1, len(group)):
                edges.append([nodes.index(group[i]), nodes.index(group[j])])
                edge_features.append(
                    create_edge_features(group[i], group[j], relation_type=1)
                )

    # PyTorch Geometric のデータオブジェクトを作成
    return Data(
        x=torch.FloatTensor(node_features),
        edge_index=torch.LongTensor(edges).t().contiguous(),
        edge_attr=torch.FloatTensor(edge_features),
        costs=torch.FloatTensor([
            costs['manufacturing'],
            costs['labor'],
            costs['shipping']
        ]),
        constraints=torch.FloatTensor([
            constraints['max_time'],
            constraints['resource_limit'],
            constraints['min_interval']
        ])
    )

# 使用例
example_data = {
    'A': {
        'planned_time': 1,
        'actual_time': 2,
        'processing_time': 1.5,
        'resource_req': 1,
        'priority': 1,
        'shipping_group': 1,
        'next_processes': ['B']
    },
    'B': {
        'planned_time': 2,
        'actual_time': 1,
        'processing_time': 1.2,
        'resource_req': 1,
        'priority': 2,
        'shipping_group': 2,
        'next_processes': ['C']
    },
    # ... 他のノード情報
}

example_time_windows = {
    'A': {'B': 1},
    'B': {'C': 1},
    # ... 他の時間窓制約
}

example_shipping_groups = [
    ['A', 'D'],
    ['B', 'E'],
    ['C', 'F']
]

example_costs = {
    'manufacturing': 20000,
    'labor': 5000,
    'shipping': 10000,
    'transition': {
        'A-B': 1000,
        'B-C': 1000,
        # ... 他の遷移コスト
    }
}

example_constraints = {
    'max_time': 3,
    'resource_limit': 2,
    'min_interval': 0.5
}

# グラフデータの生成
graph_data = create_manufacturing_graph(
    example_data,
    example_time_windows,
    example_shipping_groups,
    example_costs,
    example_constraints
)

次は、トロピカル演算によるモデリングだけど。
sonnetさんが少し待ってくれって。
今はまだ不要なのに長いPython書くから(笑)
トークン、使い切っちゃったよ。

これでも貼っておこう。工事中だよ。

復活するまで昼寝するか。


昼絵終わり。 う、答えが完璧だ。
しかも、何となくハマりはするだろうと予想はしてたけど、ハマり方が芸術的すぎる!

ダイナミックグラフをイベントグラフと考え、それではこの問題をトロピカル代数もしくは Max Plus代数で定式化してください。

プロンプト

はい、マックスプラス代数での数式表現を書きます。

## 変数定義
- x_A(k): 製品Aの製造開始時刻(kサイクル目)
- x_B(k): 製品Bの製造開始時刻(kサイクル目)
- x_C(k): 製品Cの製造開始時刻(kサイクル目)
- x_D(k): 製品Dの製造開始時刻(kサイクル目)
- x_E(k): 製品Eの製造開始時刻(kサイクル目)
- x_F(k): 製品Fの製造開始時刻(kサイクル目)
- y_i(k): 発送グループiの発送時刻(kサイクル目)

## 計画通り(Before)の製造順序制約
x_B(k) = p_A ⊗ x_A(k)
x_C(k) = p_B ⊗ x_B(k)
x_E(k) = p_D ⊗ x_D(k)
x_F(k) = p_E ⊗ x_E(k)

ここで p_i は製品iの処理時間を表す

## 変更後(After)の製造順序制約
x_A(k) = p_B ⊗ x_B(k)
x_C(k) = p_A ⊗ x_A(k)
x_E(k) = p_D ⊗ x_D(k)
x_F(k) = p_E ⊗ x_E(k)

## 発送時刻制約(Before)
y_1(k) = (p_A ⊗ x_A(k)) ⊕ (p_D ⊗ x_D(k)) // AとDの混載
y_2(k) = (p_B ⊗ x_B(k)) ⊕ (p_E ⊗ x_E(k)) // BとEの混載
y_3(k) = (p_C ⊗ x_C(k)) ⊕ (p_F ⊗ x_F(k)) // CとFの混載

## 発送時刻制約(After)
y_1(k) = p_D ⊗ x_D(k) // D単独
y_2(k) = p_A ⊗ x_A(k) // A単独
y_3(k) = (p_B ⊗ x_B(k)) ⊕ (p_E ⊗ x_E(k)) // BとEの混載
y_4(k) = (p_C ⊗ x_C(k)) ⊕ (p_F ⊗ x_F(k)) // CとFの混載

## リソース制約
以下の製品ペアは同時に処理不可:
(A,D), (B,E), (C,F)
数式で表すと:
|x_A(k) - x_D(k)| ≥ min(p_A, p_D)
|x_B(k) - x_E(k)| ≥ min(p_B, p_E)
|x_C(k) - x_F(k)| ≥ min(p_C, p_F)

## システム性能指標
完了時刻 = y_1(k) ⊕ y_2(k) ⊕ y_3(k) ⊕ y_4(k)

利益 = 製造利益 ⊕ 労務費削減 ⊖ 発送コスト
= 20000 ⊕ (After時のみ5000) ⊖ 発送車両数×10000

この定式化の重要なポイント:
1. 状態遷移の表現
- ⊗ による時間の積算
- ⊕ による並行イベントの同期

2. スケジュール変更の影響
- 製造順序の変更(A→B vs B→A)
- 発送グループの再編成
- コスト構造の変化

3. システムの特徴
- 非同期イベント
- 同期制約(リソース共有)
- 時間遅れの蓄積

この定式化を使って:
1. 実行可能なスケジュールの導出
2. システムの周期性分析
3. ボトルネックの特定
が可能になります。

特に注目すべき点や、追加したい制約条件はありますか?

回答

次にPythonコード
ふむ、さっぱり分からない。

# Max Plus代数での演算定義
def max_plus_add(x, y):
    """Max Plus代数での加算 (a ⊕ b = max(a,b))"""
    return max(x, y)

def max_plus_multiply(x, y):
    """Max Plus代数での乗算 (a ⊗ b = a + b)"""
    return x + y

class MaxPlusSystem:
    def __init__(self):
        # イベント変数の定義
        # x_i: 製品iの製造開始時刻
        # y_i: 製品iの発送時刻
        self.variables = {
            'x_A': None,  # A製造開始
            'x_B': None,  # B製造開始
            'x_C': None,  # C製造開始
            'x_D': None,  # D製造開始
            'x_E': None,  # E製造開始
            'x_F': None,  # F製造開始
            'y_AD': None, # AとDの発送
            'y_BE': None, # BとEの発送
            'y_CF': None  # CとFの発送
        }
        
        # 処理時間の定義
        self.processing_times = {
            'A': 1,
            'B': 1,
            'C': 1,
            'D': 1,
            'E': 1,
            'F': 1
        }
        
        # リソース制約(同時に処理できない製品ペア)
        self.resource_constraints = [
            ('A', 'D'),
            ('B', 'E'),
            ('C', 'F')
        ]

    def generate_constraints(self, schedule_type="Before"):
        """Max Plus代数での制約式生成"""
        constraints = []
        
        if schedule_type == "Before":
            # 計画通りの製造順序制約
            # A → B → C
            constraints.append(f"x_B >= x_A + {self.processing_times['A']}")  # x_B ≥ x_A ⊗ p_A
            constraints.append(f"x_C >= x_B + {self.processing_times['B']}")  # x_C ≥ x_B ⊗ p_B
            
            # D → E → F
            constraints.append(f"x_E >= x_D + {self.processing_times['D']}")  # x_E ≥ x_D ⊗ p_D
            constraints.append(f"x_F >= x_E + {self.processing_times['E']}")  # x_F ≥ x_E ⊗ p_E
            
        else:  # After
            # 変更後の製造順序制約
            # B → A → C
            constraints.append(f"x_A >= x_B + {self.processing_times['B']}")  # x_A ≥ x_B ⊗ p_B
            constraints.append(f"x_C >= x_A + {self.processing_times['A']}")  # x_C ≥ x_A ⊗ p_A
            
            # D → E → F (変更なし)
            constraints.append(f"x_E >= x_D + {self.processing_times['D']}")
            constraints.append(f"x_F >= x_E + {self.processing_times['E']}")

        # 発送時刻制約
        if schedule_type == "Before":
            # 計画通りの発送グループ制約
            constraints.append(f"y_AD >= max(x_A + {self.processing_times['A']}, x_D + {self.processing_times['D']})")
            constraints.append(f"y_BE >= max(x_B + {self.processing_times['B']}, x_E + {self.processing_times['E']})")
            constraints.append(f"y_CF >= max(x_C + {self.processing_times['C']}, x_F + {self.processing_times['F']})")
        else:
            # 変更後の発送制約
            constraints.append(f"y_D >= x_D + {self.processing_times['D']}")  # D単独発送
            constraints.append(f"y_A >= x_A + {self.processing_times['A']}")  # A単独発送
            constraints.append(f"y_BE >= max(x_B + {self.processing_times['B']}, x_E + {self.processing_times['E']})")
            constraints.append(f"y_CF >= max(x_C + {self.processing_times['C']}, x_F + {self.processing_times['F']})")

        return constraints

    def calculate_completion_time(self, schedule_type="Before"):
        """完了時刻の計算(最大値)"""
        constraints = self.generate_constraints(schedule_type)
        # ここで制約を解いて完了時刻を求める
        # 実際の実装では線形計画問題として解く
        pass

    def calculate_cost(self, schedule_type="Before"):
        """コストの計算"""
        manufacturing_profit = 20000
        labor_savings = 5000 if schedule_type == "After" else 0
        shipping_cost = 30000 if schedule_type == "After" else 20000
        
        return manufacturing_profit + labor_savings - shipping_cost

今の頭のイメージでは帰納的な手法であるGNNと演繹的な手法であるトロピカル演算とが独立していて、どうやったら繋がるのか?

プロンプト

なるほど、GNNとトロピカル代数の橋渡しについて考えてみましょう。以下のような接点が考えられます:

1. GNNの構造にトロピカル代数を組み込む方法
- GNNのメッセージパッシング関数をmax-plus演算で定義
- ノード間の時間伝播をトロピカル行列で表現
- アグリゲーション関数にmax演算子を使用

2. トロピカル代数による制約をGNNの学習に活用
- トロピカル演算で導出した実行可能解の空間をGNNの学習範囲として利用
- 損失関数にトロピカル代数による制約違反のペナルティを組み込む
- 各イテレーションでトロピカル代数による解との距離を最小化

3. ハイブリッドアプローチ
GNNによる予測

トロピカル代数による実行可能性チェック

フィードバックを学習に反映

4. 理論的な接点
- GNNの表現力とトロピカル幾何の関係
- メッセージパッシングの収束性とトロピカル固有値の関係
- グラフ構造の対称性とトロピカル代数の周期性

これらを統合するアプローチとして、以下のような研究の方向性が考えられます:

1. トロピカルニューラルネットワーク
- 活性化関数をmax-plus演算で定義
- 重み更新にトロピカル勾配を使用
- スケジューリング問題に特化した新しいネットワークアーキテクチャ

2. 制約付き学習フレームワーク
- トロピカル代数による制約を満たしながらGNNを学習
- 実行可能解の空間での効率的な探索
- 理論的な収束性の保証

3. スケーラブルな最適化
- GNNによる高速な近似解の生成
- トロピカル代数による解の修正
- 大規模問題への適用可能性

これらの方向性について、さらに掘り下げて検討すべき点や、特に興味のある側面はありますか?


次はこれを考えますか。


いいなと思ったら応援しよう!