{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.12.12","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,"sourceType":"competition"},{"sourceId":290885462,"sourceType":"kernelVersion"}],"dockerImageVersionId":31236,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"!ls /kaggle/input/pm-106414699-at-01-09-2026-06-31-41\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-12T12:42:11.799326Z","iopub.execute_input":"2026-01-12T12:42:11.799669Z","iopub.status.idle":"2026-01-12T12:42:11.952274Z","shell.execute_reply.started":"2026-01-12T12:42:11.799631Z","shell.execute_reply":"2026-01-12T12:42:11.950951Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!pip install /kaggle/input/pm-106414699-at-01-09-2026-06-31-41/*.whl --no-index --no-deps\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-12T12:42:11.955234Z","iopub.execute_input":"2026-01-12T12:42:11.955646Z","iopub.status.idle":"2026-01-12T12:42:31.544397Z","shell.execute_reply.started":"2026-01-12T12:42:11.9556Z","shell.execute_reply":"2026-01-12T12:42:31.543253Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import numpy as np\nimport pandas as pd\nimport torch\nimport torch.nn as nn\ndef gaussian_smooth_1d(x, sigma):\n    if sigma <= 0:\n        return x\n\n    radius = int(3 * sigma)\n    t = np.arange(-radius, radius + 1, dtype=np.float32)\n    kernel = np.exp(-(t ** 2) / (2 * sigma ** 2))\n    kernel /= kernel.sum()\n\n    return np.convolve(x, kernel, mode=\"same\")\nclass BaseRNAHelix:\n    def __init__(self, seed=42):\n        np.random.seed(seed)\n        self.rise = 2.75\n        self.twist = np.deg2rad(32.7)\n        self.radius = 9.0\n\n    def build(self, length):\n        i = np.arange(length, dtype=np.float32)\n        angle = i * self.twist\n\n        r = self.radius - 0.5 * np.sin(0.5 * angle)\n        x = r * np.cos(angle)\n        y = r * np.sin(angle)\n        z = i * self.rise\n\n        coords = np.stack([x, y, z], axis=1)\n\n        coords[:, 0] = gaussian_smooth_1d(coords[:, 0], 1.2)\n        coords[:, 1] = gaussian_smooth_1d(coords[:, 1], 1.2)\n        coords[:, 2] = gaussian_smooth_1d(coords[:, 2], 0.8)\n\n        coords -= coords.mean(axis=0, keepdims=True)\n        return coords.astype(np.float32)\nclass BaseRNAHelix:\n    def __init__(self, seed=42):\n        np.random.seed(seed)\n        self.rise = 2.75\n        self.twist = np.deg2rad(32.7)\n        self.radius = 9.0\n\n    def build(self, length):\n        i = np.arange(length, dtype=np.float32)\n        angle = i * self.twist\n\n        r = self.radius - 0.5 * np.sin(0.5 * angle)\n        x = r * np.cos(angle)\n        y = r * np.sin(angle)\n        z = i * self.rise\n\n        coords = np.stack([x, y, z], axis=1)\n\n        coords[:, 0] = gaussian_smooth_1d(coords[:, 0], 1.2)\n        coords[:, 1] = gaussian_smooth_1d(coords[:, 1], 1.2)\n        coords[:, 2] = gaussian_smooth_1d(coords[:, 2], 0.8)\n\n        coords -= coords.mean(axis=0, keepdims=True)\n        return coords.astype(np.float32)\ndef apply_base_pair_forces(coords, seq, strength=0.05):\n    L = len(seq)\n    coords = coords.copy()\n\n    pairs = {(\"A\",\"U\"),(\"U\",\"A\"),(\"G\",\"C\"),(\"C\",\"G\")}\n\n    for i in range(L):\n        for j in range(i+3, min(i+25, L)):\n            if (seq[i], seq[j]) in pairs:\n                vec = coords[j] - coords[i]\n                dist = np.linalg.norm(vec) + 1e-6\n                force = strength * vec / dist\n                coords[i] += force\n                coords[j] -= force\n\n    return coords\nclass OffsetMLP(nn.Module):\n    def __init__(self):\n        super().__init__()\n        self.net = nn.Sequential(\n            nn.Linear(7, 64),\n            nn.ReLU(),\n            nn.Linear(64, 64),\n            nn.ReLU(),\n            nn.Linear(64, 3)\n        )\n\n    def forward(self, x):\n        return self.net(x)\ndef build_features(coords, seq):\n    nt_map = {\"A\":0,\"U\":1,\"G\":2,\"C\":3}\n    L = len(seq)\n    feats = []\n\n    for i in range(L):\n        prev = coords[i-1] if i > 0 else coords[i]\n        nxt  = coords[i+1] if i < L-1 else coords[i]\n\n        f = np.concatenate([\n            coords[i],\n            nxt - prev,\n            [nt_map[seq[i]]]\n        ])\n        feats.append(f)\n\n    return np.array(feats, dtype=np.float32)\ndef message_passing(coords, steps=3):\n    coords = coords.copy()\n    for _ in range(steps):\n        new = coords.copy()\n        for i in range(1, len(coords)-1):\n            new[i] = 0.5 * coords[i] + 0.25 * (coords[i-1] + coords[i+1])\n        coords = new\n    return coords\ndef refine_structure(coords, seq, offset_model):\n    # Base-pair forces\n    coords = apply_base_pair_forces(coords, seq)\n\n    # ML offsets\n    feats = build_features(coords, seq)\n    with torch.no_grad():\n        offsets = offset_model(torch.from_numpy(feats)).numpy()\n    coords += 0.3 * offsets\n\n    # GNN smoothing\n    coords = message_passing(coords)\n\n    coords -= coords.mean(axis=0)\n    return coords.astype(np.float32)\ndef build_ensemble(seq, offset_model, n_models=5):\n    base = BaseRNAHelix().build(len(seq))\n    base = refine_structure(base, seq, offset_model)\n\n    models = [base]\n\n    for i in range(1, n_models):\n        noise = np.random.normal(0, 0.08 * i, base.shape)\n        noise = np.stack([\n            gaussian_smooth_1d(noise[:, d], 1.1) for d in range(3)\n        ], axis=1)\n        models.append(base + noise)\n\n    return models\ndef create_submission(\n    test_csv=\"/kaggle/input/stanford-rna-3d-folding-2/test_sequences.csv\",\n    out_csv=\"submission.csv\"\n):\n    offset_model = OffsetMLP()\n    offset_model.eval()\n\n    test_df = pd.read_csv(test_csv)\n    rows = []\n\n    for _, row in test_df.iterrows():\n        tid = row[\"target_id\"]\n        seq = row[\"sequence\"]\n\n        models = build_ensemble(seq, offset_model)\n\n        for i, nt in enumerate(seq):\n            entry = {\n                \"ID\": f\"{tid}_{i+1}\",\n                \"resname\": nt,\n                \"resid\": i + 1\n            }\n            for m in range(5):\n                entry[f\"x_{m+1}\"] = models[m][i, 0]\n                entry[f\"y_{m+1}\"] = models[m][i, 1]\n                entry[f\"z_{m+1}\"] = models[m][i, 2]\n            rows.append(entry)\n\n    df = pd.DataFrame(rows)\n\n    cols = [\"ID\",\"resname\",\"resid\"]\n    for i in range(1,6):\n        cols += [f\"x_{i}\",f\"y_{i}\",f\"z_{i}\"]\n\n    df = df[cols]\n    df.to_csv(out_csv, index=False, float_format=\"%.3f\")\n    print(\"submission.csv created\")\n\n    return df\nsubmission = create_submission()\nsubmission.head()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-12T13:28:08.683358Z","iopub.execute_input":"2026-01-12T13:28:08.684411Z","iopub.status.idle":"2026-01-12T13:28:14.593816Z","shell.execute_reply.started":"2026-01-12T13:28:08.684373Z","shell.execute_reply":"2026-01-12T13:28:14.592986Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}