mcpField
M
multi-objective-optimization

multi-objective-optimization

github.com/kishorkukrejaデータ・データベース8448 回インストール3 週間前に更新
リポジトリ

multi-objective-optimization について

サプライチェーンの意思決定に向けた多目的最適化スキルです。コスト、サービス、持続可能性などのトレードオフを、パレート分析や複数の手法で検討します。

主な機能

  1. 01パレート優越関係とフロント分析
  2. 02加重和法と ε 制約法
  3. 03NSGA-II と目標計画法
  4. 04サプライチェーンネットワーク設計の最適化
  5. 05対話的な探索と可視化

活用シーン

  1. 01サプライチェーンのコストとサービス水準の調整
  2. 02経済・環境・社会目標の比較
  3. 03非劣解の探索と提示

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/kishorkukreja/awesome-supply-chain --skill multi-objective-optimization

インストール後、Claude Code で/multi-objective-optimizationを入力して読み込まれたか確認します。

詳細

多目的最適化

サプライチェーンの多目的最適化に関する専門家です。コストとサービス、利益と持続可能性、効率性とレジリエンスなど、相反する目的のバランスを取るパレート最適解を見つけ、分析することを目指します。

初期評価

  1. 目的:どの目標が競合していますか?(コストの最小化、サービスの最大化、炭素排出量の最小化)
  2. 選好:既知のトレードオフがありますか、それともパレートフロンティアを見つけますか?
  3. 意思決定者:対話形式で選択しますか、それとも自動で選択しますか?
  4. 問題の規模:厳密解法で解けますか、それともヒューリスティックが必要ですか?

基本概念

パレート優越性: すべての目的において、解 x が解 y より優れている場合、x は y を支配します。

パレートフロント: 他の解に支配されない解の集合

トレードオフ: ある目的を改善すると、別の目的が悪化します。


手法

1. 重み付き和(スカラー化)
# Combine objectives with weights
objective = w1 * cost + w2 * (-service_level) + w3 * carbon

# Vary weights to get different Pareto points
for w1 in [0.2, 0.5, 0.8]:
    w2, w3 = (1-w1)/2, (1-w1)/2
    solve_with_weights(w1, w2, w3)
2. ε制約法
# Optimize one objective, constrain others
minimize cost
subject to:
    service_level ≥ 0.95
    carbon ≤ 1000
3. NSGA-II(遺伝的アルゴリズム)
from pymoo.algorithms.moo.nsga2 import NSGA2
from pymoo.optimize import minimize
from pymoo.problems import get_problem

# Multi-objective problem
problem = SupplyChainMO()

algorithm = NSGA2(pop_size=100)

res = minimize(problem,
               algorithm,
               ('n_gen', 200),
               verbose=True)

# Get Pareto front
pareto_front = res.F
4. 目標計画法
# Set target for each objective, minimize deviations
targets = {'cost': 100000, 'service': 0.98, 'carbon': 500}

minimize sum(d_minus[obj] + d_plus[obj] for obj in objectives)
subject to:
    actual[obj] + d_plus[obj] - d_minus[obj] = targets[obj]

サプライチェーンネットワーク設計:コストとサービス

from pulp import *
import numpy as np
import matplotlib.pyplot as plt

def multi_objective_network_design(customers, facilities, weights):
    """
    Network design with cost and service objectives
    
    Objective 1: Minimize total cost
    Objective 2: Minimize average distance (maximize service)
    """
    
    model = LpProblem("MultiObj_Network", LpMinimize)
    
    # Variables
    open_facility = LpVariable.dicts("Open", facilities, cat='Binary')
    flow = LpVariable.dicts("Flow",
                           [(i,j) for i in customers for j in facilities],
                           lowBound=0)
    
    # Objective: weighted combination
    w_cost, w_service = weights
    
    cost_obj = lpSum([fixed_cost[j] * open_facility[j] for j in facilities]) + \
               lpSum([transport_cost[i,j] * flow[i,j]
                     for i in customers for j in facilities])
    
    service_obj = lpSum([distance[i,j] * flow[i,j]
                        for i in customers for j in facilities])
    
    # Normalize objectives
    max_cost = estimate_max_cost()
    max_distance = estimate_max_distance()
    
    model += w_cost * (cost_obj / max_cost) + \
             w_service * (service_obj / max_distance), "Weighted_Objective"
    
    # Constraints
    for i in customers:
        model += lpSum([flow[i,j] for j in facilities]) >= demand[i]
    
    for j in facilities:
        model += lpSum([flow[i,j] for i in customers]) <= \
                 capacity[j] * open_facility[j]
    
    model.solve()
    
    return {
        'cost': value(cost_obj),
        'service': value(service_obj),
        'open_facilities': [j for j in facilities if open_facility[j].varValue > 0.5]
    }

# Generate Pareto frontier
pareto_solutions = []
for w_cost in np.linspace(0, 1, 20):
    w_service = 1 - w_cost
    sol = multi_objective_network_design(customers, facilities, (w_cost, w_service))
    pareto_solutions.append(sol)

# Plot Pareto front
costs = [s['cost'] for s in pareto_solutions]
services = [s['service'] for s in pareto_solutions]

plt.figure(figsize=(10, 6))
plt.plot(costs, services, 'o-', linewidth=2, markersize=8)
plt.xlabel('Total Cost ($)')
plt.ylabel('Average Distance (Service)')
plt.title('Pareto Frontier: Cost vs Service Trade-off')
plt.grid(True, alpha=0.3)
plt.show()

持続可能なサプライチェーン:経済・環境・社会

class TripleBottomLineOptimization:
    """
    Optimize Economic, Environmental, and Social objectives
    """
    
    def __init__(self, network_data):
        self.data = network_data
    
    def optimize_pareto(self, method='weighted_sum'):
        """
        Find Pareto-optimal solutions for triple bottom line
        
        Objectives:
        1. Economic: Minimize cost
        2. Environmental: Minimize carbon emissions
        3. Social: Maximize local employment
        """
        
        if method == 'weighted_sum':
            solutions = []
            
            # Systematically vary weights
            for w1 in [0.2, 0.4, 0.6, 0.8]:
                for w2 in [0.2, 0.4, 0.6, 0.8]:
                    w3 = max(0, 1 - w1 - w2)
                    if w1 + w2 + w3 > 0.99:  # Valid weight combination
                        sol = self.solve_weighted(w1, w2, w3)
                        solutions.append(sol)
            
            # Filter non-dominated solutions
            pareto_front = self.extract_pareto_front(solutions)
            return pareto_front
        
        elif method == 'epsilon_constraint':
            # Fix two objectives, optimize third
            pareto_front = []
            
            for carbon_limit in np.linspace(min_carbon, max_carbon, 10):
                for employment_target in np.linspace(min_emp, max_emp, 10):
                    sol = self.solve_epsilon_constraint(
                        carbon_limit=carbon_limit,
                        employment_target=employment_target
                    )
                    if sol['feasible']:
                        pareto_front.append(sol)
            
            return pareto_front
    
    def solve_weighted(self, w_economic, w_environmental, w_social):
        """Solve with weighted objectives"""
        
        model = LpProblem("Triple_Bottom_Line", LpMinimize)
        
        # Variables and constraints
        # ...
        
        # Weighted objective
        model += (
            w_economic * economic_cost +
            w_environmental * carbon_emissions +
            w_social * (-local_employment)  # Maximize employment
        )
        
        model.solve()
        
        return {
            'economic': value(economic_cost),
            'environmental': value(carbon_emissions),
            'social': value(local_employment),
            'weights': (w_economic, w_environmental, w_social)
        }
    
    def extract_pareto_front(self, solutions):
        """Filter non-dominated solutions"""
        
        pareto = []
        
        for sol in solutions:
            dominated = False
            
            for other in solutions:
                if self.dominates(other, sol):
                    dominated = True
                    break
            
            if not dominated:
                pareto.append(sol)
        
        return pareto
    
    def dominates(self, sol1, sol2):
        """Check if sol1 Pareto-dominates sol2"""
        
        # sol1 dominates if better in all objectives
        better_economic = sol1['economic'] <= sol2['economic']
        better_environmental = sol1['environmental'] <= sol2['environmental']
        better_social = sol1['social'] >= sol2['social']  # Maximize
        
        at_least_one_strictly_better = (
            sol1['economic'] < sol2['economic'] or
            sol1['environmental'] < sol2['environmental'] or
            sol1['social'] > sol2['social']
        )
        
        return (better_economic and better_environmental and better_social and
                at_least_one_strictly_better)

対話型の意思決定

def interactive_pareto_exploration(problem, decision_maker):
    """
    Interactive method: present solutions, get feedback, refine
    """
    
    # Generate initial Pareto front
    pareto_front = problem.generate_initial_pareto_front()
    
    iteration = 0
    max_iterations = 10
    
    while iteration < max_iterations:
        # Present solutions to decision maker
        print(f"\nIteration {iteration + 1}")
        print("Current Pareto Solutions:")
        for i, sol in enumerate(pareto_front):
            print(f"  {i}: Cost=${sol['cost']}, Service={sol['service']:.2%}, Carbon={sol['carbon']}")
        
        # Get feedback
        preferred_region = decision_maker.get_preference(pareto_front)
        
        if decision_maker.is_satisfied():
            break
        
        # Generate more solutions in preferred region
        new_solutions = problem.explore_region(preferred_region, n_solutions=10)
        pareto_front.extend(new_solutions)
        
        # Update Pareto front
        pareto_front = filter_non_dominated(pareto_front)
        
        iteration += 1
    
    # Final selection
    best_solution = decision_maker.select_final_solution(pareto_front)
    return best_solution

可視化

def visualize_3d_pareto_front(solutions):
    """
    Visualize 3-objective Pareto front
    """
    
    from mpl_toolkits.mplot3d import Axes3D
    
    fig = plt.figure(figsize=(12, 10))
    ax = fig.add_subplot(111, projection='3d')
    
    costs = [s['cost'] for s in solutions]
    services = [s['service'] for s in solutions]
    carbons = [s['carbon'] for s in solutions]
    
    scatter = ax.scatter(costs, services, carbons,
                        c=carbons, cmap='RdYlGn_r',
                        s=100, alpha=0.6, edgecolors='black')
    
    ax.set_xlabel('Cost ($)', fontsize=12)
    ax.set_ylabel('Service Level', fontsize=12)
    ax.set_zlabel('Carbon Emissions (tons)', fontsize=12)
    ax.set_title('3D Pareto Frontier', fontsize=14, fontweight='bold')
    
    plt.colorbar(scatter, label='Carbon Emissions')
    plt.show()

def visualize_parallel_coordinates(pareto_front):
    """
    Parallel coordinates plot for many objectives
    """
    
    from pandas.plotting import parallel_coordinates
    import pandas as pd
    
    df = pd.DataFrame(pareto_front)
    df['Solution'] = range(len(df))
    
    plt.figure(figsize=(12, 6))
    parallel_coordinates(df, 'Solution', colormap='viridis')
    plt.title('Pareto Solutions - Parallel Coordinates')
    plt.ylabel('Normalized Objective Value')
    plt.legend(loc='upper right')
    plt.grid(True, alpha=0.3)
    plt.show()

ツールとライブラリ

Python:

  • pymoo:多目的最適化
  • platypus:進化的多目的最適化
  • jmetal:多目的メタヒューリスティクス

商用:

  • modeFRONTIER:多目的設計
  • CPLEX Multi-Objective

関連スキル

  • optimization-modeling:単一目的最適化
  • metaheuristic-optimization:NSGA-II、MOEA
  • sustainable-sourcing:環境目的
  • network-design:多目的ネットワーク設計

コメント