{"metadata":{"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceId":59575,"databundleVersionId":8060720,"sourceType":"competition"},{"sourceId":8246447,"sourceType":"datasetVersion","datasetId":4892374},{"sourceId":8413600,"sourceType":"datasetVersion","datasetId":5007812},{"sourceId":8479599,"sourceType":"datasetVersion","datasetId":4517815},{"sourceId":8553271,"sourceType":"datasetVersion","datasetId":5109610},{"sourceId":8904720,"sourceType":"datasetVersion","datasetId":5353700},{"sourceId":174185912,"sourceType":"kernelVersion"}],"dockerImageVersionId":30699,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true},"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.10.13"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"!pip install /kaggle/input/whoosh-wheel-2-7-4/Whoosh-2.7.4-py2.py3-none-any.whl","metadata":{"_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","execution":{"iopub.status.busy":"2024-07-08T21:55:24.298456Z","iopub.execute_input":"2024-07-08T21:55:24.299347Z","iopub.status.idle":"2024-07-08T21:55:56.461006Z","shell.execute_reply.started":"2024-07-08T21:55:24.299312Z","shell.execute_reply":"2024-07-08T21:55:56.459852Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!pip install /kaggle/input/simanneal/simanneal-0.5.0-py2.py3-none-any.whl","metadata":{"execution":{"iopub.status.busy":"2024-07-08T21:55:56.463157Z","iopub.execute_input":"2024-07-08T21:55:56.463487Z","iopub.status.idle":"2024-07-08T21:56:28.515764Z","shell.execute_reply.started":"2024-07-08T21:55:56.463458Z","shell.execute_reply":"2024-07-08T21:56:28.514851Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from pathlib import Path\nimport polars as pl\nfrom tqdm import tqdm\nimport whoosh_utils\nfrom typing import Any, Tuple, List\nfrom concurrent.futures import ThreadPoolExecutor, as_completed\n\n\nimport pickle\nfrom dataclasses import dataclass\n\nimport numpy as np\nfrom numpy.typing import NDArray\n\nfrom simanneal import Annealer","metadata":{"execution":{"iopub.status.busy":"2024-07-08T21:56:28.517441Z","iopub.execute_input":"2024-07-08T21:56:28.517761Z","iopub.status.idle":"2024-07-08T21:56:28.525461Z","shell.execute_reply.started":"2024-07-08T21:56:28.517732Z","shell.execute_reply":"2024-07-08T21:56:28.524545Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"data_dir = Path(\"/kaggle/input/uspto-explainable-ai\")\npatent_metadata_path = data_dir / \"patent_metadata.parquet\"","metadata":{"execution":{"iopub.status.busy":"2024-07-08T21:56:28.528391Z","iopub.execute_input":"2024-07-08T21:56:28.529002Z","iopub.status.idle":"2024-07-08T21:56:28.537052Z","shell.execute_reply.started":"2024-07-08T21:56:28.528971Z","shell.execute_reply":"2024-07-08T21:56:28.536275Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"meta = (\n    pl.scan_parquet(patent_metadata_path)# Read the Parquet file\n    .with_columns(\n        pl.col(\"publication_date\").dt.year().alias(\"year\"),# Extract year and rename it as \"year\"\n        pl.col(\"publication_date\").dt.month().alias(\"month\"),# Extract month and rename it as \"month”\n    )\n    .filter(pl.col(\"publication_date\") >= pl.date(1975, 1, 1))  # Filter data to include records from 1975-01-01 onwards\n    .rename({\"cpc_codes\": \"cpc\"})# Rename the column \"cpc_codes\" to \"cpc\"\n    .collect() # Collect the data into memory and create the final DataFrame\n)","metadata":{"execution":{"iopub.status.busy":"2024-07-08T21:56:28.538241Z","iopub.execute_input":"2024-07-08T21:56:28.538558Z","iopub.status.idle":"2024-07-08T21:56:33.632342Z","shell.execute_reply.started":"2024-07-08T21:56:28.538534Z","shell.execute_reply":"2024-07-08T21:56:33.631318Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Melt the data and collect unique information\ntest_nn = pl.scan_csv(data_dir / \"test.csv\")\nunique_publications = test_nn.melt().collect().get_column(\"value\").unique()\n\n# Filter for patent metadata present only in the test dat\nfiltered_meta = meta.filter(pl.col(\"publication_number\").is_in(unique_publications))","metadata":{"execution":{"iopub.status.busy":"2024-07-08T21:56:33.633689Z","iopub.execute_input":"2024-07-08T21:56:33.634074Z","iopub.status.idle":"2024-07-08T21:56:33.698834Z","shell.execute_reply.started":"2024-07-08T21:56:33.634042Z","shell.execute_reply":"2024-07-08T21:56:33.698109Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Prepare to join with metadata\npatents = []\nunique_year_month = filtered_meta.select([\"year\", \"month\"]).unique()\n\nwith ThreadPoolExecutor() as executor:\n    futures = []\n    for year, month in tqdm(unique_year_month.iter_rows(), total=len(unique_year_month)):\n        patent_file_path = data_dir / f\"patent_data/{year}_{month}.parquet\"\n        \n        future = executor.submit(\n            lambda path: pl.scan_parquet(path).select(pl.exclude([\"claims\", \"description\"])),\n            patent_file_path\n        )\n        futures.append(future)\n\n    for future in as_completed(futures):\n        patent_data = future.result()\n        patents.append(patent_data)\n\n# Combine all patent data\nall_patents = pl.concat(patents)\n\n# Add empty \"claims\" and \"description\" columns to patent data\nall_patents = all_patents.with_columns(\n    pl.lit(\"\").alias(\"claims\"),\n    pl.lit(\"\").alias(\"description\")\n)\n\n# Perform delayed join between patent data and metadata\nmeta_with_text = (\n    filtered_meta.lazy()\n    .join(all_patents, on=\"publication_number\", how=\"left\")\n    .collect(streaming=True)\n)\n\n# Save the result as a Parquet file\noutput_path = \"meta_with_text.parquet\"\nmeta_with_text.write_parquet(output_path)","metadata":{"execution":{"iopub.status.busy":"2024-07-08T21:56:33.699861Z","iopub.execute_input":"2024-07-08T21:56:33.700135Z","iopub.status.idle":"2024-07-08T21:56:42.109734Z","shell.execute_reply.started":"2024-07-08T21:56:33.700111Z","shell.execute_reply":"2024-07-08T21:56:42.108959Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Create Index\ndocuments = meta_with_text.to_dicts()\nindex_dir = \"test_index\"\nPath(index_dir).mkdir(parents=True, exist_ok=True)\nwhoosh_utils.create_index(index_dir, documents)","metadata":{"execution":{"iopub.status.busy":"2024-07-08T21:56:42.110980Z","iopub.execute_input":"2024-07-08T21:56:42.111689Z","iopub.status.idle":"2024-07-08T21:56:42.863436Z","shell.execute_reply.started":"2024-07-08T21:56:42.111651Z","shell.execute_reply":"2024-07-08T21:56:42.862714Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Function: Select top k columns by row sums\ndef select_top_k_columns(X: Any, k: int) -> Tuple[Any, NDArray]:\n    \"\"\"\n    Selects the top k columns from the given matrix X based on their row-wise sums.\n\n    Args:\n        X (Any): Input matrix.\n        k (int): Number of columns to select.\n\n    Returns:\n        Tuple[Any, NDArray]: Subset matrix of the top k columns and their indices.\n    \"\"\"\n    # Calculate row-wise sums\n    row_sums = X.sum(axis=0)\n    \n    # Get indices of top k columns based on sums\n    top_k_indices = np.argsort(-row_sums.A1)[:k]\n    \n    # Select top k columns\n    X_top = X[:, top_k_indices]\n\n    return X_top, top_k_indices","metadata":{"execution":{"iopub.status.busy":"2024-07-08T21:56:42.864541Z","iopub.execute_input":"2024-07-08T21:56:42.864848Z","iopub.status.idle":"2024-07-08T21:56:42.870861Z","shell.execute_reply.started":"2024-07-08T21:56:42.864823Z","shell.execute_reply":"2024-07-08T21:56:42.869840Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Function: Calculate Average Precision@50\ndef ap50(preds: List[str], labels: List[str]) -> float:\n    \"\"\"\n    Calculate Average Precision@50 (AP@50).\n\n    Args:\n        preds (list[str]): List of predicted results.\n        labels (list[str]): List of actual labels.\n\n    Returns:\n        float: AP@50 score.\n    \"\"\"\n    precisions = []\n    n_found = 0\n    \n    for e, pred in enumerate(preds):\n        if pred in labels:\n            n_found += 1\n        precisions.append(n_found / (e + 1))\n    \n    return sum(precisions) / 50","metadata":{"execution":{"iopub.status.busy":"2024-07-08T21:56:42.875028Z","iopub.execute_input":"2024-07-08T21:56:42.875647Z","iopub.status.idle":"2024-07-08T21:56:42.884153Z","shell.execute_reply.started":"2024-07-08T21:56:42.875614Z","shell.execute_reply":"2024-07-08T21:56:42.883358Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Data class: Word\n@dataclass\nclass Word:\n    category: str\n    content: str\n\n    def to_str(self) -> str:\n        \"\"\"\n        Convert the Word object to its string representation.\n        \n        Returns:\n            str: String representation of the word.\n        \"\"\"\n        return f\"{self.category}:{self.content}\"","metadata":{"execution":{"iopub.status.busy":"2024-07-08T21:56:42.885232Z","iopub.execute_input":"2024-07-08T21:56:42.885519Z","iopub.status.idle":"2024-07-08T21:56:42.894599Z","shell.execute_reply.started":"2024-07-08T21:56:42.885495Z","shell.execute_reply":"2024-07-08T21:56:42.893768Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Data class: State\n@dataclass\nclass State:\n    words: List[Word]\n\n    def __post_init__(self):\n        \"\"\"\n        Initialize the state object, randomly decide if each word is used.\n        \"\"\"\n        self.use = np.random.binomial(1, 0.5, len(self.words))\n\n    def to_query(self) -> str:\n        \"\"\"\n        Convert the state object to a query string.\n        \n        Returns:\n            str: Query string.\n        \"\"\"\n        return \" OR \".join(word.to_str() for word, use in zip(self.words, self.use) if use)\n\n    def move_1(self):\n        \"\"\"\n        Randomly change the usage state of one word.\n        \n        Returns:\n            State: Updated state object.\n        \"\"\"\n        idx = np.random.choice(len(self.words))\n        self.use[idx] = 1 - self.use[idx]  # Toggle between 0 and 1\n        return self","metadata":{"execution":{"iopub.status.busy":"2024-07-08T21:56:42.895944Z","iopub.execute_input":"2024-07-08T21:56:42.896302Z","iopub.status.idle":"2024-07-08T21:56:42.906259Z","shell.execute_reply.started":"2024-07-08T21:56:42.896272Z","shell.execute_reply":"2024-07-08T21:56:42.905406Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Class: USPTOProblem using Simulated Annealing Algorithm\nclass USPTOProblem(Annealer):\n    def __init__(\n        self,\n        query_parser: Any,\n        searcher: Any,\n        target: List[str],\n        init_state: State,\n        tmax: int = 30,\n        tmin: int = 10,\n        steps: int = 100,\n        max_time: int = 8,\n        copy_strategy: str = \"deepcopy\",\n    ):\n        \"\"\"\n        Initialize the USPTO problem.\n\n        Args:\n            query_parser (Any): Query parser object.\n            searcher (Any): Searcher object for executing searches.\n            target (list[str]): List of target labels.\n            init_state (State): Initial state object.\n            tmax (int): Maximum temperature for simulated annealing.\n            tmin (int): Minimum temperature for simulated annealing.\n            steps (int): Number of steps in the annealing process.\n            max_time (int): Maximum running time in seconds.\n            copy_strategy (str): Copy strategy (\"deepcopy\" or \"slice\").\n        \"\"\"\n        super(USPTOProblem, self).__init__(init_state)\n        self.qp = query_parser\n        self.searcher = searcher\n        self.target = target\n        self.Tmax = tmax\n        self.Tmin = tmin\n        self.steps = steps\n        self.max_time = max_time\n        self.copy_strategy = copy_strategy\n\n    def move(self):\n        \"\"\"\n        Perform a move operation on the state.\n        \"\"\"\n        self.state.move_1()\n\n    def energy(self) -> float:\n        \"\"\"\n        Calculate the energy (negative AP@50 score) of the current state.\n\n        Returns:\n            float: Energy of the current state.\n        \"\"\"\n        query = self.state.to_query()\n        candidates = whoosh_utils.execute_query(query, self.qp, self.searcher)\n        ap50_score = ap50(candidates, self.target)\n\n        return -ap50_score","metadata":{"execution":{"iopub.status.busy":"2024-07-08T21:56:42.907278Z","iopub.execute_input":"2024-07-08T21:56:42.907520Z","iopub.status.idle":"2024-07-08T21:56:42.920340Z","shell.execute_reply.started":"2024-07-08T21:56:42.907499Z","shell.execute_reply":"2024-07-08T21:56:42.919491Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from whoosh.index import open_dir\nfrom whoosh.qparser import QueryParser\nfrom whoosh.fields import Schema, TEXT, ID","metadata":{"execution":{"iopub.status.busy":"2024-07-08T21:56:42.921428Z","iopub.execute_input":"2024-07-08T21:56:42.923737Z","iopub.status.idle":"2024-07-08T21:56:42.933540Z","shell.execute_reply.started":"2024-07-08T21:56:42.923712Z","shell.execute_reply":"2024-07-08T21:56:42.932683Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Define the schema for Whoosh index\nschema = Schema(\n    id=ID(stored=True),\n    content=TEXT()\n)\n\nindex_dir = \"/kaggle/working/test_index\"\n\n# Open the Whoosh index\nix = open_dir(index_dir)\n\n# Get the searcher and query parser\nsearcher = ix.searcher()\nquery_parser = QueryParser(\"content\", ix.schema)","metadata":{"execution":{"iopub.status.busy":"2024-07-08T21:56:42.934653Z","iopub.execute_input":"2024-07-08T21:56:42.935443Z","iopub.status.idle":"2024-07-08T21:56:42.944923Z","shell.execute_reply.started":"2024-07-08T21:56:42.935418Z","shell.execute_reply":"2024-07-08T21:56:42.944154Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Solve the USPTO problem using simulated annealing algorithm\ndef solve_uspto_problem(\n    query_parser: Any,\n    searcher: Any,\n    target: List[str],\n    initial_state: State\n) -> State:\n    \"\"\"\n    Solve the USPTO problem using simulated annealing algorithm.\n\n    Args:\n        query_parser (Any): Query parser.\n        searcher (Any): Searcher object for executing searches.\n        target (list[str]): List of target labels.\n        initial_state (State): Initial state object.\n\n    Returns:\n        State: Optimized state.\n    \"\"\"\n    sa = SimulatedAnnealing(\n        init_state=initial_state,\n        qp=query_parser,\n        searcher=searcher,\n        target=target\n    )\n    best_state = sa.anneal()\n    return best_state","metadata":{"execution":{"iopub.status.busy":"2024-07-08T21:56:42.945978Z","iopub.execute_input":"2024-07-08T21:56:42.946230Z","iopub.status.idle":"2024-07-08T21:56:42.955542Z","shell.execute_reply.started":"2024-07-08T21:56:42.946208Z","shell.execute_reply":"2024-07-08T21:56:42.954682Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"comp_data_dir = Path(\"/kaggle/input/uspto-explainable-ai\")\ntfidf_dir = Path(\"/kaggle/input/uspto-ti-cpc-tfidf\")\n\n# nearest neighbors and meta\ntest = pl.read_csv(comp_data_dir / \"test.csv\")\ntest_meta = pl.read_parquet(\"meta_with_text.parquet\")\n\n# test index\ntest_idx = whoosh_utils.load_index(\"./test_index\")\nsearcher = whoosh_utils.get_searcher(test_idx)\nqp = whoosh_utils.get_query_parser()","metadata":{"execution":{"iopub.status.busy":"2024-07-08T21:56:42.956531Z","iopub.execute_input":"2024-07-08T21:56:42.956844Z","iopub.status.idle":"2024-07-08T21:56:42.974683Z","shell.execute_reply.started":"2024-07-08T21:56:42.956817Z","shell.execute_reply":"2024-07-08T21:56:42.974015Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# for tfidf pickle\ndef identity(x: Any) -> Any:\n    return x\n\nwith open(tfidf_dir / \"tfidf.pkl\", \"rb\") as f:\n    ti_tfidf = pickle.load(f)\nwith open(tfidf_dir / \"cpc_cv_tfidf.pkl\", \"rb\") as f:\n    cpc_cv_tfidf = pickle.load(f)","metadata":{"execution":{"iopub.status.busy":"2024-07-08T21:56:42.975806Z","iopub.execute_input":"2024-07-08T21:56:42.976173Z","iopub.status.idle":"2024-07-08T21:56:43.269602Z","shell.execute_reply.started":"2024-07-08T21:56:42.976143Z","shell.execute_reply":"2024-07-08T21:56:43.268820Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"scores = []\nresults = []\n\nti_features = ti_tfidf.get_feature_names_out()\ncpc_features = cpc_cv_tfidf.get_feature_names_out()","metadata":{"execution":{"iopub.status.busy":"2024-07-08T21:56:43.270744Z","iopub.execute_input":"2024-07-08T21:56:43.271048Z","iopub.status.idle":"2024-07-08T21:56:43.846166Z","shell.execute_reply.started":"2024-07-08T21:56:43.271024Z","shell.execute_reply":"2024-07-08T21:56:43.845367Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"for i in tqdm(range(len(test))):\n    target = test[i].to_numpy().flatten()[1:].tolist()\n    meta_i = test_meta.filter(pl.col(\"publication_number\").is_in(target))\n\n    if len(meta_i) == 0:\n        # append dummy\n        results.append({\"publication_number\": test[i, \"publication_number\"], \"query\": \"ti:device\"})\n        print(\"\\t Append Dummy\", i)\n        continue\n\n    # TF-IDF matrix\n    ti_mat = ti_tfidf.transform(meta_i.get_column(\"title\").fill_null(\"\"))\n    cpc_mat = cpc_cv_tfidf.transform(meta_i.get_column(\"cpc\"))\n\n    # Important topk words\n    X_ti, idx = select_top_k_columns(ti_mat, k=7)\n    X_cpc, cpc_idx = select_top_k_columns(cpc_mat, k=7)\n\n    # Initialize State with topk words\n    topk_words = [Word(category=\"ti\", content=ti_features[idx[j]]) for j in range(len(idx))]\n    topk_cpc = [Word(category=\"cpc\", content=cpc_features[cpc_idx[j]]) for j in range(len(cpc_idx))]\n    words = topk_words + topk_cpc\n    state = State(words=words)\n\n    # Determine which words to use using the annealing method\n    problem = USPTOProblem(qp, searcher, target, state, steps=1000, max_time=5)\n    solution, score = problem.anneal()\n    print(f\"\\t Problem Number {i} Score:\", -score)\n    scores.append(-score)\n\n    # save publication number and query\n    results.append({\"publication_number\": test[i, \"publication_number\"], \"query\": solution.to_query()})\n\nprint(\"Average Score:\", sum(scores) / len(scores))","metadata":{"execution":{"iopub.status.busy":"2024-07-08T21:56:43.847347Z","iopub.execute_input":"2024-07-08T21:56:43.847660Z","iopub.status.idle":"2024-07-08T21:57:26.504783Z","shell.execute_reply.started":"2024-07-08T21:56:43.847634Z","shell.execute_reply":"2024-07-08T21:57:26.503825Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Remove unwanted files and directories that may cause submission errors\n!rm -rf /kaggle/working/*","metadata":{"execution":{"iopub.status.busy":"2024-07-08T21:57:26.512991Z","iopub.execute_input":"2024-07-08T21:57:26.513226Z","iopub.status.idle":"2024-07-08T21:57:27.605752Z","shell.execute_reply.started":"2024-07-08T21:57:26.513205Z","shell.execute_reply":"2024-07-08T21:57:27.604451Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"submission = pl.DataFrame(results)\nsubmission.write_csv(\"submission.csv\")\n\nsubmission","metadata":{"execution":{"iopub.status.busy":"2024-07-08T21:57:27.607413Z","iopub.execute_input":"2024-07-08T21:57:27.607739Z","iopub.status.idle":"2024-07-08T21:57:27.617279Z","shell.execute_reply.started":"2024-07-08T21:57:27.607711Z","shell.execute_reply":"2024-07-08T21:57:27.616369Z"},"trusted":true},"execution_count":null,"outputs":[]}]}