{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "118117a1",
   "metadata": {
    "id": "118117a1"
   },
   "source": "# Q-ALM — Hibrit Çoklu-QPU ALM Hedge Optimizasyonu (BVK Technology)\n\nUçtan uca akış: **sentetik banka bilançosu → BDDK standart şoklarıyla EVE/NII → hedge seçimi QUBO →\nklasik baseline'lar → çoklu kuantum cihaz → hakem → benchmark raporu + denetim izi.**\n\nTasarım ilkeleri (QSCAN ve ROI çalışmasından):\n1. Karar üreten çekirdek klasiktir ve deterministiktir.\n2. Kuantum çözücüler takılabilir modüldür; aynı QUBO tüm cihazlara gider.\n3. Kuantum katkısı yalnızca klasik sezgiseli aynı bütçede istatistiksel olarak geçerse iddia edilir.\n4. Buluta yalnızca anonim QUBO katsayıları çıkar; bilanço çıkmaz.\n5. Her koşu denetim iziyle kaydedilir (backend, kalibrasyon, seed, shot, hata azaltma).\n\nKimlik bilgisi girilmeyen cihazlar **otomatik olarak simülatöre düşer**; notebook her ortamda sonuna kadar çalışır."
  },
  {
   "cell_type": "markdown",
   "id": "987643c4",
   "metadata": {
    "id": "987643c4"
   },
   "source": "## 0. Kurulum"
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "e4bb795d",
   "metadata": {
    "id": "e4bb795d",
    "executionInfo": {
     "status": "ok",
     "timestamp": 1793390625490,
     "user_tz": -180,
     "elapsed": 53437,
     "user": {
      "displayName": "Joseph Volkan ÖZCAN",
      "userId": "11389530967736397480"
     }
    }
   },
   "outputs": [],
   "source": "import sys, subprocess, importlib\n\ndef _pip(*pkgs):\n    subprocess.run([sys.executable, \"-m\", \"pip\", \"install\", \"-q\", *pkgs], check=False)\n\nIN_COLAB = \"google.colab\" in sys.modules\nif IN_COLAB:\n    _pip(\"qiskit>=1.3\", \"qiskit-aer\", \"qiskit-ibm-runtime\", \"pandas\", \"scipy\", \"matplotlib\")\n    # İsteğe bağlı cihaz SDK'ları — yoksa adaptör simülatöre düşer\n    _pip(\"dwave-ocean-sdk\")\n    _pip(\"amazon-braket-sdk\")\n    _pip(\"pyqpanda\")\n    _pip(\"pulser\")"
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "040b69b6",
   "metadata": {
    "id": "040b69b6",
    "executionInfo": {
     "status": "ok",
     "timestamp": 1793390625890,
     "user_tz": -180,
     "elapsed": 397,
     "user": {
      "displayName": "Joseph Volkan ÖZCAN",
      "userId": "11389530967736397480"
     }
    }
   },
   "outputs": [],
   "source": "import os, json, time, math, hashlib, random, dataclasses, itertools, warnings\nfrom dataclasses import dataclass, field, asdict\nfrom typing import Dict, List, Optional, Tuple, Callable\nimport numpy as np\nimport pandas as pd\ntry:\n    display\nexcept NameError:\n    display = print\n\nwarnings.filterwarnings(\"ignore\")\nSEED = 20261029\nrng = np.random.default_rng(SEED)\nrandom.seed(SEED)"
  },
  {
   "cell_type": "markdown",
   "id": "59ffed4c",
   "metadata": {
    "id": "59ffed4c"
   },
   "source": "## 1. Konfigürasyon — cihaz erişimleri\n\nColab'da **Secrets** (🔑) panelinden girin; boş bırakılan cihaz simülatöre düşer."
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "67d77851",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "67d77851",
    "executionInfo": {
     "status": "ok",
     "timestamp": 1793390628518,
     "user_tz": -180,
     "elapsed": 2624,
     "user": {
      "displayName": "Joseph Volkan ÖZCAN",
      "userId": "11389530967736397480"
     }
    },
    "outputId": "602bb7e3-d4c4-4ed8-c7db-ca41348f4a26"
   },
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "{'ibm': '✓ anahtar var', 'origin': '✓ anahtar var', 'dwave': {'token': None}, 'braket': {'aws_region': 'us-east-1', 'device': 'arn:aws:braket:us-east-1::device/qpu/ionq/Aria-1'}, 'pasqal': {'token': None}, 'shots': 2000, 'qaoa_reps': 2, 'classical_time_budget_s': 10.0, 'use_real_hardware': True, 'max_hardware_qubits': 60}\n"
     ]
    }
   ],
   "source": "def _secret(name: str) -> Optional[str]:\n    v = os.environ.get(name)\n    if v:\n        return v\n    if IN_COLAB:\n        try:\n            from google.colab import userdata\n            return userdata.get(name)\n        except Exception:\n            return None\n    return None\n\nCONFIG = {\n    \"ibm\":    {\"token\": _secret(\"IBM_QUANTUM_TOKEN\"), \"instance\": _secret(\"IBM_QUANTUM_INSTANCE\"), \"backend\": \"ibm_fez\"},\n    \"origin\": {\"api_key\": _secret(\"ORIGIN_API_KEY\"), \"chip\": \"wukong\"},\n    \"dwave\":  {\"token\": _secret(\"DWAVE_API_TOKEN\")},\n    \"braket\": {\"aws_region\": _secret(\"AWS_DEFAULT_REGION\") or \"us-east-1\", \"device\": \"arn:aws:braket:us-east-1::device/qpu/ionq/Aria-1\"},\n    \"pasqal\": {\"token\": _secret(\"PASQAL_TOKEN\")},\n    \"shots\": 2000,\n    \"qaoa_reps\": 2,\n    \"classical_time_budget_s\": 10.0,\n    \"use_real_hardware\": True,        # False → tüm adaptörler simülatör\n    \"max_hardware_qubits\": 60,        # bunun üstü gerçek cihaza gönderilmez (maliyet)\n}\nprint({k: (\"✓ anahtar var\" if isinstance(v, dict) and any(v.get(x) for x in (\"token\", \"api_key\")) else v) for k, v in CONFIG.items()})"
  },
  {
   "cell_type": "markdown",
   "id": "15f69f95",
   "metadata": {
    "id": "15f69f95"
   },
   "source": "## 2. Sentetik banka bilançosu\n\nÜç ölçek (küçük/orta/büyük). Nakit akışları BDDK standart yaklaşımındaki 19 vade dilimine yerleştirilir.\nGetiri eğrisi TCMB benzeri bir TL eğrisi ile parametrik üretilir. **Gerçek bankada bu hücre bankanın veri bağlayıcısıyla değişir.**"
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "be905cd8",
   "metadata": {
    "id": "be905cd8",
    "executionInfo": {
     "status": "ok",
     "timestamp": 1793390628521,
     "user_tz": -180,
     "elapsed": 1,
     "user": {
      "displayName": "Joseph Volkan ÖZCAN",
      "userId": "11389530967736397480"
     }
    }
   },
   "outputs": [],
   "source": "# BDDK / Basel IRRBB standart vade dilimleri (yıl cinsinden orta nokta)\nBUCKETS = np.array([0.0028, 0.0417, 0.1667, 0.375, 0.625, 0.875, 1.25, 1.75, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, 8.5, 9.5, 12.5, 17.5, 25.0])\nBUCKET_NAMES = [\"O/N\",\"<1A\",\"1-3A\",\"3-6A\",\"6-9A\",\"9-12A\",\"1-1.5Y\",\"1.5-2Y\",\"2-3Y\",\"3-4Y\",\"4-5Y\",\"5-6Y\",\"6-7Y\",\"7-8Y\",\"8-9Y\",\"9-10Y\",\"10-15Y\",\"15-20Y\",\"20Y+\"]\n\ndef tl_curve(t: np.ndarray, r0=0.40, r_inf=0.22, k=0.9) -> np.ndarray:\n    \"\"\"Nelson-Siegel benzeri basit TL getiri eğrisi (yıllık, sürekli bileşik).\"\"\"\n    return r_inf + (r0 - r_inf) * np.exp(-k * t)\n\n# BDDK standart yaklaşımındaki altı şok (TL için büyüklükler yönetmelikten teyit edilmeli)\nSHOCKS = {\n    \"paralel_yukari\": lambda t: +0.04,\n    \"paralel_asagi\":  lambda t: -0.04,\n    \"diklesme\":       lambda t: -0.025 * np.exp(-t / 4) + 0.03 * (1 - np.exp(-t / 4)),\n    \"duzlesme\":       lambda t: +0.03 * np.exp(-t / 4) - 0.025 * (1 - np.exp(-t / 4)),\n    \"kisa_yukari\":    lambda t: +0.04 * np.exp(-t / 4),\n    \"kisa_asagi\":     lambda t: -0.04 * np.exp(-t / 4),\n}\n\n@dataclass\nclass BankBalanceSheet:\n    name: str\n    total_assets_bn_tl: float\n    equity_bn_tl: float\n    asset_cf: np.ndarray   # vade dilimi başına faize duyarlı aktif nakit akışı (milyar TL)\n    liab_cf: np.ndarray    # pasif\n\ndef synthetic_bank(name: str, total_assets_bn_tl: float, seed: int) -> BankBalanceSheet:\n    r = np.random.default_rng(seed)\n    # Türk bankası profili: kısa vadeli mevduat ağırlıklı pasif, daha uzun vadeli kredi + DİBS aktif\n    asset_w = np.array([3,4,6,6,5,5,6,6,8,7,6,5,4,4,3,3,6,5,8], float); asset_w += r.uniform(-1, 1, 19); asset_w = np.clip(asset_w, 0.5, None)\n    liab_w  = np.array([12,18,20,12,8,6,5,4,4,3,2,1.5,1,0.8,0.6,0.5,0.6,0.5,0.5], float); liab_w += r.uniform(-0.5, 0.5, 19); liab_w = np.clip(liab_w, 0.2, None)\n    rate_sensitive = 0.85 * total_assets_bn_tl\n    equity = 0.085 * total_assets_bn_tl\n    asset_cf = rate_sensitive * asset_w / asset_w.sum() * (1 + tl_curve(BUCKETS) * BUCKETS)   # anapara + faiz kabaca\n    liab_cf  = (rate_sensitive - equity) * liab_w / liab_w.sum() * (1 + tl_curve(BUCKETS) * BUCKETS * 0.8)\n    return BankBalanceSheet(name, total_assets_bn_tl, equity, asset_cf, liab_cf)\n\nBANKS = {\n    \"kucuk\": synthetic_bank(\"Küçük banka (300 mlr TL)\", 300, 1),\n    \"orta\":  synthetic_bank(\"Orta banka (1,2 tr TL)\", 1200, 2),\n    \"buyuk\": synthetic_bank(\"Büyük banka (5 tr TL)\", 5000, 3),\n}"
  },
  {
   "cell_type": "markdown",
   "id": "60dfdab8",
   "metadata": {
    "id": "60dfdab8"
   },
   "source": "## 3. EVE / NII motoru (Q-ALM-3 klasik çekirdek)\n\nEVE = Σ (aktif − pasif nakit akışı) × iskonto. ΔEVE = EVE(şok) − EVE(baz). Rasyo = en kötü ΔEVE / özkaynak (BDDK eşiği %15)."
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "f94c4982",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "f94c4982",
    "executionInfo": {
     "status": "ok",
     "timestamp": 1793390628524,
     "user_tz": -180,
     "elapsed": 2,
     "user": {
      "displayName": "Joseph Volkan ÖZCAN",
      "userId": "11389530967736397480"
     }
    },
    "outputId": "733ce683-29cd-4384-d261-b231f8e1afba"
   },
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "Küçük banka (300 mlr TL)     EVE rasyosu (hedge öncesi): 42.2%\n",
      "Orta banka (1,2 tr TL)       EVE rasyosu (hedge öncesi): 43.7%\n",
      "Büyük banka (5 tr TL)        EVE rasyosu (hedge öncesi): 44.2%\n"
     ]
    }
   ],
   "source": "def discount(t: np.ndarray, shock: Optional[Callable] = None) -> np.ndarray:\n    r = tl_curve(t) + (shock(t) if shock else 0.0)\n    return np.exp(-r * t)\n\ndef eve(asset_cf, liab_cf, shock=None) -> float:\n    d = discount(BUCKETS, shock)\n    return float(((asset_cf - liab_cf) * d).sum())\n\ndef eve_report(asset_cf, liab_cf, equity) -> pd.DataFrame:\n    base = eve(asset_cf, liab_cf)\n    rows = []\n    for nm, s in SHOCKS.items():\n        v = eve(asset_cf, liab_cf, s)\n        rows.append({\"sok\": nm, \"EVE_bn\": v, \"dEVE_bn\": v - base, \"dEVE/ozkaynak\": (v - base) / equity})\n    df = pd.DataFrame(rows)\n    return df\n\ndef eve_ratio(asset_cf, liab_cf, equity) -> float:\n    \"\"\"BDDK standart rasyo: en kötü (en negatif) ΔEVE'nin özkaynağa oranı (pozitif sayı).\"\"\"\n    df = eve_report(asset_cf, liab_cf, equity)\n    return float(max(0.0, -df[\"dEVE/ozkaynak\"].min()))\n\ndef nii_12m(asset_cf, liab_cf, shock=None) -> float:\n    \"\"\"Basit 12 aylık NII: ilk 6 dilimde yeniden fiyatlanan tutarların yeni faizle farkı.\"\"\"\n    m = BUCKETS <= 1.0\n    r = tl_curve(BUCKETS[m]) + (shock(BUCKETS[m]) if shock else 0)\n    return float(((asset_cf[m] - liab_cf[m]) * r * (1 - BUCKETS[m])).sum())\n\nfor key, b in BANKS.items():\n    print(f\"{b.name:28s} EVE rasyosu (hedge öncesi): {eve_ratio(b.asset_cf, b.liab_cf, b.equity_bn_tl):.1%}\")"
  },
  {
   "cell_type": "markdown",
   "id": "25efdedb",
   "metadata": {
    "id": "25efdedb"
   },
   "source": "## 4. Hedge aday havuzu ve QUBO formülasyonu (Q-ALM-1)\n\nAday: sabit faiz öde / değişken al **IRS'ler** farklı vadelerde ve nominallerde. Her adayın vade dilimlerine\netkisi hesaplanır (sabit bacak nakit çıkışı, değişken bacak ilk dilimde). Karar değişkeni ikilidir: aday seçildi / seçilmedi.\n\nAmaç: `min  w_risk · (EVE rasyosu − hedef)²₊  +  w_cost · taşıma maliyeti  +  cezalar (maks. işlem sayısı, karşı taraf limiti)`\n\nΔEVE adaylarda doğrusal olduğu için (iskonto sabit), rasyo = |b + A·x| / özkaynak biçimini alır; karesi QUBO'ya tam oturur."
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "4658364d",
   "metadata": {
    "id": "4658364d",
    "executionInfo": {
     "status": "ok",
     "timestamp": 1793390628563,
     "user_tz": -180,
     "elapsed": 39,
     "user": {
      "displayName": "Joseph Volkan ÖZCAN",
      "userId": "11389530967736397480"
     }
    }
   },
   "outputs": [],
   "source": "@dataclass\nclass HedgeCandidate:\n    id: str\n    tenor_y: float\n    notional_bn: float\n    counterparty: str\n    fixed_rate: float\n    cf_delta: np.ndarray = field(repr=False)   # vade dilimlerine etkisi (aktif−pasif net nakit akışı üzerine)\n    carry_cost_bn: float = 0.0                 # yıllık taşıma maliyeti (bp farkı × nominal)\n\ndef make_candidates(bank: BankBalanceSheet, n: int, seed: int) -> List[HedgeCandidate]:\n    r = np.random.default_rng(seed)\n    tenors = [1, 2, 3, 5, 7, 10]\n    cps = [\"CP-A\", \"CP-B\", \"CP-C\", \"CP-D\"]\n    scale = bank.total_assets_bn_tl * 0.02   # tek işlem ≈ aktifin %1–4'ü\n    out = []\n    for i in range(n):\n        T = tenors[i % len(tenors)]\n        N = scale * r.choice([0.5, 1.0, 2.0])\n        fixed = tl_curve(np.array([T]))[0] + r.normal(0, 0.003)\n        # Sabit faiz ödeyen banka: sabit bacak ödemeleri (pasif gibi) yıllık; değişken bacak alır (ilk dilimde yeniden fiyatlanır → aktif gibi)\n        cf = np.zeros(19)\n        for t_idx, t in enumerate(BUCKETS):\n            if t <= T and t >= 0.9:\n                cf[t_idx] -= N * fixed            # sabit kupon çıkışı\n        cf[np.argmin(np.abs(BUCKETS - T))] -= N   # sabit bacak anapara (notional) çıkışı vade sonunda\n        cf[1] += N                                # değişken bacak: ilk yeniden fiyatlama diliminde notional girişi\n        carry = N * (0.0025 + 0.0002 * T)   # yıllık taşıma maliyeti: bid-ask + CVA/sermaye marjı (25 bp + 2 bp/yıl vade)\n        out.append(HedgeCandidate(f\"IRS{i:02d}_{T}Y\", T, N, cps[i % 4], fixed, cf, carry))\n    return out\n\ndef hedge_effect_matrix(cands: List[HedgeCandidate]) -> Dict[str, np.ndarray]:\n    \"\"\"Her şok için: ΔEVE_shock(x) = b_s + a_s · x  (milyar TL).\"\"\"\n    out = {}\n    for nm, s in SHOCKS.items():\n        d_shock, d_base = discount(BUCKETS, s), discount(BUCKETS)\n        out[nm] = np.array([float((c.cf_delta * (d_shock - d_base)).sum()) for c in cands])\n    return out\n\n@dataclass\nclass QUBO:\n    Q: np.ndarray           # simetrik matris; enerji = x^T Q x (x ikili)\n    offset: float\n    labels: List[str]\n    meta: dict = field(default_factory=dict)\n    def energy(self, x: np.ndarray) -> float:\n        x = np.asarray(x, float)\n        return float(x @ self.Q @ x + self.offset)\n    def anonymized(self) -> dict:\n        \"\"\"Buluta giden tek şey: ölçeklenmiş katsayılar. Bilanço, isim, nominal yok.\"\"\"\n        s = np.abs(self.Q).max() or 1.0\n        return {\"n\": len(self.labels), \"Q\": (self.Q / s).round(6).tolist(), \"scale\": s, \"hash\": hashlib.sha256(self.Q.tobytes()).hexdigest()[:16]}\n\ndef build_hedge_qubo(bank: BankBalanceSheet, cands: List[HedgeCandidate], target_ratio=0.12,\n                     max_trades: int = None, cp_limit_bn: float = None,\n                     w_risk=1.0, w_cost=0.02, w_pen=None) -> QUBO:\n    n = len(cands)\n    E = bank.equity_bn_tl\n    eff = hedge_effect_matrix(cands)\n    base_df = eve_report(bank.asset_cf, bank.liab_cf, E)\n    worst = base_df.sort_values(\"dEVE/ozkaynak\").iloc[0][\"sok\"]     # en kötü şok (baz); diğer şoklar da cezalandırılır\n    Q = np.zeros((n, n)); off = 0.0\n    # Risk terimi: her şok için (b_s + a_s·x)/E'nin hedef eşiğini aşmaması. Yumuşak: ((b+a·x)/E + target)² ağırlıklı (negatif yönde).\n    for nm, a in eff.items():\n        b = float(base_df.loc[base_df.sok == nm, \"dEVE_bn\"].iloc[0])\n        # Negatif (zarar) şoklar tam ağırlık, en kötüsü 3×; pozitif şoklar düşük ağırlık (BDDK yalnızca en kötü negatifi sayar)\n        wgt = w_risk * (3.0 if nm == worst else (1.0 if b < 0 else 0.1))\n        # f(x) = (b + a·x)/E + target  → hedef: f ≥ 0; f² minimize edersek sıfıra çeker (hedefin etrafına)\n        bb, aa = b / E + target_ratio, a / E\n        off += wgt * bb * bb\n        for i in range(n):\n            Q[i, i] += wgt * (2 * bb * aa[i] + aa[i] ** 2)\n            for j in range(i + 1, n):\n                Q[i, j] += wgt * 2 * aa[i] * aa[j]\n    # Taşıma maliyeti (doğrusal)\n    for i, c in enumerate(cands):\n        Q[i, i] += w_cost * c.carry_cost_bn / E * 100\n    # Kardinalite cezası: (Σx − K)² ile K üstüne çıkmayı caydır (yalnızca üst sınır için yaklaşık)\n    if w_pen is None:\n        w_pen = max(0.5, np.abs(np.diag(Q)).max())\n    if max_trades is not None:\n        K = max_trades\n        off += w_pen * K * K\n        for i in range(n):\n            Q[i, i] += w_pen * (1 - 2 * K)\n            for j in range(i + 1, n):\n                Q[i, j] += 2 * w_pen\n    # Karşı taraf limiti: aynı CP'de toplam nominal > limit ise ceza (ikili çiftler)\n    if cp_limit_bn is not None:\n        for i in range(n):\n            for j in range(i + 1, n):\n                if cands[i].counterparty == cands[j].counterparty and cands[i].notional_bn + cands[j].notional_bn > cp_limit_bn:\n                    Q[i, j] += w_pen\n    Q = (Q + Q.T) / 2\n    return QUBO(Q, off, [c.id for c in cands], {\"bank\": bank.name, \"target_ratio\": target_ratio, \"max_trades\": max_trades, \"cp_limit_bn\": cp_limit_bn, \"worst_shock\": worst})\n\ndef evaluate_solution(bank: BankBalanceSheet, cands: List[HedgeCandidate], x: np.ndarray) -> dict:\n    x = np.asarray(x, int)\n    liab = bank.liab_cf.copy(); asset = bank.asset_cf.copy()\n    net_delta = sum(c.cf_delta for c, xi in zip(cands, x) if xi)\n    asset2 = asset + np.where(net_delta > 0, net_delta, 0); liab2 = liab - np.where(net_delta < 0, net_delta, 0)\n    return {\n        \"n_trades\": int(x.sum()),\n        \"notional_bn\": float(sum(c.notional_bn for c, xi in zip(cands, x) if xi)),\n        \"eve_ratio\": eve_ratio(asset2, liab2, bank.equity_bn_tl),\n        \"carry_cost_bn\": float(sum(c.carry_cost_bn for c, xi in zip(cands, x) if xi)),\n    }"
  },
  {
   "cell_type": "markdown",
   "id": "6659e149",
   "metadata": {
    "id": "6659e149"
   },
   "source": "## 5. Klasik çözücüler (baseline)\n\n- **Kesin**: n ≤ 22 için tam sayım; üstü için dal-sınır yerine zaman sınırlı çoklu-başlangıçlı tabu (gap raporlanır).\n- **Sezgisel**: benzetimli tavlama (SA) ve tabu arama. Kuantumun geçmesi gereken çıta budur."
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "a8162160",
   "metadata": {
    "id": "a8162160",
    "executionInfo": {
     "status": "ok",
     "timestamp": 1793390628580,
     "user_tz": -180,
     "elapsed": 16,
     "user": {
      "displayName": "Joseph Volkan ÖZCAN",
      "userId": "11389530967736397480"
     }
    }
   },
   "outputs": [],
   "source": "@dataclass\nclass SolveResult:\n    solver: str\n    x: np.ndarray\n    energy: float\n    wall_s: float\n    queue_s: float = 0.0\n    cost_usd: float = 0.0\n    backend: str = \"cpu\"\n    meta: dict = field(default_factory=dict)\n    top_k: List[Tuple[float, tuple]] = field(default_factory=list)   # (enerji, x) ilk 10\n\ndef _topk_from_samples(q: QUBO, samples: np.ndarray, k=10):\n    seen = {}\n    for s in samples:\n        key = tuple(int(v) for v in s)\n        if key not in seen:\n            seen[key] = q.energy(np.array(key))\n    return sorted(((e, x) for x, e in seen.items()))[:k]\n\ndef solve_exact(q: QUBO, max_n=22) -> Optional[SolveResult]:\n    n = len(q.labels)\n    if n > max_n:\n        return None\n    t0 = time.time()\n    best_e, best_x = math.inf, None\n    # Gray-code benzeri vektörleştirilmiş sayım, 2^n × n bit\n    N = 1 << n\n    chunk = 1 << 16\n    allx = None\n    for start in range(0, N, chunk):\n        idx = np.arange(start, min(N, start + chunk), dtype=np.int64)\n        X = ((idx[:, None] >> np.arange(n)) & 1).astype(float)\n        E = np.einsum(\"bi,ij,bj->b\", X, q.Q, X) + q.offset\n        j = int(E.argmin())\n        if E[j] < best_e:\n            best_e, best_x = float(E[j]), X[j].astype(int)\n    return SolveResult(\"exact_enum\", best_x, best_e, time.time() - t0, meta={\"n\": n})\n\ndef local_search(q: QUBO, x: np.ndarray, max_iter=2000) -> np.ndarray:\n    x = x.copy(); n = len(x); e = q.energy(x)\n    improved = True; it = 0\n    while improved and it < max_iter:\n        improved = False; it += 1\n        for i in np.random.permutation(n):\n            x[i] ^= 1; e2 = q.energy(x)\n            if e2 < e - 1e-12:\n                e = e2; improved = True\n            else:\n                x[i] ^= 1\n    return x\n\ndef solve_sa(q: QUBO, time_budget_s=5.0, seed=0) -> SolveResult:\n    r = np.random.default_rng(seed); n = len(q.labels); t0 = time.time()\n    Q = q.Q; best_x = None; best_e = math.inf; samples = []\n    while time.time() - t0 < time_budget_s:\n        x = r.integers(0, 2, n); e = q.energy(x)\n        T0, T1 = max(1e-3, np.abs(Q).max()), 1e-4; steps = 400 * n\n        for s in range(steps):\n            T = T0 * (T1 / T0) ** (s / steps)\n            i = r.integers(n)\n            # delta of flipping i: Q sym → ΔE = (1-2x_i)(Q_ii + 2 Σ_{j≠i} Q_ij x_j)\n            d = (1 - 2 * x[i]) * (Q[i, i] + 2 * (Q[i] @ x - Q[i, i] * x[i]))\n            if d < 0 or r.random() < math.exp(-d / T):\n                x[i] ^= 1; e += d\n        x = local_search(q, x); e = q.energy(x); samples.append(x.copy())\n        if e < best_e:\n            best_e, best_x = e, x.copy()\n    return SolveResult(\"simulated_annealing\", best_x, best_e, time.time() - t0, meta={\"restarts\": len(samples)}, top_k=_topk_from_samples(q, np.array(samples)))\n\ndef solve_tabu(q: QUBO, time_budget_s=5.0, seed=0, tenure=None) -> SolveResult:\n    r = np.random.default_rng(seed); n = len(q.labels); t0 = time.time(); tenure = tenure or max(3, n // 5)\n    x = r.integers(0, 2, n); e = q.energy(x); best_x, best_e = x.copy(), e; tabu = np.zeros(n, int); it = 0; samples = [x.copy()]\n    while time.time() - t0 < time_budget_s:\n        it += 1\n        deltas = (1 - 2 * x) * (np.diag(q.Q) + 2 * (q.Q @ x - np.diag(q.Q) * x))\n        order = np.argsort(deltas)\n        for i in order:\n            if tabu[i] <= it or e + deltas[i] < best_e:\n                x[i] ^= 1; e += deltas[i]; tabu[i] = it + tenure; break\n        if e < best_e:\n            best_e, best_x = e, x.copy(); samples.append(x.copy())\n        if it % (20 * n) == 0:   # periyodik yeniden başlatma\n            x = r.integers(0, 2, n); e = q.energy(x)\n    return SolveResult(\"tabu_search\", best_x, best_e, time.time() - t0, meta={\"iters\": it}, top_k=_topk_from_samples(q, np.array(samples)))"
  },
  {
   "cell_type": "markdown",
   "id": "2bb57b48",
   "metadata": {
    "id": "2bb57b48"
   },
   "source": "## 6. Kuantum cihaz adaptörleri\n\nOrtak arayüz: `BackendAdapter.sample(qubo, shots) -> (samples, meta)`. Her adaptör:\n- kendi SDK'sı yoksa veya kimlik bilgisi girilmemişse **simülatöre düşer** ve bunu meta'ya yazar,\n- yalnızca `qubo.anonymized()` çıktısını kullanır (ham bilanço buluta çıkmaz),\n- kuyruk süresini, shot sayısını, cihaz kalibrasyon bilgisini ve tahmini maliyeti kaydeder.\n\nGate tabanlı cihazlar (IBM, Origin, IonQ/Braket, Pasqal-dijital) QAOA; D-Wave annealing; Pasqal-analog için QUBO→UD-MIS gömme burada sadece iskelet."
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "ff1688b2",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 310
    },
    "id": "ff1688b2",
    "executionInfo": {
     "status": "ok",
     "timestamp": 1793390673140,
     "user_tz": -180,
     "elapsed": 44558,
     "user": {
      "displayName": "Joseph Volkan ÖZCAN",
      "userId": "11389530967736397480"
     }
    },
    "outputId": "badd54b4-6564-445f-fbb3-32076c9119b4"
   },
   "outputs": [
    {
     "output_type": "stream",
     "name": "stderr",
     "text": [
      "qiskit_runtime_service._discover_account:WARNING:2026-10-30 20:03:50,224: Loading account with the given token. A saved account will not be used.\n",
      "qiskit_runtime_service.__init__:WARNING:2026-10-30 20:04:25,840: 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: student-ahmeteminselvi, student-cesur.bagci, student-gamzekevser.temur, student-ahmet.bilgen, student-gumusemirhan, student-selimserdartafrali, student-onur.gungor2, student-serdalkorkutavci, student-ozlemdundar_sau, student-ozden.ayberk, student-2533101003, student-asyrigel, student-atilla.bcn, student-emre62722, student-yekta.sokelen, student-serhatsevim01, student-ozdemir, student-mehmetbozdemir21, student-hie, student-suleymanemir94041, student-ubilecen, student-pinar.tan.ozcan, student-stglysn, student-h.emredemirel, student-yuce.nursultan, student-tsinanergun, student-cagri20049, student-gulsugun02, student-k.iremkaynar, student-yasinntosun, student-tevfik.sen, student-2533101003, student-denizguzelay, student-muh.oguz.sezgin, open-instance, student-mervekaragoz7, student-cempeker1985, student-cuneytsargullar, student-acarsefa621, student-betul.altinok, student-mustafamurathazneci, student-seo.fates, student-eyup.erkaya, student-onur.gungor2, student-sina.kusoglu. 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",
      "qiskit_runtime_service.backends:WARNING:2026-10-30 20:04:25,841: Using instance: student-ahmeteminselvi, plan: open\n"
     ]
    },
    {
     "output_type": "execute_result",
     "data": {
      "text/plain": [
       "       anahtar                       backend  \\\n",
       "0          ibm                       ibm_fez   \n",
       "1       origin   origin_wukong→aer_simulator   \n",
       "2        dwave      dwave_advantage→neal_sim   \n",
       "3  braket_ionq     braket_ionq→aer_simulator   \n",
       "4       pasqal  pasqal_fresnel→aer_simulator   \n",
       "5      gpu_sim                 aer_simulator   \n",
       "\n",
       "                                           tedarikçi     tür  gerçek  \\\n",
       "0                  IBM Quantum (Heron r2, 156 kübit)    gate    True   \n",
       "1      Origin Quantum (Wukong, 72 kübit) [SİMÜLATÖR]    gate   False   \n",
       "2           D-Wave Advantage (annealing) [SİMÜLATÖR]  anneal   False   \n",
       "3  Amazon Braket (IonQ Aria, tuzaklı iyon) [SİMÜL...    gate   False   \n",
       "4             Pasqal (nötr atom, analog) [SİMÜLATÖR]  analog   False   \n",
       "5                           IBM Qiskit Aer (GPU/CPU)    gate   False   \n",
       "\n",
       "                               düşme nedeni  \n",
       "0                                      None  \n",
       "1  pyqpanda yok: No module named 'pyqpanda'  \n",
       "2                                 token yok  \n",
       "3        braket: You must specify a region.  \n",
       "4                                 token yok  \n",
       "5                                      None  "
      ],
      "text/html": [
       "\n",
       "  <div id=\"df-20063930-30e3-4053-ae9d-9eb652eee76f\" 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>anahtar</th>\n",
       "      <th>backend</th>\n",
       "      <th>tedarikçi</th>\n",
       "      <th>tür</th>\n",
       "      <th>gerçek</th>\n",
       "      <th>düşme nedeni</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>ibm</td>\n",
       "      <td>ibm_fez</td>\n",
       "      <td>IBM Quantum (Heron r2, 156 kübit)</td>\n",
       "      <td>gate</td>\n",
       "      <td>True</td>\n",
       "      <td>None</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>origin</td>\n",
       "      <td>origin_wukong→aer_simulator</td>\n",
       "      <td>Origin Quantum (Wukong, 72 kübit) [SİMÜLATÖR]</td>\n",
       "      <td>gate</td>\n",
       "      <td>False</td>\n",
       "      <td>pyqpanda yok: No module named 'pyqpanda'</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>dwave</td>\n",
       "      <td>dwave_advantage→neal_sim</td>\n",
       "      <td>D-Wave Advantage (annealing) [SİMÜLATÖR]</td>\n",
       "      <td>anneal</td>\n",
       "      <td>False</td>\n",
       "      <td>token yok</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>braket_ionq</td>\n",
       "      <td>braket_ionq→aer_simulator</td>\n",
       "      <td>Amazon Braket (IonQ Aria, tuzaklı iyon) [SİMÜL...</td>\n",
       "      <td>gate</td>\n",
       "      <td>False</td>\n",
       "      <td>braket: You must specify a region.</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>pasqal</td>\n",
       "      <td>pasqal_fresnel→aer_simulator</td>\n",
       "      <td>Pasqal (nötr atom, analog) [SİMÜLATÖR]</td>\n",
       "      <td>analog</td>\n",
       "      <td>False</td>\n",
       "      <td>token yok</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>5</th>\n",
       "      <td>gpu_sim</td>\n",
       "      <td>aer_simulator</td>\n",
       "      <td>IBM Qiskit Aer (GPU/CPU)</td>\n",
       "      <td>gate</td>\n",
       "      <td>False</td>\n",
       "      <td>None</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-20063930-30e3-4053-ae9d-9eb652eee76f')\"\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-20063930-30e3-4053-ae9d-9eb652eee76f 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-20063930-30e3-4053-ae9d-9eb652eee76f');\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\": \"pd\",\n  \"rows\": 6,\n  \"fields\": [\n    {\n      \"column\": \"anahtar\",\n      \"properties\": {\n        \"dtype\": \"string\",\n        \"num_unique_values\": 6,\n        \"samples\": [\n          \"ibm\",\n          \"origin\",\n          \"gpu_sim\"\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    },\n    {\n      \"column\": \"backend\",\n      \"properties\": {\n        \"dtype\": \"string\",\n        \"num_unique_values\": 6,\n        \"samples\": [\n          \"ibm_fez\",\n          \"origin_wukong\\u2192aer_simulator\",\n          \"aer_simulator\"\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    },\n    {\n      \"column\": \"tedarik\\u00e7i\",\n      \"properties\": {\n        \"dtype\": \"string\",\n        \"num_unique_values\": 6,\n        \"samples\": [\n          \"IBM Quantum (Heron r2, 156 k\\u00fcbit)\",\n          \"Origin Quantum (Wukong, 72 k\\u00fcbit) [S\\u0130M\\u00dcLAT\\u00d6R]\",\n          \"IBM Qiskit Aer (GPU/CPU)\"\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    },\n    {\n      \"column\": \"t\\u00fcr\",\n      \"properties\": {\n        \"dtype\": \"string\",\n        \"num_unique_values\": 3,\n        \"samples\": [\n          \"gate\",\n          \"anneal\",\n          \"analog\"\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    },\n    {\n      \"column\": \"ger\\u00e7ek\",\n      \"properties\": {\n        \"dtype\": \"boolean\",\n        \"num_unique_values\": 2,\n        \"samples\": [\n          false,\n          true\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    },\n    {\n      \"column\": \"d\\u00fc\\u015fme nedeni\",\n      \"properties\": {\n        \"dtype\": \"string\",\n        \"num_unique_values\": 3,\n        \"samples\": [\n          \"pyqpanda yok: No module named 'pyqpanda'\",\n          \"token yok\"\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    }\n  ]\n}"
      }
     },
     "metadata": {},
     "execution_count": 8
    }
   ],
   "source": "def qubo_to_ising(Q: np.ndarray) -> Tuple[np.ndarray, np.ndarray, float]:\n    \"\"\"x = (1 - z)/2 dönüşümü. Enerji = Σ h_i z_i + Σ_{i<j} J_ij z_i z_j + c\"\"\"\n    n = len(Q); h = np.zeros(n); J = np.zeros((n, n)); c = 0.0\n    for i in range(n):\n        c += Q[i, i] / 2; h[i] -= Q[i, i] / 2\n        for j in range(i + 1, n):\n            q = Q[i, j] * 2   # simetrik matristen tam çift katsayısı\n            c += q / 4; h[i] -= q / 4; h[j] -= q / 4; J[i, j] += q / 4\n    return h, J, c\n\ndef build_qaoa_circuit(Q: np.ndarray, reps: int, params: np.ndarray):\n    from qiskit import QuantumCircuit\n    n = len(Q); h, J, _ = qubo_to_ising(Q)\n    scale = max(np.abs(h).max(), np.abs(J).max(), 1e-9)\n    h, J = h / scale, J / scale\n    qc = QuantumCircuit(n, n); qc.h(range(n))\n    for p in range(reps):\n        g, b = params[p], params[reps + p]\n        for i in range(n):\n            if abs(h[i]) > 1e-9: qc.rz(2 * g * h[i], i)\n        for i in range(n):\n            for j in range(i + 1, n):\n                if abs(J[i, j]) > 1e-9: qc.rzz(2 * g * J[i, j], i, j)\n        qc.rx(2 * b, range(n))\n    qc.measure(range(n), range(n))\n    return qc\n\ndef counts_to_samples(counts: dict, n: int) -> np.ndarray:\n    rows = []\n    for bitstr, c in counts.items():\n        x = np.array([int(ch) for ch in bitstr[::-1][:n]])   # qiskit little-endian\n        rows.extend([x] * int(c))\n    return np.array(rows)\n\nclass BackendAdapter:\n    name = \"base\"; vendor = \"-\"; kind = \"gate\"; usd_per_min = 0.0\n    def __init__(self, cfg: dict): self.cfg = cfg; self.real = False; self.fallback_reason = None\n    def available(self) -> bool: return False\n    def calibration(self) -> dict: return {}\n    def sample(self, q: QUBO, shots: int, params=None) -> Tuple[np.ndarray, dict]: raise NotImplementedError\n    def cost_usd(self, qpu_seconds: float) -> float: return self.usd_per_min * qpu_seconds / 60\n\nclass AerSimulatorAdapter(BackendAdapter):\n    name = \"aer_simulator\"; vendor = \"IBM Qiskit Aer (GPU/CPU)\"; kind = \"gate\"\n    def __init__(self, cfg, noise_from=None):\n        super().__init__(cfg)\n        from qiskit_aer import AerSimulator\n        self.sim = AerSimulator(seed_simulator=SEED)\n    def available(self): return True\n    def sample(self, q, shots, params=None):\n        from qiskit import transpile\n        n = len(q.labels); reps = CONFIG[\"qaoa_reps\"]\n        params = params if params is not None else np.array([0.6] * reps + [0.4] * reps)\n        qc = transpile(build_qaoa_circuit(q.anonymized_Q, reps, params), self.sim)\n        t0 = time.time(); res = self.sim.run(qc, shots=shots).result(); wall = time.time() - t0\n        return counts_to_samples(res.get_counts(), n), {\"backend\": self.name, \"real_hardware\": False, \"shots\": shots, \"qpu_s\": wall, \"queue_s\": 0.0, \"depth\": qc.depth(), \"reps\": reps}\n\nclass IBMAdapter(BackendAdapter):\n    name = \"ibm_fez\"; vendor = \"IBM Quantum (Heron r2, 156 kübit)\"; usd_per_min = 72.0   # Flex Plan\n    def available(self):\n        if not (CONFIG[\"use_real_hardware\"] and self.cfg.get(\"token\")): self.fallback_reason = \"token yok\"; return False\n        try:\n            from qiskit_ibm_runtime import QiskitRuntimeService\n            self.service = QiskitRuntimeService(channel=\"ibm_quantum_platform\", token=self.cfg[\"token\"], instance=self.cfg.get(\"instance\"))\n            self.backend = self.service.backend(self.cfg[\"backend\"]); return True\n        except Exception as e:\n            self.fallback_reason = f\"bağlantı: {e}\"[:120]; return False\n    def calibration(self):\n        try:\n            p = self.backend.properties(); return {\"last_update\": str(p.last_update_date), \"median_cz_err\": float(np.median([g.parameters[0].value for g in p.gates if g.gate == \"cz\"]))}\n        except Exception: return {}\n    def sample(self, q, shots, params=None):\n        from qiskit import transpile\n        from qiskit_ibm_runtime import SamplerV2\n        n = len(q.labels); reps = CONFIG[\"qaoa_reps\"]\n        params = params if params is not None else np.array([0.6] * reps + [0.4] * reps)\n        qc = transpile(build_qaoa_circuit(q.anonymized_Q, reps, params), self.backend, optimization_level=3, seed_transpiler=SEED)\n        sampler = SamplerV2(mode=self.backend); sampler.options.default_shots = shots\n        sampler.options.dynamical_decoupling.enable = True; sampler.options.twirling.enable_gates = True\n        t0 = time.time(); job = sampler.run([qc]); res = job.result(); wall = time.time() - t0\n        counts = res[0].data.c.get_counts()\n        m = job.metrics(); qpu_s = m.get(\"usage\", {}).get(\"quantum_seconds\", wall)\n        return counts_to_samples(counts, n), {\"backend\": self.backend.name, \"real_hardware\": True, \"shots\": shots, \"qpu_s\": qpu_s, \"queue_s\": max(0.0, wall - qpu_s), \"depth\": qc.depth(), \"reps\": reps, \"job_id\": job.job_id(), \"mitigation\": \"DD+gate twirling\", **self.calibration()}\n\nclass OriginAdapter(BackendAdapter):\n    name = \"origin_wukong\"; vendor = \"Origin Quantum (Wukong, 72 kübit)\"; usd_per_min = 30.0   # VARSAYIM — fiyat kamuya açık değil\n    def available(self):\n        if not (CONFIG[\"use_real_hardware\"] and self.cfg.get(\"api_key\")): self.fallback_reason = \"api_key yok\"; return False\n        try:\n            import pyqpanda as pq; self.pq = pq; return True\n        except Exception as e:\n            self.fallback_reason = f\"pyqpanda yok: {e}\"[:120]; return False\n    def sample(self, q, shots, params=None):\n        # pyQPanda QCloud akışı: OriginQASM/QASM3 üzerinden devre gönderimi. SDK sürümüne göre API adları değişebilir.\n        from qiskit import qasm3\n        n = len(q.labels); reps = CONFIG[\"qaoa_reps\"]\n        params = params if params is not None else np.array([0.6] * reps + [0.4] * reps)\n        qc = build_qaoa_circuit(q.anonymized_Q, reps, params)\n        qasm = qasm3.dumps(qc)\n        pq = self.pq\n        machine = pq.QCloud(); machine.init_qvm(self.cfg[\"api_key\"])\n        prog = pq.convert_qasm_string_to_qprog(qasm, machine)[0] if hasattr(pq, \"convert_qasm_string_to_qprog\") else None\n        t0 = time.time(); result = machine.real_chip_measure(prog, shots, chip_id=pq.real_chip_type.origin_wukong); wall = time.time() - t0\n        counts = {k: int(v * shots) for k, v in result.items()}\n        return counts_to_samples(counts, n), {\"backend\": \"origin_wukong\", \"real_hardware\": True, \"shots\": shots, \"qpu_s\": wall, \"queue_s\": 0.0, \"depth\": qc.depth(), \"reps\": reps, \"note\": \"yalnızca sentetik veri\"}\n\nclass DWaveAdapter(BackendAdapter):\n    name = \"dwave_advantage\"; vendor = \"D-Wave Advantage (annealing)\"; kind = \"anneal\"; usd_per_min = 2000.0 / 60  # yaklaşık; Leap sözleşmesine bağlı\n    def available(self):\n        if not (CONFIG[\"use_real_hardware\"] and self.cfg.get(\"token\")): self.fallback_reason = \"token yok\"; return False\n        try:\n            from dwave.system import DWaveSampler, EmbeddingComposite\n            self.sampler = EmbeddingComposite(DWaveSampler(token=self.cfg[\"token\"])); return True\n        except Exception as e:\n            self.fallback_reason = f\"ocean: {e}\"[:120]; return False\n    def sample(self, q, shots, params=None):\n        import dimod\n        n = len(q.labels); Qd = {(i, j): float(q.anonymized_Q[i, j] * (1 if i == j else 2)) for i in range(n) for j in range(i, n) if abs(q.anonymized_Q[i, j]) > 1e-12}\n        bqm = dimod.BinaryQuadraticModel.from_qubo(Qd)\n        t0 = time.time(); ss = self.sampler.sample(bqm, num_reads=shots, label=\"QALM\"); wall = time.time() - t0\n        samples = np.array([[int(s[i]) for i in range(n)] for s in ss.samples()]); occ = ss.record.num_occurrences\n        samples = np.repeat(samples, occ, axis=0)\n        info = ss.info.get(\"timing\", {})\n        return samples, {\"backend\": \"dwave_advantage\", \"real_hardware\": True, \"shots\": shots, \"qpu_s\": info.get(\"qpu_access_time\", wall * 1e6) / 1e6, \"queue_s\": 0.0, \"chain_break\": float(np.mean(ss.record.chain_break_fraction)) if \"chain_break_fraction\" in ss.record.dtype.names else None}\n\nclass SimulatedAnnealingAdapter(BackendAdapter):\n    \"\"\"D-Wave yoksa dimod/neal yerine kendi SA'mız: annealing kolunun simülatör yedeği.\"\"\"\n    name = \"neal_sim\"; vendor = \"Annealing simülatörü\"; kind = \"anneal\"\n    def available(self): return True\n    def sample(self, q, shots, params=None):\n        r = solve_sa(q, time_budget_s=min(2.0, 0.002 * shots), seed=SEED)\n        samples = np.array([np.array(x) for _, x in r.top_k] or [r.x])\n        return samples, {\"backend\": self.name, \"real_hardware\": False, \"shots\": shots, \"qpu_s\": r.wall_s, \"queue_s\": 0.0}\n\nclass BraketAdapter(BackendAdapter):\n    name = \"braket_ionq\"; vendor = \"Amazon Braket (IonQ Aria, tuzaklı iyon)\"; usd_per_min = 0.0  # Braket: görev + shot başı ücret\n    def available(self):\n        if not CONFIG[\"use_real_hardware\"]: self.fallback_reason = \"use_real_hardware=False\"; return False\n        try:\n            from braket.aws import AwsDevice; self.dev = AwsDevice(self.cfg[\"device\"]); return True\n        except Exception as e:\n            self.fallback_reason = f\"braket: {e}\"[:120]; return False\n    def sample(self, q, shots, params=None):\n        from braket.circuits import Circuit\n        n = len(q.labels); reps = CONFIG[\"qaoa_reps\"]\n        params = params if params is not None else np.array([0.6] * reps + [0.4] * reps)\n        h, J, _ = qubo_to_ising(q.anonymized_Q); s = max(np.abs(h).max(), np.abs(J).max(), 1e-9); h, J = h / s, J / s\n        c = Circuit(); [c.h(i) for i in range(n)]\n        for p in range(reps):\n            g, b = params[p], params[reps + p]\n            for i in range(n):\n                if abs(h[i]) > 1e-9: c.rz(i, 2 * g * h[i])\n            for i in range(n):\n                for j in range(i + 1, n):\n                    if abs(J[i, j]) > 1e-9: c.zz(i, j, 2 * g * J[i, j])\n            [c.rx(i, 2 * b) for i in range(n)]\n        t0 = time.time(); task = self.dev.run(c, shots=shots); res = task.result(); wall = time.time() - t0\n        counts = {k[::-1]: v for k, v in res.measurement_counts.items()}   # Braket big-endian → qiskit düzeni\n        return counts_to_samples(counts, n), {\"backend\": self.dev.name, \"real_hardware\": True, \"shots\": shots, \"qpu_s\": wall, \"queue_s\": 0.0, \"task_arn\": task.id, \"cost_note\": \"$0.30/görev + $0.03/shot (Aria)\"}\n\nclass PasqalAdapter(BackendAdapter):\n    \"\"\"Nötr atom (analog). QUBO → UD-MIS gömmesi tüm QUBO'lar için mümkün değildir; burada iskelet + simülatöre düşüş.\"\"\"\n    name = \"pasqal_fresnel\"; vendor = \"Pasqal (nötr atom, analog)\"; kind = \"analog\"\n    def available(self):\n        if not (CONFIG[\"use_real_hardware\"] and self.cfg.get(\"token\")): self.fallback_reason = \"token yok\"; return False\n        try:\n            import pulser; return True\n        except Exception as e:\n            self.fallback_reason = f\"pulser: {e}\"[:120]; return False\n    def sample(self, q, shots, params=None):\n        raise NotImplementedError(\"QUBO→atom dizilimi gömmesi Faz 3 Ar-Ge kapsamında; şimdilik simülatöre düşer.\")\n\ndef make_adapters() -> Dict[str, BackendAdapter]:\n    adapters = {\n        \"ibm\": IBMAdapter(CONFIG[\"ibm\"]), \"origin\": OriginAdapter(CONFIG[\"origin\"]),\n        \"dwave\": DWaveAdapter(CONFIG[\"dwave\"]), \"braket_ionq\": BraketAdapter(CONFIG[\"braket\"]), \"pasqal\": PasqalAdapter(CONFIG[\"pasqal\"]),\n    }\n    resolved = {}\n    aer = AerSimulatorAdapter(CONFIG); neal = SimulatedAnnealingAdapter(CONFIG)\n    for k, a in adapters.items():\n        if a.available():\n            a.real = True; resolved[k] = a\n        else:\n            fb = neal if a.kind == \"anneal\" else aer\n            resolved[k] = type(f\"{k}_fallback\", (fb.__class__,), {\"name\": f\"{a.name}→{fb.name}\", \"vendor\": a.vendor + \" [SİMÜLATÖR]\", \"kind\": a.kind, \"usd_per_min\": 0.0})(CONFIG)\n            resolved[k].fallback_reason = a.fallback_reason\n    resolved[\"gpu_sim\"] = aer\n    return resolved\n\nADAPTERS = make_adapters()\npd.DataFrame([{\"anahtar\": k, \"backend\": a.name, \"tedarikçi\": a.vendor, \"tür\": a.kind, \"gerçek\": getattr(a, \"real\", False), \"düşme nedeni\": a.fallback_reason} for k, a in ADAPTERS.items()])"
  },
  {
   "cell_type": "markdown",
   "id": "4bab47b4",
   "metadata": {
    "id": "4bab47b4"
   },
   "source": "## 7. Hibrit çözücü: QAOA parametre optimizasyonu + klasik yerel arama (IBM–Vanguard kalıbı)\n\nParametreler **simülatörde** (ucuz) optimize edilir, sonra sabitlenmiş parametrelerle **gerçek cihazda** örneklenir;\ndönen örnekler klasik yerel aramadan geçirilir (sampling-based VQA + local search)."
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "122f0aa3",
   "metadata": {
    "id": "122f0aa3",
    "executionInfo": {
     "status": "ok",
     "timestamp": 1793390673142,
     "user_tz": -180,
     "elapsed": 4,
     "user": {
      "displayName": "Joseph Volkan ÖZCAN",
      "userId": "11389530967736397480"
     }
    }
   },
   "outputs": [],
   "source": "# QUBO'ya anonim matrisi ekleyen küçük yardımcı\ndef _anon_Q(self):\n    return np.array(self.anonymized()[\"Q\"])\nQUBO.anonymized_Q = property(_anon_Q)\n\ndef optimize_qaoa_params(q: QUBO, reps: int, iters: int = 40, shots: int = 500, seed=SEED) -> np.ndarray:\n    \"\"\"CVaR(α=0.25) amaç fonksiyonuyla Nelder-Mead; yalnızca simülatörde çalışır.\"\"\"\n    from scipy.optimize import minimize\n    sim = ADAPTERS[\"gpu_sim\"]\n    def cvar_obj(p):\n        samples, _ = sim.sample(q, shots, params=np.array(p))\n        E = np.sort(np.array([q.energy(s) for s in samples]))\n        return float(E[: max(1, int(0.25 * len(E)))].mean())\n    x0 = np.array([0.6] * reps + [0.4] * reps)\n    res = minimize(cvar_obj, x0, method=\"Nelder-Mead\", options={\"maxiter\": iters, \"xatol\": 1e-2, \"fatol\": 1e-3})\n    return res.x\n\ndef solve_quantum(q: QUBO, adapter: BackendAdapter, shots: int, params: np.ndarray) -> SolveResult:\n    t0 = time.time()\n    samples, meta = adapter.sample(q, shots, params=params)\n    # Klasik post-processing: en iyi 20 benzersiz örneği yerel aramayla iyileştir\n    uniq = {tuple(int(v) for v in s) for s in samples}\n    cand = sorted(uniq, key=lambda x: q.energy(np.array(x)))[:20]\n    best_x, best_e = None, math.inf; improved = []\n    for x in cand:\n        y = local_search(q, np.array(x)); e = q.energy(y); improved.append(y)\n        if e < best_e: best_e, best_x = e, y\n    wall = time.time() - t0\n    cost = adapter.cost_usd(meta.get(\"qpu_s\", 0.0)) if getattr(adapter, \"real\", False) else 0.0\n    return SolveResult(f\"qaoa+ls@{adapter.name}\", best_x, best_e, wall, meta.get(\"queue_s\", 0.0), cost, adapter.name,\n                       {**meta, \"raw_best_energy\": float(min(q.energy(np.array(x)) for x in cand)), \"n_unique_samples\": len(uniq)},\n                       top_k=_topk_from_samples(q, np.array(improved)))"
  },
  {
   "cell_type": "markdown",
   "id": "3f6cefff",
   "metadata": {
    "id": "3f6cefff"
   },
   "source": "## 8. Yönlendirici ve hakem\n\n**Yönlendirici**: problem boyutuna, cihaz türüne, kuyruk ve maliyet bütçesine göre hangi cihazlara gönderileceğini seçer.\n**Hakem**: tüm sonuçları aynı amaç fonksiyonunda kıyaslar, cihazlar arası uyumu ölçer, iş metriğine (EVE rasyosu, taşıma maliyeti) çevirir ve denetim izi yazar."
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "7d849e93",
   "metadata": {
    "id": "7d849e93",
    "executionInfo": {
     "status": "ok",
     "timestamp": 1793390673143,
     "user_tz": -180,
     "elapsed": 0,
     "user": {
      "displayName": "Joseph Volkan ÖZCAN",
      "userId": "11389530967736397480"
     }
    }
   },
   "outputs": [],
   "source": "def route(q: QUBO, adapters: Dict[str, BackendAdapter], budget_usd: float = 50.0) -> List[str]:\n    n = len(q.labels); chosen = [\"gpu_sim\"]\n    for k, a in adapters.items():\n        if k == \"gpu_sim\": continue\n        if n > CONFIG[\"max_hardware_qubits\"] and getattr(a, \"real\", False): continue\n        est = a.cost_usd(5.0) if getattr(a, \"real\", False) else 0.0   # ~5 s QPU varsayımıyla\n        if est > budget_usd: continue\n        if a.kind == \"analog\": continue   # Pasqal gömmesi Faz 3\n        chosen.append(k)\n    return chosen\n\n@dataclass\nclass AuditRecord:\n    run_id: str; timestamp: str; bank: str; qubo_hash: str; n_vars: int; solver: str; backend: str\n    energy: float; wall_s: float; queue_s: float; cost_usd: float; eve_ratio: float; carry_cost_bn: float; n_trades: int\n    real_hardware: bool; meta: dict\n\nAUDIT: List[AuditRecord] = []\n\ndef referee(bank, cands, q: QUBO, results: List[SolveResult], exact: Optional[SolveResult]) -> pd.DataFrame:\n    ref_e = exact.energy if exact else min(r.energy for r in results)\n    rows = []\n    for r in results:\n        ev = evaluate_solution(bank, cands, r.x)\n        rec = AuditRecord(hashlib.md5(f\"{q.meta['bank']}{r.solver}{time.time()}\".encode()).hexdigest()[:10], time.strftime(\"%Y-%m-%dT%H:%M:%S\"),\n                          q.meta[\"bank\"], q.anonymized()[\"hash\"], len(q.labels), r.solver, r.backend, r.energy, r.wall_s, r.queue_s, r.cost_usd,\n                          ev[\"eve_ratio\"], ev[\"carry_cost_bn\"], ev[\"n_trades\"], bool(r.meta.get(\"real_hardware\", False)), {k: v for k, v in r.meta.items() if k != \"real_hardware\"})\n        AUDIT.append(rec)\n        rows.append({\"çözücü\": r.solver, \"enerji\": r.energy, \"gap_%\": 100 * (r.energy - ref_e) / (abs(ref_e) + 1e-9), \"EVE_rasyo\": ev[\"eve_ratio\"],\n                     \"taşıma_mlr\": ev[\"carry_cost_bn\"], \"işlem\": ev[\"n_trades\"], \"süre_s\": r.wall_s, \"kuyruk_s\": r.queue_s, \"maliyet_$\": r.cost_usd, \"gerçek\": rec.real_hardware})\n    df = pd.DataFrame(rows).sort_values(\"enerji\").reset_index(drop=True)\n    # Cihazlar arası uyum: iki kuantum kolunun top-10 kümelerinin Jaccard örtüşmesi\n    qres = [r for r in results if r.solver.startswith(\"qaoa\")]\n    for a, b in itertools.combinations(qres, 2):\n        A = {x for _, x in a.top_k}; B = {x for _, x in b.top_k}\n        jac = len(A & B) / max(1, len(A | B))\n        print(f\"Cihazlar arası uyum  {a.backend} ↔ {b.backend}: Jaccard(top-10) = {jac:.2f}  {'→ sinyal tutarlı' if jac >= 0.3 else '→ gürültü şüphesi'}\")\n    return df"
  },
  {
   "cell_type": "markdown",
   "id": "3142a539",
   "metadata": {
    "id": "3142a539"
   },
   "source": "## 9. Uçtan uca koşu — tek banka, tek problem"
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4ec7bc62",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "4ec7bc62",
    "outputId": "312e4cec-5a5d-4d45-b245-923d5a4a57f8"
   },
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "\n",
      "=== Büyük banka (5 tr TL) | 16 aday | hedef EVE rasyo ≤ 12% | maks 6 işlem\n",
      "Hedge öncesi EVE rasyosu: 44.2%  (en kötü şok: paralel_yukari)\n",
      "Buluta giden içerik: {'n': 16, 'scale': np.float64(5.622397124517773), 'hash': 'b6272c78a1fc9d94'}\n"
     ]
    }
   ],
   "source": "def run_pipeline(bank_key: str, n_cands: int = 16, target_ratio=0.12, max_trades=6, cp_limit_bn=None, shots=None, verbose=True):\n    bank = BANKS[bank_key]; shots = shots or CONFIG[\"shots\"]\n    cands = make_candidates(bank, n_cands, seed=SEED + n_cands)\n    cp_limit = cp_limit_bn or bank.total_assets_bn_tl * 0.03\n    q = build_hedge_qubo(bank, cands, target_ratio, max_trades, cp_limit)\n    if verbose:\n        print(f\"\\n=== {bank.name} | {n_cands} aday | hedef EVE rasyo ≤ {target_ratio:.0%} | maks {max_trades} işlem\")\n        print(f\"Hedge öncesi EVE rasyosu: {eve_ratio(bank.asset_cf, bank.liab_cf, bank.equity_bn_tl):.1%}  (en kötü şok: {q.meta['worst_shock']})\")\n        print(\"Buluta giden içerik:\", {k: v for k, v in q.anonymized().items() if k != 'Q'})\n    results = []\n    exact = solve_exact(q)\n    if exact: results.append(exact)\n    tb = CONFIG[\"classical_time_budget_s\"]\n    results.append(solve_sa(q, tb, seed=SEED)); results.append(solve_tabu(q, tb, seed=SEED))\n    params = optimize_qaoa_params(q, CONFIG[\"qaoa_reps\"])\n    for k in route(q, ADAPTERS):\n        try:\n            results.append(solve_quantum(q, ADAPTERS[k], shots, params))\n        except Exception as e:\n            print(f\"  [{k}] atlandı: {e}\")\n    df = referee(bank, cands, q, results, exact)\n    if verbose:\n        display(df.style.format({\"enerji\": \"{:.4f}\", \"gap_%\": \"{:.2f}\", \"EVE_rasyo\": \"{:.1%}\", \"taşıma_mlr\": \"{:.3f}\", \"süre_s\": \"{:.2f}\", \"kuyruk_s\": \"{:.1f}\", \"maliyet_$\": \"{:.2f}\"}))\n        best = df.iloc[0]; print(f\"Önerilen sepet ({best['çözücü']}): {best['işlem']} işlem, EVE rasyosu {best['EVE_rasyo']:.1%}, taşıma maliyeti {best['taşıma_mlr']:.3f} mlr TL/yıl\")\n    return q, cands, results, df\n\nq, cands, results, df = run_pipeline(\"buyuk\", n_cands=16)"
  },
  {
   "cell_type": "markdown",
   "id": "f9e74c48",
   "metadata": {
    "id": "f9e74c48"
   },
   "source": "## 10. Benchmark protokolü — 3 ölçek × problem boyutları × tekrar\n\nKabul kuralı (plan dokümanı §Benchmark): kuantum kolu, en az bir boyut sınıfında klasik sezgiseli **aynı bütçede** eşleşik testte (p<0,05) geçerse \"hibrit kuantum katkısı\" denir. Aksi hâlde ürün klasik çekirdekle satılır."
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "23cd80d9",
   "metadata": {
    "id": "23cd80d9"
   },
   "outputs": [],
   "source": "from scipy import stats\n\ndef benchmark(sizes=(12, 16, 20), banks=(\"kucuk\", \"orta\", \"buyuk\"), repeats=3, shots=1000):\n    CONFIG[\"classical_time_budget_s\"] = 3.0\n    rows = []\n    for bk in banks:\n        for n in sizes:\n            for rep in range(repeats):\n                global SEED; SEED = 20261029 + rep\n                _, _, res, d = run_pipeline(bk, n_cands=n, shots=shots, verbose=False)\n                for _, r in d.iterrows():\n                    rows.append({\"banka\": bk, \"n\": n, \"tekrar\": rep, **r.to_dict()})\n    return pd.DataFrame(rows)\n\nBENCH = benchmark()\nsummary = BENCH.groupby([\"n\", \"çözücü\"]).agg(gap_ort=(\"gap_%\", \"mean\"), gap_std=(\"gap_%\", \"std\"), sure=(\"süre_s\", \"mean\"), maliyet=(\"maliyet_$\", \"sum\"), EVE=(\"EVE_rasyo\", \"mean\")).round(3)\ndisplay(summary)\n\n# Eşleşik test: her (banka, n, tekrar) için kuantum vs en iyi klasik sezgisel gap'i\npiv = BENCH.pivot_table(index=[\"banka\", \"n\", \"tekrar\"], columns=\"çözücü\", values=\"gap_%\")\nheur = piv[[c for c in piv.columns if c in (\"simulated_annealing\", \"tabu_search\")]].min(axis=1)\nverdicts = []\nfor col in [c for c in piv.columns if c.startswith(\"qaoa\")]:\n    diff = (heur - piv[col]).dropna()\n    if len(diff) >= 3:\n        t = stats.ttest_rel(heur.loc[diff.index], piv[col].loc[diff.index])\n        verdicts.append({\"kuantum_kolu\": col, \"ort_gap_fark(klasik−kuantum)\": diff.mean(), \"p\": t.pvalue, \"karar\": \"HİBRİT KUANTUM KATKISI\" if (t.pvalue < 0.05 and diff.mean() > 0) else \"klasik ürün + Ar-Ge\"})\nVERDICT = pd.DataFrame(verdicts); display(VERDICT)"
  },
  {
   "cell_type": "markdown",
   "id": "6728358e",
   "metadata": {
    "id": "6728358e"
   },
   "source": "## 11. Grafikler"
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1f7d6def",
   "metadata": {
    "id": "1f7d6def"
   },
   "outputs": [],
   "source": "import matplotlib.pyplot as plt\nfig, ax = plt.subplots(1, 2, figsize=(12, 4))\nfor sol, g in BENCH.groupby(\"çözücü\"):\n    gg = g.groupby(\"n\")[\"gap_%\"].mean(); ax[0].plot(gg.index, gg.values, marker=\"o\", label=sol)\nax[0].set_xlabel(\"ikili değişken sayısı\"); ax[0].set_ylabel(\"optimalite boşluğu (%)\"); ax[0].set_title(\"Çözüm kalitesi\"); ax[0].legend(fontsize=7)\nfor sol, g in BENCH.groupby(\"çözücü\"):\n    gg = g.groupby(\"n\")[\"süre_s\"].mean(); ax[1].plot(gg.index, gg.values, marker=\"o\", label=sol)\nax[1].set_yscale(\"log\"); ax[1].set_xlabel(\"ikili değişken sayısı\"); ax[1].set_ylabel(\"süre (s, log)\"); ax[1].set_title(\"Süre (kuyruk dahil)\")\nplt.tight_layout(); plt.show()\n\n# EVE rasyosu: hedge öncesi vs sonrası (büyük banka, son koşu)\nb = BANKS[\"buyuk\"]; before = eve_report(b.asset_cf, b.liab_cf, b.equity_bn_tl)\nplt.figure(figsize=(7, 3)); plt.bar(before[\"sok\"], before[\"dEVE/ozkaynak\"] * 100); plt.axhline(-15, color=\"r\", ls=\"--\", label=\"BDDK eşiği −15%\")\nplt.ylabel(\"ΔEVE / özkaynak (%)\"); plt.title(\"Hedge öncesi şok etkileri — büyük banka\"); plt.xticks(rotation=30); plt.legend(); plt.tight_layout(); plt.show()"
  },
  {
   "cell_type": "markdown",
   "id": "722ef11b",
   "metadata": {
    "id": "722ef11b"
   },
   "source": "## 12. Denetim izi ve rapor çıktıları"
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "59a2f8ad",
   "metadata": {
    "id": "59a2f8ad"
   },
   "outputs": [],
   "source": "audit_df = pd.DataFrame([asdict(a) for a in AUDIT])\naudit_df.to_json(\"qalm_audit_trail.json\", orient=\"records\", indent=2, force_ascii=False)\nBENCH.to_csv(\"qalm_benchmark.csv\", index=False)\nsummary.to_csv(\"qalm_benchmark_summary.csv\")\nVERDICT.to_csv(\"qalm_verdict.csv\", index=False)\nprint(f\"{len(audit_df)} koşu kaydedildi → qalm_audit_trail.json | benchmark → qalm_benchmark.csv\")\nprint(\"\\nGerçek donanımda koşan kayıtlar:\", int(audit_df[\"real_hardware\"].sum()), \"| toplam QPU maliyeti: $%.2f\" % audit_df[\"cost_usd\"].sum())\naudit_df[[\"timestamp\", \"bank\", \"n_vars\", \"solver\", \"backend\", \"energy\", \"eve_ratio\", \"wall_s\", \"cost_usd\", \"real_hardware\"]].tail(12)"
  },
  {
   "cell_type": "markdown",
   "id": "60626c58",
   "metadata": {
    "id": "60626c58"
   },
   "source": "## 13. Sonraki adımlar (koda bağlı)\n\n- [ ] Bölüm 2'yi bankanın gerçek vade dilimi raporuyla değiştir (BDDK IRRBB şablonu → `asset_cf/liab_cf`).\n- [ ] `SHOCKS` büyüklüklerini yönetmeliğin Resmi Gazete metninden teyit et.\n- [ ] Q-ALM-2 (menkul değer yeniden dengeleme) için ikinci QUBO üreticisi: aynı `QUBO` sınıfı, aynı hakem.\n- [ ] Origin ve Pasqal adaptörlerinin SDK çağrılarını güncel sürümle test et (API adları değişebilir).\n- [ ] `max_hardware_qubits` ve bütçeyi Flex Plan kotasına bağla; IBM kalibrasyon verisini yönlendirici skoruna ekle.\n- [ ] Hakem çıktısını bankanın model validasyon şablonuna (girdi, yöntem, sınır, denetim izi) dönüştür."
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "name": "python3"
  },
  "colab": {
   "provenance": [],
   "machine_shape": "hm"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}