{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.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":87793,"databundleVersionId":11403143,"sourceType":"competition"},{"sourceId":10855324,"sourceType":"datasetVersion","datasetId":6742586},{"sourceId":224830487,"sourceType":"kernelVersion"}],"dockerImageVersionId":30918,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import sys\nsys.path.append('/kaggle/usr/lib/ribonanza-tm-score')\n\nfrom metric import score","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-15T15:38:50.669399Z","iopub.execute_input":"2025-03-15T15:38:50.669853Z","iopub.status.idle":"2025-03-15T15:38:50.675615Z","shell.execute_reply.started":"2025-03-15T15:38:50.669822Z","shell.execute_reply":"2025-03-15T15:38:50.673429Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import pandas as pd\nimport numpy as np\nfrom scipy.spatial.transform import Rotation as R\nimport random\nimport kagglehub\n\nseed = 21\nnp.random.seed(seed)\nrandom.seed(seed)\n\ntest_seqs = pd.read_csv('/kaggle/input/stanford-rna-3d-folding/test_sequences.csv')\ntrain_seqs = pd.read_csv('/kaggle/input/stanford-rna-3d-folding/train_sequences.csv')\nvalidation_seqs = pd.read_csv('/kaggle/input/stanford-rna-3d-folding/validation_sequences.csv')\ntrain_labels = pd.read_csv('/kaggle/input/stanford-rna-3d-folding/train_labels.csv')\nvalidation_labels = pd.read_csv('/kaggle/input/stanford-rna-3d-folding/validation_labels.csv')\nsample_submission = pd.read_csv('/kaggle/input/stanford-rna-3d-folding/sample_submission.csv')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-15T15:38:50.677031Z","iopub.execute_input":"2025-03-15T15:38:50.677586Z","iopub.status.idle":"2025-03-15T15:38:51.571060Z","shell.execute_reply.started":"2025-03-15T15:38:50.677547Z","shell.execute_reply":"2025-03-15T15:38:51.568823Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"test_seqs.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-15T15:38:51.573190Z","iopub.execute_input":"2025-03-15T15:38:51.573564Z","iopub.status.idle":"2025-03-15T15:38:51.608687Z","shell.execute_reply.started":"2025-03-15T15:38:51.573533Z","shell.execute_reply":"2025-03-15T15:38:51.607383Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"validation_labels.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-15T15:38:51.610145Z","iopub.execute_input":"2025-03-15T15:38:51.610446Z","iopub.status.idle":"2025-03-15T15:38:51.645334Z","shell.execute_reply.started":"2025-03-15T15:38:51.610427Z","shell.execute_reply":"2025-03-15T15:38:51.643279Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def generate_rna_structure(sequence):\n    \n    n_residues = len(sequence)\n    coordinates = np.zeros((n_residues, 3))\n    \n    # Initialize first 3 residues in a helical pattern\n    for i in range(min(3, n_residues)):\n        angle = i * 0.6  # ~34 degrees between residues\n        coordinates[i] = [10.0 * np.cos(angle), 10.0 * np.sin(angle), i * 2.5]\n    \n    # Track current folding direction (initially along z-axis)\n    current_direction = np.array([0.0, 0.0, 1.0])\n    \n    # Define complementary base pairs\n    complementary = {'G': 'C', 'C': 'G', 'A': 'U', 'U': 'A'}\n    \n    # Build the rest of the structure\n    for i in range(3, n_residues):\n        current_base = sequence[i]\n        has_pair = False\n        pair_idx = -1\n        \n        # Look for complementary bases within 15-nucleotide window\n        window_size = min(i, 15)\n        for j in range(i-window_size, i):\n            if j >= 0 and sequence[j] == complementary.get(current_base, 'X'):\n                has_pair = True\n                pair_idx = j\n                break\n        \n        # 70% chance of forming base pair if found within 10 positions\n        if has_pair and i - pair_idx <= 10 and random.random() < 0.7:\n            # Position current nucleotide to pair with its complement\n            pair_pos = coordinates[pair_idx]\n            random_offset = np.random.normal(0, 1, 3) * 2.0  # Natural variation\n            base_pair_distance = 10.0 + random.uniform(-1.0, 1.0)  # ~10Å is typical\n            \n            # Calculate position relative to structure center\n            center = np.mean(coordinates[:i], axis=0)\n            direction = center - pair_pos\n            direction = direction / (np.linalg.norm(direction) + 1e-10)  # Normalize\n            \n            # Place nucleotide and update direction\n            coordinates[i] = pair_pos + direction * base_pair_distance + random_offset\n            current_direction = np.random.normal(0, 0.3, 3)\n            current_direction = current_direction / (np.linalg.norm(current_direction) + 1e-10)\n        \n        else:\n            # No base-pairing: continue folding with direction changes\n            \n            # 30% chance of significant direction change\n            if random.random() < 0.3:\n                angle = random.uniform(0.2, 0.6)  # 11-34 degrees\n                axis = np.random.normal(0, 1, 3)\n                axis = axis / (np.linalg.norm(axis) + 1e-10)  # Normalize\n                rotation = R.from_rotvec(angle * axis)\n                current_direction = rotation.apply(current_direction)\n            else:\n                # Small random perturbation\n                current_direction += np.random.normal(0, 0.15, 3)\n                current_direction = current_direction / (np.linalg.norm(current_direction) + 1e-10)\n            \n            # Typical nucleotide spacing in RNA: 3.5-4.5Å\n            step_size = random.uniform(3.5, 4.5)\n            coordinates[i] = coordinates[i-1] + step_size * current_direction\n    \n    return coordinates","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-15T15:38:51.646977Z","iopub.execute_input":"2025-03-15T15:38:51.647393Z","iopub.status.idle":"2025-03-15T15:38:51.670515Z","shell.execute_reply.started":"2025-03-15T15:38:51.647355Z","shell.execute_reply":"2025-03-15T15:38:51.669194Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# List to store all prediction records\nall_predictions = []\n\n# For each sequence, generate 5 slightly different structure predictions\nfor _, row in validation_seqs.iterrows():\n    target_id = row['target_id']\n    sequence = row['sequence']\n\n    # Generate 5 different structure models for this RNA\n    predictions = []\n    for i in range(1, 6):\n        # Use different seeds for each prediction to ensure diversity\n        seed_value = hash(target_id) % 10000 + i * 1000\n        predictions.append(generate_rna_structure(sequence))\n\n    # For each residue in the sequence\n    for j in range(len(sequence)):\n        pred_row = {\n            'ID': f\"{target_id}_{j+1}\",\n            'resname': sequence[j],\n            'resid': j + 1\n        }\n\n        # Add coordinates from all 5 predictions\n        for i in range(1, 6):\n            pred_row[f'x_{i}'] = predictions[i-1][j][0]\n            pred_row[f'y_{i}'] = predictions[i-1][j][1]\n            pred_row[f'z_{i}'] = predictions[i-1][j][2]\n\n        all_predictions.append(pred_row)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-15T15:38:51.671639Z","iopub.execute_input":"2025-03-15T15:38:51.672082Z","iopub.status.idle":"2025-03-15T15:38:51.970723Z","shell.execute_reply.started":"2025-03-15T15:38:51.672043Z","shell.execute_reply":"2025-03-15T15:38:51.969309Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Create DataFrame with predictions\nsubmission_df = pd.DataFrame(all_predictions)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-15T15:38:51.972925Z","iopub.execute_input":"2025-03-15T15:38:51.973296Z","iopub.status.idle":"2025-03-15T15:38:51.988210Z","shell.execute_reply.started":"2025-03-15T15:38:51.973265Z","shell.execute_reply":"2025-03-15T15:38:51.987137Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Ensure the submission file has the correct format\ncolumn_order = ['ID', 'resname', 'resid']\nfor i in range(1, 6):\n    for coord in ['x', 'y', 'z']:\n        column_order.append(f'{coord}_{i}')\nsubmission_df = submission_df[column_order]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-15T15:38:51.989418Z","iopub.execute_input":"2025-03-15T15:38:51.989699Z","iopub.status.idle":"2025-03-15T15:38:52.026731Z","shell.execute_reply.started":"2025-03-15T15:38:51.989679Z","shell.execute_reply":"2025-03-15T15:38:52.025104Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Save the submission file\nsubmission_df.to_csv('submission.csv', index=False)\nprint(f\"Generated predictions for {len(validation_seqs)} RNA sequences\")\nsubmission_df.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-15T15:38:52.027705Z","iopub.execute_input":"2025-03-15T15:38:52.028091Z","iopub.status.idle":"2025-03-15T15:38:52.122609Z","shell.execute_reply.started":"2025-03-15T15:38:52.028056Z","shell.execute_reply":"2025-03-15T15:38:52.120929Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Evaluation\n-------------","metadata":{}},{"cell_type":"code","source":"submission_df = pd.read_csv('submission.csv')\nsubmission_df['target_id'] = submission_df['ID'].str.split('_').str[0]\n\nMAX_LEN = 700\n\n# the three variants\ndef crop_pad_zero_tail(df, max_len=MAX_LEN):\n    \"\"\"\n    Truncate sequences beyond max_len by zeroing out coordinate values.\n    Preserves original data for residues within the length threshold,\n    while setting coordinates of longer sequences to (0,0,0) origin.\n    \"\"\"\n    out = df.copy()\n    mask = out['resid'] > max_len\n    for i in range(1, 6):\n        out.loc[mask, f'x_{i}'] = 0.0\n        out.loc[mask, f'y_{i}'] = 0.0\n        out.loc[mask, f'z_{i}'] = 0.0\n    return out\n\ndef crop_pad_centroid_tail(df, max_len=MAX_LEN):\n    \"\"\"\n    Truncate sequences beyond max_len by replacing tail coordinates\n    with the centroid of the original sequence's first max_len residues.\n    Maintains structural context by using the average position of\n    the initial sequence segment for longer protein chains.\n    \"\"\"\n    out = df.copy()\n    for tid, sub in df.groupby('target_id'):\n        idx_crop = sub[sub['resid'] <= max_len].index\n        idx_tail = sub[sub['resid'] >  max_len].index\n        for i in range(1, 6):\n            xs = df.loc[idx_crop, f'x_{i}']\n            ys = df.loc[idx_crop, f'y_{i}']\n            zs = df.loc[idx_crop, f'z_{i}']\n            cent = np.array([xs.mean(), ys.mean(), zs.mean()])\n            out.loc[idx_tail, f'x_{i}'] = cent[0]\n            out.loc[idx_tail, f'y_{i}'] = cent[1]\n            out.loc[idx_tail, f'z_{i}'] = cent[2]\n    return out\n\n# prepare each submission\nsub_full           = submission_df.copy()                               # baseline\nsub_zero_tail      = crop_pad_zero_tail(submission_df)                  # zero → tail\nsub_centroid_tail  = crop_pad_centroid_tail(submission_df)              # centroid → tail\n\n# score them\ntm_full          = score(validation_labels, sub_full.copy(),           row_id_column_name='ID')\ntm_zero_tail     = score(validation_labels, sub_zero_tail.copy(),      row_id_column_name='ID')\ntm_centroid_tail = score(validation_labels, sub_centroid_tail.copy(),  row_id_column_name='ID')\n\n# print results\nprint(f\"Full-length TM-score:             {tm_full:.4f}\")\nprint(f\"Zero-fill tail (>700) TM-score:   {tm_zero_tail:.4f}\")\nprint(f\"Centroid-fill tail (>700) TM-score: {tm_centroid_tail:.4f}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-15T15:38:52.123527Z","iopub.execute_input":"2025-03-15T15:38:52.123831Z","iopub.status.idle":"2025-03-15T15:40:07.466901Z","shell.execute_reply.started":"2025-03-15T15:38:52.123800Z","shell.execute_reply":"2025-03-15T15:40:07.465295Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}