{"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,"sourceType":"competition"}],"dockerImageVersionId":31239,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import os\nimport random\nimport tempfile\nimport subprocess\nimport numpy as np\nimport pandas as pd\n\nfrom scipy.spatial.transform import Rotation\n\n# an attempt to work from an article\n\nclass TemplateBasedModeler:\n    \n    def __init__(self):\n        self.a_form_params = {\n            'rise': 2.8,\n            'twist': 32.7,\n            'radius': 9.0\n        }\n    \n    def search_templates(self, sequence, max_templates=5):\n        templates = []\n        length = len(sequence)\n        \n        for i in range(max_templates):\n            variation = 0.8 + 0.4 * random.random()\n            params = {\n                'rise': self.a_form_params['rise'] * (0.9 + 0.2 * random.random()),\n                'twist': np.deg2rad(self.a_form_params['twist'] * (0.9 + 0.2 * random.random())),\n                'radius': self.a_form_params['radius'] * variation\n            }\n            \n            coords = []\n            for j in range(length):\n                angle = j * params['twist']\n                x = params['radius'] * np.cos(angle)\n                y = params['radius'] * np.sin(angle)\n                z = j * params['rise']\n                coords.append([x, y, z])\n            \n            template = {\n                'id': f'template_{i}',\n                'coords': np.array(coords),\n                'confidence': 0.9 - i*0.15\n            }\n            templates.append(template)\n        \n        return templates\n    \n    def align_templates(self, templates, sequence):\n        aligned = []\n        \n        for template in templates:\n            coords = template['coords']\n            centered = coords - np.mean(coords, axis=0)\n            \n            if random.random() > 0.3:\n                rotation = Rotation.random().as_matrix()\n                centered = centered @ rotation\n            \n            template['aligned_coords'] = centered\n            aligned.append(template)\n        \n        aligned.sort(key=lambda x: x['confidence'], reverse=True)\n        return aligned[:5]\n\nclass RNAProPredictor:\n    \n    def __init__(self):\n        self.template_modeler = TemplateBasedModeler()\n        self.use_tbm = True\n        \n    def _generate_helix(self, length, params):\n        coords = []\n        for i in range(length):\n            angle = i * params['twist']\n            x = params['radius'] * np.cos(angle)\n            y = params['radius'] * np.sin(angle)\n            z = i * params['rise']\n            coords.append([x, y, z])\n        return np.array(coords)\n    \n    def _add_secondary_structure(self, sequence, coords):\n        length = len(sequence)\n        for i in range(length - 4):\n            pairs = [('G', 'C'), ('C', 'G'), ('A', 'U'), ('U', 'A')]\n            if (sequence[i], sequence[i+4]) in pairs:\n                noise = np.random.normal(0, 0.2, 3)\n                coords[i] += noise\n                coords[i+4] -= noise * 0.8\n        return coords\n    \n    def _generate_de_novo_model(self, sequence):\n        length = len(sequence)\n        variation = 0.8 + 0.4 * random.random()\n        params = {\n            'rise': 2.8 * (0.9 + 0.2 * random.random()),\n            'twist': np.deg2rad(32.7 * (0.9 + 0.2 * random.random())),\n            'radius': 9.0 * variation\n        }\n        \n        base_coords = self._generate_helix(length, params)\n        base_coords = self._add_secondary_structure(sequence, base_coords)\n        \n        if random.random() > 0.7:\n            rotation = Rotation.from_euler('xyz', \n                [random.uniform(-30, 30) for _ in range(3)], degrees=True).as_matrix()\n            base_coords = base_coords @ rotation\n        \n        return base_coords\n    \n    def _generate_variations(self, base_model, sequence, num_variations):\n        variations = []\n        length = len(sequence)\n        \n        for i in range(num_variations):\n            if i == 0:\n                model = base_model.copy()\n            else:\n                model = base_model.copy()\n                scale = 0.1 + i * 0.05\n                \n                for j in range(length):\n                    noise = np.random.normal(0, scale, 3)\n                    model[j] += noise\n                \n                if i >= 2:\n                    rotation = Rotation.random().as_matrix()\n                    model = model @ rotation\n            \n            variations.append(model)\n        \n        return variations\n    \n    def predict_ensemble(self, sequence, num_models=5):\n        models = []\n        \n        if self.use_tbm and len(sequence) > 10:\n            templates = self.template_modeler.search_templates(sequence)\n            aligned_templates = self.template_modeler.align_templates(templates, sequence)\n            \n            for i, template in enumerate(aligned_templates[:min(3, len(aligned_templates))]):\n                base = template['aligned_coords']\n                vars_from_template = self._generate_variations(base, sequence, 2)\n                models.extend(vars_from_template)\n        \n        if len(models) < num_models:\n            de_novo_base = self._generate_de_novo_model(sequence)\n            de_novo_vars = self._generate_variations(de_novo_base, sequence, \n                                                    num_models - len(models))\n            models.extend(de_novo_vars)\n        \n        return models[:num_models]\n\ndef create_kaggle_submission(test_file, output_file='submission.csv'):\n    predictor = RNAProPredictor()\n    test_df = pd.read_csv(test_file)\n    \n    all_data = []\n    \n    for _, row in test_df.iterrows():\n        target_id = row['target_id']\n        sequence = row['sequence']\n        \n        models = predictor.predict_ensemble(sequence, 5)\n        \n        for i, nucleotide in enumerate(sequence):\n            residue_id = f\"{target_id}_{i+1}\"\n            row_data = {\n                'ID': residue_id,\n                'resname': nucleotide,\n                'resid': i + 1\n            }\n            \n            for model_idx, model_coords in enumerate(models, 1):\n                if model_idx <= 5:\n                    row_data[f'x_{model_idx}'] = float(model_coords[i, 0])\n                    row_data[f'y_{model_idx}'] = float(model_coords[i, 1])\n                    row_data[f'z_{model_idx}'] = float(model_coords[i, 2])\n            \n            all_data.append(row_data)\n    \n    submission_df = pd.DataFrame(all_data)\n    \n    column_order = ['ID', 'resname', 'resid']\n    for i in range(1, 6):\n        column_order.extend([f'x_{i}', f'y_{i}', f'z_{i}'])\n    \n    submission_df = submission_df[column_order]\n    submission_df.to_csv(output_file, index=False, float_format='%.3f')\n    \n    return submission_df\n\nsubmission = create_kaggle_submission(\n    '/kaggle/input/stanford-rna-3d-folding-2/test_sequences.csv',\n    'submission.csv'\n)\nsubmission.head()","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2026-01-11T06:47:34.74088Z","iopub.execute_input":"2026-01-11T06:47:34.741037Z","iopub.status.idle":"2026-01-11T06:47:37.762437Z","shell.execute_reply.started":"2026-01-11T06:47:34.741019Z","shell.execute_reply":"2026-01-11T06:47:37.76177Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}