{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":118765,"databundleVersionId":15231210,"isSourceIdPinned":false,"sourceType":"competition"},{"sourceId":713996,"sourceType":"modelInstanceVersion","isSourceIdPinned":false,"modelInstanceId":542581,"modelId":555776}],"dockerImageVersionId":31234,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import torch\nimport torch.nn as nn\nimport pandas as pd\nimport numpy as np\nfrom torch.utils.data import Dataset, DataLoader\nfrom scipy.ndimage import gaussian_filter1d\n\n# ---------------- ML Model ----------------\nWINDOW = 20\nBATCH_SIZE = 1024\nDEVICE = \"cuda\" if torch.cuda.is_available() else \"cpu\"\n\nclass RNAModel(nn.Module):\n    def __init__(self):\n        super().__init__()\n        self.net = nn.Sequential(\n            nn.Linear((2*WINDOW+1)*4, 512),\n            nn.ReLU(),\n            nn.Linear(512, 256),\n            nn.ReLU(),\n            nn.Linear(256, 3)\n        )\n\n    def forward(self, x):\n        return self.net(x)\n\nmodel = RNAModel().to(DEVICE)\nstate = torch.load(\"/kaggle/input/a-model-for-ran-comp/pytorch/default/1/rna_model.pt\", map_location=DEVICE)\nmodel.load_state_dict(state)\nmodel.eval()\n\n# ---------------- One-hot and Dataset ----------------\nMAP = {\"A\":0, \"C\":1, \"G\":2, \"U\":3}\ndef one_hot(seq):\n    arr = np.zeros((len(seq), 4), dtype=np.float32)\n    for i, c in enumerate(seq):\n        arr[i, MAP[c]] = 1.0\n    return arr\n\nclass RNATestDataset(Dataset):\n    def __init__(self, seqs_df):\n        self.samples = []\n        self.ids = []\n        self.seqs = []\n        for _, row in seqs_df.iterrows():\n            tid = row[\"target_id\"]\n            seq = row[\"sequence\"]\n            enc = one_hot(seq)\n            L = len(seq)\n            self.seqs.append(seq)\n            for i in range(L):\n                left = max(0, i - WINDOW)\n                right = min(L, i + WINDOW + 1)\n                x = np.zeros((2*WINDOW+1,4), dtype=np.float32)\n                x[left-i+WINDOW:right-i+WINDOW] = enc[left:right]\n                self.samples.append(torch.from_numpy(x.flatten()))\n                self.ids.append(f\"{tid}_{i+1}\")\n                \n    def __len__(self):\n        return len(self.samples)\n    \n    def __getitem__(self, idx):\n        return self.samples[idx], self.ids[idx]\n\n# ---------------- Submission Generator ----------------\ndef make_submission(test_csv=\"/kaggle/input/stanford-rna-3d-folding-2/test_sequences.csv\",\n                    out_csv=\"submission.csv\"):\n    df = pd.read_csv(test_csv)\n    dataset = RNATestDataset(df)\n    loader = DataLoader(dataset, batch_size=BATCH_SIZE, shuffle=False)\n\n    all_preds = []\n\n    with torch.no_grad():\n        for x, _ in loader:\n            x = x.to(DEVICE)\n            y = model(x)\n            y = y.cpu().numpy()\n            all_preds.append(y)\n\n    preds = np.vstack(all_preds)\n    \n    # Make ensemble of 5 using slight Gaussian noise for diversity\n    ensemble = [preds]\n    for i in range(1,5):\n        noise = np.random.normal(0, 0.05*i, preds.shape)\n        noise = gaussian_filter1d(noise, 1.0, axis=0)\n        ensemble.append(preds + noise)\n    \n    # Generate rows in Kaggle format\n    rows = []\n    idx = 0\n    for _, r in df.iterrows():\n        tid = r[\"target_id\"]\n        seq = r[\"sequence\"]\n        L = len(seq)\n        for i in range(L):\n            row = {\"ID\": f\"{tid}_{i+1}\", \"resname\": seq[i], \"resid\": i+1}\n            for m in range(5):\n                row[f\"x_{m+1}\"] = ensemble[m][idx,0]\n                row[f\"y_{m+1}\"] = ensemble[m][idx,1]\n                row[f\"z_{m+1}\"] = ensemble[m][idx,2]\n            rows.append(row)\n            idx += 1\n\n    out_cols = [\"ID\",\"resname\",\"resid\"] + [f\"{a}_{i}\" for i in range(1,6) for a in (\"x\",\"y\",\"z\")]\n    out = pd.DataFrame(rows)[out_cols]\n    out.to_csv(out_csv, index=False, float_format=\"%.3f\")\n    print(\"submission.csv generated\")\n    return out\n\n# ---------------- Run ----------------\nsubmission = make_submission()\nsubmission.head()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-09T06:43:02.894163Z","iopub.execute_input":"2026-01-09T06:43:02.894559Z","iopub.status.idle":"2026-01-09T06:43:03.774634Z","shell.execute_reply.started":"2026-01-09T06:43:02.894531Z","shell.execute_reply":"2026-01-09T06:43:03.773771Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2026-01-09T06:37:10.678019Z","iopub.execute_input":"2026-01-09T06:37:10.679135Z","iopub.status.idle":"2026-01-09T06:37:10.714542Z","shell.execute_reply.started":"2026-01-09T06:37:10.679099Z","shell.execute_reply":"2026-01-09T06:37:10.713772Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"submission.head(5)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-09T06:43:08.932534Z","iopub.execute_input":"2026-01-09T06:43:08.933383Z","iopub.status.idle":"2026-01-09T06:43:08.949774Z","shell.execute_reply.started":"2026-01-09T06:43:08.933348Z","shell.execute_reply":"2026-01-09T06:43:08.948757Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-09T06:37:12.808899Z","iopub.execute_input":"2026-01-09T06:37:12.809841Z","iopub.status.idle":"2026-01-09T06:37:12.819075Z","shell.execute_reply.started":"2026-01-09T06:37:12.80981Z","shell.execute_reply":"2026-01-09T06:37:12.818272Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-09T06:38:10.140363Z","iopub.execute_input":"2026-01-09T06:38:10.140733Z","iopub.status.idle":"2026-01-09T06:38:10.148121Z","shell.execute_reply.started":"2026-01-09T06:38:10.140705Z","shell.execute_reply":"2026-01-09T06:38:10.147197Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-09T06:38:11.623737Z","iopub.execute_input":"2026-01-09T06:38:11.624041Z","iopub.status.idle":"2026-01-09T06:38:11.825345Z","shell.execute_reply.started":"2026-01-09T06:38:11.624018Z","shell.execute_reply":"2026-01-09T06:38:11.824578Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-09T06:38:34.866013Z","iopub.execute_input":"2026-01-09T06:38:34.866738Z","iopub.status.idle":"2026-01-09T06:38:34.910322Z","shell.execute_reply.started":"2026-01-09T06:38:34.866707Z","shell.execute_reply":"2026-01-09T06:38:34.909483Z"}},"outputs":[],"execution_count":null}]}