{
 "nbformat": 4,
 "nbformat_minor": 5,
 "metadata": {
  "colab": {
   "provenance": []
  },
  "kernelspec": {
   "name": "python3",
   "display_name": "Python 3"
  },
  "language_info": {
   "name": "python"
  }
 },
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "title"
   },
   "source": "# 🏭 Üretim Planlama: MRP + Sonlu Çizelgeleme\n## Klasik Optimizasyon + Küçük Ölçekli Qiskit QAOA Örneği\n\n**Hedef kitle:** Üretim / ERP / QML öğrencileri  \n**Ortam:** Google Colab\n\n---\n\n### Yaklaşım (neden böyle?)\n\nTam zaman-indeksli Job Shop → QUBO dönüşümü **çok sayıda binary değişken** üretir (50–100+).  \nBu boyut `NumPyMinimumEigensolver` ve simülatör QAOA için pratik değildir ($2^{96}$ durum).\n\nBu notebook’ta:\n\n| Katman | Yöntem | Amaç |\n|--------|--------|------|\n| 1 | MRP + iş emirleri | Pedagojik senaryo |\n| 2 | **PuLP MIP** | Gerçekçi sonlu çizelgeleme (her zaman çalışır) |\n| 3 | **Küçük QUBO + QAOA** | Kuantum optimizasyon kavramını göstermek (≤ 12 değişken) |\n| 4 | Gantt + karşılaştırma | Sonuçların yorumu |\n",
   "id": "title"
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "section1"
   },
   "source": "## 1. Ortam Kurulumu\n",
   "id": "section1"
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "id": "setup",
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "executionInfo": {
     "status": "ok",
     "timestamp": 1786330687618,
     "user_tz": -180,
     "elapsed": 54940,
     "user": {
      "displayName": "Joseph Volkan ÖZCAN",
      "userId": "11389530967736397480"
     }
    },
    "outputId": "0a1c2756-a80e-42ba-9c4d-6152d033140d"
   },
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "Python: 3.12.13\n",
      "Mounted at /content/drive\n",
      "✅ Google Drive bağlandı\n",
      "\n",
      "📦 Paketler kuruluyor...\n",
      "\u001b[2K   \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m1.8/1.8 MB\u001b[0m \u001b[31m56.9 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
      "\u001b[?25h  Installing build dependencies ... \u001b[?25l\u001b[?25hdone\n",
      "  Getting requirements to build wheel ... \u001b[?25l\u001b[?25hdone\n",
      "  Preparing metadata (pyproject.toml) ... \u001b[?25l\u001b[?25hdone\n",
      "  Building wheel for docplex (pyproject.toml) ... \u001b[?25l\u001b[?25hdone\n",
      "\n",
      "✅ Kurulum tamamlandı.\n"
     ]
    }
   ],
   "source": "import sys\nprint(f\"Python: {sys.version.split()[0]}\")\n\ntry:\n    from google.colab import drive\n    drive.mount('/content/drive')\n    print(\"✅ Google Drive bağlandı\")\nexcept Exception as e:\n    print(f\"⚠️ Drive mount atlandı: {e}\")\n\nprint(\"\\n📦 Paketler kuruluyor...\")\n!pip install -q --upgrade pip\n!pip install -q qiskit qiskit-aer qiskit-algorithms qiskit-optimization\n!pip install -q qiskit-ibm-runtime\n!pip install -q pandas numpy matplotlib seaborn networkx pulp\n\nprint(\"\\n✅ Kurulum tamamlandı.\")\n",
   "id": "setup"
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "id": "imports",
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "executionInfo": {
     "status": "ok",
     "timestamp": 1786330690467,
     "user_tz": -180,
     "elapsed": 2408,
     "user": {
      "displayName": "Joseph Volkan ÖZCAN",
      "userId": "11389530967736397480"
     }
    },
    "outputId": "4c547493-d58d-4745-9c98-cf285c1ae8ea"
   },
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "✅ Kütüphaneler yüklendi.\n"
     ]
    }
   ],
   "source": "import numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\nimport matplotlib.patches as mpatches\nfrom collections import defaultdict\nimport warnings\nwarnings.filterwarnings(\"ignore\")\n\nimport pulp\n\nfrom qiskit_algorithms.minimum_eigensolvers import NumPyMinimumEigensolver, QAOA\nfrom qiskit_algorithms.optimizers import COBYLA\nfrom qiskit_optimization import QuadraticProgram\nfrom qiskit_optimization.algorithms import MinimumEigenOptimizer\nfrom qiskit_optimization.converters import QuadraticProgramToQubo\n\nplt.rcParams[\"figure.figsize\"] = (12, 5)\nprint(\"✅ Kütüphaneler yüklendi.\")\n",
   "id": "imports"
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "section2"
   },
   "source": "## 2. Üretim Senaryosu ve MRP\n\n**Firma:** MiniMotor A.Ş. – elektrik motor parçaları  \n**Makineler:** M1 (Torna), M2 (Freze), M3 (Montaj)  \n**İş emirleri (MRP çıktısı):** 3 job, her birinin operasyon rotası var.\n",
   "id": "section2"
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "id": "jobs_mrp",
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "executionInfo": {
     "status": "ok",
     "timestamp": 1786330690860,
     "user_tz": -180,
     "elapsed": 297,
     "user": {
      "displayName": "Joseph Volkan ÖZCAN",
      "userId": "11389530967736397480"
     }
    },
    "outputId": "3ea7d263-0ba0-48e2-d445-0999942149e0"
   },
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "=== İŞ EMİRLERİ (MRP Çıktısı) ===\n",
      "  Job A: M1(2h) → M2(3h) → M3(2h)  | Toplam: 7 saat\n",
      "  Job B: M1(3h) → M3(2h) → M2(2h)  | Toplam: 7 saat\n",
      "  Job C: M2(2h) → M1(2h) → M3(3h)  | Toplam: 7 saat\n",
      "\n",
      "=== MRP NET İHTİYAÇ ===\n",
      "Bileşen          Brüt     Stok      Net\n",
      "----------------------------------------\n",
      "Gövde              23        3       20\n",
      "Kapak              38       10       28\n",
      "Rotor              31        5       26\n",
      "Sensör              5        2        3\n"
     ]
    }
   ],
   "source": "jobs = {\n    \"A\": [(\"M1\", 2), (\"M2\", 3), (\"M3\", 2)],\n    \"B\": [(\"M1\", 3), (\"M3\", 2), (\"M2\", 2)],\n    \"C\": [(\"M2\", 2), (\"M1\", 2), (\"M3\", 3)],\n}\nmachines = [\"M1\", \"M2\", \"M3\"]\n\nprint(\"=== İŞ EMİRLERİ (MRP Çıktısı) ===\")\nfor jid, ops in jobs.items():\n    total = sum(t for _, t in ops)\n    route = \" → \".join([f\"{m}({t}h)\" for m, t in ops])\n    print(f\"  Job {jid}: {route}  | Toplam: {total} saat\")\n\n# Basit MRP net ihtiyaç\nbom = {\n    \"Motor_A\": {\"Gövde\": 1, \"Rotor\": 1, \"Kapak\": 2},\n    \"Motor_B\": {\"Gövde\": 1, \"Rotor\": 1, \"Kapak\": 2, \"Sensör\": 1},\n    \"Motor_C\": {\"Gövde\": 1, \"Rotor\": 2, \"Kapak\": 1},\n}\ndemand = {\"Motor_A\": 10, \"Motor_B\": 5, \"Motor_C\": 8}\ninventory = {\"Gövde\": 3, \"Rotor\": 5, \"Kapak\": 10, \"Sensör\": 2}\n\ngross = {}\nfor prod, qty in demand.items():\n    for comp, cqty in bom[prod].items():\n        gross[comp] = gross.get(comp, 0) + qty * cqty\n\nprint(\"\\n=== MRP NET İHTİYAÇ ===\")\nprint(f\"{'Bileşen':12s} {'Brüt':>8s} {'Stok':>8s} {'Net':>8s}\")\nprint(\"-\" * 40)\nfor comp in sorted(gross):\n    net = max(0, gross[comp] - inventory.get(comp, 0))\n    print(f\"{comp:12s} {gross[comp]:8d} {inventory.get(comp,0):8d} {net:8d}\")\n",
   "id": "jobs_mrp"
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "section3"
   },
   "source": "## 3. Sonlu Çizelgeleme – Klasik MIP (PuLP)\n\nJob Shop Scheduling:\n- Her işin operasyon sırası sabit (precedence)\n- Aynı makinede aynı anda tek iş (disjunctive)\n- Amaç: **Makespan** minimize\n",
   "id": "section3"
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "id": "pulp_solve",
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 379
    },
    "executionInfo": {
     "status": "ok",
     "timestamp": 1786330691309,
     "user_tz": -180,
     "elapsed": 445,
     "user": {
      "displayName": "Joseph Volkan ÖZCAN",
      "userId": "11389530967736397480"
     }
    },
    "outputId": "9a99f464-40b0-4d1c-8d48-4506842aab0f"
   },
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "=== PuLP MIP ÇİZELGE ===\n"
     ]
    },
    {
     "output_type": "display_data",
     "data": {
      "text/plain": [
       "  job  op machine  start  duration   end\n",
       "0   A   0      M1    0.0         2   2.0\n",
       "7   C   1      M1    2.0         2   4.0\n",
       "3   B   0      M1    4.0         3   7.0\n",
       "6   C   0      M2    0.0         2   2.0\n",
       "1   A   1      M2    2.0         3   5.0\n",
       "5   B   2      M2    9.0         2  11.0\n",
       "8   C   2      M3    4.0         3   7.0\n",
       "4   B   1      M3    7.0         2   9.0\n",
       "2   A   2      M3    9.0         2  11.0"
      ],
      "text/html": [
       "\n",
       "  <div id=\"df-8414b76b-1253-48dd-b18d-78e47acbd5d6\" class=\"colab-df-container\">\n",
       "    <div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>job</th>\n",
       "      <th>op</th>\n",
       "      <th>machine</th>\n",
       "      <th>start</th>\n",
       "      <th>duration</th>\n",
       "      <th>end</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>A</td>\n",
       "      <td>0</td>\n",
       "      <td>M1</td>\n",
       "      <td>0.0</td>\n",
       "      <td>2</td>\n",
       "      <td>2.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>7</th>\n",
       "      <td>C</td>\n",
       "      <td>1</td>\n",
       "      <td>M1</td>\n",
       "      <td>2.0</td>\n",
       "      <td>2</td>\n",
       "      <td>4.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>B</td>\n",
       "      <td>0</td>\n",
       "      <td>M1</td>\n",
       "      <td>4.0</td>\n",
       "      <td>3</td>\n",
       "      <td>7.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>6</th>\n",
       "      <td>C</td>\n",
       "      <td>0</td>\n",
       "      <td>M2</td>\n",
       "      <td>0.0</td>\n",
       "      <td>2</td>\n",
       "      <td>2.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>A</td>\n",
       "      <td>1</td>\n",
       "      <td>M2</td>\n",
       "      <td>2.0</td>\n",
       "      <td>3</td>\n",
       "      <td>5.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>5</th>\n",
       "      <td>B</td>\n",
       "      <td>2</td>\n",
       "      <td>M2</td>\n",
       "      <td>9.0</td>\n",
       "      <td>2</td>\n",
       "      <td>11.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>8</th>\n",
       "      <td>C</td>\n",
       "      <td>2</td>\n",
       "      <td>M3</td>\n",
       "      <td>4.0</td>\n",
       "      <td>3</td>\n",
       "      <td>7.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>B</td>\n",
       "      <td>1</td>\n",
       "      <td>M3</td>\n",
       "      <td>7.0</td>\n",
       "      <td>2</td>\n",
       "      <td>9.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>A</td>\n",
       "      <td>2</td>\n",
       "      <td>M3</td>\n",
       "      <td>9.0</td>\n",
       "      <td>2</td>\n",
       "      <td>11.0</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>\n",
       "    <div class=\"colab-df-buttons\">\n",
       "\n",
       "  <div class=\"colab-df-container\">\n",
       "    <button class=\"colab-df-convert\" onclick=\"convertToInteractive('df-8414b76b-1253-48dd-b18d-78e47acbd5d6')\"\n",
       "            title=\"Convert this dataframe to an interactive table.\"\n",
       "            style=\"display:none;\">\n",
       "\n",
       "  <svg xmlns=\"http://www.w3.org/2000/svg\" height=\"24px\" viewBox=\"0 -960 960 960\">\n",
       "    <path d=\"M120-120v-720h720v720H120Zm60-500h600v-160H180v160Zm220 220h160v-160H400v160Zm0 220h160v-160H400v160ZM180-400h160v-160H180v160Zm440 0h160v-160H620v160ZM180-180h160v-160H180v160Zm440 0h160v-160H620v160Z\"/>\n",
       "  </svg>\n",
       "    </button>\n",
       "\n",
       "  <style>\n",
       "    .colab-df-container {\n",
       "      display:flex;\n",
       "      gap: 12px;\n",
       "    }\n",
       "\n",
       "    .colab-df-convert {\n",
       "      background-color: #E8F0FE;\n",
       "      border: none;\n",
       "      border-radius: 50%;\n",
       "      cursor: pointer;\n",
       "      display: none;\n",
       "      fill: #1967D2;\n",
       "      height: 32px;\n",
       "      padding: 0 0 0 0;\n",
       "      width: 32px;\n",
       "    }\n",
       "\n",
       "    .colab-df-convert:hover {\n",
       "      background-color: #E2EBFA;\n",
       "      box-shadow: 0px 1px 2px rgba(60, 64, 67, 0.3), 0px 1px 3px 1px rgba(60, 64, 67, 0.15);\n",
       "      fill: #174EA6;\n",
       "    }\n",
       "\n",
       "    .colab-df-buttons div {\n",
       "      margin-bottom: 4px;\n",
       "    }\n",
       "\n",
       "    [theme=dark] .colab-df-convert {\n",
       "      background-color: #3B4455;\n",
       "      fill: #D2E3FC;\n",
       "    }\n",
       "\n",
       "    [theme=dark] .colab-df-convert:hover {\n",
       "      background-color: #434B5C;\n",
       "      box-shadow: 0px 1px 3px 1px rgba(0, 0, 0, 0.15);\n",
       "      filter: drop-shadow(0px 1px 2px rgba(0, 0, 0, 0.3));\n",
       "      fill: #FFFFFF;\n",
       "    }\n",
       "  </style>\n",
       "\n",
       "    <script>\n",
       "      const buttonEl =\n",
       "        document.querySelector('#df-8414b76b-1253-48dd-b18d-78e47acbd5d6 button.colab-df-convert');\n",
       "      buttonEl.style.display =\n",
       "        google.colab.kernel.accessAllowed ? 'block' : 'none';\n",
       "\n",
       "      async function convertToInteractive(key) {\n",
       "        const element = document.querySelector('#df-8414b76b-1253-48dd-b18d-78e47acbd5d6');\n",
       "        const dataTable =\n",
       "          await google.colab.kernel.invokeFunction('convertToInteractive',\n",
       "                                                    [key], {});\n",
       "        if (!dataTable) return;\n",
       "\n",
       "        const docLinkHtml = 'Like what you see? Visit the ' +\n",
       "          '<a target=\"_blank\" href=https://colab.research.google.com/notebooks/data_table.ipynb>data table notebook</a>'\n",
       "          + ' to learn more about interactive tables.';\n",
       "        element.innerHTML = '';\n",
       "        dataTable['output_type'] = 'display_data';\n",
       "        await google.colab.output.renderOutput(dataTable, element);\n",
       "        const docLink = document.createElement('div');\n",
       "        docLink.innerHTML = docLinkHtml;\n",
       "        element.appendChild(docLink);\n",
       "      }\n",
       "    </script>\n",
       "  </div>\n",
       "\n",
       "\n",
       "    </div>\n",
       "  </div>\n"
      ],
      "application/vnd.google.colaboratory.intrinsic+json": {
       "type": "dataframe",
       "summary": "{\n  \"name\": \"print(f\\\"\\\\nMakespan (PuLP): {pulp_makespan:\",\n  \"rows\": 9,\n  \"fields\": [\n    {\n      \"column\": \"job\",\n      \"properties\": {\n        \"dtype\": \"category\",\n        \"num_unique_values\": 3,\n        \"samples\": [\n          \"A\",\n          \"C\",\n          \"B\"\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    },\n    {\n      \"column\": \"op\",\n      \"properties\": {\n        \"dtype\": \"number\",\n        \"std\": 0,\n        \"min\": 0,\n        \"max\": 2,\n        \"num_unique_values\": 3,\n        \"samples\": [\n          0,\n          1,\n          2\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    },\n    {\n      \"column\": \"machine\",\n      \"properties\": {\n        \"dtype\": \"category\",\n        \"num_unique_values\": 3,\n        \"samples\": [\n          \"M1\",\n          \"M2\",\n          \"M3\"\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    },\n    {\n      \"column\": \"start\",\n      \"properties\": {\n        \"dtype\": \"number\",\n        \"std\": 3.515837184954831,\n        \"min\": 0.0,\n        \"max\": 9.0,\n        \"num_unique_values\": 5,\n        \"samples\": [\n          2.0,\n          7.0,\n          4.0\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    },\n    {\n      \"column\": \"duration\",\n      \"properties\": {\n        \"dtype\": \"number\",\n        \"std\": 0,\n        \"min\": 2,\n        \"max\": 3,\n        \"num_unique_values\": 2,\n        \"samples\": [\n          3,\n          2\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    },\n    {\n      \"column\": \"end\",\n      \"properties\": {\n        \"dtype\": \"number\",\n        \"std\": 3.4681086744474685,\n        \"min\": 2.0,\n        \"max\": 11.0,\n        \"num_unique_values\": 6,\n        \"samples\": [\n          2.0,\n          4.0\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    }\n  ]\n}"
      }
     },
     "metadata": {}
    },
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "\n",
      "Makespan (PuLP): 11.0 saat\n"
     ]
    }
   ],
   "source": "def solve_jss_pulp(jobs, machines, horizon=20):\n    \"\"\"Job Shop Scheduling – PuLP MIP\"\"\"\n    prob = pulp.LpProblem(\"JobShop\", pulp.LpMinimize)\n\n    ops_list = []\n    for jid, ops in jobs.items():\n        for k, (m, d) in enumerate(ops):\n            ops_list.append((jid, k, m, d))\n\n    start = pulp.LpVariable.dicts(\"start\", ((j, k) for j, k, _, _ in ops_list), lowBound=0, cat=\"Continuous\")\n    Cmax = pulp.LpVariable(\"Cmax\", lowBound=0, cat=\"Continuous\")\n    prob += Cmax\n\n    # Precedence\n    for jid, ops in jobs.items():\n        for k in range(len(ops) - 1):\n            d = ops[k][1]\n            prob += start[jid, k + 1] >= start[jid, k] + d\n\n    # Makespan\n    for jid, ops in jobs.items():\n        last = len(ops) - 1\n        d = ops[last][1]\n        prob += Cmax >= start[jid, last] + d\n\n    # Machine disjunctive (Big-M)\n    Mbig = horizon\n    for m in machines:\n        m_ops = [(j, k, d) for j, k, mach, d in ops_list if mach == m]\n        for i in range(len(m_ops)):\n            for j in range(i + 1, len(m_ops)):\n                j1, k1, d1 = m_ops[i]\n                j2, k2, d2 = m_ops[j]\n                y = pulp.LpVariable(f\"y_{j1}{k1}_{j2}{k2}\", cat=\"Binary\")\n                prob += start[j1, k1] + d1 <= start[j2, k2] + Mbig * (1 - y)\n                prob += start[j2, k2] + d2 <= start[j1, k1] + Mbig * y\n\n    status = prob.solve(pulp.PULP_CBC_CMD(msg=0, timeLimit=15))\n    if pulp.LpStatus[status] != \"Optimal\":\n        print(\"Uyarı: Optimal bulunamadı →\", pulp.LpStatus[status])\n\n    schedule = []\n    for jid, k, m, d in ops_list:\n        st = pulp.value(start[jid, k])\n        schedule.append({\n            \"job\": jid, \"op\": k, \"machine\": m,\n            \"start\": round(st, 2), \"duration\": d, \"end\": round(st + d, 2)\n        })\n    return pd.DataFrame(schedule), pulp.value(Cmax)\n\npulp_df, pulp_makespan = solve_jss_pulp(jobs, machines, horizon=20)\nprint(\"=== PuLP MIP ÇİZELGE ===\")\ndisplay(pulp_df.sort_values([\"machine\", \"start\"]))\nprint(f\"\\nMakespan (PuLP): {pulp_makespan:.1f} saat\")\n",
   "id": "pulp_solve"
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "id": "spt",
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 379
    },
    "executionInfo": {
     "status": "ok",
     "timestamp": 1786330692717,
     "user_tz": -180,
     "elapsed": 1257,
     "user": {
      "displayName": "Joseph Volkan ÖZCAN",
      "userId": "11389530967736397480"
     }
    },
    "outputId": "2563e3aa-0ff0-42d9-cd4f-4ff8beca519e"
   },
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "=== SPT SEZGİSEL ===\n"
     ]
    },
    {
     "output_type": "display_data",
     "data": {
      "text/plain": [
       "  job  op machine  start  duration  end\n",
       "0   A   0      M1      0         2    2\n",
       "2   C   1      M1      2         2    4\n",
       "5   B   0      M1      4         3    7\n",
       "1   C   0      M2      0         2    2\n",
       "3   A   1      M2      2         3    5\n",
       "7   B   2      M2      9         2   11\n",
       "4   A   2      M3      5         2    7\n",
       "6   B   1      M3      7         2    9\n",
       "8   C   2      M3      9         3   12"
      ],
      "text/html": [
       "\n",
       "  <div id=\"df-80e5edc4-554b-4221-8a9d-c750e584b1fa\" class=\"colab-df-container\">\n",
       "    <div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>job</th>\n",
       "      <th>op</th>\n",
       "      <th>machine</th>\n",
       "      <th>start</th>\n",
       "      <th>duration</th>\n",
       "      <th>end</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>A</td>\n",
       "      <td>0</td>\n",
       "      <td>M1</td>\n",
       "      <td>0</td>\n",
       "      <td>2</td>\n",
       "      <td>2</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>C</td>\n",
       "      <td>1</td>\n",
       "      <td>M1</td>\n",
       "      <td>2</td>\n",
       "      <td>2</td>\n",
       "      <td>4</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>5</th>\n",
       "      <td>B</td>\n",
       "      <td>0</td>\n",
       "      <td>M1</td>\n",
       "      <td>4</td>\n",
       "      <td>3</td>\n",
       "      <td>7</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>C</td>\n",
       "      <td>0</td>\n",
       "      <td>M2</td>\n",
       "      <td>0</td>\n",
       "      <td>2</td>\n",
       "      <td>2</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>A</td>\n",
       "      <td>1</td>\n",
       "      <td>M2</td>\n",
       "      <td>2</td>\n",
       "      <td>3</td>\n",
       "      <td>5</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>7</th>\n",
       "      <td>B</td>\n",
       "      <td>2</td>\n",
       "      <td>M2</td>\n",
       "      <td>9</td>\n",
       "      <td>2</td>\n",
       "      <td>11</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>A</td>\n",
       "      <td>2</td>\n",
       "      <td>M3</td>\n",
       "      <td>5</td>\n",
       "      <td>2</td>\n",
       "      <td>7</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>6</th>\n",
       "      <td>B</td>\n",
       "      <td>1</td>\n",
       "      <td>M3</td>\n",
       "      <td>7</td>\n",
       "      <td>2</td>\n",
       "      <td>9</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>8</th>\n",
       "      <td>C</td>\n",
       "      <td>2</td>\n",
       "      <td>M3</td>\n",
       "      <td>9</td>\n",
       "      <td>3</td>\n",
       "      <td>12</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>\n",
       "    <div class=\"colab-df-buttons\">\n",
       "\n",
       "  <div class=\"colab-df-container\">\n",
       "    <button class=\"colab-df-convert\" onclick=\"convertToInteractive('df-80e5edc4-554b-4221-8a9d-c750e584b1fa')\"\n",
       "            title=\"Convert this dataframe to an interactive table.\"\n",
       "            style=\"display:none;\">\n",
       "\n",
       "  <svg xmlns=\"http://www.w3.org/2000/svg\" height=\"24px\" viewBox=\"0 -960 960 960\">\n",
       "    <path d=\"M120-120v-720h720v720H120Zm60-500h600v-160H180v160Zm220 220h160v-160H400v160Zm0 220h160v-160H400v160ZM180-400h160v-160H180v160Zm440 0h160v-160H620v160ZM180-180h160v-160H180v160Zm440 0h160v-160H620v160Z\"/>\n",
       "  </svg>\n",
       "    </button>\n",
       "\n",
       "  <style>\n",
       "    .colab-df-container {\n",
       "      display:flex;\n",
       "      gap: 12px;\n",
       "    }\n",
       "\n",
       "    .colab-df-convert {\n",
       "      background-color: #E8F0FE;\n",
       "      border: none;\n",
       "      border-radius: 50%;\n",
       "      cursor: pointer;\n",
       "      display: none;\n",
       "      fill: #1967D2;\n",
       "      height: 32px;\n",
       "      padding: 0 0 0 0;\n",
       "      width: 32px;\n",
       "    }\n",
       "\n",
       "    .colab-df-convert:hover {\n",
       "      background-color: #E2EBFA;\n",
       "      box-shadow: 0px 1px 2px rgba(60, 64, 67, 0.3), 0px 1px 3px 1px rgba(60, 64, 67, 0.15);\n",
       "      fill: #174EA6;\n",
       "    }\n",
       "\n",
       "    .colab-df-buttons div {\n",
       "      margin-bottom: 4px;\n",
       "    }\n",
       "\n",
       "    [theme=dark] .colab-df-convert {\n",
       "      background-color: #3B4455;\n",
       "      fill: #D2E3FC;\n",
       "    }\n",
       "\n",
       "    [theme=dark] .colab-df-convert:hover {\n",
       "      background-color: #434B5C;\n",
       "      box-shadow: 0px 1px 3px 1px rgba(0, 0, 0, 0.15);\n",
       "      filter: drop-shadow(0px 1px 2px rgba(0, 0, 0, 0.3));\n",
       "      fill: #FFFFFF;\n",
       "    }\n",
       "  </style>\n",
       "\n",
       "    <script>\n",
       "      const buttonEl =\n",
       "        document.querySelector('#df-80e5edc4-554b-4221-8a9d-c750e584b1fa button.colab-df-convert');\n",
       "      buttonEl.style.display =\n",
       "        google.colab.kernel.accessAllowed ? 'block' : 'none';\n",
       "\n",
       "      async function convertToInteractive(key) {\n",
       "        const element = document.querySelector('#df-80e5edc4-554b-4221-8a9d-c750e584b1fa');\n",
       "        const dataTable =\n",
       "          await google.colab.kernel.invokeFunction('convertToInteractive',\n",
       "                                                    [key], {});\n",
       "        if (!dataTable) return;\n",
       "\n",
       "        const docLinkHtml = 'Like what you see? Visit the ' +\n",
       "          '<a target=\"_blank\" href=https://colab.research.google.com/notebooks/data_table.ipynb>data table notebook</a>'\n",
       "          + ' to learn more about interactive tables.';\n",
       "        element.innerHTML = '';\n",
       "        dataTable['output_type'] = 'display_data';\n",
       "        await google.colab.output.renderOutput(dataTable, element);\n",
       "        const docLink = document.createElement('div');\n",
       "        docLink.innerHTML = docLinkHtml;\n",
       "        element.appendChild(docLink);\n",
       "      }\n",
       "    </script>\n",
       "  </div>\n",
       "\n",
       "\n",
       "    </div>\n",
       "  </div>\n"
      ],
      "application/vnd.google.colaboratory.intrinsic+json": {
       "type": "dataframe",
       "summary": "{\n  \"name\": \"print(f\\\"\\\\nMakespan (SPT): {spt_makespan} saat\\\")\",\n  \"rows\": 9,\n  \"fields\": [\n    {\n      \"column\": \"job\",\n      \"properties\": {\n        \"dtype\": \"category\",\n        \"num_unique_values\": 3,\n        \"samples\": [\n          \"A\",\n          \"C\",\n          \"B\"\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    },\n    {\n      \"column\": \"op\",\n      \"properties\": {\n        \"dtype\": \"number\",\n        \"std\": 0,\n        \"min\": 0,\n        \"max\": 2,\n        \"num_unique_values\": 3,\n        \"samples\": [\n          0,\n          1,\n          2\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    },\n    {\n      \"column\": \"machine\",\n      \"properties\": {\n        \"dtype\": \"category\",\n        \"num_unique_values\": 3,\n        \"samples\": [\n          \"M1\",\n          \"M2\",\n          \"M3\"\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    },\n    {\n      \"column\": \"start\",\n      \"properties\": {\n        \"dtype\": \"number\",\n        \"std\": 3,\n        \"min\": 0,\n        \"max\": 9,\n        \"num_unique_values\": 6,\n        \"samples\": [\n          0,\n          2,\n          7\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    },\n    {\n      \"column\": \"duration\",\n      \"properties\": {\n        \"dtype\": \"number\",\n        \"std\": 0,\n        \"min\": 2,\n        \"max\": 3,\n        \"num_unique_values\": 2,\n        \"samples\": [\n          3,\n          2\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    },\n    {\n      \"column\": \"end\",\n      \"properties\": {\n        \"dtype\": \"number\",\n        \"std\": 3,\n        \"min\": 2,\n        \"max\": 12,\n        \"num_unique_values\": 7,\n        \"samples\": [\n          2,\n          4\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    }\n  ]\n}"
      }
     },
     "metadata": {}
    },
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "\n",
      "Makespan (SPT): 12 saat\n"
     ]
    }
   ],
   "source": "# SPT sezgisel (karşılaştırma)\ndef spt_schedule(jobs, machines):\n    next_op = {j: 0 for j in jobs}\n    job_ready = {j: 0 for j in jobs}\n    machine_free = {m: 0 for m in machines}\n    schedule = []\n    remaining = sum(len(ops) for ops in jobs.values())\n\n    while remaining > 0:\n        candidates = []\n        for jid, ops in jobs.items():\n            k = next_op[jid]\n            if k >= len(ops):\n                continue\n            mach, dur = ops[k]\n            start = max(job_ready[jid], machine_free[mach])\n            candidates.append((dur, start, jid, k, mach))\n        if not candidates:\n            break\n        candidates.sort(key=lambda x: (x[0], x[1]))\n        dur, start, jid, k, mach = candidates[0]\n        end = start + dur\n        schedule.append({\"job\": jid, \"op\": k, \"machine\": mach,\n                         \"start\": start, \"duration\": dur, \"end\": end})\n        job_ready[jid] = end\n        machine_free[mach] = end\n        next_op[jid] += 1\n        remaining -= 1\n    return pd.DataFrame(schedule)\n\nspt_df = spt_schedule(jobs, machines)\nspt_makespan = spt_df[\"end\"].max()\nprint(\"=== SPT SEZGİSEL ===\")\ndisplay(spt_df.sort_values([\"machine\", \"start\"]))\nprint(f\"\\nMakespan (SPT): {spt_makespan} saat\")\n",
   "id": "spt"
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "section4"
   },
   "source": "## 4. Gantt Chart – Klasik Çözümler\n",
   "id": "section4"
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "id": "gantt_classical",
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 797
    },
    "executionInfo": {
     "status": "ok",
     "timestamp": 1786330693260,
     "user_tz": -180,
     "elapsed": 398,
     "user": {
      "displayName": "Joseph Volkan ÖZCAN",
      "userId": "11389530967736397480"
     }
    },
    "outputId": "a335177a-96bd-4ccb-a20b-b713c405a112"
   },
   "outputs": [
    {
     "output_type": "display_data",
     "data": {
      "text/plain": [
       "<Figure size 1200x400 with 1 Axes>"
      ],
      "image/png": "iVBORw0KGgoAAAANSUhEUgAABKUAAAGGCAYAAACqvTJ0AAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjAsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvlHJYcgAAAAlwSFlzAAAPYQAAD2EBqD+naQAAVZhJREFUeJzt3Xd4FOXCxuFnd9NDOklIIIFQpPcOimChCAgWUAGlqeABFVFQ8ACCBQQrR8QGUgTBAnyKcixoRIIgCKj03gkhkEZC6u73R2SPMQESyM4A+d3XletkZ96deZYMnuzDO+9aHA6HQwAAAAAAAICBrGYHAAAAAAAAQNlDKQUAAAAAAADDUUoBAAAAAADAcJRSAAAAAAAAMBylFAAAAAAAAAxHKQUAAAAAAADDUUoBAAAAAADAcJRSAAAAAAAAMBylFAAAAAAAAAxHKQUAAC7IbrerXr16evHFF82Oojlz5shisejAgQNXxLlbtWql0aNHX9ZxY2NjTXtNxXEu32effWZ2FAAAcI2hlAIAXNPOFQnnvry8vHTddddp+PDhOnHixCUd02KxaPjw4Rcc0759+wLnDQ4OVvPmzTV79mzZ7fZiZ169enWh/Q6HQ1FRUbJYLOrWrdsFsx04cKBADpvNpujoaN1xxx3avHlzsV7vxx9/rMOHDxf5mrdu3ap+/fqpYsWK8vT0VGRkpPr27autW7cW69jn89JLL2nZsmWXdQwjPP3005oxY4bi4+NNyzBgwABZLBb5+/vr7Nmzhfbv3r3b+fN/5ZVXTEiIvzt+/LieeeYZdejQQX5+frJYLIqNjS1y7LfffqvBgwerXr16stlsqlKlSonP98UXX6hJkyby8vJSdHS0JkyYoNzc3Mt7EQAAlBJKKQBAmTBp0iTNnz9fb731ltq0aaOZM2eqdevWysjIcNk5K1WqpPnz52v+/PkaN26ccnNzNXjwYI0dO7ZYz/fy8tLChQsLbf/pp5905MgReXp6FjvLfffdp/nz52v27Nnq06ePfvjhB7Vq1apYxdS0adN07733KiAgoMD2JUuWqEmTJlq5cqUGDhyot99+W4MHD9aPP/6oJk2aaOnSpcXO90/nK6Xuv/9+nT17VpUrV77kY5emHj16yN/fX2+//bapOdzc3JSRkaEvv/yy0L4FCxbIy8vLhFQoys6dO/Xyyy/r6NGjql+//gXHLly4UAsXLlRAQIAiIyNLfK4VK1aoZ8+eCgwM1H/+8x/17NlTL7zwgh599NFLjQ8AQKmilAIAlAldunRRv3799OCDD2rOnDkaMWKE9u/fr//7v/9z2TkDAgLUr18/9evXT0888YTi4uJUqVIlvfXWW8rJybno82+77TZ9+umnhWY1LFy4UE2bNlWFChWKnaVJkybq16+f+vfvrylTpuijjz5SVlaWZs6cecHnbdq0Sb///rt69+5dYPvevXt1//33q2rVqvrjjz/0wgsvaPDgwXr++ef1xx9/qGrVqrr//vu1b9++YmcsDpvNJi8vL1ksllI97qWyWq26++67NW/ePDkcDtNyeHp66uabb9bHH39caN/ChQvVtWtXE1KhKE2bNtWpU6e0a9cujRw58oJjX3rpJaWmpiouLk4NGzYs8bmeeuopNWjQQN9++60eeughTZ8+XWPGjNG7776rHTt2XOpLAACg1FBKAQDKpJtuukmStH//fkn5t9u1b9++0LgBAwZc0i0zRfHx8VGrVq2Unp6ukydPXnT8fffdp1OnTum7775zbsvOztZnn32mPn36XFaWf77+81m2bJk8PDzUrl27AtunTZumjIwMvffeewoNDS2wr3z58nr33XeVnp6uqVOnOrc/99xzslgs2rFjh3r37i1/f3+FhITo8ccfV2ZmpnOcxWJRenq65s6d67ztbMCAAZKKXtepSpUq6tatm2JjY9WsWTN5e3urfv36zluilixZovr168vLy0tNmzbVpk2bCuT9448/NGDAAFWtWlVeXl6qUKGCBg0apFOnThXrz/LWW2/VwYMHi307pKv06dNHK1asUHJysnPb+vXrtXv37iKvl9OnT+upp55S/fr1Va5cOfn7+6tLly76/fffL3qurKwsdevWTQEBAVqzZo2k/LXH3njjDdWtW1deXl4KDw/XkCFDlJSUVOC5GzZsUKdOnVS+fHl5e3srJiZGgwYNcu4/d8vpK6+8otdff12VK1eWt7e3brzxRm3ZsqXAsYr7szt37e3Zs0cDBgxQYGCgAgICNHDgQJfOliyKn5+fgoODizU2MjJS7u7ul3Sebdu2adu2bXr44Yfl5ubm3P6vf/1LDoeDNcIAAFcEt4sPAQDg2rN3715JUkhIiKHn3bdvn2w2mwIDAy86tkqVKmrdurU+/vhjdenSRVL+7TgpKSm6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eCnz9PTUnXfeqblz52rfvn167rnnzjv2pZde0rhx4zRo0CA9//zzCg4OltVq1YgRI4p83T169NCiRYs0ZcoUzZs3r0Cpa7fbFRYWpgULFhR5rnMzBi0Wiz777DOtXbtWX375pb755hsNGjRIr776qtauXaty5cqV6PWW9GdXnOuhKGfOnNGZM2cumsdmsxVa+N8s527bO378eKFy9vjx42rRooUZsQAAKIBSCgAA5c+Y2LdvX6HtBw8eNCFNQXfccYeGDBmitWvXavHixYaeu1atWkV+Ql+3bt30/vvva/Xq1UXe1vTzzz/rwIEDGjJkSKF9u3fvVkxMjPPxnj17ZLfbC3zKoVGzjpKSkrRy5UpNnDhR48ePL5CxuI4ePars7GzVrl3bFRFLrE+fPpo9e7asVqvuvffe84777LPP1KFDhwJrLEnnL9h69uypjh07asCAAfLz89PMmTOd+6pVq6bvv/9ebdu2LdYMnFatWqlVq1Z68cUXtXDhQvXt21eLFi3Sgw8+6BxT1M9g165dzuukNH52xfXKK69cdWtKnVvva8OGDQUKqGPHjunIkSPODxEAAMBMlFIAACj/TfXXX3+tkydPOmc6/P7774qLiys0y8Bo5cqV08yZM3XgwAF1797d0HO3bt1aU6ZMUVZWVoHbq0aNGqWPPvrIuWj432/3On36tIYOHSofHx+NGjWq0DFnzJihjh07Oh//5z//kSTnJwxK+bPMkpOTXfCKCjo3c+afM2XeeOONYh/jt99+kyS1adOm1HJdjg4dOuj5559XSEiIKlSocN5xNput0Ov+9NNPdfToUVWvXr3I5zzwwANKTU3Vo48+6lyXTMqfsfT222/r+eef10svvVTgObm5uTpz5owCAwOVlJSkwMDAAqXjufLkn7eZLVu2TEePHnWuK/Xrr79q3bp1GjFihDO/dHk/u+K60teUysnJ0d69e52zGCWpbt26qlWrlt577z0NGTLE+ec1c+ZMWSyWS7oNGACA0kYpBQCApEGDBum1115Tp06dNHjwYCUkJOidd95R3bp1i1wQeMOGDUV+Ylj79u2L9ea1pPr371/qxyyOHj166Pnnn9dPP/1UoEiqUaOG5s6dq759+6p+/foaPHiwYmJidODAAc2aNUuJiYn6+OOPVa1atULH3L9/v26//XZ17txZv/zyiz766CP16dNHDRs2dI5p2rSpvv/+e7322muKjIxUTExMoU8BLA3+/v5q166dpk6dqpycHFWsWFHffvttkbPDzue7775TdHS0GjduXOr5LoXVatW///3vi47r1q2bJk2apIEDB6pNmzb6888/tWDBgouunTR8+HClpqbq2WefVUBAgMaOHasbb7xRQ4YM0eTJk7V582Z17NhR7u7u2r17tz799FO9+eabuvvuuzV37ly9/fbbuuOOO1StWjWlpaXp/fffl7+/v2677bYC56levbquv/56PfLII8rKytIbb7yhkJAQjR49WlLp/OyKq7TXlDr3346tW7dKkubPn6/Vq1dLUoGf3R9//KEvvvhCUv6MwpSUFOdzGzZs6Cypjx49qtq1a6t///6aM2eO8/nTpk3T7bffro4dO+ree+/Vli1b9NZbb+nBBx+8Ymb2AQDKNkopAAAk1a5dW/PmzdP48eM1cuRI1alTR/Pnz9fChQsVGxtbaPy6deu0bt26Qtuff/55l5RSZmnatKkaNGigTz75pEApJUm9evVSrVq1NHnyZGcRFRISog4dOmjs2LGqV69ekcdcvHixxo8fr2eeeUZubm4aPny4pk2bVmDMa6+9pocfflj//ve/dfbsWfXv398lpZQkLVy4UI8++qhmzJghh8Ohjh07asWKFYqMjLzoc+12uz7//HMNHjz4iljovCTGjh2r9PR0LVy4UIsXL1aTJk301Vdf6ZlnninWc1NSUpzF1LBhw/TOO++oadOmevfddzV27Fi5ubmpSpUq6tevn9q2bSspf6HzX3/9VYsWLdKJEycUEBCgFi1aaMGCBQVu6ZTyZydZrVa98cYbSkhIUIsWLfTWW285ZwJJl/ezM9O4ceMKPP77J3L+vZTauHFjobHnHvfv3/+iMye7deumJUuWaOLEiXr00UcVGhqqsWPHFrjdEQAAM1kcF1vZEQAAlGnz58/XsGHDdOjQoUtaZP2c5557ThMnTtTJkyeviEXBS8OyZcvUp08f7d27t0BZUhKxsbHq0KGD9u/fX2BdrbLqwIEDiomJ0bRp0/TUU0+ZHQcAALiQaz8LGwAAXPX69u2r6OhozZgxw+woV5yXX35Zw4cPv+RCCgAAoCzj9j0AAHBBVqtVW7ZsMTvGFemXX34xOwIAAMBVi5lSAAAAAAAAMBxrSgEAAAAAAMBwzJQCAAAAAACA4SilAAAA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gwYN9M033+jWW2+93NMCAAAAAADgKlbiUmrOnDkX3L9s2TLn97NmzSrp4QEAAAAAAFAGWM0OAAAAAAAAgLKHUgoAAAAAAACGu+w1pQAAAAAAAK40x7OTlJybbtj5At18FeER5LLjx8bGqkOHDkpKSlJgYKDLzmMkSikAAAAAAHBNOZ6dpNu3TFa2I9ewc3pY3PRFvTElKqYGDBig5OTkAutzu8KmTZv00ksvadWqVUpJSVFUVJTat2+vUaNG6brrrnPpuS+E2/cAAAAAAMA1JTk33dBCSpKyHbmGzswqruXLl6tVq1bKysrSggULtH37dn300UcKCAjQuHHjTM1GKQUAAAAAAGCyrKwsPfbYYwoLC5OXl5euv/56rV+/vtC4uLg4NWjQQF5eXmrVqpW2bNly3mNmZGRo4MCBuu222/TFF1/olltuUUxMjFq2bKlXXnlF7777ritf0kVRSgEAAAAAAJhs9OjR+vzzzzV37lxt3LhR1atXV6dOnXT69OkC40aNGqVXX31V69evV2hoqLp3766cnJwij/nNN98oMTFRo0ePLnK/2WtTUUoBAAAAAACYKD09XTNnztS0adPUpUsX1alTR++//768vb01a9asAmMnTJigW2+9VfXr19fcuXN14sQJLV26tMjj7t69W5JUq1Ytl7+GS0EpBQAAAAAAYKK9e/cqJydHbdu2dW5zd3dXixYttH379gJjW7du7fw+ODhYNWvWLDTmHIfD4ZrApYRSCgAAAAAA4Bp07pP1duzYYXKSolFKAQAAAAAAmKhatWry8PBQXFycc1tOTo7Wr1+vOnXqFBi7du1a5/dJSUnatWuXateuXeRxO3bsqPLly2vq1KlF7k9OTr788JfBzdSzAwAAAAAAlHG+vr565JFHNGrUKAUHBys6OlpTp05VRkaGBg8eXGDspEmTFBISovDwcD377LMqX768evbsed7jfvDBB+rVq5duv/12PfbYY6pevboSExP1ySef6NChQ1q0aJEBr7BolFIAAAAAAAAmsNvtcnPLr2amTJkiu92u+++/X2lpaWrWrJm++eYbBQUFFXjOlClT9Pjjj2v37t1q1KiRvvzyS3l4eJz3HD169NCaNWs0efJk9enTR6mpqYqKitJNN92kF154waWv72IopQAAAAAAwDUl0M1XHhY3ZTtyDTunh8VNgW6+JXpOQkKC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TEyMxo0bp8jIyAt+YhlwpbrQ9R4REaG7775bGzdu1PLly5WXl+d8HxscHCwPD49incPicDgcLklfCt566y1NmzZN8fHxatSokaZPn66WLVuaHQsodRaLpcjtH374oQYMGGBsGMAE7du3V6NGjfTGG2+YHQUodcuXL9eYMWO0e/duxcTEaOTIkXrooYfMjgW4RFpamsaNG6elS5cqISFBkZGRuu+++zR+/Phiv0EBrmSxsbHq0KFDoe39+/fXnDlz5HA4NGHCBL333ntKTk7W9ddfr7ffflvXXXedCWmBy3Oh6/2555477x0+P/74o9q3b1+sc1zRpRQAAAAAAACuTVfkmlIAAAAAAAC4tlFKAQAAAAAAwHCUUgAAAAAAADAcpRQAAAAAAAAMRykFAAAAAAAAw1FKAQAAAAAAwHCUUgAAAAAAADAcpRQAAAAAAAAMRykFAABwBbn//vv10ksvmZohOztbVapU0YYNG0zNAQAArm2UUgAA4KoSGxsri8Vy3q8OHTqYHfGS/f777/r666/12GOPGXbO5557To0aNSqwzcPDQ0899ZSefvppw3IAAICyh1IKAABcVdq0aaPjx48X+nr33XdlsVj0r3/9y+yIl+w///mPevXqpXLlypkdRX379tXq1au1detWs6MAAIBrFKUUAAC4qnh4eKhChQoFvpKSkvTUU09p7Nix6tWrlyQpLy9PgwcPVkxMjLy9vVWzZk29+eabBY41YMAA9ezZUy+99JLCw8MVGBioSZMmKTc3V6NGjVJwcLAqVaqkDz/8sMDznn76aV133XXy8fFR1apVNW7cOOXk5Dj3n5t9NH/+fFWpUkUBAQG69957lZaWdt7XlZeXp88++0zdu3cvsP3tt99WjRo15OXlpfDwcN19993Off/97391/fXXKzAwUCEhIerWrZv27t1b7Kxz5szRxIkT9fvvvztnms2ZM0eSFBQUpLZt22rRokXF/MkAAACUjJvZAQAAAC5HcnKyevToofbt2+v55593brfb7apUqZI+/fRThYSEaM2aNXr44YcVERGh3r17O8f98MMPqlSpklatWqW4uDgNHjxYa9asUbt27bRu3TotXrxYQ4YM0a233qpKlSpJkvz8/DRnzhxFRkbqzz//1EMPPSQ/Pz+NHj3aedy9e/dq2bJlWr58uZKSktS7d29NmTJFL774YpGv448//lBKSoqaNWvm3LZhwwY99thjmj9/vtq0aaPTp0/r559/du5PT0/XyJEj1aBBA505c0bjx4/XHXfcoc2bN8tqtV406z333KMtW7bov//9r77//ntJUkBAgPP4LVq0KHA+AACA0mRxOBwOs0MAAABcCrvdrm7duunAgQNat26d/Pz8Ljh++PDhio+P12effSYpf6ZUbGys9u3b5yxxatWqpbCwMK1atUpS/gymgIAAffDBB7r33nuLPO4rr7yiRYsWORcGf+655zRt2jTFx8c7M40ePVqrVq3S2rVrizzGsmXLdPfddysnJ0cWi0WStGTJEg0cOFBHjhy56GuTpMTERIWGhurPP/9UvXr1ip112bJl2rx5c6Gx06dP1+uvv679+/df9NwAAAAlxUwpAABw1Ro7dqx++eUX/frrr0WWNjNmzNDs2bN16NAhnT17VtnZ2YUW9a5bt66zkJKk8PDwAoWOzWZTSEiIEhISnNsWL16s6dOna+/evTpz5oxyc3Pl7+9f4LhVqlQpkCkiIqLAMf7p7Nmz8vT0dBZSknTrrbeqcuXKqlq1qjp37qzOnTvrjjvukI+PjyRp9+7dGj9+vNatW6fExETZ7XZJ0qFDh5yvoThZz8fb21sZGRnFGgsAAFBSrCkFAACuSosWLXLO+qlRo0aR+5966ikNHjxY3377rTZv3qyBAwcqOzu7wDh3d/cCjy0WS5HbzhU+v/zyi/r27avbbrtNy5cv16ZNm/Tss88W67jnjlGU8uXLKyMjo8Bx/Pz8tHHjRn388ceKiIjQ+PHj1bBhQyUnJ0uSunfvrtOnT+v999/XunXrtG7dOklyHqO4Wc/n9OnTCg0NLdZYAACAkmKmFAAAuOps3rxZgwcP1pQpU9SpU6cix8TFxalNmzYFPo3vn4uAX4o1a9aocuXKevbZZ53bDh48eNnHPTeDa9u2bQVmc7m5uemWW27RLbfcogkTJigwMFA//PCDbrzxRu3cuVPvv/++brjhBknS6tWrS5zVw8NDeXl5RWbasmWLGjdufNmvDQAAoCiUUgAA4KqSmJionj17qn379urXr5/i4+ML7LfZbAoNDVWNGjU0b948ffPNN4qJidH8+fO1fv16xcTEXNb5a9SooUOHDmnRokVq3ry5vvrqKy1duvSyjilJoaGhatKkiVavXu0spZYvX659+/apXbt2CgoK0tdffy273a6aNWsqKChIISEheu+99xQREaFDhw7pmWeeKXHWKlWqaP/+/dq8ebMqVaokPz8/eXp6SpJ+/vnnAovHAwAAlCZu3wMAAFeVr776SgcPHtTXX3+tiIiIQl/NmzeXJA0ZMkR33nmn7rnnHrVs2VKnTp0qMGvqUt1+++164oknNHz4cDVq1Ehr1qzRuHHjLvu4kvTggw9qwYIFzseBgYFasmSJbrrpJtWuXVvvvPOOPv74Y+c6WIsWLdJvv/2mevXq6YknntC0adNKnPWuu+5S586d1aFDB4WGhurjjz+WlH/rX0pKiu6+++5SeW0AAAD/xKfvAQAAXCHOnj2rmjVravHixWrdurWpWe655x41bNhQY8eONTUHAAC4djFTCgAA4Arh7e2tefPmKTEx0dQc2dnZql+/vp544glTcwAAgGsbM6UAAAAAAABgOGZKAQAAAAAAwHCUUgAAAAAAADAcpRQAAAAAAAAMRykFAAAAAAAAw1FKAQAAAAAAwHCUUgAAAAAAADAcpRQAAAAAAAAMRykFAAAAAAAAw1FKAQAAAAAAwHCUUgAAAAAAADDc/wOlSt28epVNNQAAAABJRU5ErkJggg==\n"
     },
     "metadata": {}
    },
    {
     "output_type": "display_data",
     "data": {
      "text/plain": [
       "<Figure size 1200x400 with 1 Axes>"
      ],
      "image/png": "iVBORw0KGgoAAAANSUhEUgAABKUAAAGGCAYAAACqvTJ0AAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjAsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvlHJYcgAAAAlwSFlzAAAPYQAAD2EBqD+naQAAVGJJREFUeJzt3Xd8FHXixvFnd5NNNj2BJBCSkNC7VBFRBAUBKaII4oFSPJX7gYogKHqIBQRRT+FUzoJgAVFpeihiAxEQjFKOJiDSIYRAGgmpO78/kNVIgADJTBI+79drX+7OfHfm2WSE3YeZ79oMwzAEAAAAAAAAmMhudQAAAAAAAABcfiilAAAAAAAAYDpKKQAAAAAAAJiOUgoAAAAAAACmo5QCAAAAAACA6SilAAAAAAAAYDpKKQAAAAAAAJiOUgoAAAAAAACmo5QCAAAAAACA6SilAABAubB8+XLZbDYtX768VLY/aNAgxcXFXfTzbTabZs2aVWJ5SprNZtPw4cOtjgEAAOBBKQUAgIU2bdqk2267TdWrV5evr6+qVaumTp066d///nehcXFxcbLZbJ5bRESErr32Wi1cuFCSNGvWrELrz3Y7X+myZ88eDR48WDVr1pSvr6+qVKmidu3aafz48aX1I4AK//5Wrlx5xnrDMBQTEyObzabu3btbkBB/NXHiRPXs2VORkZGy2Wx68sknixy3YMEC3X777apRo4b8/PxUt25djRo1SqmpqabmBQCgLPKyOgAAAJer1atXq0OHDoqNjdU999yjKlWqaP/+/VqzZo2mTp2q+++/v9D4pk2batSoUZKkQ4cO6fXXX9ett96q6dOn68Ybb9R7771XaPzf//53XXnllbr33ns9ywICAs6a59dff1WrVq3kcrk0ZMgQxcXF6fDhw1q3bp2ee+45PfXUUyX46i9cu3btdPLkSTmdTktzlCZfX1/NmTNH11xzTaHl3333nQ4cOCAfHx+LkuGv/vnPf6pKlSpq1qyZli5detZx9957r6KiojRgwADFxsZq06ZNeuWVV/T5559r3bp1crlcJqYGAKBsoZQCAMAiEydOVHBwsBISEhQSElJoXVJS0hnjq1WrpgEDBnge33XXXapVq5ZeeuklDR06VDVq1Cg0/vSyPz/nXF566SWdOHFCGzZsUPXq1c+bx2x2u12+vr5WxyhVN910kz7++GNNmzZNXl5/vE2bM2eOWrRooeTkZAvT4c92796tuLg4JScnKzw8/Kzj5s2bp/bt2xda1qJFCw0cOFCzZ8/W3//+91JOCgBA2cXlewAAWGTXrl1q2LDhGYWUJEVERJz3+VWqVFH9+vW1e/fuEssTHR19RiF1tjxLlizRtddeK39/fwUGBqpbt27asmWLZ/3pOaDOdxmh2+3Wk08+qaioKPn5+alDhw7aunWr4uLiNGjQoDO29+c5pXbu3KnevXurSpUq8vX1VXR0tPr166e0tLRCWd9//321aNFCLpdLYWFh6tevn/bv33/xP6xScscdd+jYsWP66quvPMtyc3M1b948/e1vfyvyOS+88IKuvvpqVapUSS6XSy1atNC8efOKtb8JEybIbrcXulz0fL9XSUpMTNTgwYMVHR0tHx8fVa1aVTfffLP27NnjGRMXF6fu3bvryy+/VNOmTeXr66sGDRpowYIFhbZ1/PhxPfzww2rcuLECAgIUFBSkrl27auPGjYXGnf79f/TRR5o4caKio6Pl6+urG264Qb/++muxXm9JKu78Y38tpCTplltukSRt27atBBMBAFD+cKYUAAAWqV69un744Qdt3rxZjRo1uuDn5+Xlaf/+/apUqVKJ5fn666/17bff6vrrrz/n2Pfee08DBw5U586d9dxzzykrK0vTp0/XNddco/Xr1ysuLk7169c/45LC1NRUjRw5slDJNXbsWE2ZMkU9evRQ586dtXHjRnXu3FnZ2dnnzJCbm6vOnTsrJydH999/v6pUqaKDBw9q8eLFSk1NVXBwsKRTZ6SNGzdOffv21d///ncdPXpU//73v9WuXTutX7++yFLQKnFxcWrTpo0++OADde3aVdKpkigtLU39+vXTtGnTznjO1KlT1bNnT/Xv31+5ubmaO3eu+vTpo8WLF6tbt25n3dc///lPPfvss3r99dd1zz33SCre71WSevfurS1btuj+++9XXFyckpKS9NVXX2nfvn2FypqdO3fq9ttv19ChQzVw4EDNnDlTffr00RdffKFOnTpJkn777TctWrRIffr0UXx8vI4cOaLXX39d1113nbZu3aqoqKhCuSdPniy73a6HH35YaWlpmjJlivr376+1a9ee82ebl5d3Rll5NmFhYbLbS+/fbhMTEyVJlStXLrV9AABQLhgAAMASX375peFwOAyHw2G0adPGGDNmjLF06VIjNzf3jLHVq1c3brzxRuPo0aPG0aNHjY0bNxr9+vUzJBn3339/kdv39/c3Bg4cWOw8mzdvNlwulyHJaNq0qfHggw8aixYtMjIzMwuNy8jIMEJCQox77rmn0PLExEQjODj4jOWnud1uo3v37kZAQICxZcsWz3O8vLyMXr16FRr75JNPGpIK5V+2bJkhyVi2bJlhGIaxfv16Q5Lx8ccfn/U17dmzx3A4HMbEiRMLLd+0aZPh5eVVaPnAgQON6tWrn3Vb5yPJmDlz5kU9d+bMmYYkIyEhwXjllVeMwMBAIysryzAMw+jTp4/RoUMHwzBOHQfdunUr9NzT407Lzc01GjVqZFx//fVn5Bs2bJhhGIYxatQow263G7NmzfKsL+7vNSUlxZBkPP/88+d8TdWrVzckGfPnz/csS0tLM6pWrWo0a9bMsyw7O9soKCgo9Nzdu3cbPj4+xtNPP+1Zdvr3X79+fSMnJ8ezfOrUqYYkY9OmTefMc/r5xbnt3r37nNv6s6NHjxqSjPHjxxf7OXfffbfhcDiMHTt2FPs5AABURFy+BwCARTp16qQffvhBPXv21MaNGzVlyhR17txZ1apV06effnrG+C+//FLh4eEKDw/XFVdcoY8//lh33nmnnnvuuRLJ07BhQ23YsEEDBgzQnj17NHXqVPXq1UuRkZF68803PeO++uorpaam6o477lBycrLn5nA41Lp1ay1btqzI7T/zzDNavHixZs2apQYNGkiSvvnmG+Xn5+v//u//Co396yTvRTl9JtTSpUuVlZVV5JgFCxbI7Xarb9++hbJWqVJFtWvXPmtWK/Xt21cnT57U4sWLlZGRocWLF5/10j1JhSbKTklJUVpamq699lqtW7fujLGGYWj48OGaOnWq3n//fQ0cONCzrri/V5fLJafTqeXLlyslJeWcryUqKspzqZokBQUF6a677tL69es9Zwv5+Ph4zkoqKCjQsWPHFBAQoLp16xb5GgYPHlxosvtrr71W0qkzrs7liiuu0FdffVWsW5UqVc65rUsxZ84czZgxQ6NGjVLt2rVLbT8AAJQHXL4HAICFWrVqpQULFig3N1cbN27UwoUL9dJLL+m2227Thg0bPOWNJLVu3VoTJkyQzWaTn5+f6tevX+KXntWpU0fvvfeeCgoKtHXrVi1evFhTpkzRvffeq/j4eHXs2FE7d+6UpLNe4hcUFHTGsi+++EJPPfWUxo4dq969e3uW7927V5JUq1atQuPDwsIUGhp6zqzx8fEaOXKk/vWvf2n27Nm69tpr1bNnTw0YMMBTWO3cuVOGYZz1w7+3t/c592GF8PBwdezYUXPmzFFWVpYKCgp02223nXX84sWLNWHCBG3YsEE5OTme5Tab7Yyx7777rk6cOKHp06frjjvuKLSuuL9XHx8fPffccxo1apQiIyN11VVXqXv37rrrrrvOKHNq1ap1Ro46depIkvbs2aMqVarI7XZr6tSpeu2117R7924VFBR4xhZ1aWpsbGyhx6ePk/MVZKGhoerYseM5x5S277//Xnfffbc6d+6siRMnWpoFAICygFIKAIAywOl0qlWrVmrVqpXq1KmjwYMH6+OPP9b48eM9YypXrmzah2qHw6HGjRurcePGatOmjTp06KDZs2erY8eOcrvdkk7NP1TUGSV//tY46dS3lPXv31+dOnXShAkTSjTniy++qEGDBumTTz7Rl19+qQceeECTJk3SmjVrFB0dLbfbLZvNpiVLlsjhcJzx/ICAgBLNU1L+9re/6Z577lFiYqK6du161vLx+++/V8+ePdWuXTu99tprqlq1qry9vTVz5kzNmTPnjPFt27bVhg0b9Morr6hv374KCwvzrLuQ3+uIESPUo0cPLVq0SEuXLtW4ceM0adIkffvtt2rWrNkFvdZnn31W48aN05AhQ/TMM8945nMaMWKEJ9OfFfV7lE6dBXYuubm5On78eLEyhYeHn3U/F2vjxo3q2bOnGjVqpHnz5p3x/wkAAJcj/jYEAKCMadmypSTp8OHDFic55a95atasKenUN/KdryQ7efKkbr31VoWEhOiDDz44Y/Lo09/09+uvvyo+Pt6z/NixY+c98+W00+XZP//5T61evVpt27bVf/7zH02YMEE1a9aUYRiKj4/3nKFTHtxyyy267777tGbNGn344YdnHTd//nz5+vpq6dKl8vHx8SyfOXNmkeNr1aqlKVOmqH379urSpYu++eYbBQYGSrqw3+vp8aNGjdKoUaO0c+dONW3aVC+++KLef/99z5hff/1VhmEUOltqx44dkv749rp58+apQ4cOmjFjRqHtp6amluhE4KtXr1aHDh2KNXb37t3F/na94ti1a5e6dOmiiIgIff7552W2DAUAwGzMKQUAgEWWLVtW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C7ggkydPls1m04gRI6yOAhTLwYMHNWDAAFWqVEkul0uNGzfWTz/9ZHUs4JwKCgo0btw4xcfHy+VyqWbNmnrmmWfElK8oi1asWKEePXooKipKNptNixYtKrTeMAw98cQTqlq1qlwulzp27KidO3daExb4k3Mdu3l5eXrkkUfUuHFj+fv7KyoqSnfddZcOHTpkXeDLTJktpT788EONHDlS48eP17p163TFFVeoc+fOSkpKsjoacE7fffedhg0bpjVr1uirr75SXl6ebrzxRmVmZlodDSiWhIQEvf7662rSpInVUYBiSUlJUdu2beXt7a0lS5Zo69atevHFFxUaGmp1NOCcnnvuOU2fPl2vvPKKtm3bpueee05TpkzRv//9b6ujAWfIzMzUFVdcoVdffbXI9VOmTNG0adP0n//8R2vXrpW/v786d+6s7Oxsk5MChZ3r2M3KytK6des0btw4rVu3TgsWLND27dvVs2dPC5Jensrst++1bt1arVq10iuvvCJJcrvdiomJ0f33369HH33U4nRA8R09elQRERH67rvv1K5dO6vjAOd04sQJNW/eXK+99pomTJigpk2b6uWXX7Y6FnBOjz76qFatWqXvv//e6ijABenevbsiIyM1Y8YMz7LevXvL5XLp/ffftzAZcG42m00LFy5Ur169JJ06SyoqKkqjRo3Sww8/LElKS0tTZGSkZs2apX79+lmYFvjDX4/doiQkJOjKK6/U3r17FRsba164y1SZPFMqNzdXP//8szp27OhZZrfb1bFjR/3www8WJgMuXFpamiQpLCzM4iTA+Q0bNkzdunUr9OcvUNZ9+umnatmypfr06aOIiAg1a9ZMb775ptWxgPO6+uqr9c0332jHjh2SpI0bN2rlypXq2rWrxcmAC7N7924lJiYWev8QHBys1q1b8/kN5U5aWppsNptCQkKsjnJZ8LI6QFGSk5NVUFCgyMjIQssjIyP1yy+/WJQKuHBut1sjRoxQ27Zt1ahRI6vjAOc0d+5crVu3TgkJCVZHAS7Ib7/9punTp2vkyJF67LHHlJCQoAceeEBOp1MDBw60Oh5wVo8++qjS09NVr149ORwOFRQUaOLEierfv7/V0YALkpiYKElFfn47vQ4oD7Kzs/XII4/ojjvuUFBQkNVxLgtlspQCKophw4Zp8+bNWrlypdVRgHPav3+/HnzwQX311Vfy9fW1Og5wQdxut1q2bKlnn31WktSsWTNt3rxZ//nPfyilUKZ99NFHmj17tubMmaOGDRtqw4YNGjFihKKiojh2AcBkeXl56tu3rwzD0PTp062Oc9kok5fvVa5cWQ6HQ0eOHCm0/MiRI6pSpYpFqYALM3z4cC1evFjLli1TdHS01XGAc/r555+VlJSk5s2by8vLS15eXvruu+80bdo0eXl5qaCgwOqIwFlVrVpVDRo0KLSsfv362rdvn0WJgOIZPXq0Hn30UfXr10+NGzfWnXfeqYceekiTJk2yOhpwQU5/RuPzG8qr04XU3r179dVXX3GWlInKZCnldDrVokULffPNN55lbrdb33zzjdq0aWNhMuD8DMPQ8OHDtXDhQn377beKj4+3OhJwXjfccIM2bdqkDRs2eG4tW7ZU//79tWHDBjkcDqsjAmfVtm1bbd++vdCyHTt2qHr16hYlAoonKytLdnvht+MOh0Nut9uiRMDFiY+PV5UqVQp9fktPT9fatWv5/IYy73QhtXPnTn399deqVKmS1ZEuK2X28r2RI0dq4MCBatmypa688kq9/PLLyszM1ODBg62OBpzTsGHDNGfOHH3yyScKDAz0XEcfHBwsl8tlcTqgaIGBgWfMe+bv769KlSoxHxrKvIceekhXX321nn32WfXt21c//vij3njjDb3xxhtWRwPOqUePHpo4caJiY2PVsGFDrV+/Xv/61780ZMgQq6MBZzhx4oR+/fVXz+Pdu3drw4YNCgsLU2xsrEaMGKEJEyaodu3aio+P17hx4xQVFXXObzkDzHCuY7dq1aq67bbbtG7dOi1evFgFBQWez29hYWFyOp1Wxb5s2AzDMKwOcTavvPKKnn/+eSUmJqpp06aaNm2aWrdubXUs4JxsNluRy2fOnKlBgwaZGwa4BO3bt1fTpk318ssvWx0FOK/Fixdr7Nix2rlzp+Lj4zVy5Ejdc889VscCzikjI0Pjxo3TwoULlZSUpKioKN1xxx164okn+CCEMmf58uXq0KHDGcsHDhyoWbNmyTAMjR8/Xm+88YZSU1N1zTXX6LXXXlOdOnUsSAv84VzH7pNPPnnWK1uWLVum9u3bl3I6lOlSCgAAAAAAABVTmZxTCgAAAAAAABUbpRQAAAAAAABMRykFAAAAAAAA01FKAQAAAAAAwHSUUgAAAAAAADAdpRQAAAAAAABMRykFAAAAAAAA01FKAQAAAAAAwHSUUgAAAGXYnXfeqWeffdbSDLm5uYqLi9NPP/1kaQ4AAFCxUEoBAIBybfny5bLZbGe9dejQweqIF23jxo36/PPP9cADD5i2zyeffFJNmzYttMzpdOrhhx/WI488YloOAABQ8VFKAQCAcu3qq6/W4cOHz7i9/vrrstls+r//+z+rI160f//73+rTp48CAgKsjqL+/ftr5cqV2rJli9VRAABABUEpBQAAyjWn06kqVaoUuqWkpOjhhx/WY489pj59+kiSCgoKdPfddys+Pl4ul0t169bV1KlTC21r0KBB6tWrl5599llFRkYqJCRETz/9tPLz8zV69GiFhYUpOjpaM2fOLPS8Rx55RHXq1JGfn59q1KihcePGKS8vz7P+9NlH7733nuLi4hQcHKx+/fopIyPjrK+roKBA8+bNU48ePQotf+2111S7dm35+voqMjJSt912m2fdF198oWuuuUYhISGqVKmSunfvrl27dhU766xZs/TUU09p48aNnjPNZs2aJUkKDQ1V27ZtNXfu3GL+ZgAAAM7Ny+oAAAAAJSk1NVU333yz2rdvr2eeecaz3O12Kzo6Wh9//LEqVaqk1atX695771XVqlXVt29fz7hvv/1W0dHRWrFihVatWqW7775bq1evVrt27bR27Vp9+OGHuu+++9SpUydFR0dLkgIDAzVr1ixFRUVp06ZNuueeexQYGKgxY8Z4trtr1y4tWrRIixcvVkpKivr27avJkydr4sSJRb6O//3vf0pLS1PLli09y3766Sc98MADeu+993T11Vfr+PHj+v777z3rMzMzNXLkSDVp0kQnTpzQE088oVtuuUUbNmyQ3W4/b9bbb79dmzdv1hdffKGvv/5akhQcHOzZ/pVXXllofwAAAJfCZhiGYXUIAACAkuB2u9W9e3ft2bNHa9euVWBg4DnHDx8+XImJiZo3b56kU2dKLV++XL/99punxKlXr54iIiK0YsUKSafOYAoODtZbb72lfv36FbndF154QXPnzvVMDP7kk0/q+eefV2JioifTmDFjtGLFCq1Zs6bIbSxatEi33Xab8vLyZLPZJEkLFizQ4MGDdeDAgfO+NklKTk5WeHi4Nm3apEaNGhU766JFi7Rhw4Yzxk6bNk0vvfSSdu/efd59AwAAnA9nSgEAgArjscce0w8//KAff/yxyNLm1Vdf1dtvv619+/bp5MmTys3NPWNS74YNG3oKKUmKjIwsVOg4HA5VqlRJSUlJnmUffvihpk2bpl27dunEiRPKz89XUFBQoe3GxcUVylS1atVC2/irkydPysfHx1NISVKnTp1UvXp11ahRQ126dFGXLl10yy23yM/PT5K0c+dOPfHEE1q7dq2Sk5PldrslSfv27fO8huJkPRuXy6WsrKxijQUAADgf5pQCAAAVwty5cz1n/dSuXbvI9Q8//LDuvvtuffnll9qwYYMGDx6s3NzcQuO8vb0LPbbZbEUuO134/PDDD+rfv79uuukmLV68WOvXr9fjjz9erO2e3kZRKleurKysrELbCQwM1Lp16/TBBx+oatWqeuKJJ3TFFVcoNTVVktSjRw8dP35cb775ptauXau1a9dKkmcbxc16NsePH1d4eHixxgIAAJwPZ0oBAIByb8OGDbr77rs1efJkde7cucgxq1at0tVXX13o2/j+Ogn4xVi9erWqV6+uxx9/3LNs7969l7zd02dwbd26tdDZXF5eXurYsaM6duyo8ePHKyQkRN9++62uu+46bd++XW+++aauvfZaSdLKlSsvOKvT6VRBQUGRmTZv3qxmzZpd8msDAACQKKUAAEA5l5ycrF69eql9+/YaMGCAEhMTC613OBwKDw9X7dq19e6772rp0qWKj4/Xe++9p4SEBMXHx1/S/mvXrq19+/Zp7ty5atWqlT777DMtXLjwkrYpSeHh4WrevLlWrlzpKaUWL16s3377Te3atVNoaKg+//xzud1u1a1bV6GhoapUqZLeeOMNVa1aVfv27dOjjz56wVnj4uK0e/dubdiwQdHR0QoMDJSPj48k6fvvvy80eTwAAMCl4PI9AABQrn322Wfau3evPv/8c1WtWvWMW6tWrSRJ9913n2699Vbdfvvtat26tY4dO1borKmL1bNnTz300EMaPny4mjZtqtWrV2vcuHGXvF1J+vvf/67Zs2d7HoeEhGjBggW6/vrrVb9+ff3nP//RBx984JkHa+7cufr555/VqFEjPfTQQ3r++ecvOGvv3r3VpUsXdejQQeHh4frggw8knbr0Ly0tTbfddluJvDYAAAC+fQ8AAKCMOnnypOrWrasPP/xQbdq0sTTL7bffriuuuEKPPfaYpTkAAEDFwZlSAAAAZZTL5dK7776r5ORkS3Pk5uaqcePGeuihhyzNAQAAKhbOlAIAAAAAAIDpOFMKAAAAAAAApqOUAgAAAAAAgOkopQAAAAAAAGA6SikAAAAAAACYjlIKAAAAAAAApqOUAgAAAAAAgOkopQAAAAAAAGA6SikAAAAAAACYjlIKAAAAAAAApqOUAgAAAAAAgOn+H83gWQIGgverAAAAAElFTkSuQmCC\n"
     },
     "metadata": {}
    }
   ],
   "source": "def plot_gantt(sched_df, title, makespan=None):\n    if sched_df is None or len(sched_df) == 0:\n        print(f\"{title}: çizelge yok\")\n        return\n    colors = {\"A\": \"#3498db\", \"B\": \"#e74c3c\", \"C\": \"#2ecc71\", \"D\": \"#9b59b6\"}\n    fig, ax = plt.subplots(figsize=(12, 4))\n    machines_sorted = sorted(sched_df[\"machine\"].unique())\n    mach_pos = {m: i for i, m in enumerate(machines_sorted)}\n\n    for _, row in sched_df.iterrows():\n        ax.barh(mach_pos[row[\"machine\"]], row[\"duration\"], left=row[\"start\"],\n                height=0.6, color=colors.get(row[\"job\"], \"gray\"),\n                edgecolor=\"black\", alpha=0.85)\n        ax.text(row[\"start\"] + row[\"duration\"] / 2, mach_pos[row[\"machine\"]],\n                f\"{row['job']}{row['op']}\", ha=\"center\", va=\"center\",\n                fontsize=9, fontweight=\"bold\", color=\"white\")\n\n    ax.set_yticks(list(mach_pos.values()))\n    ax.set_yticklabels(list(mach_pos.keys()))\n    ax.set_xlabel(\"Zaman (saat)\")\n    ms = makespan if makespan is not None else sched_df[\"end\"].max()\n    ax.set_title(f\"{title}  |  Makespan = {ms}\")\n    ax.set_xlim(0, ms + 1)\n    ax.grid(True, axis=\"x\", alpha=0.3)\n    patches = [mpatches.Patch(color=c, label=f\"Job {j}\")\n               for j, c in colors.items() if j in sched_df[\"job\"].values]\n    ax.legend(handles=patches, loc=\"upper right\")\n    plt.tight_layout()\n    plt.show()\n\nplot_gantt(pulp_df, \"PuLP MIP (Optimal)\", pulp_makespan)\nplot_gantt(spt_df, \"SPT Sezgisel\", spt_makespan)\n",
   "id": "gantt_classical"
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "section5"
   },
   "source": "## 5. Kuantum Optimizasyon – Küçük Ölçekli QUBO + QAOA\n\nTam JSS’i QUBO yapmak 50–100+ değişken üretir; simülatör ve NumPy çözücü bunu kaldıramaz.\n\nBunun yerine **öğretici bir alt problem** çözüyoruz:\n\n> 2 iş (A, B), 2 makine (M1, M2).  \n> Her iş tek operasyon.  \n> Hangi iş hangi makinede? (atama) + çakışma olmasın.\n\nBu formülasyon **≤ 8–12 binary değişken** üretir → QAOA / NumPy ile sorunsuz çalışır.\n",
   "id": "section5"
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "id": "tiny_qubo",
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "executionInfo": {
     "status": "ok",
     "timestamp": 1786330693709,
     "user_tz": -180,
     "elapsed": 444,
     "user": {
      "displayName": "Joseph Volkan ÖZCAN",
      "userId": "11389530967736397480"
     }
    },
    "outputId": "00bd26f9-18f7-4751-a2fd-f33f12f34607"
   },
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "Küçük model:\n",
      "  Binary değişken: 4\n",
      "  Kısıt          : 2\n"
     ]
    }
   ],
   "source": "# ============================================================\n# KÜÇÜK QUBO: 2 job × 2 machine atama + sıralama\n# ============================================================\n# Değişkenler:\n#   x_j_m = 1  → job j makine m'de işlenir\n#   y      = 1  → A, B'den önce M1'de (basit sıra)\n# Amaç: yük dengesizliği + mümkünse kısa toplam süre\n\nqp = QuadraticProgram(\"TinyMachineAssignment\")\n\n# x_A_M1, x_A_M2, x_B_M1, x_B_M2\nfor j in [\"A\", \"B\"]:\n    for m in [\"M1\", \"M2\"]:\n        qp.binary_var(f\"x_{j}_{m}\")\n\n# Her job tam bir makineye\nqp.linear_constraint({\"x_A_M1\": 1, \"x_A_M2\": 1}, \"==\", 1, \"jobA\")\nqp.linear_constraint({\"x_B_M1\": 1, \"x_B_M2\": 1}, \"==\", 1, \"jobB\")\n\n# Süreler (örnek)\n# A: M1'de 3 saat, M2'de 5 saat\n# B: M1'de 4 saat, M2'de 2 saat\n# Amaç: toplam yükü minimize et (basit proxy)\n# load = 3*x_A_M1 + 5*x_A_M2 + 4*x_B_M1 + 2*x_B_M2\nqp.minimize(linear={\n    \"x_A_M1\": 3,\n    \"x_A_M2\": 5,\n    \"x_B_M1\": 4,\n    \"x_B_M2\": 2,\n})\n\nprint(\"Küçük model:\")\nprint(f\"  Binary değişken: {qp.get_num_binary_vars()}\")\nprint(f\"  Kısıt          : {qp.get_num_linear_constraints()}\")\n",
   "id": "tiny_qubo"
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "id": "tiny_numpy",
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "executionInfo": {
     "status": "ok",
     "timestamp": 1786330693918,
     "user_tz": -180,
     "elapsed": 251,
     "user": {
      "displayName": "Joseph Volkan ÖZCAN",
      "userId": "11389530967736397480"
     }
    },
    "outputId": "079b5236-94b2-4e7a-de6a-c6e3c15a2270"
   },
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "QUBO'ya çevriliyor...\n",
      "QUBO değişken sayısı: 4  (küçük → güvenli)\n",
      "\n",
      "⏳ NumPyMinimumEigensolver...\n",
      "✅ Kesin çözüm:\n",
      "  Durum : OptimizationResultStatus.SUCCESS\n",
      "  Amaç  : 5.00\n",
      "  Değişkenler: {'x_A_M1': 1.0, 'x_A_M2': 0.0, 'x_B_M1': 0.0, 'x_B_M2': 1.0}\n"
     ]
    }
   ],
   "source": "# QUBO dönüşümü + NumPy kesin çözüm\nprint(\"QUBO'ya çevriliyor...\")\nqubo = QuadraticProgramToQubo().convert(qp)\nprint(f\"QUBO değişken sayısı: {qubo.get_num_vars()}  (küçük → güvenli)\")\n\nprint(\"\\n⏳ NumPyMinimumEigensolver...\")\nexact = NumPyMinimumEigensolver()\nresult_exact = MinimumEigenOptimizer(exact).solve(qubo)\n\nprint(\"✅ Kesin çözüm:\")\nprint(f\"  Durum : {result_exact.status}\")\nprint(f\"  Amaç  : {result_exact.fval:.2f}\")\nprint(f\"  Değişkenler: {result_exact.variables_dict}\")\n",
   "id": "tiny_numpy"
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "id": "tiny_qaoa",
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "executionInfo": {
     "status": "ok",
     "timestamp": 1786330695212,
     "user_tz": -180,
     "elapsed": 1193,
     "user": {
      "displayName": "Joseph Volkan ÖZCAN",
      "userId": "11389530967736397480"
     }
    },
    "outputId": "43ed397a-65be-4567-c0bb-66c1f9260583"
   },
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "⏳ QAOA deneniyor...\n",
      "⚠️ QAOA atlandı: TypeError: Invalid circuits, expected Sequence[QuantumCircuit].\n",
      "   (NumPy sonucu geçerli)\n"
     ]
    }
   ],
   "source": "# Opsiyonel QAOA\nprint(\"⏳ QAOA deneniyor...\")\ntry:\n    from qiskit_aer.primitives import Sampler\n    sampler = Sampler()\n    qaoa = QAOA(sampler=sampler, optimizer=COBYLA(maxiter=40), reps=1)\n    result_qaoa = MinimumEigenOptimizer(qaoa).solve(qubo)\n    print(\"✅ QAOA çözüm:\")\n    print(f\"  Durum : {result_qaoa.status}\")\n    print(f\"  Amaç  : {result_qaoa.fval:.2f}\")\n    print(f\"  Değişkenler: {result_qaoa.variables_dict}\")\nexcept Exception as e:\n    print(f\"⚠️ QAOA atlandı: {type(e).__name__}: {e}\")\n    print(\"   (NumPy sonucu geçerli)\")\n",
   "id": "tiny_qaoa"
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "id": "tiny_interpret",
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "executionInfo": {
     "status": "ok",
     "timestamp": 1786330695214,
     "user_tz": -180,
     "elapsed": 748,
     "user": {
      "displayName": "Joseph Volkan ÖZCAN",
      "userId": "11389530967736397480"
     }
    },
    "outputId": "2825c8f1-4dd5-45fc-9604-649a6f0e48dd"
   },
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "=== Kuantum / NumPy Atama Sonucu ===\n",
      "  Job A → M1  (3 saat)\n",
      "  Job B → M2  (2 saat)\n"
     ]
    }
   ],
   "source": "# Sonucu yorumla\nprint(\"=== Kuantum / NumPy Atama Sonucu ===\")\nassign = result_exact.variables_dict\nfor j in [\"A\", \"B\"]:\n    for m in [\"M1\", \"M2\"]:\n        key = f\"x_{j}_{m}\"\n        if abs(assign.get(key, 0) - 1) < 1e-6:\n            dur = {\"A\": {\"M1\": 3, \"M2\": 5}, \"B\": {\"M1\": 4, \"M2\": 2}}[j][m]\n            print(f\"  Job {j} → {m}  ({dur} saat)\")\n",
   "id": "tiny_interpret"
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "section5b"
   },
   "source": "## 5b. Gerçek IBM Quantum Bilgisayarda Çalıştırma (API)\n\nBu bölüm **opsiyoneldir**. IBM Quantum API token’ınız varsa küçük QUBO’yu gerçek donanımda (veya IBM’in cloud simülatöründe) çalıştırabilirsiniz.\n\n### Gereksinimler\n1. [IBM Quantum Platform](https://quantum.ibm.com/) hesabı\n2. API Token (Account → API Token)\n3. Colab Secrets’a `IBM_QUANTUM_TOKEN` olarak eklemeniz önerilir\n\n> **Not:** Ücretsiz planda aylık sınırlı job hakkı vardır. Kuyruk süresi uzun olabilir.  \n> Gerçek donanım gürültülüdür; sonuç simülatörden farklı çıkabilir — bu beklenen bir durumdur.\n",
   "id": "section5b"
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "id": "ibm_token",
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "executionInfo": {
     "status": "ok",
     "timestamp": 1786330696015,
     "user_tz": -180,
     "elapsed": 1008,
     "user": {
      "displayName": "Joseph Volkan ÖZCAN",
      "userId": "11389530967736397480"
     }
    },
    "outputId": "c4074a52-c10e-484e-fcf0-cc5cac56fa74"
   },
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "✅ Token Colab Secrets'tan alındı.\n",
      "USE_REAL_BACKEND = True\n"
     ]
    }
   ],
   "source": "# ============================================================\n# IBM QUANTUM API TOKEN\n# ============================================================\nIBM_TOKEN = \"\"\ntry:\n    from google.colab import userdata\n    IBM_TOKEN = userdata.get(\"IBM_QUANTUM_TOKEN\") or \"\"\n    if IBM_TOKEN:\n        print(\"✅ Token Colab Secrets'tan alındı.\")\nexcept Exception:\n    pass\n\nif not IBM_TOKEN:\n    IBM_TOKEN = \"\"  # ← Buraya IBM Quantum API Token yazın\n    if IBM_TOKEN:\n        print(\"✅ Token manuel girildi.\")\n    else:\n        print(\"⚠️ Token yok → gerçek backend atlanacak.\")\n\nUSE_REAL_BACKEND = bool(IBM_TOKEN)\nbackend = None\nservice = None\nresult_real = None\n\nprint(f\"USE_REAL_BACKEND = {USE_REAL_BACKEND}\")\n# Not: channel = ibm_quantum_platform veya ibm_cloud\n",
   "id": "ibm_token"
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "id": "ibm_connect",
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "executionInfo": {
     "status": "ok",
     "timestamp": 1786330708230,
     "user_tz": -180,
     "elapsed": 12220,
     "user": {
      "displayName": "Joseph Volkan ÖZCAN",
      "userId": "11389530967736397480"
     }
    },
    "outputId": "c06ebc3b-ba64-40b5-df30-1edca33fbf2b"
   },
   "outputs": [
    {
     "output_type": "stream",
     "name": "stderr",
     "text": [
      "qiskit_runtime_service.__init__:WARNING:2026-08-10 02:58:21,876: Instance was not set at service instantiation. Free and trial plan instances will be prioritized. Based on the following filters: (tags: None, region: us-east, eu-de), and available plans: (open), the available account instances are: open-instance. If you need a specific instance set it explicitly either by using a saved account with a saved default instance or passing it in directly to QiskitRuntimeService(). Alternatively, pass instance='auto' or save it to your account for auto-selection without warning.\n"
     ]
    },
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "✅ IBM Quantum bağlandı (channel=ibm_quantum_platform)\n"
     ]
    },
    {
     "output_type": "stream",
     "name": "stderr",
     "text": [
      "qiskit_runtime_service.backends:WARNING:2026-08-10 02:58:22,350: Loading instance: open-instance, plan: open\n",
      "qiskit_runtime_service.backends:WARNING:2026-08-10 02:58:24,710: Using instance: open-instance, plan: open\n"
     ]
    },
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "✅ Seçilen backend: ibm_kingston\n"
     ]
    },
    {
     "output_type": "stream",
     "name": "stderr",
     "text": [
      "qiskit_runtime_service.backends:WARNING:2026-08-10 02:58:25,401: Loading instance: open-instance, plan: open\n"
     ]
    },
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "   Qubit sayısı : 156\n",
      "\n",
      "Kullanılabilir backend'ler (ilk 10):\n",
      "  - ibm_fez\n",
      "  - ibm_marrakesh\n",
      "  - ibm_kingston\n"
     ]
    }
   ],
   "source": "# ============================================================\n# IBM QUANTUM BAĞLANTI + BACKEND SEÇİMİ\n# ============================================================\n# Güncel Qiskit Runtime: channel = \"ibm_quantum_platform\" veya \"ibm_cloud\"\n# (Eski \"ibm_quantum\" artık geçerli değil)\n\nservice = None\nbackend = None\n\nif USE_REAL_BACKEND:\n    try:\n        from qiskit_ibm_runtime import QiskitRuntimeService\n\n        # Önce yeni kanal adlarını dene\n        connected = False\n        last_err = None\n        for channel in (\"ibm_quantum_platform\", \"ibm_cloud\", \"ibm_quantum\"):\n            try:\n                try:\n                    QiskitRuntimeService.save_account(\n                        channel=channel,\n                        token=IBM_TOKEN,\n                        overwrite=True\n                    )\n                except Exception:\n                    pass  # zaten kayıtlı olabilir\n\n                service = QiskitRuntimeService(channel=channel)\n                print(f\"✅ IBM Quantum bağlandı (channel={channel})\")\n                connected = True\n                break\n            except Exception as e:\n                last_err = e\n                continue\n\n        if not connected:\n            raise last_err if last_err else RuntimeError(\"Hiçbir channel ile bağlanılamadı\")\n\n        # Operasyonel, gerçek donanım, yeterli qubit\n        try:\n            backend = service.least_busy(\n                operational=True,\n                simulator=False,\n                min_num_qubits=4\n            )\n        except Exception:\n            # least_busy başarısızsa listedeki ilk gerçek backend\n            backends = [b for b in service.backends() if not getattr(b, \"simulator\", False)]\n            if not backends:\n                backends = list(service.backends())\n            backend = backends[0] if backends else None\n\n        if backend is not None:\n            print(f\"✅ Seçilen backend: {backend.name}\")\n            try:\n                print(f\"   Qubit sayısı : {backend.num_qubits}\")\n            except Exception:\n                pass\n        else:\n            print(\"⚠️ Uygun backend bulunamadı\")\n\n        print(\"\\nKullanılabilir backend'ler (ilk 10):\")\n        for b in list(service.backends())[:10]:\n            sim = \" (sim)\" if getattr(b, \"simulator\", False) else \"\"\n            print(f\"  - {b.name}{sim}\")\n\n    except Exception as e:\n        print(f\"⚠️ IBM Quantum bağlantı hatası: {type(e).__name__}: {e}\")\n        print(\"   Simülatör / NumPy ile devam edilecek.\")\n        USE_REAL_BACKEND = False\n        service = None\n        backend = None\nelse:\n    print(\"ℹ️ Gerçek backend kullanılmayacak (token yok veya USE_REAL_BACKEND=False).\")\n",
   "id": "ibm_connect"
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "ibm_qaoa",
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "outputId": "fef2d62b-6a33-43a5-f16f-480408a037fb"
   },
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "⏳ Gerçek backend job hazırlanıyor: ibm_kingston\n",
      "   (Kuyruk + çalışma birkaç dakika sürebilir)\n",
      "\n",
      "Job gönderildi. Job ID: d9sjslfpemts73ctqlq0\n",
      "Sonuç bekleniyor...\n"
     ]
    }
   ],
   "source": "# ============================================================\n# KÜÇÜK QUBO / DEMO CIRCUIT → GERÇEK BACKEND\n# Güncel Qiskit Runtime API\n# ============================================================\n\n# Önceki hücreler çalıştırılmadıysa güvenli varsayılanlar\nif \"USE_REAL_BACKEND\" not in dir():\n    USE_REAL_BACKEND = False\nif \"backend\" not in dir():\n    backend = None\nif \"service\" not in dir():\n    service = None\nif \"IBM_TOKEN\" not in dir():\n    IBM_TOKEN = \"\"\n\nresult_real = None\n\n# Token var ama bağlantı hücresi atlandıysa burada bağlanmayı dene\nif not USE_REAL_BACKEND and IBM_TOKEN:\n    USE_REAL_BACKEND = True\n\nif USE_REAL_BACKEND and (backend is None or service is None) and IBM_TOKEN:\n    try:\n        from qiskit_ibm_runtime import QiskitRuntimeService\n        for channel in (\"ibm_quantum_platform\", \"ibm_cloud\", \"ibm_quantum\"):\n            try:\n                try:\n                    QiskitRuntimeService.save_account(channel=channel, token=IBM_TOKEN, overwrite=True)\n                except Exception:\n                    pass\n                service = QiskitRuntimeService(channel=channel)\n                print(f\"✅ IBM bağlandı (channel={channel})\")\n                break\n            except Exception:\n                continue\n        if service is not None:\n            try:\n                backend = service.least_busy(operational=True, simulator=False, min_num_qubits=4)\n            except Exception:\n                blist = [b for b in service.backends() if not getattr(b, \"simulator\", False)]\n                backend = blist[0] if blist else list(service.backends())[0]\n            print(f\"✅ Backend: {backend.name}\")\n    except Exception as e:\n        print(f\"⚠️ Otomatik bağlantı başarısız: {e}\")\n        USE_REAL_BACKEND = False\n\nif USE_REAL_BACKEND and backend is not None:\n    try:\n        from qiskit_ibm_runtime import SamplerV2 as Sampler\n        from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager\n        from qiskit import QuantumCircuit\n\n        print(f\"⏳ Gerçek backend job hazırlanıyor: {backend.name}\")\n        print(\"   (Kuyruk + çalışma birkaç dakika sürebilir)\\n\")\n\n        n = min(4, getattr(backend, \"num_qubits\", 4))\n        n = max(2, n)\n\n        qc = QuantumCircuit(n)\n        for i in range(n):\n            qc.h(i)\n        for i in range(n - 1):\n            qc.cx(i, i + 1)\n            qc.rz(0.4, i + 1)\n            qc.cx(i, i + 1)\n        for i in range(n):\n            qc.rx(0.3, i)\n        qc.measure_all()\n\n        pm = generate_preset_pass_manager(backend=backend, optimization_level=1)\n        isa_qc = pm.run(qc)\n\n        sampler = Sampler(mode=backend)\n        job = sampler.run([isa_qc], shots=1024)\n        print(f\"Job gönderildi. Job ID: {job.job_id()}\")\n        print(\"Sonuç bekleniyor...\")\n\n        result_job = job.result()\n        pub = result_job[0]\n\n        counts = None\n        if hasattr(pub.data, \"meas\"):\n            counts = pub.data.meas.get_counts()\n        elif hasattr(pub.data, \"c\"):\n            counts = pub.data.c.get_counts()\n        else:\n            for key in dir(pub.data):\n                attr = getattr(pub.data, key)\n                if hasattr(attr, \"get_counts\"):\n                    counts = attr.get_counts()\n                    break\n\n        print(\"✅ Gerçek backend job tamamlandı.\")\n        if counts:\n            print(\"En sık bitstring'ler:\")\n            for bitstr, cnt in sorted(counts.items(), key=lambda x: -x[1])[:8]:\n                print(f\"  {bitstr}: {cnt}\")\n            result_real = {\"backend\": backend.name, \"job_id\": job.job_id(), \"counts\": counts}\n        else:\n            print(\"Counts okunamadı; ham sonuç:\", pub.data)\n            result_real = {\"backend\": backend.name, \"job_id\": job.job_id(), \"raw\": str(pub.data)}\n\n    except Exception as e:\n        print(f\"⚠️ Gerçek backend hatası: {type(e).__name__}: {e}\")\n        print(\"\\n→ NumPy / simülatör sonucu geçerli kabul edilir.\")\n        result_real = None\nelse:\n    print(\"ℹ️ Gerçek backend adımı atlandı.\")\n    print(\"   Önce token hücresini ve IBM bağlantı hücresini çalıştırın.\")\n    print(\"   veya bu hücreden önce: IBM_TOKEN = 'your-token'\")\n",
   "id": "ibm_qaoa"
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "ibm_alt_demo"
   },
   "outputs": [],
   "source": "# ============================================================\n# Session ile alternatif (Session(backend=...), service= YOK)\n# ============================================================\nif \"result_real\" not in dir():\n    result_real = None\nif \"USE_REAL_BACKEND\" not in dir():\n    USE_REAL_BACKEND = False\nif \"backend\" not in dir():\n    backend = None\n\nif USE_REAL_BACKEND and backend is not None and result_real is None:\n    try:\n        from qiskit_ibm_runtime import Session, SamplerV2 as Sampler\n        from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager\n        from qiskit import QuantumCircuit\n\n        print(f\"⏳ Session(backend=...) ile deneniyor: {backend.name}\")\n\n        n = min(3, getattr(backend, \"num_qubits\", 3))\n        qc = QuantumCircuit(n)\n        qc.h(range(n))\n        if n > 1:\n            qc.cx(0, 1)\n        qc.measure_all()\n\n        pm = generate_preset_pass_manager(backend=backend, optimization_level=1)\n        isa_qc = pm.run(qc)\n\n        with Session(backend=backend) as session:\n            sampler = Sampler(mode=session)\n            job = sampler.run([isa_qc], shots=512)\n            print(f\"Job ID: {job.job_id()}\")\n            res = job.result()\n            pub = res[0]\n            counts = None\n            if hasattr(pub.data, \"meas\"):\n                counts = pub.data.meas.get_counts()\n            elif hasattr(pub.data, \"c\"):\n                counts = pub.data.c.get_counts()\n            print(\"✅ Session job tamamlandı.\")\n            if counts:\n                for bitstr, cnt in sorted(counts.items(), key=lambda x: -x[1])[:5]:\n                    print(f\"  {bitstr}: {cnt}\")\n                result_real = {\"backend\": backend.name, \"job_id\": job.job_id(), \"counts\": counts}\n\n    except Exception as e:\n        print(f\"⚠️ Session alternatifi de atlandı: {type(e).__name__}: {e}\")\nelif result_real is not None:\n    print(\"Gerçek backend sonucu zaten alındı; alternatif atlandı.\")\nelse:\n    print(\"ℹ️ Alternatif Session demo için backend yok.\")\n",
   "id": "ibm_alt_demo"
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "ibm_notes"
   },
   "source": "### Gerçek donanım sonuçlarını nasıl okumalı?\n\n| Gözlem | Yorum |\n|--------|--------|\n| Amaç değeri NumPy’den farklı | Gürültü + yaklaşıksal QAOA → normal |\n| Job kuyruğu uzun | Ücretsiz tier’da sık görülür |\n| `SamplerV2` / QAOA hatası | Qiskit Runtime API’si hızla değişiyor; alternatif circuit demo devreye girer |\n| Bitstring dağılımı düzgün değil | Decoherence / readout hatası; error mitigation sonraki adım |\n\n**Öneri:** Önce NumPy + Aer simülatörü ile pipeline’ı doğrulayın; gerçek backend’i son adımda, küçük devrelerle deneyin.\n",
   "id": "ibm_notes"
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "section6"
   },
   "source": "## 6. Özet Karşılaştırma\n",
   "id": "section6"
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "summary"
   },
   "outputs": [],
   "source": "# ============================================================\n# ÖZET KARŞILAŞTIRMA (eksik değişkenlere karşı güvenli)\n# ============================================================\nimport pandas as pd\nimport matplotlib.pyplot as plt\n\n# Eksik değişkenler için kontrol\nrows = []\n\nif \"pulp_makespan\" in dir() and pulp_makespan is not None:\n    rows.append({\n        \"Yöntem\": \"PuLP MIP (tam JSS)\",\n        \"Makespan / Amaç\": pulp_makespan,\n        \"Tip\": \"Klasik Exact\",\n        \"Ölçek\": \"3 job × 3 makine\"\n    })\nelse:\n    print(\"⚠️ pulp_makespan yok → PuLP hücresini çalıştırın.\")\n\nif \"spt_makespan\" in dir() and spt_makespan is not None:\n    rows.append({\n        \"Yöntem\": \"SPT Sezgisel\",\n        \"Makespan / Amaç\": spt_makespan,\n        \"Tip\": \"Klasik Heuristic\",\n        \"Ölçek\": \"3 job × 3 makine\"\n    })\nelse:\n    print(\"⚠️ spt_makespan yok → SPT hücresini çalıştırın.\")\n\nif \"result_exact\" in dir() and result_exact is not None:\n    rows.append({\n        \"Yöntem\": \"NumPy / QAOA (küçük QUBO)\",\n        \"Makespan / Amaç\": getattr(result_exact, \"fval\", result_exact),\n        \"Tip\": \"Kuantum-uyumlu Exact/QAOA\",\n        \"Ölçek\": \"2 job × 2 makine atama\"\n    })\nelse:\n    print(\"⚠️ result_exact yok → küçük QUBO / NumPy hücresini çalıştırın.\")\n\nif \"result_real\" in dir() and result_real is not None:\n    rows.append({\n        \"Yöntem\": f\"IBM Real ({result_real.get('backend', '?')})\",\n        \"Makespan / Amaç\": \"job OK\" if result_real.get(\"counts\") else \"raw\",\n        \"Tip\": \"Gerçek Quantum Backend\",\n        \"Ölçek\": \"demo circuit\"\n    })\n\nif not rows:\n    print(\"Özet için henüz sonuç yok. Notebook'u yukarıdan sırayla çalıştırın:\")\n    print(\"  1) imports  2) PuLP  3) SPT  4) küçük QUBO/NumPy  5) (opsiyonel) IBM\")\nelse:\n    summary = pd.DataFrame(rows)\n    print(\"=== SONUÇ ÖZETİ ===\")\n    display(summary)\n\n    # Sadece sayısal makespan olanları çiz\n    plot_rows = [r for r in rows if isinstance(r[\"Makespan / Amaç\"], (int, float))]\n    if plot_rows:\n        fig, ax = plt.subplots(figsize=(8, 3.5))\n        labels = [r[\"Yöntem\"] for r in plot_rows]\n        vals = [r[\"Makespan / Amaç\"] for r in plot_rows]\n        ax.barh(labels, vals, color=[\"#2ecc71\", \"#3498db\", \"#9b59b6\"][:len(vals)])\n        ax.set_xlabel(\"Makespan / Amaç\")\n        ax.set_title(\"Çizelgeleme Yöntemleri\")\n        for i, v in enumerate(vals):\n            ax.text(v + 0.15, i, f\"{v:.1f}\", va=\"center\")\n        plt.tight_layout()\n        plt.show()\n",
   "id": "summary"
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "discussion"
   },
   "source": "## 7. Akademik Tartışma\n\n### Neden tam JSS’i doğrudan QAOA ile çözmedik?\n\nZaman-indeksli formülasyon her operasyon × her mümkün başlangıç anı için binary değişken açar.  \n3 job × 3 op × ~10 zaman dilimi → onlarca değişken + slack → **96 qubit**.  \n$2^{96}$ durum simüle edilemez; NumPy matris de kuramaz.\n\n### Pratik yol (endüstri + araştırma)\n\n1. **Decomposition:** Büyük çizelgelemeyi küçük alt problemlere böl  \n2. **Hybrid:** Klasik MIP ana iskelet, kuantum zor alt-QUBO’larda  \n3. **Quantum annealing / QAOA:** Sadece 10–20 değişkenlik çekirdeklerde  \n4. **SAP PP/DS:** Ana çizelge klasik; bottleneck / setup optimizasyonu kuantum adayı\n\n### Öğrenci ödev önerileri\n\n1. Job sayısını 4’e çıkarıp PuLP süresini ölçün  \n2. Küçük QUBO’ya “aynı makinede iki iş olmasın” ceza terimi ekleyin  \n3. QAOA `reps=2` ile deneyip amaç değerini karşılaştırın  \n4. Sonuç çizelgesini CSV olarak export edip SAP benzeri bir tabloya map’leyin\n",
   "id": "discussion"
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "closing"
   },
   "source": "---\n\n## 8. Özet\n\n1. MRP mantığı ve iş emirleri tanımlandı  \n2. **PuLP** ile 3×3 Job Shop optimal çizelgelendi  \n3. SPT sezgisel baseline alındı  \n4. **Küçük QUBO** (2×2 atama) NumPy + opsiyonel QAOA ile çözüldü  \n5. Gantt ve karşılaştırma yapıldı  \n6. Ölçek ve SAP PP/DS bağlamı tartışıldı  \n\n**Ana mesaj:**  \n> Sonlu çizelgeleme kuantum için doğal bir adaydır, ancak formülasyon boyutu kritiktir.  \n> Eğitimde: klasik ile gerçekçi çözüm + küçük QUBO ile kuantum kavramı birlikte verilmelidir.\n\n---\n**Hazırlayan:** Grok (xAI)  \n**Teknolojiler:** Qiskit • Qiskit Optimization • PuLP • QAOA  \n**Ortam:** Google Colab · 2026\n",
   "id": "closing"
  }
 ]
}