{
 "nbformat": 4,
 "nbformat_minor": 0,
 "metadata": {
  "kernelspec": {
   "name": "python3",
   "display_name": "Python 3"
  },
  "language_info": {
   "name": "python"
  }
 },
 "cells": [
  {
   "cell_type": "code",
   "source": [
    "import numpy as np\n",
    "import pandas as pd\n",
    "import itertools\n",
    "from collections import defaultdict\n",
    "\n",
    "# --- Configuration & Foundational Data ---\n",
    "N_SCENARIOS = 5000\n",
    "ACTUAL_GPU_SUPPLY_2026 = 185000\n",
    "GPUS_PER_SERVER = 1.2\n",
    "MAX_SERVERS_2026 = int(ACTUAL_GPU_SUPPLY_2026 / GPUS_PER_SERVER)\n",
    "\n",
    "# Financial & Operational Data\n",
    "GPU_BENCHMARK_COST = 30000\n",
    "OTHER_COMPONENTS_COST = 9840\n",
    "SERVER_REVENUE = 49090.91\n",
    "PLANT_FC = {'US': 7800e3, 'Mexico': 7200e3, 'Malaysia': 6900e3}\n",
    "SHIPPING_LANE_FC = {\n",
    "    ('US', 'NA'): 450e3, ('US', 'APAC'): 850e3, ('US', 'EU'): 650e3,\n",
    "    ('Mexico', 'NA'): 500e3, ('Mexico', 'APAC'): 900e3, ('Mexico', 'EU'): 700e3,\n",
    "    ('Malaysia', 'NA'): 700e3, ('Malaysia', 'APAC'): 500e3, ('Malaysia', 'EU'): 600e3,\n",
    "}\n",
    "LOGISTICS_VC = {'Ocean': 180, 'Air': 620}\n",
    "CO2_EMISSIONS = {'Ocean': 50, 'Air': 310} # In kg per unit\n",
    "\n",
    "# Simulation Assumptions\n",
    "DEMAND_BASELINE = 215000\n",
    "DEMAND_VOLATILITY = 0.20\n",
    "CARBON_PRICE_RANGE = (50, 150)\n",
    "DEMAND_MIX = {'NA': 0.45, 'APAC': 0.35, 'EU': 0.20}\n",
    "\n",
    "class StrategicDecisionEngine:\n",
    "    \"\"\"A complete engine for strategic simulation, feasibility analysis, and decision support.\"\"\"\n",
    "\n",
    "    def __init__(self, decision_inputs):\n",
    "        self.inputs = decision_inputs\n",
    "        self.all_strategy_results = []\n",
    "        self.feasible_strategies = []\n",
    "        self.infeasible_strategies = []\n",
    "\n",
    "        self.hard_constraints = self.inputs['hard_constraints']\n",
    "        self.soft_constraints = self.inputs['soft_constraints']\n",
    "        self.weights = self.inputs['objective_weights']\n",
    "\n",
    "    def _evaluate_single_strategy(self, strategy_def):\n",
    "        \"\"\"Runs a full Monte Carlo simulation for one strategy.\"\"\"\n",
    "        plant_map = strategy_def['plant_map']\n",
    "        logistics_mix = strategy_def['logistics_mix']\n",
    "        fixed_cost = sum(PLANT_FC[plant] for plant in set(plant_map.values()))\n",
    "        for region, plant in plant_map.items():\n",
    "            fixed_cost += SHIPPING_LANE_FC.get((plant, region), 0)\n",
    "\n",
    "        total_bom_vc = (GPUS_PER_SERVER * GPU_BENCHMARK_COST) + OTHER_COMPONENTS_COST\n",
    "        avg_logistics_vc = (logistics_mix['Ocean'] * LOGISTICS_VC['Ocean']) + (logistics_mix['Air'] * LOGISTICS_VC['Air'])\n",
    "        avg_co2_kg = (logistics_mix['Ocean'] * CO2_EMISSIONS['Ocean']) + (logistics_mix['Air'] * CO2_EMISSIONS['Air'])\n",
    "\n",
    "        scenarios = []\n",
    "        for _ in range(N_SCENARIOS):\n",
    "            demand = np.random.normal(DEMAND_BASELINE, DEMAND_VOLATILITY)\n",
    "            carbon_price = np.random.uniform(CARBON_PRICE_RANGE[0], CARBON_PRICE_RANGE[1])\n",
    "            units_sold = min(demand, MAX_SERVERS_2026)\n",
    "\n",
    "            carbon_cost_per_unit = (avg_co2_kg / 1000) * carbon_price # kg -> tons for price\n",
    "            total_vc_per_unit = total_bom_vc + avg_logistics_vc + carbon_cost_per_unit\n",
    "            total_cost = fixed_cost + (units_sold * total_vc_per_unit)\n",
    "\n",
    "            profit = (units_sold * SERVER_REVENUE) - total_cost\n",
    "\n",
    "            # *** BUG FIX IS HERE ***\n",
    "            # Convert total kg to thousands of tons (k-tons) by dividing by 1 million\n",
    "            co2_k_tons = (units_sold * avg_co2_kg) / 1_000_000\n",
    "\n",
    "            scenarios.append({'profit': profit, 'co2_k_tons': co2_k_tons, 'units_sold': units_sold})\n",
    "\n",
    "        scenario_df = pd.DataFrame(scenarios)\n",
    "        num_unique_plants = len(set(plant_map.values()))\n",
    "        resilience_score = {1: 10, 2: 60, 3: 100}.get(num_unique_plants, 0)\n",
    "\n",
    "        return {\n",
    "            \"definition\": strategy_def,\n",
    "            \"results_df\": scenario_df,\n",
    "            \"metrics\": {\n",
    "                \"profit\": scenario_df['profit'].mean(),\n",
    "                \"growth\": scenario_df['units_sold'].mean(),\n",
    "                \"sustainability\": scenario_df['co2_k_tons'].mean(),\n",
    "                \"resilience\": resilience_score\n",
    "            }\n",
    "        }\n",
    "\n",
    "    def _check_feasibility_and_penalties(self, result):\n",
    "        \"\"\"Analyzes a strategy's results against hard and soft constraints.\"\"\"\n",
    "        df = result['results_df']\n",
    "        violations = {\n",
    "            'profit': (df['profit'] < self.hard_constraints['min_profit_M'] * 1e6).mean(),\n",
    "            'co2': (df['co2_k_tons'] > self.hard_constraints['max_co2_ktons']).mean(),\n",
    "            'ocean': (result['definition']['logistics_mix']['Ocean'] < self.hard_constraints['min_ocean_share']),\n",
    "            'resilience': (result['metrics']['resilience'] < self.hard_constraints['min_resilience_score'])\n",
    "        }\n",
    "        total_violation_prob = 1 - (1 - violations['profit']) * (1 - violations['co2'])\n",
    "        is_feasible = (total_violation_prob < 0.05) and not violations['ocean'] and not violations['resilience']\n",
    "\n",
    "        profit_shortfall = np.maximum(0, self.soft_constraints['target_profit_M'] * 1e6 - df['profit']).mean()\n",
    "        co2_excess = np.maximum(0, df['co2_k_tons'] - self.soft_constraints['target_co2_ktons']).mean()\n",
    "        profit_penalty, co2_penalty = profit_shortfall / 1e6, co2_excess / 0.1\n",
    "        total_penalty = profit_penalty + co2_penalty\n",
    "\n",
    "        result['feasibility'] = {'is_feasible': is_feasible, 'violation_probability': total_violation_prob, 'violations': violations}\n",
    "        result['penalty_score'] = total_penalty\n",
    "        return result\n",
    "\n",
    "    def _score_strategies(self):\n",
    "        \"\"\"Calculates a weighted Strategic Score for all feasible strategies.\"\"\"\n",
    "        if not self.feasible_strategies: return\n",
    "        min_vals = {key: min(s['metrics'][key] for s in self.feasible_strategies) for key in self.weights}\n",
    "        max_vals = {key: max(s['metrics'][key] for s in self.feasible_strategies) for key in self.weights}\n",
    "\n",
    "        for strat in self.feasible_strategies:\n",
    "            total_score = 0\n",
    "            for objective, weight in self.weights.items():\n",
    "                val, range_val = strat['metrics'][objective], max_vals[objective] - min_vals[objective]\n",
    "                norm_score = 100 if range_val == 0 else 100 * (val - min_vals[objective]) / range_val\n",
    "                if objective == 'sustainability': norm_score = 100 - norm_score\n",
    "                total_score += norm_score * (weight / 100.0)\n",
    "            strat['strategic_score'] = total_score\n",
    "        self.feasible_strategies.sort(key=lambda x: x['strategic_score'], reverse=True)\n",
    "\n",
    "    def run(self):\n",
    "        \"\"\"Executes the entire end-to-end analysis.\"\"\"\n",
    "        plants, regions = ['US', 'Mexico', 'Malaysia'], ['NA', 'APAC', 'EU']\n",
    "        all_plant_maps = [dict(zip(regions, p)) for p in itertools.product(plants, repeat=len(regions))]\n",
    "        all_logistics_mixes = [{'Ocean': i/10, 'Air': (10-i)/10} for i in range(11)]\n",
    "        strategy_definitions = [{'plant_map': p_map, 'logistics_mix': l_mix} for p_map in all_plant_maps for l_mix in all_logistics_mixes]\n",
    "\n",
    "        print(f\"Analyzing {len(strategy_definitions)} potential strategies across {N_SCENARIOS} scenarios each...\")\n",
    "        for i, strat_def in enumerate(strategy_definitions):\n",
    "            print(f\"  - Evaluating strategy {i+1}/{len(strategy_definitions)}...\", end='\\r')\n",
    "            result = self._evaluate_single_strategy(strat_def)\n",
    "            result = self._check_feasibility_and_penalties(result)\n",
    "            self.all_strategy_results.append(result)\n",
    "            if result['feasibility']['is_feasible']: self.feasible_strategies.append(result)\n",
    "            else: self.infeasible_strategies.append(result)\n",
    "\n",
    "        print(\"\\nAnalysis complete.\")\n",
    "        self._score_strategies()\n",
    "        self._display_report()\n",
    "\n",
    "    def _display_report(self):\n",
    "        \"\"\"Prints the final, formatted executive summary.\"\"\"\n",
    "        print(\"\\n\" + \"=\"*80 + \"\\n** Executive Summary: Strategic Analysis for 2026 **\\n\" + \"=\"*80)\n",
    "        if not self.feasible_strategies:\n",
    "            print(\"\\n**Feasibility Check: No strategy was found that meets all hard constraints.**\\n\")\n",
    "            self._display_relaxation_advice()\n",
    "        else:\n",
    "            prob = self.feasible_strategies[0]['feasibility']['violation_probability']\n",
    "            print(f\"\\n**Feasibility Check: Found {len(self.feasible_strategies)} feasible strategies.**\")\n",
    "            print(f\"The top-ranked strategy is feasible in {100*(1-prob):.1f}% of simulated scenarios.\\n\")\n",
    "\n",
    "        print(\"-\" * 80 + \"\\n** Strategy Recommendations **\\n\" + \"-\" * 80)\n",
    "\n",
    "        if self.feasible_strategies:\n",
    "            self._print_strategy_card(\"Recommended Strategy (Highest Strategic Score)\", self.feasible_strategies[0])\n",
    "        self._print_strategy_card(\"Most Profitable Strategy\", max(self.all_strategy_results, key=lambda x: x['metrics']['profit']), is_alt=True)\n",
    "        self._print_strategy_card(\"Most Resilient Strategy\", max(self.all_strategy_results, key=lambda x: x['metrics']['resilience']), is_alt=True)\n",
    "\n",
    "        if not self.feasible_strategies and self.infeasible_strategies:\n",
    "            self._print_strategy_card(\"Least Infeasible Strategy (Best effort)\", min(self.infeasible_strategies, key=lambda x: x['penalty_score']), is_alt=True)\n",
    "\n",
    "    def _print_strategy_card(self, title, strat, is_alt=False):\n",
    "        \"\"\"Prints a formatted summary of a single strategy with a detailed operational plan.\"\"\"\n",
    "        print(f\"\\n--- {title} ---\")\n",
    "        if is_alt and not strat['feasibility']['is_feasible']:\n",
    "            prob = strat['feasibility']['violation_probability']\n",
    "            print(f\"  **Warning:** This strategy is NOT feasible (violates constraints in {prob:.1%} of scenarios).\")\n",
    "\n",
    "        metrics, definition = strat['metrics'], strat['definition']\n",
    "        print(f\"  - Profile: Expected Profit: ${metrics['profit']/1e6:,.1f}M | Expected CO2: {metrics['sustainability']:,.2f} k-tons | Resilience: {metrics['resilience']:.0f}/100\")\n",
    "\n",
    "        print(\"\\n  - Operational Plan:\")\n",
    "        for region in ['NA', 'APAC', 'EU']:\n",
    "            plant = definition['plant_map'][region]\n",
    "            units = metrics['growth'] * DEMAND_MIX[region]\n",
    "            logistics = definition['logistics_mix']\n",
    "\n",
    "            print(f\"    - {region} Market ({units:,.0f} units):\")\n",
    "            print(f\"        - Assemble In:  '{plant}'\")\n",
    "            print(f\"        - Ship Via:     {logistics['Ocean'] * 100:.0f}% Ocean, {logistics['Air'] * 100:.0f}% Air\")\n",
    "\n",
    "    def _display_relaxation_advice(self):\n",
    "        \"\"\"Provides actionable advice if no feasible solutions were found.\"\"\"\n",
    "        print(\"-\" * 80 + \"\\n** Relaxation Engine: Advice to find a feasible solution **\\n\" + \"-\" * 80)\n",
    "        best_infeasible = min(self.infeasible_strategies, key=lambda x: x['penalty_score'])\n",
    "        violations = best_infeasible['feasibility']['violations']\n",
    "\n",
    "        print(\"The primary reason for infeasibility is the combination of ambitious constraints.\")\n",
    "        print(\"The 'least infeasible' strategy failed because:\")\n",
    "        if violations['resilience']:\n",
    "            print(f\"  - The Resilience Score of {best_infeasible['metrics']['resilience']:.0f} did not meet the required minimum of {self.hard_constraints['min_resilience_score']:.0f}.\")\n",
    "        if violations['profit'] > 0.05:\n",
    "            profit_miss = self.hard_constraints['min_profit_M'] - (best_infeasible['metrics']['profit'] / 1e6)\n",
    "            print(f\"  - The Profit Constraint (${self.hard_constraints['min_profit_M']:.0f}M) was missed by ~${profit_miss:.1f}M on average.\")\n",
    "        if violations['co2'] > 0.05:\n",
    "            co2_excess = best_infeasible['metrics']['sustainability'] - self.hard_constraints['max_co2_ktons']\n",
    "            print(f\"  - The CO2 Constraint ({self.hard_constraints['max_co2_ktons']:.1f} k-tons) was exceeded by ~{co2_excess:.2f} k-tons on average.\")\n",
    "        if violations['ocean']:\n",
    "            print(f\"  - The Ocean Freight Constraint was violated (actual was {best_infeasible['definition']['logistics_mix']['Ocean']*100:.0f}% vs. required {self.hard_constraints['min_ocean_share']*100:.0f}%).\")\n",
    "\n",
    "        print(\"\\n**Recommendation:** To find a feasible solution, consider one of the following trade-offs:\")\n",
    "        if violations['resilience']: print(f\"  1. Relax the Minimum Resilience Score from {self.hard_constraints['min_resilience_score']:.0f} to a lower tier (e.g., 10 or 60).\")\n",
    "        print(f\"  2. Relax the Minimum Profit target from ${self.hard_constraints['min_profit_M']:.0f}M to ${self.hard_constraints['min_profit_M']*0.98:.0f}M.\")\n",
    "        print(f\"  3. Relax the Maximum CO2 target from {self.hard_constraints['max_co2_ktons']:.1f} k-tons to {self.hard_constraints['max_co2_ktons']*1.05:.1f} k-tons.\")\n",
    "        if violations['ocean']: print(f\"  4. Relax the Minimum Ocean Freight Share from {self.hard_constraints['min_ocean_share']*100:.0f}% to {self.hard_constraints['min_ocean_share']*0.95*100:.0f}%.\")\n",
    "        print(\"Re-running the tool with one of these relaxed constraints is likely to yield feasible strategies.\")\n",
    "\n",
    "def get_interactive_inputs():\n",
    "    \"\"\"Guides the user through a questionnaire to build the decision_input dictionary.\"\"\"\n",
    "    profiles = {\n",
    "        \"1\": {\"name\": \"Profit First\", \"weights\": {\"profit\": 60, \"resilience\": 10, \"sustainability\": 30}},\n",
    "        \"2\": {\"name\": \"Balanced Growth\", \"weights\": {\"profit\": 40, \"resilience\": 30, \"sustainability\": 30}},\n",
    "        \"3\": {\"name\": \"Resilience First\", \"weights\": {\"profit\": 20, \"resilience\": 50, \"sustainability\": 30}},\n",
    "    }\n",
    "\n",
    "    print(\"=\"*80 + \"\\nStep 1: Choose a Strategic Profile\\n\" + \"-\"*80)\n",
    "    for key, val in profiles.items(): print(f\"  [{key}] {val['name']}\")\n",
    "    choice = input(\"Select a profile [1-3] or press Enter for custom: \")\n",
    "\n",
    "    if choice in profiles:\n",
    "        weights = profiles[choice]['weights']\n",
    "        print(f\"Selected '{profiles[choice]['name']}'.\")\n",
    "    else:\n",
    "        print(\"\\n-- Custom Priority Weights --\\nPlease allocate 100 points across the following objectives:\")\n",
    "        weights = {}\n",
    "        while True:\n",
    "            try:\n",
    "                weights['profit'] = int(input(f\"  - Points for Profit: \"))\n",
    "                weights['resilience'] = int(input(f\"  - Points for Resilience: \"))\n",
    "                weights['sustainability'] = int(input(f\"  - Points for Sustainability (Low CO2): \"))\n",
    "                if sum(weights.values()) == 100: break\n",
    "                else: print(f\"Error: Points sum to {sum(weights.values())}, not 100. Please try again.\\n\")\n",
    "            except ValueError: print(\"Error: Please enter whole numbers.\\n\")\n",
    "\n",
    "    print(\"\\n\" + \"=\"*80 + \"\\nStep 2: Define Constraints (Your Non-Negotiable Rules)\\n\" + \"-\"*80)\n",
    "    hard_constraints, soft_constraints = {}, {}\n",
    "    try:\n",
    "        hard_constraints['min_resilience_score'] = int(input(\"  - Hard Constraint: Minimum Resilience Score (10=Low, 60=Med, 100=High, e.g., 60): \"))\n",
    "        hard_constraints['min_profit_M'] = float(input(\"  - Hard Constraint: Minimum Profit (in millions, e.g., 210): \"))\n",
    "        soft_constraints['target_profit_M'] = float(input(\"  - Soft Target:   Target Profit (in millions, e.g., 220): \"))\n",
    "        hard_constraints['max_co2_ktons'] = float(input(\"  - Hard Constraint: Maximum CO2 (in k-tons, e.g., 8.0): \"))\n",
    "        soft_constraints['target_co2_ktons'] = float(input(\"  - Soft Target:   Target CO2 (in k-tons, e.g., 7.5): \"))\n",
    "        hard_constraints['min_ocean_share'] = float(input(\"  - Hard Constraint: Minimum Ocean Freight Share (decimal, e.g., 0.8): \"))\n",
    "    except ValueError:\n",
    "        print(\"Invalid input. Using default values.\")\n",
    "        hard_constraints = {'min_resilience_score': 60, 'min_profit_M': 210, 'max_co2_ktons': 8.0, 'min_ocean_share': 0.8}\n",
    "        soft_constraints = {'target_profit_M': 220, 'target_co2_ktons': 7.5}\n",
    "\n",
    "    return {\"objective_weights\": weights, \"hard_constraints\": hard_constraints, \"soft_constraints\": soft_constraints}\n",
    "\n",
    "if __name__ == '__main__':\n",
    "    executive_inputs = get_interactive_inputs()\n",
    "    sde = StrategicDecisionEngine(executive_inputs)\n",
    "    sde.run()"
   ],
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "ORbPCgLZkSbz",
    "executionInfo": {
     "status": "ok",
     "timestamp": 1775293098672,
     "user_tz": 420,
     "elapsed": 40473,
     "user": {
      "displayName": "Rodrigo Zepeda Pérez",
      "userId": "14597575368001951544"
     }
    },
    "outputId": "7f26ceff-1207-43bd-ef58-2f9a0e90326c"
   },
   "execution_count": 16,
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "================================================================================\n",
      "Step 1: Choose a Strategic Profile\n",
      "--------------------------------------------------------------------------------\n",
      "  [1] Profit First\n",
      "  [2] Balanced Growth\n",
      "  [3] Resilience First\n",
      "Select a profile [1-3] or press Enter for custom: 2\n",
      "Selected 'Balanced Growth'.\n",
      "\n",
      "================================================================================\n",
      "Step 2: Define Constraints (Your Non-Negotiable Rules)\n",
      "--------------------------------------------------------------------------------\n",
      "  - Hard Constraint: Minimum Resilience Score (10=Low, 60=Med, 100=High, e.g., 60): 60\n",
      "  - Hard Constraint: Minimum Profit (in millions, e.g., 210): 210\n",
      "  - Soft Target:   Target Profit (in millions, e.g., 220): 220\n",
      "  - Hard Constraint: Maximum CO2 (in k-tons, e.g., 8.0): 17\n",
      "  - Soft Target:   Target CO2 (in k-tons, e.g., 7.5): 8\n",
      "  - Hard Constraint: Minimum Ocean Freight Share (decimal, e.g., 0.8): .8\n",
      "Analyzing 297 potential strategies across 5000 scenarios each...\n",
      "  - Evaluating strategy 297/297...\n",
      "Analysis complete.\n",
      "\n",
      "================================================================================\n",
      "** Executive Summary: Strategic Analysis for 2026 **\n",
      "================================================================================\n",
      "\n",
      "**Feasibility Check: Found 72 feasible strategies.**\n",
      "The top-ranked strategy is feasible in 100.0% of simulated scenarios.\n",
      "\n",
      "--------------------------------------------------------------------------------\n",
      "** Strategy Recommendations **\n",
      "--------------------------------------------------------------------------------\n",
      "\n",
      "--- Recommended Strategy (Highest Strategic Score) ---\n",
      "  - Profile: Expected Profit: $449.1M | Expected CO2: 7.71 k-tons | Resilience: 100/100\n",
      "\n",
      "  - Operational Plan:\n",
      "    - NA Market (69,375 units):\n",
      "        - Assemble In:  'Mexico'\n",
      "        - Ship Via:     100% Ocean, 0% Air\n",
      "    - APAC Market (53,958 units):\n",
      "        - Assemble In:  'Malaysia'\n",
      "        - Ship Via:     100% Ocean, 0% Air\n",
      "    - EU Market (30,833 units):\n",
      "        - Assemble In:  'US'\n",
      "        - Ship Via:     100% Ocean, 0% Air\n",
      "\n",
      "--- Most Profitable Strategy ---\n",
      "  **Warning:** This strategy is NOT feasible (violates constraints in 0.0% of scenarios).\n",
      "  - Profile: Expected Profit: $464.0M | Expected CO2: 7.71 k-tons | Resilience: 10/100\n",
      "\n",
      "  - Operational Plan:\n",
      "    - NA Market (69,375 units):\n",
      "        - Assemble In:  'Malaysia'\n",
      "        - Ship Via:     100% Ocean, 0% Air\n",
      "    - APAC Market (53,958 units):\n",
      "        - Assemble In:  'Malaysia'\n",
      "        - Ship Via:     100% Ocean, 0% Air\n",
      "    - EU Market (30,833 units):\n",
      "        - Assemble In:  'Malaysia'\n",
      "        - Ship Via:     100% Ocean, 0% Air\n",
      "\n",
      "--- Most Resilient Strategy ---\n",
      "  **Warning:** This strategy is NOT feasible (violates constraints in 100.0% of scenarios).\n",
      "  - Profile: Expected Profit: $377.0M | Expected CO2: 47.79 k-tons | Resilience: 100/100\n",
      "\n",
      "  - Operational Plan:\n",
      "    - NA Market (69,375 units):\n",
      "        - Assemble In:  'US'\n",
      "        - Ship Via:     0% Ocean, 100% Air\n",
      "    - APAC Market (53,958 units):\n",
      "        - Assemble In:  'Mexico'\n",
      "        - Ship Via:     0% Ocean, 100% Air\n",
      "    - EU Market (30,833 units):\n",
      "        - Assemble In:  'Malaysia'\n",
      "        - Ship Via:     0% Ocean, 100% Air\n"
     ]
    }
   ]
  },
  {
   "cell_type": "code",
   "source": [
    "import numpy as np\n",
    "import pandas as pd\n",
    "import itertools\n",
    "from collections import defaultdict\n",
    "import math\n",
    "\n",
    "# --- Configuration & Foundational Data ---\n",
    "N_SCENARIOS = 5000\n",
    "ACTUAL_GPU_SUPPLY_2026 = 185000\n",
    "GPUS_PER_SERVER = 1.2\n",
    "MAX_SERVERS_2026 = int(ACTUAL_GPU_SUPPLY_2026 / GPUS_PER_SERVER)\n",
    "\n",
    "# Financial & Operational Data\n",
    "GPU_BENCHMARK_COST = 30000\n",
    "OTHER_COMPONENTS_COST = 9840\n",
    "SERVER_REVENUE = 49090.91\n",
    "PLANT_FC = {'US': 7800e3, 'Mexico': 7200e3, 'Malaysia': 6900e3}\n",
    "SHIPPING_LANE_FC = {\n",
    "    ('US', 'NA'): 450e3, ('US', 'APAC'): 850e3, ('US', 'EU'): 650e3,\n",
    "    ('Mexico', 'NA'): 500e3, ('Mexico', 'APAC'): 900e3, ('Mexico', 'EU'): 700e3,\n",
    "    ('Malaysia', 'NA'): 700e3, ('Malaysia', 'APAC'): 500e3, ('Malaysia', 'EU'): 600e3,\n",
    "}\n",
    "LOGISTICS_VC = {'Ocean': 180, 'Air': 620}\n",
    "CO2_EMISSIONS = {'Ocean': 50, 'Air': 310} # In kg per unit\n",
    "\n",
    "# Simulation Assumptions\n",
    "DEMAND_BASELINE = 215000\n",
    "DEMAND_VOLATILITY = 0.20\n",
    "DEMAND_MIX = {'NA': 0.45, 'APAC': 0.35, 'EU': 0.20}\n",
    "CARBON_PRICE_RANGE = (50, 150)\n",
    "\n",
    "class StrategicDecisionEngine:\n",
    "    \"\"\"A complete engine for strategic simulation with per-market logistics.\"\"\"\n",
    "\n",
    "    def __init__(self, decision_inputs):\n",
    "        self.inputs = decision_inputs\n",
    "        self.all_strategy_results = []\n",
    "        self.feasible_strategies = []\n",
    "        self.infeasible_strategies = []\n",
    "        self.hard_constraints = self.inputs['hard_constraints']\n",
    "        self.soft_constraints = self.inputs['soft_constraints']\n",
    "        self.weights = self.inputs['objective_weights']\n",
    "\n",
    "    def _evaluate_single_strategy(self, strategy_def):\n",
    "        plant_map = strategy_def['plant_map']\n",
    "        logistics_plan = strategy_def['logistics_plan']\n",
    "        fixed_cost = sum(PLANT_FC[plant] for plant in set(plant_map.values()))\n",
    "        for region, plant in plant_map.items():\n",
    "            fixed_cost += SHIPPING_LANE_FC.get((plant, region), 0)\n",
    "\n",
    "        demand_std_dev = DEMAND_BASELINE * DEMAND_VOLATILITY\n",
    "        scenarios = []\n",
    "        for _ in range(N_SCENARIOS):\n",
    "            total_demand = np.random.normal(DEMAND_BASELINE, demand_std_dev)\n",
    "            units_sold_total = min(total_demand, MAX_SERVERS_2026)\n",
    "            total_profit, total_co2_k_tons = 0, 0\n",
    "\n",
    "            for region, mix_share in DEMAND_MIX.items():\n",
    "                units_for_region = units_sold_total * mix_share\n",
    "                logistics_mix = logistics_plan[region]\n",
    "                avg_logistics_vc = (logistics_mix['Ocean'] * LOGISTICS_VC['Ocean']) + (logistics_mix['Air'] * LOGISTICS_VC['Air'])\n",
    "                avg_co2_kg = (logistics_mix['Ocean'] * CO2_EMISSIONS['Ocean']) + (logistics_mix['Air'] * CO2_EMISSIONS['Air'])\n",
    "                carbon_price = np.random.uniform(*CARBON_PRICE_RANGE)\n",
    "                carbon_cost_per_unit = (avg_co2_kg / 1000) * carbon_price\n",
    "                total_vc_per_unit = (GPUS_PER_SERVER * GPU_BENCHMARK_COST) + OTHER_COMPONENTS_COST + avg_logistics_vc + carbon_cost_per_unit\n",
    "                total_profit += units_for_region * (SERVER_REVENUE - total_vc_per_unit)\n",
    "                total_co2_k_tons += (units_for_region * avg_co2_kg) / 1_000_000\n",
    "\n",
    "            total_profit -= fixed_cost\n",
    "            scenarios.append({'profit': total_profit, 'co2_k_tons': total_co2_k_tons, 'units_sold': units_sold_total})\n",
    "\n",
    "        scenario_df = pd.DataFrame(scenarios)\n",
    "        resilience_score = {1: 10, 2: 60, 3: 100}.get(len(set(plant_map.values())), 0)\n",
    "\n",
    "        return {\n",
    "            \"definition\": strategy_def, \"results_df\": scenario_df,\n",
    "            \"metrics\": {\"profit\": scenario_df['profit'].mean(), \"growth\": scenario_df['units_sold'].mean(),\n",
    "                        \"sustainability\": scenario_df['co2_k_tons'].mean(), \"resilience\": resilience_score}\n",
    "        }\n",
    "\n",
    "    def _check_feasibility_and_penalties(self, result):\n",
    "        df = result['results_df']\n",
    "        ocean_violation = any(result['definition']['logistics_plan'][r]['Ocean'] > self.hard_constraints['max_ocean_share'] for r in DEMAND_MIX)\n",
    "        violations = {\n",
    "            'profit': (df['profit'] < self.hard_constraints['min_profit_M'] * 1e6).mean(),\n",
    "            'co2': (df['co2_k_tons'] > self.hard_constraints['max_co2_ktons']).mean(),\n",
    "            'ocean': ocean_violation,\n",
    "            'resilience': (result['metrics']['resilience'] < self.hard_constraints['min_resilience_score'])\n",
    "        }\n",
    "        total_violation_prob = 1 - (1 - violations['profit']) * (1 - violations['co2'])\n",
    "        is_feasible = (total_violation_prob < 0.05) and not violations['ocean'] and not violations['resilience']\n",
    "        profit_shortfall = np.maximum(0, self.soft_constraints['target_profit_M'] * 1e6 - df['profit']).mean()\n",
    "        co2_excess = np.maximum(0, df['co2_k_tons'] - self.soft_constraints['target_co2_ktons']).mean()\n",
    "        total_penalty = (profit_shortfall / 1e6) + (co2_excess / 0.1)\n",
    "        result['feasibility'] = {'is_feasible': is_feasible, 'violation_probability': total_violation_prob, 'violations': violations}\n",
    "        result['penalty_score'] = total_penalty\n",
    "        return result\n",
    "\n",
    "    def _score_strategies(self):\n",
    "        if not self.feasible_strategies: return\n",
    "        min_vals = {key: min(s['metrics'][key] for s in self.feasible_strategies) for key in self.weights}\n",
    "        max_vals = {key: max(s['metrics'][key] for s in self.feasible_strategies) for key in self.weights}\n",
    "\n",
    "        for strat in self.feasible_strategies:\n",
    "            total_score = 0\n",
    "            for objective, weight in self.weights.items():\n",
    "                val = strat['metrics'][objective]\n",
    "                range_val = max_vals[objective] - min_vals[objective]\n",
    "                norm_score = 100 if range_val == 0 else 100 * (val - min_vals[objective]) / range_val\n",
    "                if objective == 'sustainability': norm_score = 100 - norm_score # Lower is better for CO2\n",
    "                total_score += norm_score * (weight / 100.0)\n",
    "            strat['strategic_score'] = total_score\n",
    "\n",
    "        self.feasible_strategies.sort(key=lambda x: x['strategic_score'], reverse=True)\n",
    "\n",
    "    def run(self):\n",
    "        plants, regions = ['US', 'Mexico', 'Malaysia'], ['NA', 'APAC', 'EU']\n",
    "        all_plant_maps = [dict(zip(regions, p)) for p in itertools.product(plants, repeat=len(regions))]\n",
    "        logistics_options = [{'Ocean': 0.1, 'Air': 0.9}, {'Ocean': 0.8, 'Air': 0.2}, {'Ocean': 1.0, 'Air': 0.0}]\n",
    "        logistics_combinations = list(itertools.product(logistics_options, repeat=len(regions)))\n",
    "        all_logistics_plans = [dict(zip(regions, combo)) for combo in logistics_combinations]\n",
    "        strategy_definitions = [{'plant_map': p_map, 'logistics_plan': l_plan} for p_map in all_plant_maps for l_plan in all_logistics_plans]\n",
    "\n",
    "        print(f\"\\nAnalyzing {len(strategy_definitions)} potential strategies...\")\n",
    "        for i, strat_def in enumerate(strategy_definitions):\n",
    "            print(f\"  - Evaluating strategy {i+1}/{len(strategy_definitions)}...\", end='\\r')\n",
    "            result = self._evaluate_single_strategy(strat_def)\n",
    "            result = self._check_feasibility_and_penalties(result)\n",
    "            (self.feasible_strategies if result['feasibility']['is_feasible'] else self.infeasible_strategies).append(result)\n",
    "\n",
    "        print(\"\\nAnalysis complete.\")\n",
    "        self._score_strategies()\n",
    "        self._display_report()\n",
    "\n",
    "    def _display_report(self):\n",
    "        print(\"\\n\" + \"=\"*80 + \"\\n** Executive Summary: Strategic Analysis for 2026 **\\n\" + \"=\"*80)\n",
    "        if not self.feasible_strategies:\n",
    "            print(\"\\n**Feasibility Check: No strategy was found that meets all hard constraints.**\\n\")\n",
    "            if self.infeasible_strategies: self._display_relaxation_advice()\n",
    "        else:\n",
    "            top_score = self.feasible_strategies[0]['strategic_score']\n",
    "            print(f\"\\n**Feasibility Check: Found {len(self.feasible_strategies)} feasible strategies.**\")\n",
    "            print(f\"The top-ranked strategy achieved a Strategic Score of {top_score:.1f}/100 based on your priorities.\\n\")\n",
    "\n",
    "        print(\"-\" * 80 + \"\\n** Strategy Recommendations **\\n\" + \"-\" * 80)\n",
    "        if self.feasible_strategies:\n",
    "            self._print_strategy_card(f\"Recommended Strategy (Score: {self.feasible_strategies[0]['strategic_score']:.1f})\", self.feasible_strategies[0])\n",
    "            if len(self.feasible_strategies) > 1:\n",
    "                self._print_strategy_card(f\"Alternative Strategy (Score: {self.feasible_strategies[1]['strategic_score']:.1f})\", self.feasible_strategies[1], is_alt=True)\n",
    "        elif self.infeasible_strategies:\n",
    "            best_infeasible = min(self.infeasible_strategies, key=lambda x: x['penalty_score'])\n",
    "            self._print_strategy_card(\"Least Infeasible Strategy (For Analysis)\", best_infeasible, is_alt=True)\n",
    "\n",
    "    def _print_strategy_card(self, title, strat, is_alt=False):\n",
    "        print(f\"\\n--- {title} ---\")\n",
    "        if not strat['feasibility']['is_feasible']: print(f\"  **Warning:** This strategy is NOT feasible.\")\n",
    "        metrics, definition = strat['metrics'], strat['definition']\n",
    "        print(f\"  - Profile: Profit: ${metrics['profit']/1e6:,.1f}M | CO2: {metrics['sustainability']:,.2f} k-tons | Resilience: {metrics['resilience']:.0f}/100\")\n",
    "        print(\"\\n  - Operational Plan:\")\n",
    "        for region in ['NA', 'APAC', 'EU']:\n",
    "            plant, logistics = definition['plant_map'][region], definition['logistics_plan'][region]\n",
    "            units = metrics['growth'] * DEMAND_MIX[region]\n",
    "            print(f\"    - {region} Market ({units:,.0f} units): Assemble in '{plant}', Ship via {logistics['Ocean']:.0%} Ocean / {logistics['Air']:.0%} Air\")\n",
    "\n",
    "    def _display_relaxation_advice(self):\n",
    "        print(\"-\" * 80 + \"\\n** Relaxation Engine: Path to a Feasible Solution **\\n\" + \"-\" * 80)\n",
    "        compliant_infeasible = [\n",
    "            s for s in self.infeasible_strategies\n",
    "            if not s['feasibility']['violations']['ocean'] and not s['feasibility']['violations']['resilience']\n",
    "        ]\n",
    "\n",
    "        if not compliant_infeasible:\n",
    "            print(\"No strategies could be found that satisfy even the basic Ocean Freight and Resilience constraints.\")\n",
    "            print(\"Consider relaxing one of these two fundamental business rules first.\")\n",
    "            return\n",
    "\n",
    "        best_infeasible = min(compliant_infeasible, key=lambda x: x['penalty_score'])\n",
    "        violations = best_infeasible['feasibility']['violations']\n",
    "        metrics = best_infeasible['metrics']\n",
    "\n",
    "        print(\"After enforcing your core Ocean and Resilience rules, no strategy was feasible.\")\n",
    "        print(\"Analysis of the 'closest compliant' strategy shows the following gaps:\")\n",
    "        recommendations = []\n",
    "\n",
    "        if violations['profit'] > 0.05:\n",
    "            required_M, actual_M = self.hard_constraints['min_profit_M'], metrics['profit'] / 1e6\n",
    "            gap_M = required_M - actual_M\n",
    "            if gap_M > 0:\n",
    "                new_profit_target = math.floor(actual_M)\n",
    "                recommendations.append(f\"  1. Lower the 'Minimum Profit' from ${required_M:,.1f}M to ${new_profit_target:,.1f}M.\")\n",
    "                print(f\"  - PROFIT: Minimum of ${required_M:,.1f}M was missed by ${gap_M:,.1f}M.\")\n",
    "\n",
    "        if violations['co2'] > 0.05:\n",
    "            required_ktons, actual_ktons = self.hard_constraints['max_co2_ktons'], metrics['sustainability']\n",
    "            gap_ktons = actual_ktons - required_ktons\n",
    "            if gap_ktons > 0:\n",
    "                new_co2_target = math.ceil(actual_ktons * 10) / 10\n",
    "                recommendations.append(f\"  2. Increase the 'Maximum CO2' from {required_ktons:,.2f} to {new_co2_target:,.2f} k-tons.\")\n",
    "                print(f\"  - CO2:    Maximum of {required_ktons:,.2f} k-tons was exceeded by {gap_ktons:,.2f} k-tons.\")\n",
    "\n",
    "        if recommendations:\n",
    "            print(\"\\n**Recommendation:** To achieve a feasible strategy while respecting your Ocean/Resilience rules,\")\n",
    "            print(\"you must make a trade-off. Re-run with ONE of the following adjustments:\")\n",
    "            for rec in recommendations:\n",
    "                print(rec)\n",
    "        else:\n",
    "             print(\"\\nNo single clear adjustment found. The closest strategy fails on a combination of factors.\")\n",
    "\n",
    "def get_interactive_inputs():\n",
    "    \"\"\"Guides the user through a questionnaire to build the decision_input dictionary.\"\"\"\n",
    "    profiles = {\n",
    "        \"1\": {\"name\": \"Profit First\", \"weights\": {\"profit\": 60, \"resilience\": 10, \"sustainability\": 30}},\n",
    "        \"2\": {\"name\": \"Balanced Growth\", \"weights\": {\"profit\": 40, \"resilience\": 30, \"sustainability\": 30}},\n",
    "        \"3\": {\"name\": \"Resilience First\", \"weights\": {\"profit\": 20, \"resilience\": 50, \"sustainability\": 30}},\n",
    "    }\n",
    "\n",
    "    print(\"=\"*80 + \"\\nStep 1: Choose a Strategic Profile\\n\" + \"-\"*80)\n",
    "    for key, val in profiles.items():\n",
    "        print(f\"  [{key}] {val['name']}\")\n",
    "    choice = input(\"Select a profile [1-3] or press Enter for custom: \")\n",
    "\n",
    "    if choice in profiles:\n",
    "        weights = profiles[choice]['weights']\n",
    "        print(f\"Selected '{profiles[choice]['name']}'.\")\n",
    "    else:\n",
    "        print(\"\\n-- Custom Priority Weights --\\nPlease allocate 100 points across the following objectives:\")\n",
    "        weights = {}\n",
    "        while True:\n",
    "            try:\n",
    "                weights['profit'] = int(input(f\"  - Points for Profit: \"))\n",
    "                weights['resilience'] = int(input(f\"  - Points for Resilience: \"))\n",
    "                weights['sustainability'] = int(input(f\"  - Points for Sustainability (Low CO2): \"))\n",
    "                if sum(weights.values()) == 100:\n",
    "                    break\n",
    "                else:\n",
    "                    print(f\"Error: Points sum to {sum(weights.values())}, not 100. Please try again.\\n\")\n",
    "            except ValueError:\n",
    "                print(\"Error: Please enter whole numbers.\\n\")\n",
    "\n",
    "    print(\"\\n\" + \"=\"*80 + \"\\nStep 2: Define Constraints (Your Non-Negotiable Rules)\\n\" + \"-\"*80)\n",
    "    hard_constraints, soft_constraints = {}, {}\n",
    "    try:\n",
    "        hard_constraints['min_resilience_score'] = int(input(\"  - Min Resilience Score (10, 60, 100): \"))\n",
    "        hard_constraints['min_profit_M'] = float(input(\"  - Min Profit (in millions): \"))\n",
    "        soft_constraints['target_profit_M'] = float(input(\"  - Target Profit (in millions): \"))\n",
    "        hard_constraints['max_co2_ktons'] = float(input(\"  - Max CO2 (in k-tons): \"))\n",
    "        soft_constraints['target_co2_ktons'] = float(input(\"  - Target CO2 (in k-tons): \"))\n",
    "        hard_constraints['max_ocean_share'] = float(input(\"  - Max Ocean Freight Share per Market (0.0 to 1.0): \"))\n",
    "    except ValueError:\n",
    "        print(\"\\nInvalid input. Using default values for demonstration.\")\n",
    "        hard_constraints = {'min_resilience_score': 60, 'min_profit_M': 210, 'max_co2_ktons': 8.5, 'max_ocean_share': 0.8}\n",
    "        soft_constraints = {'target_profit_M': 220, 'target_co2_ktons': 8.0}\n",
    "\n",
    "    return {\"objective_weights\": weights, \"hard_constraints\": hard_constraints, \"soft_constraints\": soft_constraints}\n",
    "\n",
    "if __name__ == '__main__':\n",
    "    executive_inputs = get_interactive_inputs()\n",
    "    sde = StrategicDecisionEngine(executive_inputs)\n",
    "    sde.run()"
   ],
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "mSdsoG9fLXGH",
    "executionInfo": {
     "status": "ok",
     "timestamp": 1775321857367,
     "user_tz": 420,
     "elapsed": 100873,
     "user": {
      "displayName": "Rodrigo Zepeda Pérez",
      "userId": "14597575368001951544"
     }
    },
    "outputId": "f57094ee-432c-47a1-fc9b-f262216f62e6"
   },
   "execution_count": 1,
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "================================================================================\n",
      "Step 1: Choose a Strategic Profile\n",
      "--------------------------------------------------------------------------------\n",
      "  [1] Profit First\n",
      "  [2] Balanced Growth\n",
      "  [3] Resilience First\n",
      "Select a profile [1-3] or press Enter for custom: 2\n",
      "Selected 'Balanced Growth'.\n",
      "\n",
      "================================================================================\n",
      "Step 2: Define Constraints (Your Non-Negotiable Rules)\n",
      "--------------------------------------------------------------------------------\n",
      "  - Min Resilience Score (10, 60, 100): 60\n",
      "  - Min Profit (in millions): 200\n",
      "  - Target Profit (in millions): 220\n",
      "  - Max CO2 (in k-tons): 18\n",
      "  - Target CO2 (in k-tons): 9\n",
      "  - Max Ocean Freight Share per Market (0.0 to 1.0): .8\n",
      "\n",
      "Analyzing 729 potential strategies...\n",
      "  - Evaluating strategy 729/729...\n",
      "Analysis complete.\n",
      "\n",
      "================================================================================\n",
      "** Executive Summary: Strategic Analysis for 2026 **\n",
      "================================================================================\n",
      "\n",
      "**Feasibility Check: Found 24 feasible strategies.**\n",
      "The top-ranked strategy achieved a Strategic Score of 62.7/100 based on your priorities.\n",
      "\n",
      "--------------------------------------------------------------------------------\n",
      "** Strategy Recommendations **\n",
      "--------------------------------------------------------------------------------\n",
      "\n",
      "--- Recommended Strategy (Score: 62.7) ---\n",
      "  - Profile: Profit: $436.5M | CO2: 15.55 k-tons | Resilience: 60/100\n",
      "\n",
      "  - Operational Plan:\n",
      "    - NA Market (68,589 units): Assemble in 'US', Ship via 80% Ocean / 20% Air\n",
      "    - APAC Market (53,347 units): Assemble in 'US', Ship via 80% Ocean / 20% Air\n",
      "    - EU Market (30,484 units): Assemble in 'Malaysia', Ship via 80% Ocean / 20% Air\n",
      "\n",
      "--- Alternative Strategy (Score: 62.3) ---\n",
      "  - Profile: Profit: $437.3M | CO2: 15.55 k-tons | Resilience: 60/100\n",
      "\n",
      "  - Operational Plan:\n",
      "    - NA Market (68,613 units): Assemble in 'Malaysia', Ship via 80% Ocean / 20% Air\n",
      "    - APAC Market (53,366 units): Assemble in 'Malaysia', Ship via 80% Ocean / 20% Air\n",
      "    - EU Market (30,495 units): Assemble in 'Mexico', Ship via 80% Ocean / 20% Air\n"
     ]
    }
   ]
  },
  {
   "cell_type": "code",
   "source": [
    "import numpy as np\n",
    "import pandas as pd\n",
    "import itertools\n",
    "from collections import defaultdict\n",
    "import math\n",
    "\n",
    "# ==============================================================================\n",
    "# --- Configuration & Foundational Data ---\n",
    "# This section contains all the core assumptions and data points for the simulation.\n",
    "# ==============================================================================\n",
    "\n",
    "# --- Simulation Control ---\n",
    "N_SCENARIOS = 5000  # Number of Monte Carlo simulations to run for each strategy.\n",
    "\n",
    "# --- Production & Supply Chain Data ---\n",
    "ACTUAL_GPU_SUPPLY_2026 = 185000      # Total number of high-end GPUs available for the year.\n",
    "GPUS_PER_SERVER = 1.2               # Average number of GPUs used per server unit.\n",
    "MAX_SERVERS_2026 = int(ACTUAL_GPU_SUPPLY_2026 / GPUS_PER_SERVER) # The absolute maximum number of servers we can produce.\n",
    "\n",
    "# --- Financial Data ---\n",
    "GPU_BENCHMARK_COST = 30000          # Cost of GPUs per server.\n",
    "OTHER_COMPONENTS_COST = 9840        # Cost of all other components (CPU, RAM, chassis, etc.) per server.\n",
    "SERVER_REVENUE = 49090.91           # Revenue generated from selling one server.\n",
    "\n",
    "# --- Fixed Costs ---\n",
    "# Annual fixed costs associated with operating each manufacturing plant.\n",
    "PLANT_FC = {'US': 7800e3, 'Mexico': 7200e3, 'Malaysia': 6900e3}\n",
    "# Annual fixed costs for maintaining a logistics channel from a plant to a market region.\n",
    "SHIPPING_LANE_FC = {\n",
    "    ('US', 'NA'): 450e3, ('US', 'APAC'): 850e3, ('US', 'EU'): 650e3,\n",
    "    ('Mexico', 'NA'): 500e3, ('Mexico', 'APAC'): 900e3, ('Mexico', 'EU'): 700e3,\n",
    "    ('Malaysia', 'NA'): 700e3, ('Malaysia', 'APAC'): 500e3, ('Malaysia', 'EU'): 600e3,\n",
    "}\n",
    "\n",
    "# --- Variable Costs & Environmental Impact ---\n",
    "# Variable costs for shipping one server unit by different methods.\n",
    "LOGISTICS_VC = {'Ocean': 180, 'Air': 620}\n",
    "# CO2 emissions in kilograms for shipping one server unit. Note the high impact of air freight.\n",
    "CO2_EMISSIONS = {'Ocean': 50, 'Air': 310}\n",
    "\n",
    "# --- Market & Economic Assumptions ---\n",
    "DEMAND_BASELINE = 215000            # Expected average total market demand for servers in 2026.\n",
    "DEMAND_VOLATILITY = 0.20            # The standard deviation of demand, as a percentage of the baseline.\n",
    "# The geographical mix of total demand. Must sum to 1.0.\n",
    "DEMAND_MIX = {'NA': 0.45, 'APAC': 0.35, 'EU': 0.20}\n",
    "# The potential range for the price of carbon in $/ton. This simulates regulatory uncertainty.\n",
    "CARBON_PRICE_RANGE = (50, 150)\n",
    "\n",
    "class StrategicDecisionEngine:\n",
    "    \"\"\"\n",
    "    A comprehensive simulation engine to analyze and recommend global supply chain strategies.\n",
    "\n",
    "    This class takes high-level business objectives and constraints, simulates thousands of\n",
    "    potential futures for hundreds of different strategies, and provides a clear, data-driven\n",
    "    recommendation. It helps answer the question: \"What is the best way to structure our\n",
    "    global operations to maximize profit, resilience, and sustainability, given future\n",
    "    uncertainty?\"\n",
    "    \"\"\"\n",
    "\n",
    "    def __init__(self, decision_inputs):\n",
    "        \"\"\"Initializes the engine with the user's strategic priorities and rules.\"\"\"\n",
    "        self.inputs = decision_inputs\n",
    "        self.all_strategy_results = []\n",
    "        self.feasible_strategies = []\n",
    "        self.infeasible_strategies = []\n",
    "        # Extract user-defined rules and priorities for easy access.\n",
    "        self.hard_constraints = self.inputs['hard_constraints']\n",
    "        self.soft_constraints = self.inputs['soft_constraints']\n",
    "        self.weights = self.inputs['objective_weights']\n",
    "\n",
    "    def _evaluate_single_strategy(self, strategy_def):\n",
    "        \"\"\"\n",
    "        Runs a full Monte Carlo simulation for a single proposed strategy.\n",
    "\n",
    "        Args:\n",
    "            strategy_def (dict): A dictionary defining the strategy, containing:\n",
    "                - 'plant_map': Which plant serves which market (e.g., {'NA': 'Mexico', ...}).\n",
    "                - 'logistics_plan': The Ocean/Air shipping mix for each market.\n",
    "\n",
    "        Returns:\n",
    "            dict: A dictionary containing the strategy's definition, raw simulation results,\n",
    "                  and key performance metrics (profit, CO2, etc.).\n",
    "        \"\"\"\n",
    "        plant_map = strategy_def['plant_map']\n",
    "        logistics_plan = strategy_def['logistics_plan']\n",
    "\n",
    "        # --- Calculate Total Fixed Costs for the Strategy ---\n",
    "        # 1. Add plant costs (only for unique plants used in the strategy).\n",
    "        fixed_cost = sum(PLANT_FC[plant] for plant in set(plant_map.values()))\n",
    "        # 2. Add the costs for each active shipping lane.\n",
    "        for region, plant in plant_map.items():\n",
    "            fixed_cost += SHIPPING_LANE_FC.get((plant, region), 0)\n",
    "\n",
    "        # --- Run the Monte Carlo Simulation ---\n",
    "        demand_std_dev = DEMAND_BASELINE * DEMAND_VOLATILITY\n",
    "        scenarios = []\n",
    "        for _ in range(N_SCENARIOS):\n",
    "            # Simulate total demand for this one scenario (future).\n",
    "            total_demand = np.random.normal(DEMAND_BASELINE, demand_std_dev)\n",
    "            # We can't sell more servers than we can produce.\n",
    "            units_sold_total = min(total_demand, MAX_SERVERS_2026)\n",
    "\n",
    "            total_profit = 0\n",
    "            total_co2_k_tons = 0\n",
    "\n",
    "            # Calculate profit and CO2 contributions from each market region.\n",
    "            for region, mix_share in DEMAND_MIX.items():\n",
    "                units_for_region = units_sold_total * mix_share\n",
    "                logistics_mix = logistics_plan[region]  # Get the specific logistics mix for this region.\n",
    "\n",
    "                # Calculate the weighted average variable costs and emissions for this region.\n",
    "                avg_logistics_vc = (logistics_mix['Ocean'] * LOGISTICS_VC['Ocean']) + (logistics_mix['Air'] * LOGISTICS_VC['Air'])\n",
    "                avg_co2_kg = (logistics_mix['Ocean'] * CO2_EMISSIONS['Ocean']) + (logistics_mix['Air'] * CO2_EMISSIONS['Air'])\n",
    "\n",
    "                # Simulate a random carbon price for this scenario to model regulatory risk.\n",
    "                carbon_price = np.random.uniform(*CARBON_PRICE_RANGE)\n",
    "                # Calculate the carbon tax cost per server.\n",
    "                carbon_cost_per_unit = (avg_co2_kg / 1000) * carbon_price # Convert kg to tons for pricing.\n",
    "\n",
    "                # Calculate the total variable cost for one server shipped to this region.\n",
    "                total_vc_per_unit = (GPUS_PER_SERVER * GPU_BENCHMARK_COST) + OTHER_COMPONENTS_COST + avg_logistics_vc + carbon_cost_per_unit\n",
    "\n",
    "                # Calculate the total profit and CO2 for this region and add to the totals.\n",
    "                total_profit += units_for_region * (SERVER_REVENUE - total_vc_per_unit)\n",
    "                total_co2_k_tons += (units_for_region * avg_co2_kg) / 1_000_000 # Convert kg to kilo-tons.\n",
    "\n",
    "            # Subtract the total fixed costs at the end to get the final profit for this scenario.\n",
    "            total_profit -= fixed_cost\n",
    "\n",
    "            scenarios.append({'profit': total_profit, 'co2_k_tons': total_co2_k_tons, 'units_sold': units_sold_total})\n",
    "\n",
    "        # --- Aggregate Simulation Results ---\n",
    "        scenario_df = pd.DataFrame(scenarios)\n",
    "        # Calculate resilience score: more plants = more resilient.\n",
    "        resilience_score = {1: 10, 2: 60, 3: 100}.get(len(set(plant_map.values())), 0)\n",
    "\n",
    "        return {\n",
    "            \"definition\": strategy_def,\n",
    "            \"results_df\": scenario_df,\n",
    "            \"metrics\": {\n",
    "                \"profit\": scenario_df['profit'].mean(),\n",
    "                \"growth\": scenario_df['units_sold'].mean(),\n",
    "                \"sustainability\": scenario_df['co2_k_tons'].mean(),\n",
    "                \"resilience\": resilience_score\n",
    "            }\n",
    "        }\n",
    "\n",
    "    def _check_feasibility_and_penalties(self, result):\n",
    "        \"\"\"\n",
    "        Checks if a simulated strategy meets the user's 'hard constraints'.\n",
    "\n",
    "        Args:\n",
    "            result (dict): The output from the `_evaluate_single_strategy` method.\n",
    "\n",
    "        Returns:\n",
    "            dict: The original result dictionary, updated with feasibility information\n",
    "                  and a 'penalty_score' for ranking infeasible options.\n",
    "        \"\"\"\n",
    "        df = result['results_df']\n",
    "\n",
    "        # Check for violations of the hard constraints.\n",
    "        # 'ocean' and 'resilience' are simple checks on the strategy's definition.\n",
    "        ocean_violation = any(result['definition']['logistics_plan'][r]['Ocean'] > self.hard_constraints['max_ocean_share'] for r in DEMAND_MIX)\n",
    "        resilience_violation = result['metrics']['resilience'] < self.hard_constraints['min_resilience_score']\n",
    "\n",
    "        # For 'profit' and 'co2', we calculate the *probability* of violating the constraint across all scenarios.\n",
    "        profit_violation_prob = (df['profit'] < self.hard_constraints['min_profit_M'] * 1e6).mean()\n",
    "        co2_violation_prob = (df['co2_k_tons'] > self.hard_constraints['max_co2_ktons']).mean()\n",
    "\n",
    "        violations_summary = {\n",
    "            'profit': profit_violation_prob, 'co2': co2_violation_prob,\n",
    "            'ocean': ocean_violation, 'resilience': resilience_violation\n",
    "        }\n",
    "\n",
    "        # A strategy is considered 'feasible' if it doesn't violate the simple rules AND the probability\n",
    "        # of violating the financial/CO2 rules is less than 5%.\n",
    "        is_feasible = (profit_violation_prob < 0.05) and (co2_violation_prob < 0.05) and not ocean_violation and not resilience_violation\n",
    "\n",
    "        # Calculate a penalty score for ranking infeasible strategies (how badly did it miss?).\n",
    "        profit_shortfall = np.maximum(0, self.soft_constraints['target_profit_M'] * 1e6 - df['profit']).mean()\n",
    "        co2_excess = np.maximum(0, df['co2_k_tons'] - self.soft_constraints['target_co2_ktons']).mean()\n",
    "        total_penalty = (profit_shortfall / 1e6) + (co2_excess / 0.1)\n",
    "\n",
    "        result['feasibility'] = {'is_feasible': is_feasible, 'violations': violations_summary}\n",
    "        result['penalty_score'] = total_penalty\n",
    "        return result\n",
    "\n",
    "    def _score_strategies(self):\n",
    "        \"\"\"Calculates a weighted 'Strategic Score' for all feasible strategies.\"\"\"\n",
    "        if not self.feasible_strategies: return\n",
    "\n",
    "        # To score fairly, we first normalize each metric from 0 to 100.\n",
    "        min_vals = {key: min(s['metrics'][key] for s in self.feasible_strategies) for key in self.weights}\n",
    "        max_vals = {key: max(s['metrics'][key] for s in self.feasible_strategies) for key in self.weights}\n",
    "\n",
    "        for strat in self.feasible_strategies:\n",
    "            total_score = 0\n",
    "            for objective, weight in self.weights.items():\n",
    "                val = strat['metrics'][objective]\n",
    "                range_val = max_vals[objective] - min_vals[objective]\n",
    "                # Normalize the value to a 0-100 scale.\n",
    "                norm_score = 100 if range_val == 0 else 100 * (val - min_vals[objective]) / range_val\n",
    "                # For sustainability, lower is better, so we flip the score.\n",
    "                if objective == 'sustainability': norm_score = 100 - norm_score\n",
    "                # Apply the user's chosen weights.\n",
    "                total_score += norm_score * (weight / 100.0)\n",
    "            strat['strategic_score'] = total_score\n",
    "\n",
    "        # Sort the feasible strategies by their final score, from best to worst.\n",
    "        self.feasible_strategies.sort(key=lambda x: x['strategic_score'], reverse=True)\n",
    "\n",
    "    def run(self):\n",
    "        \"\"\"\n",
    "        Executes the entire end-to-end analysis.\n",
    "        This is the main driver function of the class.\n",
    "        \"\"\"\n",
    "        plants, regions = ['US', 'Mexico', 'Malaysia'], ['NA', 'APAC', 'EU']\n",
    "\n",
    "        # --- Generate All Possible Strategies ---\n",
    "        # 1. Generate every possible combination of plants serving regions (3^3 = 27).\n",
    "        all_plant_maps = [dict(zip(regions, p)) for p in itertools.product(plants, repeat=len(regions))]\n",
    "\n",
    "        # 2. Define the discrete logistics options we want to test.\n",
    "        logistics_options = [{'Ocean': 0.1, 'Air': 0.9}, {'Ocean': 0.8, 'Air': 0.2}, {'Ocean': 1.0, 'Air': 0.0}]\n",
    "        # 3. Generate every combination of these logistics options across the 3 regions (3^3 = 27).\n",
    "        all_logistics_plans = [dict(zip(regions, combo)) for combo in itertools.product(logistics_options, repeat=len(regions))]\n",
    "\n",
    "        # 4. Create the final list of all strategies to test (27 * 27 = 729).\n",
    "        strategy_definitions = [{'plant_map': p_map, 'logistics_plan': l_plan} for p_map in all_plant_maps for l_plan in all_logistics_plans]\n",
    "\n",
    "        # --- Evaluate and Classify Each Strategy ---\n",
    "        print(f\"\\nAnalyzing {len(strategy_definitions)} potential strategies...\")\n",
    "        for i, strat_def in enumerate(strategy_definitions):\n",
    "            print(f\"  - Evaluating strategy {i+1}/{len(strategy_definitions)}...\", end='\\r')\n",
    "            result = self._evaluate_single_strategy(strat_def)\n",
    "            result = self._check_feasibility_and_penalties(result)\n",
    "\n",
    "            # Add the result to the appropriate list based on its feasibility.\n",
    "            if result['feasibility']['is_feasible']:\n",
    "                self.feasible_strategies.append(result)\n",
    "            else:\n",
    "                self.infeasible_strategies.append(result)\n",
    "\n",
    "        print(\"\\nAnalysis complete.\")\n",
    "\n",
    "        # Score and rank the results, then display the final report.\n",
    "        self._score_strategies()\n",
    "        self._display_report()\n",
    "\n",
    "    def _display_report(self):\n",
    "        \"\"\"Prints the final, formatted executive summary and recommendations.\"\"\"\n",
    "        print(\"\\n\" + \"=\"*80 + \"\\n** Executive Summary: Strategic Analysis for 2026 **\\n\" + \"=\"*80)\n",
    "\n",
    "        if not self.feasible_strategies:\n",
    "            print(\"\\n**Feasibility Check: No strategy was found that meets all hard constraints.**\\n\")\n",
    "            if self.infeasible_strategies:\n",
    "                self._display_relaxation_advice() # Provide advice on how to find a feasible solution.\n",
    "        else:\n",
    "            top_score = self.feasible_strategies[0]['strategic_score']\n",
    "            print(f\"\\n**Feasibility Check: Found {len(self.feasible_strategies)} feasible strategies.**\")\n",
    "            print(f\"The top-ranked strategy achieved a Strategic Score of {top_score:.1f}/100 based on your priorities.\\n\")\n",
    "\n",
    "        print(\"-\" * 80 + \"\\n** Strategy Recommendations **\\n\" + \"-\" * 80)\n",
    "        if self.feasible_strategies:\n",
    "            # Show the top-ranked strategy.\n",
    "            self._print_strategy_card(f\"Recommended Strategy (Score: {self.feasible_strategies[0]['strategic_score']:.1f})\", self.feasible_strategies[0])\n",
    "            # If available, show the next-best option as an alternative.\n",
    "            if len(self.feasible_strategies) > 1:\n",
    "                self._print_strategy_card(f\"Alternative Strategy (Score: {self.feasible_strategies[1]['strategic_score']:.1f})\", self.feasible_strategies[1], is_alt=True)\n",
    "        elif self.infeasible_strategies:\n",
    "            # If no strategies were feasible, show the \"least bad\" one for analysis.\n",
    "            best_infeasible = min(self.infeasible_strategies, key=lambda x: x['penalty_score'])\n",
    "            self._print_strategy_card(\"Least Infeasible Strategy (For Analysis)\", best_infeasible, is_alt=True)\n",
    "\n",
    "    def _print_strategy_card(self, title, strat, is_alt=False):\n",
    "        \"\"\"Prints a formatted summary card for a single strategy.\"\"\"\n",
    "        print(f\"\\n--- {title} ---\")\n",
    "        if not strat['feasibility']['is_feasible']:\n",
    "             print(f\"  **Warning:** This strategy is NOT feasible and is shown for analysis only.\")\n",
    "\n",
    "        metrics, definition = strat['metrics'], strat['definition']\n",
    "        print(f\"  - Profile: Profit: ${metrics['profit']/1e6:,.1f}M | CO2: {metrics['sustainability']:,.2f} k-tons | Resilience: {metrics['resilience']:.0f}/100\")\n",
    "        print(\"\\n  - Operational Plan:\")\n",
    "        for region in ['NA', 'APAC', 'EU']:\n",
    "            plant, logistics = definition['plant_map'][region], definition['logistics_plan'][region]\n",
    "            units = metrics['growth'] * DEMAND_MIX[region]\n",
    "            print(f\"    - {region} Market ({units:,.0f} units): Assemble in '{plant}', Ship via {logistics['Ocean']:.0%} Ocean / {logistics['Air']:.0%} Air\")\n",
    "\n",
    "    def _display_relaxation_advice(self):\n",
    "        \"\"\"\n",
    "        Provides intelligent advice if no feasible solutions were found.\n",
    "        This function prioritizes the user's agility/resilience constraints.\n",
    "        \"\"\"\n",
    "        print(\"-\" * 80 + \"\\n** Relaxation Engine: Path to a Feasible Solution **\\n\" + \"-\" * 80)\n",
    "\n",
    "        # 1. Filter out any strategies that already violate the most important business rules (ocean share and resilience).\n",
    "        compliant_infeasible = [\n",
    "            s for s in self.infeasible_strategies\n",
    "            if not s['feasibility']['violations']['ocean'] and not s['feasibility']['violations']['resilience']\n",
    "        ]\n",
    "\n",
    "        if not compliant_infeasible:\n",
    "            print(\"No strategies could be found that satisfy even the basic Ocean Freight and Resilience constraints.\")\n",
    "            print(\"This suggests the constraints are fundamentally too tight. Consider relaxing one of these first.\")\n",
    "            return\n",
    "\n",
    "        # 2. From the compliant-but-infeasible pool, find the 'best' one (the one that failed by the smallest margin on profit/CO2).\n",
    "        best_infeasible = min(compliant_infeasible, key=lambda x: x['penalty_score'])\n",
    "\n",
    "        violations = best_infeasible['feasibility']['violations']\n",
    "        metrics = best_infeasible['metrics']\n",
    "\n",
    "        print(\"After enforcing your core Ocean and Resilience rules, no strategy was feasible.\")\n",
    "        print(\"Analysis of the 'closest compliant' strategy shows the following financial gaps:\")\n",
    "        recommendations = []\n",
    "\n",
    "        # 3. Generate advice based on the gaps in this 'best compliant' strategy.\n",
    "        if violations['profit'] > 0.05:\n",
    "            required_M, actual_M = self.hard_constraints['min_profit_M'], metrics['profit'] / 1e6\n",
    "            gap_M = required_M - actual_M\n",
    "            if gap_M > 0:\n",
    "                new_profit_target = math.floor(actual_M)\n",
    "                recommendations.append(f\"  1. Lower the 'Minimum Profit' from ${required_M:,.1f}M to ${new_profit_target:,.1f}M.\")\n",
    "                print(f\"  - PROFIT: Minimum of ${required_M:,.1f}M was missed by ${gap_M:,.1f}M.\")\n",
    "\n",
    "        if violations['co2'] > 0.05:\n",
    "            required_ktons, actual_ktons = self.hard_constraints['max_co2_ktons'], metrics['sustainability']\n",
    "            gap_ktons = actual_ktons - required_ktons\n",
    "            if gap_ktons > 0:\n",
    "                new_co2_target = math.ceil(actual_ktons * 10) / 10\n",
    "                recommendations.append(f\"  2. Increase the 'Maximum CO2' from {required_ktons:,.2f} to {new_co2_target:,.2f} k-tons.\")\n",
    "                print(f\"  - CO2:    Maximum of {required_ktons:,.2f} k-tons was exceeded by {gap_ktons:,.2f} k-tons.\")\n",
    "\n",
    "        if recommendations:\n",
    "            print(\"\\n**Recommendation:** To find a feasible solution while respecting your Ocean/Resilience rules,\")\n",
    "            print(\"you must make a trade-off. Re-run with ONE of the following adjustments:\")\n",
    "            for rec in recommendations:\n",
    "                print(rec)\n",
    "        else:\n",
    "             print(\"\\nNo single clear adjustment found. The closest strategy fails on a combination of factors.\")\n",
    "\n",
    "def get_interactive_inputs():\n",
    "    \"\"\"Guides the user through a questionnaire to build the decision_input dictionary.\"\"\"\n",
    "    # Define the preset profiles for easy selection.\n",
    "    profiles = {\n",
    "        \"1\": {\"name\": \"Profit First\", \"weights\": {\"profit\": 60, \"resilience\": 10, \"sustainability\": 30}},\n",
    "        \"2\": {\"name\": \"Balanced Growth\", \"weights\": {\"profit\": 40, \"resilience\": 30, \"sustainability\": 30}},\n",
    "        \"3\": {\"name\": \"Resilience First\", \"weights\": {\"profit\": 20, \"resilience\": 50, \"sustainability\": 30}},\n",
    "    }\n",
    "\n",
    "    # --- Step 1: Get Strategic Priorities (Weights) ---\n",
    "    print(\"=\"*80 + \"\\nStep 1: Choose a Strategic Profile\\n\" + \"-\"*80)\n",
    "    for key, val in profiles.items():\n",
    "        print(f\"  [{key}] {val['name']}\")\n",
    "    choice = input(\"Select a profile [1-3] or press Enter for custom: \")\n",
    "\n",
    "    if choice in profiles:\n",
    "        weights = profiles[choice]['weights']\n",
    "        print(f\"Selected '{profiles[choice]['name']}'.\")\n",
    "    else:\n",
    "        # Custom weights input if the user doesn't choose a preset.\n",
    "        print(\"\\n-- Custom Priority Weights --\\nPlease allocate 100 points across the following objectives:\")\n",
    "        weights = {}\n",
    "        while True:\n",
    "            try:\n",
    "                weights['profit'] = int(input(f\"  - Points for Profit: \"))\n",
    "                weights['resilience'] = int(input(f\"  - Points for Resilience: \"))\n",
    "                weights['sustainability'] = int(input(f\"  - Points for Sustainability (Low CO2): \"))\n",
    "                if sum(weights.values()) == 100:\n",
    "                    break\n",
    "                else:\n",
    "                    print(f\"Error: Points sum to {sum(weights.values())}, not 100. Please try again.\\n\")\n",
    "            except ValueError:\n",
    "                print(\"Error: Please enter whole numbers.\\n\")\n",
    "\n",
    "    # --- Step 2: Get Hard and Soft Constraints ---\n",
    "    print(\"\\n\" + \"=\"*80 + \"\\nStep 2: Define Constraints (Your Non-Negotiable Rules)\\n\" + \"-\"*80)\n",
    "    hard_constraints, soft_constraints = {}, {}\n",
    "    try:\n",
    "        hard_constraints['min_resilience_score'] = int(input(\"  - Min Resilience Score (10, 60, 100): \"))\n",
    "        hard_constraints['min_profit_M'] = float(input(\"  - Min Profit (in millions): \"))\n",
    "        soft_constraints['target_profit_M'] = float(input(\"  - Target Profit (in millions): \"))\n",
    "        hard_constraints['max_co2_ktons'] = float(input(\"  - Max CO2 (in k-tons): \"))\n",
    "        soft_constraints['target_co2_ktons'] = float(input(\"  - Target CO2 (in k-tons): \"))\n",
    "        hard_constraints['max_ocean_share'] = float(input(\"  - Max Ocean Freight Share per Market (0.0 to 1.0): \"))\n",
    "    except ValueError:\n",
    "        print(\"\\nInvalid input. Using default values for demonstration.\")\n",
    "        hard_constraints = {'min_resilience_score': 60, 'min_profit_M': 210, 'max_co2_ktons': 8.5, 'max_ocean_share': 0.8}\n",
    "        soft_constraints = {'target_profit_M': 220, 'target_co2_ktons': 8.0}\n",
    "\n",
    "    # Return all the user inputs in a single dictionary.\n",
    "    return {\"objective_weights\": weights, \"hard_constraints\": hard_constraints, \"soft_constraints\": soft_constraints}\n",
    "\n",
    "if __name__ == '__main__':\n",
    "    \"\"\"\n",
    "    Main execution block. This code runs when the script is executed directly.\n",
    "    \"\"\"\n",
    "    # 1. Get all the necessary inputs from the user.\n",
    "    executive_inputs = get_interactive_inputs()\n",
    "    # 2. Create an instance of the decision engine with those inputs.\n",
    "    sde = StrategicDecisionEngine(executive_inputs)\n",
    "    # 3. Run the full analysis.\n",
    "    sde.run()"
   ],
   "metadata": {
    "id": "9kuPeQlZw37s"
   },
   "execution_count": null,
   "outputs": []
  }
 ]
}