{"metadata":{"kaggle":{"accelerator":"none","dataSources":[{"sourceId":59575,"databundleVersionId":8060720,"sourceType":"competition"},{"sourceId":8479599,"sourceType":"datasetVersion","datasetId":4517815},{"sourceId":9041038,"sourceType":"datasetVersion","datasetId":5450573},{"sourceId":174185912,"sourceType":"kernelVersion"}],"dockerImageVersionId":30698,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":false},"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-26T06:06:20.983808Z","iopub.execute_input":"2024-07-26T06:06:20.984265Z","iopub.status.idle":"2024-07-26T06:06:54.640343Z","shell.execute_reply.started":"2024-07-26T06:06:20.984225Z","shell.execute_reply":"2024-07-26T06:06:54.638758Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from transformers import set_seed\nimport ctypes, gc\nimport torch\nimport random\nimport numpy as np\n\nlibc = ctypes.CDLL(\"libc.so.6\")\n# Seed the same seed to all \ndef seed_everything(seed=42):\n    torch.backends.cudnn.deterministic = True\n    torch.backends.cudnn.benchmark = True\n    random.seed(seed)\n    np.random.seed(seed)\n    torch.manual_seed(seed)\n    set_seed(seed)\n    \ndef clear_memory():\n    libc.malloc_trim(0)\n    torch.cuda.empty_cache()\n    gc.collect()\n\nSEED = 42\nseed_everything(SEED)\n# Set the GPUs\nDEVICE = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")","metadata":{"execution":{"iopub.status.busy":"2024-07-26T06:06:54.642529Z","iopub.execute_input":"2024-07-26T06:06:54.642872Z","iopub.status.idle":"2024-07-26T06:07:11.517409Z","shell.execute_reply.started":"2024-07-26T06:06:54.642841Z","shell.execute_reply":"2024-07-26T06:07:11.516266Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import pickle\nfrom dataclasses import dataclass\nfrom pathlib import Path\n\nimport numpy as np\nfrom numpy.typing import NDArray\nimport polars as pl\nimport pandas as pd\nfrom tqdm.auto import tqdm\nfrom typing import Any\nimport whoosh_utils","metadata":{"execution":{"iopub.status.busy":"2024-07-26T06:07:11.518736Z","iopub.execute_input":"2024-07-26T06:07:11.519309Z","iopub.status.idle":"2024-07-26T06:07:42.999659Z","shell.execute_reply.started":"2024-07-26T06:07:11.51928Z","shell.execute_reply":"2024-07-26T06:07:42.998188Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# meta with text","metadata":{}},{"cell_type":"code","source":"meta = pl.scan_parquet(\"/kaggle/input/uspto-explainable-ai/patent_metadata.parquet\")\nmeta = (\n    meta  \n    .filter(pl.col(\"publication_date\").is_not_null())\n    .with_columns(\n        pl.col(\"publication_date\").dt.year().alias(\"year\"),\n        pl.col(\"publication_date\").dt.month().alias(\"month\"),\n    )\n    .rename({\"cpc_codes\": \"cpc\"})\n    .collect()\n)","metadata":{"execution":{"iopub.status.busy":"2024-07-26T06:07:43.002527Z","iopub.execute_input":"2024-07-26T06:07:43.002986Z","iopub.status.idle":"2024-07-26T06:07:48.336666Z","shell.execute_reply.started":"2024-07-26T06:07:43.002945Z","shell.execute_reply":"2024-07-26T06:07:48.335546Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import csv\nimport itertools\nwith open('/kaggle/input/uspto-explainable-ai/test.csv', newline='') as csvfile:\n    reader = csv.reader(csvfile)\n    test = list(reader)[1:]\n\ntest_pub = set(itertools.chain.from_iterable(test))","metadata":{"execution":{"iopub.status.busy":"2024-07-26T06:07:48.337982Z","iopub.execute_input":"2024-07-26T06:07:48.338431Z","iopub.status.idle":"2024-07-26T06:07:48.347525Z","shell.execute_reply.started":"2024-07-26T06:07:48.338369Z","shell.execute_reply":"2024-07-26T06:07:48.346461Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# meta = meta.filter(pl.col(\"publication_number\").is_in(test_pub))\n# all_pub = test_pub\nmeta = pl.concat([meta.filter(pl.col(\"publication_number\").is_in(test_pub)), meta.filter(~pl.col(\"publication_number\").is_in(test_pub)).sample(200000-len(test_pub))])\nall_pub = set(meta.get_column(\"publication_number\"))","metadata":{"execution":{"iopub.status.busy":"2024-07-26T06:07:48.348805Z","iopub.execute_input":"2024-07-26T06:07:48.349124Z","iopub.status.idle":"2024-07-26T06:07:52.599088Z","shell.execute_reply.started":"2024-07-26T06:07:48.349098Z","shell.execute_reply":"2024-07-26T06:07:52.598065Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import ctypes\nimport os\nfrom concurrent.futures import ThreadPoolExecutor\n\ncomp_data_dir = Path(\"/kaggle/input/uspto-explainable-ai/patent_data\")\n\ndef process_file(file_name):\n    patent_path = comp_data_dir / file_name\n    patent = pl.read_parquet(patent_path).filter(pl.col(\"publication_number\").is_in(all_pub))\n    \n    patent = patent.with_columns([\n        pl.when(pl.col(\"description\") != '')\n          .then(True)\n          .otherwise(False)\n          .alias(\"description\"),\n        pl.when(pl.col(\"claims\") != '')\n          .then(True)\n          .otherwise(False)\n          .alias(\"claims\"),\n        pl.when(pl.col(\"abstract\") != '')\n          .then(True)\n          .otherwise(False)\n          .alias(\"abstract\")\n    ])\n    return patent\n\nfile_names = [f\"{year}_{month}.parquet\" for (year, month), _ in meta.group_by([\"year\", \"month\"])]\n\nwith ThreadPoolExecutor() as executor:\n    patents = list(tqdm(executor.map(process_file, file_names), total=len(file_names)))\n    \npatent = pl.concat(patents)","metadata":{"execution":{"iopub.status.busy":"2024-07-26T06:07:52.600338Z","iopub.execute_input":"2024-07-26T06:07:52.600755Z","iopub.status.idle":"2024-07-26T06:27:44.880672Z","shell.execute_reply.started":"2024-07-26T06:07:52.600726Z","shell.execute_reply":"2024-07-26T06:27:44.879319Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"meta_with_text = (\n    meta\n    .join(patent, on=\"publication_number\", how=\"left\")\n    .with_columns(\n        pl.col(\"title\").fill_null(\"\").alias(\"title\")\n    )\n)","metadata":{"execution":{"iopub.status.busy":"2024-07-26T06:27:44.882623Z","iopub.execute_input":"2024-07-26T06:27:44.882981Z","iopub.status.idle":"2024-07-26T06:27:44.943483Z","shell.execute_reply.started":"2024-07-26T06:27:44.882944Z","shell.execute_reply":"2024-07-26T06:27:44.942541Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def dataframe_to_dict(df, key_column):\n    keys = df[key_column].to_list()\n    dict_list = df.drop(key_column).to_dicts()\n    return {key: value for key, value in zip(keys, dict_list)}\n\ntest_meta_with_text = meta_with_text.filter(pl.col(\"publication_number\").is_in(test_pub))\nmeta_with_text_dict = dataframe_to_dict(test_meta_with_text, 'publication_number')","metadata":{"execution":{"iopub.status.busy":"2024-07-26T06:27:44.944992Z","iopub.execute_input":"2024-07-26T06:27:44.945324Z","iopub.status.idle":"2024-07-26T06:27:44.963494Z","shell.execute_reply.started":"2024-07-26T06:27:44.945298Z","shell.execute_reply":"2024-07-26T06:27:44.962596Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# test index","metadata":{}},{"cell_type":"code","source":"from sklearn.feature_extraction.text import ENGLISH_STOP_WORDS\n\nimport re\nimport whoosh\nNUMBER_REGEX = re.compile(r'^(\\d+|\\d{1,3}(,\\d{3})*)(\\.\\d+)?$')\n\nclass NumberFilter(whoosh.analysis.Filter):\n    def __call__(self, tokens):\n        for t in tokens:\n            if not NUMBER_REGEX.match(t.text):\n                yield t\n\ndef _define_uspto_whoosh_schema():\n#     BRS_STOPWORDS = ['an', 'are', 'by', 'for', 'if', 'into', 'is', 'no', 'not', 'of', 'on', 'such',\n#         'that', 'the', 'their', 'then', 'there', 'these', 'they', 'this', 'to', 'was', 'will']\n\n#     # Prevent both stopwords and numbers from ever being indexed.\n    custom_analyzer = whoosh.analysis.StandardAnalyzer(stoplist=ENGLISH_STOP_WORDS) | NumberFilter()\n    # Schema fields match a subset of the USPTO list of searchable indexes:\n    # https://ppubs.uspto.gov/pubwebapp/static/pages/searchable-indexes.html\n    schema = whoosh.fields.Schema(\n        id=whoosh.fields.ID(stored=True),\n        ti=whoosh.fields.TEXT(analyzer=custom_analyzer, stored=False),\n#         ab=whoosh.fields.TEXT(analyzer=custom_analyzer, stored=False),\n#         clm=whoosh.fields.TEXT(analyzer=custom_analyzer, stored=False),\n#         detd=whoosh.fields.TEXT(analyzer=custom_analyzer, stored=False),\n        cpc=whoosh.fields.KEYWORD(stored=True, scorable=True)\n    )\n    return schema","metadata":{"execution":{"iopub.status.busy":"2024-07-26T06:27:44.967571Z","iopub.execute_input":"2024-07-26T06:27:44.967944Z","iopub.status.idle":"2024-07-26T06:27:45.772112Z","shell.execute_reply.started":"2024-07-26T06:27:44.967915Z","shell.execute_reply":"2024-07-26T06:27:45.771015Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from whoosh.index import create_in\nimport os\ndef create_index(output_dir, documents, limitmb=5000):\n    try:\n        ix = create_in(output_dir, schema=_define_uspto_whoosh_schema())\n        writer = ix.writer(procs=os.cpu_count(), multisegment=True, limitmb=limitmb)\n        for document in documents:\n            writer.add_document(\n                id=document['publication_number'],\n                ti=document['title'],\n#                 ab=document['abstract'],\n#                 clm=document['claims'],\n#                 detd=document['description'],\n                cpc=document['cpc']\n            )\n        writer.commit(optimize=True)\n    except Exception as e:\n        print(f\"An error occurred: {str(e)}\")\n    finally:\n        ix.close()","metadata":{"execution":{"iopub.status.busy":"2024-07-26T06:27:45.773263Z","iopub.execute_input":"2024-07-26T06:27:45.7736Z","iopub.status.idle":"2024-07-26T06:27:45.780763Z","shell.execute_reply.started":"2024-07-26T06:27:45.773572Z","shell.execute_reply":"2024-07-26T06:27:45.779705Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"Path(\"test_index\").mkdir(parents=True, exist_ok=True)\n# whoosh_utils.create_index(\"test_index\", meta_with_text.to_dicts())\ncreate_index(\"test_index\", meta_with_text.to_dicts())","metadata":{"execution":{"iopub.status.busy":"2024-07-26T06:27:45.781965Z","iopub.execute_input":"2024-07-26T06:27:45.782297Z","iopub.status.idle":"2024-07-26T06:28:33.527466Z","shell.execute_reply.started":"2024-07-26T06:27:45.782271Z","shell.execute_reply":"2024-07-26T06:28:33.525641Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# test_idx = whoosh_utils.load_index(\"./test_index\")\n# searcher = whoosh_utils.get_searcher(test_idx)\n\ntest_idx = whoosh.index.open_dir(\"./test_index\", schema=_define_uspto_whoosh_schema())\nsearcher=test_idx.searcher(weighting=whoosh.scoring.TF_IDF())\n\nqp = whoosh_utils.get_query_parser()","metadata":{"execution":{"iopub.status.busy":"2024-07-26T06:28:33.529437Z","iopub.execute_input":"2024-07-26T06:28:33.529835Z","iopub.status.idle":"2024-07-26T06:28:33.655005Z","shell.execute_reply.started":"2024-07-26T06:28:33.5298Z","shell.execute_reply":"2024-07-26T06:28:33.653729Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# helper functions","metadata":{}},{"cell_type":"code","source":"def select_top_k_columns(X, k):  \n    row_sums = X.sum(axis=0)  \n      \n    row_sums = np.asarray(row_sums).squeeze()  \n      \n    top_k_indices = np.argsort(-row_sums)[:k]  \n      \n    max_sum = row_sums[top_k_indices[0]] \n    top_k_scores = row_sums[top_k_indices] / max_sum  \n      \n    return top_k_indices, top_k_scores\n\n\n\n# https://www.kaggle.com/competitions/uspto-explainable-ai/discussion/499981#2791642\n# https://www.kaggle.com/competitions/uspto-explainable-ai/discussion/513097#2908301\ndef ap50(preds: list[str], labels: list[str]) -> float:\n    ap = 0\n    n_found = 0\n    labels = set(labels)\n    for e, i in enumerate(preds):\n        if i in labels:\n            n_found += 1\n        ap += n_found / (e + 1) # this is the line that is probably incorrect for competition\n    return ap / 50","metadata":{"execution":{"iopub.status.busy":"2024-07-26T06:28:33.656319Z","iopub.execute_input":"2024-07-26T06:28:33.65669Z","iopub.status.idle":"2024-07-26T06:28:33.665196Z","shell.execute_reply.started":"2024-07-26T06:28:33.65666Z","shell.execute_reply":"2024-07-26T06:28:33.663983Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# def to_query(terms):\n#     return f'({\" OR \".join(terms)})'\n\ndef to_query(terms):\n    categories = {}\n    for prefix, term in terms:\n        if prefix not in categories:\n            categories[prefix] = []\n        categories[prefix].append(term)\n    \n    category_queries = [\n        f\"({' OR '.join(f'{prefix}:{term}' if prefix else term for term in terms)})\"\n        for prefix, terms in categories.items()\n    ]\n    \n    return \"\".join(category_queries)\n\ndef exec_query(query, target):\n    cand = whoosh_utils.execute_query(query, qp, searcher)\n    score = ap50(cand,target)\n    return score","metadata":{"execution":{"iopub.status.busy":"2024-07-26T06:36:26.926646Z","iopub.execute_input":"2024-07-26T06:36:26.927083Z","iopub.status.idle":"2024-07-26T06:36:26.935792Z","shell.execute_reply.started":"2024-07-26T06:36:26.927052Z","shell.execute_reply":"2024-07-26T06:36:26.934602Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# simulated annealing","metadata":{}},{"cell_type":"code","source":"# https://github.com/perrygeo/simanneal/blob/master/simanneal/anneal.py\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\nfrom __future__ import unicode_literals\nimport abc\nimport copy\nimport datetime\nimport math\nimport pickle\nimport random\nimport signal\nimport sys\nimport time\n\n\ndef round_figures(x, n):\n    \"\"\"Returns x rounded to n significant figures.\"\"\"\n    return round(x, int(n - math.ceil(math.log10(abs(x)))))\n\n\ndef time_string(seconds):\n    \"\"\"Returns time in seconds as a string formatted HHHH:MM:SS.\"\"\"\n    s = int(round(seconds))  # round to nearest second\n    h, s = divmod(s, 3600)  # get hours and remainder\n    m, s = divmod(s, 60)  # split remainder into minutes and seconds\n    return \"%4i:%02i:%02i\" % (h, m, s)\n\n\nclass Annealer(object):\n    \"\"\"Performs simulated annealing by calling functions to calculate\n    energy and make moves on a state.  The temperature schedule for\n    annealing may be provided manually or estimated automatically.\n    \"\"\"\n\n    __metaclass__ = abc.ABCMeta\n\n    # defaults\n    Tmax = 25000.0\n    Tmin = 2.5\n    steps = 50000\n    max_time = 8  # seconds\n    updates = 100\n    copy_strategy = \"deepcopy\"\n    user_exit = False\n    save_state_on_exit = False\n\n    # placeholders\n    best_state = None\n    best_energy = None\n    start = None\n\n    def __init__(self, initial_state=None, load_state=None):\n        if initial_state is not None:\n            self.state = self.copy_state(initial_state)\n        elif load_state:\n            self.load_state(load_state)\n        else:\n            raise ValueError(\n                \"No valid values supplied for neither \\\n            initial_state nor load_state\"\n            )\n\n        signal.signal(signal.SIGINT, self.set_user_exit)\n\n    def save_state(self, fname=None):\n        \"\"\"Saves state to pickle\"\"\"\n        if not fname:\n            date = datetime.datetime.now().strftime(\"%Y-%m-%dT%Hh%Mm%Ss\")\n            fname = date + \"_energy_\" + str(self.energy()) + \".state\"\n        with open(fname, \"wb\") as fh:\n            pickle.dump(self.state, fh)\n\n    def load_state(self, fname=None):\n        \"\"\"Loads state from pickle\"\"\"\n        with open(fname, \"rb\") as fh:\n            self.state = pickle.load(fh)\n\n    @abc.abstractmethod\n    def move(self):\n        \"\"\"Create a state change\"\"\"\n        pass\n\n    @abc.abstractmethod\n    def energy(self):\n        \"\"\"Calculate state's energy\"\"\"\n        pass\n\n    def set_user_exit(self, signum, frame):\n        \"\"\"Raises the user_exit flag, further iterations are stopped\"\"\"\n        self.user_exit = True\n\n    def set_schedule(self, schedule):\n        \"\"\"Takes the output from `auto` and sets the attributes\"\"\"\n        self.Tmax = schedule[\"tmax\"]\n        self.Tmin = schedule[\"tmin\"]\n        self.steps = int(schedule[\"steps\"])\n        self.updates = int(schedule[\"updates\"])\n\n    def copy_state(self, state):\n        \"\"\"Returns an exact copy of the provided state\n        Implemented according to self.copy_strategy, one of\n\n        * deepcopy: use copy.deepcopy (slow but reliable)\n        * slice: use list slices (faster but only works if state is list-like)\n        * method: use the state's copy() method\n        \"\"\"\n        if self.copy_strategy == \"deepcopy\":\n            return copy.deepcopy(state)\n        elif self.copy_strategy == \"slice\":\n            return state[:]\n        elif self.copy_strategy == \"method\":\n            return state.copy()\n        else:\n            raise RuntimeError(\n                \"No implementation found for \" + 'the self.copy_strategy \"%s\"' % self.copy_strategy\n            )\n\n#     def update(self, *args, **kwargs):\n#         \"\"\"Wrapper for internal update.\n\n#         If you override the self.update method,\n#         you can chose to call the self.default_update method\n#         from your own Annealer.\n#         \"\"\"\n#         self.default_update(*args, **kwargs)\n\n#     def default_update(self, step, T, E, acceptance, improvement):\n#         \"\"\"Default update, outputs to stderr.\n\n#         Prints the current temperature, energy, acceptance rate,\n#         improvement rate, elapsed time, and remaining time.\n\n#         The acceptance rate indicates the percentage of moves since the last\n#         update that were accepted by the Metropolis algorithm.  It includes\n#         moves that decreased the energy, moves that left the energy\n#         unchanged, and moves that increased the energy yet were reached by\n#         thermal excitation.\n\n#         The improvement rate indicates the percentage of moves since the\n#         last update that strictly decreased the energy.  At high\n#         temperatures it will include both moves that improved the overall\n#         state and moves that simply undid previously accepted moves that\n#         increased the energy by thermal excititation.  At low temperatures\n#         it will tend toward zero as the moves that can decrease the energy\n#         are exhausted and moves that would increase the energy are no longer\n#         thermally accessible.\"\"\"\n\n#         elapsed = time.time() - self.start\n#         if step == 0:\n#             print(\n#                 \"\\n Temperature        Energy    Accept   Improve     Elapsed   Remaining\",\n#                 file=sys.stderr,\n#             )\n#             print(\n#                 \"\\r{Temp:12.5f}  {Energy:12.2f}                      {Elapsed:s}            \".format(\n#                     Temp=T, Energy=E, Elapsed=time_string(elapsed)\n#                 ),\n#                 file=sys.stderr,\n#                 end=\"\",\n#             )\n#             sys.stderr.flush()\n#         else:\n#             remain = (self.steps - step) * (elapsed / step)\n#             print(\n#                 \"\\r{Temp:12.5f}  {Energy:12.2f}   {Accept:7.2%}   {Improve:7.2%}  {Elapsed:s}  {Remaining:s}\".format(\n#                     Temp=T,\n#                     Energy=E,\n#                     Accept=acceptance,\n#                     Improve=improvement,\n#                     Elapsed=time_string(elapsed),\n#                     Remaining=time_string(remain),\n#                 ),\n#                 file=sys.stderr,\n#                 end=\"\",\n#             )\n#             sys.stderr.flush()\n\n    def anneal(self):\n        \"\"\"Minimizes the energy of a system by simulated annealing.\n\n        Parameters\n        state : an initial arrangement of the system\n\n        Returns\n        (state, energy): the best state and energy found.\n        \"\"\"\n        step = 0\n        self.start = time.time()\n\n        # Precompute factor for exponential cooling from Tmax to Tmin\n        if self.Tmin <= 0.0:\n            raise Exception(\n                'Exponential cooling requires a minimum \"\\\n                \"temperature greater than zero.'\n            )\n        Tfactor = -math.log(self.Tmax / self.Tmin)\n\n        # Note initial state\n        T = self.Tmax\n        E = self.energy()\n        prevState = self.copy_state(self.state)\n        prevEnergy = E\n        self.best_state = self.copy_state(self.state)\n        self.best_energy = E\n        trials = accepts = improves = 0\n        if self.updates > 0:\n            updateWavelength = self.steps / self.updates\n#             self.update(step, T, E, None, None)\n\n        # Attempt moves to new states\n        while (\n            (step < self.steps)\n            and (not self.user_exit)\n            and ((time.time() - self.start) <= self.max_time)\n        ):\n            step += 1\n            T = self.Tmax * math.exp(Tfactor * step / self.steps)\n            dE = self.move()\n            if dE is None:\n                E = self.energy()\n                dE = E - prevEnergy\n            else:\n                E += dE\n            trials += 1\n            if dE > 0.0 and math.exp(-dE / T) < random.random():\n                # Restore previous state\n                self.state = self.copy_state(prevState)\n                E = prevEnergy\n            else:\n                # Accept new state and compare to best state\n                accepts += 1\n                if dE < 0.0:\n                    improves += 1\n                prevState = self.copy_state(self.state)\n                prevEnergy = E\n                if E < self.best_energy:\n                    self.best_state = self.copy_state(self.state)\n                    self.best_energy = E\n            if self.updates > 1:\n                if (step // updateWavelength) > ((step - 1) // updateWavelength):\n#                     self.update(step, T, E, accepts / trials, improves / trials)\n                    trials = accepts = improves = 0\n\n        self.state = self.copy_state(self.best_state)\n        if self.save_state_on_exit:\n            self.save_state()\n\n        # Return best state and energy\n        return self.best_state, -self.best_energy","metadata":{"execution":{"iopub.status.busy":"2024-07-26T06:36:29.479737Z","iopub.execute_input":"2024-07-26T06:36:29.480147Z","iopub.status.idle":"2024-07-26T06:36:29.508008Z","shell.execute_reply.started":"2024-07-26T06:36:29.480116Z","shell.execute_reply":"2024-07-26T06:36:29.506746Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"@dataclass\nclass State:\n    def __init__(self, words, random=False):\n        self.words = words\n        self.use = np.random.binomial(1, 0.5, len(words)) if random else [1] * len(words)\n    \n    def to_terms(self):\n        terms = [word for word, use in zip(self.words, self.use) if use]\n        return terms\n    \n    def move_1(self):\n        \"\"\"Change whether word is used or not\"\"\"\n        idx = np.random.choice(len(self.words))\n        self.use[idx] = 1 - self.use[idx]\n        return self\n\n\nclass USPTOProblem(Annealer):\n    def __init__(\n        self,\n        qp: 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        super(USPTOProblem, self).__init__(init_state)\n        self.qp = qp\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        self.state.move_1()\n\n    def energy(self):\n        query = to_query(self.state.to_terms())\n        cand = whoosh_utils.execute_query(query, self.qp, self.searcher)\n        ap50_score = ap50(cand, self.target)\n        return -ap50_score","metadata":{"execution":{"iopub.status.busy":"2024-07-26T06:28:33.710807Z","iopub.execute_input":"2024-07-26T06:28:33.711288Z","iopub.status.idle":"2024-07-26T06:28:33.727527Z","shell.execute_reply.started":"2024-07-26T06:28:33.711247Z","shell.execute_reply":"2024-07-26T06:28:33.726355Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# genetic algorithm","metadata":{}},{"cell_type":"code","source":"class GeneticAlgorithmForKeywordSelection:\n    def __init__(self, qp, searcher, target, keywords, population_size=10, mutation_rate=0.05, crossover_rate=0.9, max_generations=10, patience = None, present_rate=0.8):\n        self.keywords = keywords\n        self.mutation_rate = mutation_rate\n        self.crossover_rate = crossover_rate\n        self.max_generations = max_generations\n        self.population = [np.random.choice([0, 1], size=len(keywords), p=[1-present_rate, present_rate]) for _ in range(population_size - 1)]\n        self.population.append(np.ones(len(keywords), dtype=int))\n        self.qp = qp\n        self.searcher = searcher\n        self.target = target\n        self.patience = patience if patience else max_generations\n        self.population_size = population_size if population_size else 2 * self.num_workers\n#         self.base_score = self.fitness(self.population[-1])\n        \n    def fitness(self, individual):\n        query = to_query(self.to_terms(individual))     \n        cand = whoosh_utils.execute_query(query, self.qp, self.searcher)\n        ap50_score = ap50(cand, self.target)\n        return ap50_score\n    \n    def select(self, fitness_scores):\n        scores_indices = list(sorted(enumerate(fitness_scores), key=lambda x: x[1], reverse=True))\n        top_indices = [idx for idx, score in scores_indices[:len(fitness_scores)//2]]\n        return [self.population[i] for i in top_indices]\n\n    def crossover(self, parent1, parent2):\n        if random.random() < self.crossover_rate:\n            point = random.randint(1, len(self.keywords) - 1)\n            return np.concatenate([parent1[:point], parent2[point:]])\n        return parent1 if random.random() < 0.5 else parent2\n    \n    def to_terms(self,individual):\n        terms = [word for word, use in zip(self.keywords, individual) if use]\n        return terms\n    \n    def mutate(self, individual):\n        for i in range(len(self.keywords)):\n            if random.random() < self.mutation_rate:\n                individual[i] = 1 - individual[i]\n        return individual\n    \n    def process_individuals(self,parents):\n        parent1,parent2=parents[0],parents[1]\n        child = self.crossover(parent1, parent2)\n        child = self.mutate(child)\n        return child\n    \n    def run(self):\n        best_score = 0\n        no_improvement_count = 0\n        for generation in range(self.max_generations):\n            fitness_scores = [self.fitness(individual) for individual in self.population]  \n              \n            current_best_score = max(fitness_scores)\n            new_population = self.select(fitness_scores)\n            if current_best_score > best_score:\n                best_score = current_best_score\n                no_improvement_count = 0\n            else:\n                no_improvement_count += 1\n\n            if no_improvement_count >= self.patience:\n                break\n\n            self.population = [self.process_individuals(random.sample(new_population, 2)) for _ in range(self.population_size)]\n            \n#             print(f\"Generation {generation}: Best Score = {best_score}\")\n  \n        fitness_scores = [self.fitness(individual) for individual in self.population]  \n        best_solution = self.population[np.argmax(fitness_scores)]\n        result = self.to_terms(best_solution)\n        return result","metadata":{"execution":{"iopub.status.busy":"2024-07-26T06:36:33.875147Z","iopub.execute_input":"2024-07-26T06:36:33.875592Z","iopub.status.idle":"2024-07-26T06:36:33.894216Z","shell.execute_reply.started":"2024-07-26T06:36:33.875558Z","shell.execute_reply":"2024-07-26T06:36:33.892788Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# TF-IDF","metadata":{}},{"cell_type":"code","source":"from joblib import load\ndef identity(x: Any) -> Any:\n    return x\n\ncpc_tfidf = load('/kaggle/input/uspto-tifidf/cpc_tfidf.joblib')\nti_tfidf = load('/kaggle/input/uspto-tifidf/ti_tfidf.joblib')\nab_tfidf = load('/kaggle/input/uspto-tifidf/ab_tfidf.joblib')","metadata":{"execution":{"iopub.status.busy":"2024-07-26T06:28:33.752124Z","iopub.execute_input":"2024-07-26T06:28:33.752605Z","iopub.status.idle":"2024-07-26T06:28:40.531869Z","shell.execute_reply.started":"2024-07-26T06:28:33.752564Z","shell.execute_reply":"2024-07-26T06:28:40.530677Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def top_cpc_tfidf(cpc_list, k = 25):\n\n    mat = cpc_tfidf.transform(cpc_list)\n    idx, scores = select_top_k_columns(mat, k)\n    topk_words = cpc_tfidf.get_feature_names_out()[idx]\n    word_scores = list(zip([(\"cpc\", kw) for kw in topk_words], scores))\n    \n    return word_scores\n\n\ndef top_ti_tfidf(ti_list, k = 25):\n\n    mat = ti_tfidf.transform(ti_list)\n    idx, scores = select_top_k_columns(mat, k)\n    topk_words = ti_tfidf.get_feature_names_out()[idx]\n    word_scores = list(zip([(\"ti\", kw) for kw in topk_words], scores))\n    \n    return word_scores","metadata":{"execution":{"iopub.status.busy":"2024-07-26T06:36:38.582573Z","iopub.execute_input":"2024-07-26T06:36:38.583002Z","iopub.status.idle":"2024-07-26T06:36:38.590668Z","shell.execute_reply.started":"2024-07-26T06:36:38.58297Z","shell.execute_reply":"2024-07-26T06:36:38.589462Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# query generation","metadata":{}},{"cell_type":"code","source":"def ensemble_keywords(lists, threshold = 0):\n    keyword_scores = []\n\n    # Aggregate keywords and their scores with weights\n    for keywords, weight in lists:\n        for keyword, score in keywords:\n            keyword_scores.append((keyword, score * weight))\n\n    # Select keywords with combined weight above the threshold\n    sorted_keywords = sorted(keyword_scores, key=lambda x: x[1], reverse=True)\n    keywords_only = [x[0] for x in sorted_keywords if x[1] > threshold]\n    \n    return keywords_only","metadata":{"execution":{"iopub.status.busy":"2024-07-26T06:36:40.858392Z","iopub.execute_input":"2024-07-26T06:36:40.859699Z","iopub.status.idle":"2024-07-26T06:36:40.86633Z","shell.execute_reply.started":"2024-07-26T06:36:40.859658Z","shell.execute_reply":"2024-07-26T06:36:40.865032Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def process_row(row):\n    publication_number=row[0]\n    target = row[1:]\n    \n    cpc_list = np.empty(50, dtype=object)\n    ti_list = np.empty(50, dtype=object)\n    ab_list = np.empty(50, dtype=object)\n    clm_list = np.empty(50, dtype=object)\n    detd_list = np.empty(50, dtype=object)\n    \n    for i, pub_num in enumerate(target):\n        cpc_list[i] = meta_with_text_dict[pub_num][\"cpc\"]\n        ti_list[i] = meta_with_text_dict[pub_num][\"title\"]\n        ab_list[i] = meta_with_text_dict[pub_num][\"abstract\"]\n        clm_list[i] = meta_with_text_dict[pub_num][\"claims\"]\n        detd_list[i] = meta_with_text_dict[pub_num][\"description\"]\n\n    limit = 26\n    \n    not_ti = ''\n    not_cpc = ''\n    not_ab = ''\n    not_clm = ''\n    not_detd = ''\n    \n    lists = []\n    if all(not s for s in cpc_list):\n        not_cpc = \"(NOT cpc:*)\"\n        limit-=1\n    else:\n        l = top_cpc_tfidf(cpc_list, 21)\n        lists.append((l,1))\n        \n    if all(not s for s in ti_list):\n        not_ti = \"(NOT ti:*)\"\n        limit-=1\n    else:\n        l = top_ti_tfidf(ti_list, 21)\n        lists.append((l,1))\n        \n    if all(not s for s in ab_list):\n        not_ab = \"(NOT ab:*)\"\n        limit-=1\n    if all(not s for s in clm_list):\n        not_clm = \"(NOT clm:*)\"\n        limit-=1\n    if all(not s for s in detd_list):\n        not_detd = \"(NOT detd:*)\"\n        limit-=1\n    \n    keywords =  ensemble_keywords(lists)\n\n#     query = to_query(keywords)\n#     scores_0 = exec_query(query, target)\n    \n    while len(keywords)>limit:\n        ga = GeneticAlgorithmForKeywordSelection(qp, searcher, target, keywords, max_generations=10, patience = 5)\n        keywords = ga.run()\n        \n        if len(keywords)>limit:\n            keywords.pop()\n            \n#     state = State(words=keywords)\n#     problem = USPTOProblem(qp, searcher, target, state, steps=1000, max_time=10)\n#     best_state, score = problem.anneal()\n#     keywords = best_state.to_terms()  \n    \n    if len(keywords) == limit and all(prefix == keywords[0][0] for prefix, _ in keywords):\n        keywords.pop()\n        \n    query = to_query(keywords)\n#     score = exec_query(query, target)\n    query = f\"{not_ti}{not_cpc}{not_ab}{not_clm}{not_detd}{query}\"\n\n    return {\"publication_number\": publication_number, \"query\": query}","metadata":{"execution":{"iopub.status.busy":"2024-07-26T06:36:42.688332Z","iopub.execute_input":"2024-07-26T06:36:42.68949Z","iopub.status.idle":"2024-07-26T06:36:42.704174Z","shell.execute_reply.started":"2024-07-26T06:36:42.689432Z","shell.execute_reply":"2024-07-26T06:36:42.702944Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from multiprocessing import Pool\nwith Pool() as pool:\n    results = pool.map(process_row, test)\nsub = pl.DataFrame(results)","metadata":{"execution":{"iopub.status.busy":"2024-07-26T06:36:45.718874Z","iopub.execute_input":"2024-07-26T06:36:45.719278Z","iopub.status.idle":"2024-07-26T06:38:04.36704Z","shell.execute_reply.started":"2024-07-26T06:36:45.71925Z","shell.execute_reply":"2024-07-26T06:38:04.365604Z"},"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-26T06:38:04.370055Z","iopub.execute_input":"2024-07-26T06:38:04.370581Z","iopub.status.idle":"2024-07-26T06:38:05.522781Z","shell.execute_reply.started":"2024-07-26T06:38:04.370533Z","shell.execute_reply":"2024-07-26T06:38:05.521354Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"sub.write_csv(\"submission.csv\")\n\nsub","metadata":{"execution":{"iopub.status.busy":"2024-07-26T06:38:05.524665Z","iopub.execute_input":"2024-07-26T06:38:05.525113Z","iopub.status.idle":"2024-07-26T06:38:05.535361Z","shell.execute_reply.started":"2024-07-26T06:38:05.52507Z","shell.execute_reply":"2024-07-26T06:38:05.534243Z"},"trusted":true},"execution_count":null,"outputs":[]}]}