From e0c218024ccdc40039cd8f75e0ad0143a812fcb5 Mon Sep 17 00:00:00 2001 From: Mateusz Praski Date: Thu, 7 May 2026 19:53:23 +0200 Subject: [PATCH 1/2] Added various bugfixes for needed for release 1.0.0 --- NOTICE.md | 27 ++ bbttest/bbt/_utils.py | 14 +- bbttest/bbt/alg.py | 10 +- bbttest/bbt/model.py | 8 +- examples/02_critical_difference_diagram.ipynb | 441 ++++++++++++++++++ pyproject.toml | 1 + tests/bbt/test__utils.py | 12 + tests/bbt/test_alg.py | 122 +++++ uv.lock | 11 + 9 files changed, 634 insertions(+), 12 deletions(-) create mode 100644 NOTICE.md create mode 100644 examples/02_critical_difference_diagram.ipynb diff --git a/NOTICE.md b/NOTICE.md new file mode 100644 index 0000000..2169899 --- /dev/null +++ b/NOTICE.md @@ -0,0 +1,27 @@ +# NOTICE + +## bbtcomp (reference implementation) + +- Repository: https://github.com/jwainer/bbtcomp/tree/master +- Reference paper: Jacques Wainer, "A Bayesian Bradley-Terry model to compare multiple ML algorithms on multiple data sets", JMLR 24 (2023), http://jmlr.org/papers/v24/22-0907.html + +MIT License +Copyright (c) 2022 bbtcomp authors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/bbttest/bbt/_utils.py b/bbttest/bbt/_utils.py index dc120fc..a7d7927 100644 --- a/bbttest/bbt/_utils.py +++ b/bbttest/bbt/_utils.py @@ -25,19 +25,25 @@ def is_literal_value(value: object, typx: object) -> bool: def _validate_params(func): - from inspect import signature + from inspect import Parameter, signature sig = signature(func) + accepts_kwargs = any( + param.kind == Parameter.VAR_KEYWORD for param in sig.parameters.values() + ) @wraps(func) def wrapper(*args, **kwargs): - for kwarg in kwargs: - if kwarg not in sig.parameters: - raise ValueError(f"Unexpected keyword argument '{kwarg}'") + if not accepts_kwargs: + for kwarg in kwargs: + if kwarg not in sig.parameters: + raise ValueError(f"Unexpected keyword argument '{kwarg}'") bound_args = sig.bind(*args, **kwargs) bound_args.apply_defaults() for name, value in bound_args.arguments.items(): param = sig.parameters[name] + if param.kind == Parameter.VAR_KEYWORD: + continue # If type annotation is a Literal, validate the value if param.annotation is not param.empty and is_literal_value( value, param.annotation diff --git a/bbttest/bbt/alg.py b/bbttest/bbt/alg.py index 73df17b..98c973a 100644 --- a/bbttest/bbt/alg.py +++ b/bbttest/bbt/alg.py @@ -79,11 +79,11 @@ def _construct_lrope( lrope_value: float, ) -> np.ndarray: logger.debug("Using paired BBT test.") - no_algs = data.shape[1] + no_algs = len(alg_names) no_pairs = no_algs * (no_algs - 1) // 2 - out_array = -1 * np.ones( + out_array = np.zeros( (no_pairs, 5), # alg_1, alg_2, 1_wins, 2_wins, ties - dtype=np.int32, + dtype=np.float32, ) for dataset_name in data[dataset_col].unique(): data_subset = data[data[dataset_col] == dataset_name] @@ -114,9 +114,9 @@ def _solve_ties(table: np.ndarray, tie_solver: str) -> np.ndarray: if tie_solver == "davidson": return table if tie_solver == "spread": - tie_val = np.ceil(table[:, TIE_COL] / 2).astype(int) + tie_val = table[:, TIE_COL] / 2 elif tie_solver == "add": - tie_val = table[:, TIE_COL].astype(int) + tie_val = table[:, TIE_COL] else: tie_val = 0 table[:, ALG1_COL] += tie_val diff --git a/bbttest/bbt/model.py b/bbttest/bbt/model.py index 685cc33..0fcce2c 100644 --- a/bbttest/bbt/model.py +++ b/bbttest/bbt/model.py @@ -9,8 +9,8 @@ def _build_bbt_model( player1: list[int], player2: list[int], - win1: list[int], - win2: list[int], + win1: list[float], + win2: list[float], ties: list[int] | None, hyp: str, scale: float, @@ -46,7 +46,9 @@ def _build_bbt_model( K = len(np.unique(np.concatenate((p1_idx, p2_idx)))) # Transformed data - n = w1 + w2 + n = (w1 + w2).astype(np.int32) + w1 = w1.astype(np.int32) + w2 = w2.astype(np.int32) if use_davidson: ties_arr = np.array(ties, dtype=int) nn = n + ties_arr diff --git a/examples/02_critical_difference_diagram.ipynb b/examples/02_critical_difference_diagram.ipynb new file mode 100644 index 0000000..3ac1e8f --- /dev/null +++ b/examples/02_critical_difference_diagram.ipynb @@ -0,0 +1,441 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "207d5d8e", + "metadata": {}, + "source": [ + "# Visualizing multi-dataset multi-algorithm results with Critical Difference Diagram\n", + "\n", + "---\n", + "\n", + "## Introduction\n", + "\n", + "This notebook introduces the Critical Difference Diagram in the context of the Bayesian Bradley-Terry (BBT) model implemented in `bbt-test`. We use posterior model strengths ($\\beta$) to obtain a global ranking, then summarize pairwise posterior comparisons $P(A_i > A_j \\mid D)$ with ROPE-based interpretation to identify groups of models that are practically equivalent. The resulting diagram provides a compact view of which models are better, equivalent, or inconclusive under the chosen BBT interpretation (`weak` or `strong`).\n", + "\n", + "This notebook assumes the reader is familiar with the BBT model and `bbt-test` basics described in the [first notebook](01_simple_bbt_comparison.ipynb).\n", + "\n", + "Steps:\n", + "1. [Loading the data](#Loading-the-data)\n", + "2. [Fitting the BBT model](#Fitting-the-BBT-model)\n", + "3. [Plotting the Critical Difference Diagram](#Plotting-the-Critical-Difference-Diagram)\n" + ] + }, + { + "cell_type": "markdown", + "id": "65e40e77", + "metadata": {}, + "source": [ + "## Necessary imports" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "734af1b0", + "metadata": {}, + "outputs": [], + "source": [ + "import matplotlib.pyplot as plt\n", + "import pandas as pd\n", + "\n", + "import bbttest" + ] + }, + { + "cell_type": "markdown", + "id": "2c33da34", + "metadata": {}, + "source": [ + "## Loading the data" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "6540c65a", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
datasetAtomPair_countCDDDCLAMPChemBERTa-10M-MTRChemFM-3BChemGPT-4.7MECFP_countGEMGNN-GraphCL-sum...chemformer_maskcoatigrover_largemat_masking_2Mmol2vecmol_r_tag_1024molbertrmat_4Munimolv1unimolv2
0AMES0.8145250.8355240.8459980.8426450.8194070.7335190.8481700.7126310.778957...0.8017070.8062240.8149410.8176820.8063540.8127810.8539840.8554190.7725520.805847
1Bioavailability_Ma0.7120050.6687730.6418360.7445960.6704360.6957100.6910540.7005320.676921...0.7031930.6217160.8839370.6867310.7263050.5874630.6835720.7283010.6601260.765381
2CYP1A2_Veith0.9288640.9237140.9507840.9261630.9038250.8679910.9278670.8753830.897117...0.9044430.8970140.8569610.9269050.9133440.9013230.9293590.9308570.9018990.915601
3CYP2C19_Veith0.8581620.8721140.9223050.8712850.8536530.7899820.8744440.7702660.834949...0.8396800.8488420.7794360.8746050.8515390.8513610.8851870.8807570.8265590.863196
4CYP2C9_Substrate_CarbonMangels0.5963100.6551820.6977750.6920780.6679330.6524690.6272380.5973960.658709...0.6405320.6724090.5721650.6245250.6448720.6049920.6466360.6663050.5949540.679327
\n", + "

5 rows × 26 columns

\n", + "
" + ], + "text/plain": [ + " dataset AtomPair_count CDDD CLAMP \\\n", + "0 AMES 0.814525 0.835524 0.845998 \n", + "1 Bioavailability_Ma 0.712005 0.668773 0.641836 \n", + "2 CYP1A2_Veith 0.928864 0.923714 0.950784 \n", + "3 CYP2C19_Veith 0.858162 0.872114 0.922305 \n", + "4 CYP2C9_Substrate_CarbonMangels 0.596310 0.655182 0.697775 \n", + "\n", + " ChemBERTa-10M-MTR ChemFM-3B ChemGPT-4.7M ECFP_count GEM \\\n", + "0 0.842645 0.819407 0.733519 0.848170 0.712631 \n", + "1 0.744596 0.670436 0.695710 0.691054 0.700532 \n", + "2 0.926163 0.903825 0.867991 0.927867 0.875383 \n", + "3 0.871285 0.853653 0.789982 0.874444 0.770266 \n", + "4 0.692078 0.667933 0.652469 0.627238 0.597396 \n", + "\n", + " GNN-GraphCL-sum ... chemformer_mask coati grover_large \\\n", + "0 0.778957 ... 0.801707 0.806224 0.814941 \n", + "1 0.676921 ... 0.703193 0.621716 0.883937 \n", + "2 0.897117 ... 0.904443 0.897014 0.856961 \n", + "3 0.834949 ... 0.839680 0.848842 0.779436 \n", + "4 0.658709 ... 0.640532 0.672409 0.572165 \n", + "\n", + " mat_masking_2M mol2vec mol_r_tag_1024 molbert rmat_4M unimolv1 \\\n", + "0 0.817682 0.806354 0.812781 0.853984 0.855419 0.772552 \n", + "1 0.686731 0.726305 0.587463 0.683572 0.728301 0.660126 \n", + "2 0.926905 0.913344 0.901323 0.929359 0.930857 0.901899 \n", + "3 0.874605 0.851539 0.851361 0.885187 0.880757 0.826559 \n", + "4 0.624525 0.644872 0.604992 0.646636 0.666305 0.594954 \n", + "\n", + " unimolv2 \n", + "0 0.805847 \n", + "1 0.765381 \n", + "2 0.915601 \n", + "3 0.863196 \n", + "4 0.679327 \n", + "\n", + "[5 rows x 26 columns]" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "df = pd.read_csv(\"../datasets/benchmarking_mol.csv\")\n", + "df.head()" + ] + }, + { + "cell_type": "markdown", + "id": "f57ea570", + "metadata": {}, + "source": [ + "## Fitting the BBT model" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "e53da6fe", + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "23e91c7c02124b6abdede6a5fa094c0c", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Constructing win table: 0%| | 0/300 [00:00NUTS: [sigma, beta]\n", + ">Metropolis: [win1_rep]\n" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "27d643fd01f04033a3fa412b9d5f040b", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Output()" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
\n"
+      ],
+      "text/plain": []
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "name": "stderr",
+     "output_type": "stream",
+     "text": [
+      "Sampling 4 chains for 1_000 tune and 1_000 draw iterations (4_000 + 4_000 draws total) took 13 seconds.\n",
+      "The rhat statistic is larger than 1.01 for some parameters. This indicates problems during sampling. See https://arxiv.org/abs/1903.08008 for details\n",
+      "The effective sample size per chain is smaller than 100 for some parameters.  A higher number is needed for reliable rhat and ess computation. See https://arxiv.org/abs/1903.08008 for details\n"
+     ]
+    },
+    {
+     "data": {
+      "text/plain": [
+       ""
+      ]
+     },
+     "execution_count": 3,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "model = bbttest.PyBBT(\n",
+    "    local_rope_value=0.01,\n",
+    "    hyper_prior=\"log_normal\",\n",
+    "    tie_solver=\"spread\",\n",
+    "    maximize=True,\n",
+    "    scale=1.0,\n",
+    ")\n",
+    "\n",
+    "model.fit(\n",
+    "    data=df,\n",
+    "    dataset_col=\"dataset\",\n",
+    "    random_seed=42,\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "5d3ce85c",
+   "metadata": {},
+   "source": [
+    "## Plotting the Critical Difference Diagram"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 4,
+   "id": "2c55484f",
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "image/png": "iVBORw0KGgoAAAANSUhEUgAABFEAAAJ8CAYAAAA2+z51AAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjcsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvTLEjVAAAAAlwSFlzAAAPYQAAD2EBqD+naQAAkDVJREFUeJzt3Qe4VcXZsOGhGEEEe4kFOxoFe4uKUYOxYYkt6mfFWKMp9q6osZdYYo0mloi9xGiMir3ErrHFHrEbRY0FG+z/eub/Zn+L7QEWes6etc957usiwOEYFmuvNeWdd97pVqvVakGSJEmSJEkT1X3ifyxJkiRJkiSDKJIkSZIkSSWZiSJJkiRJklSCQRRJkiRJkqQSDKJIkiRJkiSVYBBFkiRJkiSpBIMokiRJkiRJJRhEkSRJkiRJKsEgiiRJkiRJUgkGUSRJkiRJkkowiCJJkiRJklSCQRRJkiRJkqQSDKJIkiRJkiSVYBBFkiRJkiSpBIMokiRJkiRJJRhEkSRJkiRJKsEgiiRJkiRJUgkGUSRJkiRJkkowiCJJkiRJklSCQRRJkiRJkqQSDKJIkiRJkiSVYBBFkiRJkiSpBIMokiRJkiRJJRhEkSRJkiRJKsEgiiRJkiRJUgkGUSRJkiRJkkowiCJJkiRJklSCQRRJkiRJkqQSDKJIkiRJkiSVYBBFkiRJkiSpBIMokiRJkiRJJRhEkSRJkiRJKsEgiiRJkiRJUgkGUSRJkiRJkkowiCJJkiRJklSCQRRJkiRJkqQSDKJIkiRJkiSVYBBFkiRJkiSpBIMokiRJkiRJJRhEkSRJkiRJKsEgiiRJkiRJUgkGUSRJkiRJkkowiCJJkiRJklSCQRRJkiRJkqQSDKJIkiRJkiSVYBBFkiRJkiSpBIMokiRJkiRJJRhEkSRJkiRJKsEgiiRJkiRJUgkGUSRJkiRJkkowiCJJkiRJklSCQRRJkiRJkqQSDKJIkiRJkiSVYBBFkiRJkiSpBIMokiRJkiRJJRhEkSRJkiRJKsEgiiRJkiRJUgkGUSRJkiRJkkowiCJJkiRJklSCQRRJkiRJkqQSDKJIkiRJkiSVYBBFkiRJkiSpBIMokiRJkiRJJRhEkSRJkiRJKsEgiiRJkiRJUgkGUSRJkiRJkkowiCJJkiRJklSCQRRJkiRJkqQSDKJIkiRJkiSVYBBFkiRJkiSpBIMokiRJkiRJJRhEkSRJkiRJKsEgiiRJkiRJUgkGUSRJkiRJkkowiCJJkiRJklSCQRRJkiRJkqQSDKJIkiRJkiSVYBBFkiRJkiSpBIMokiRJkiRJJRhEkSRJkiRJKsEgiiRJkiRJUgkGUSRJkiRJkkowiCJJkiRJklSCQRRJkiRJkqQSDKJIkiRJkiSVYBBFkiRJkiSpBIMokiRJkiRJJRhEkSRJkiRJKsEgiiRJkiRJUgkGUSRJkiRJkkowiCJJkiRJklSCQRRJkiRJkqQSDKJIkiRJkiSVYBBFkiRJkiSpBIMokiRJkiRJJRhEkSRJkiRJKsEgiiRJkiRJUgkGUSRJkiRJkkowiCJJkiRJklSCQRRJkiRJkqQSDKJIkiRJkiSVYBBFkiRJkiSpBIMokiRJkiRJJRhEkSRJkiRJKsEgiiRJkiRJUgkGUSRJkiRJkkowiCJJkiRJklSCQRRJkiRJkqQSDKJIkiRJkiSVYBBFkiRJkiSpBIMokiRJkiRJJRhEkSRJkiRJKsEgiiRJkiRJUgkGUSRJkiRJkkowiCJJkiRJklSCQRRJkiRJkqQSDKJIkiRJkiSVYBBFkiRJkiSpBIMokiRJkiRJJRhEkSRJkiRJKsEgiiRJkiRJUgkGUSRJkiRJkkowiCJJkiRJklSCQRRJkiRJkqQSDKJIkiRJkiQZRJEkSZIkSWofZqJIkiRJkiSVYBBFkiRJkiSpBIMokiRJkiRJJRhEkSRJkiRJKsEgiiRJkiRJUgkGUSRJkiRJkkowiCJJkiRJklSCQRRJkiRJkqQSDKJIkiRJkiSVYBBFkiRJkiSpBIMokiRJkiRJJRhEkSRJkiRJKsEgiiRJkiRJUgkGUSRJkiRJkkowiCJJkiRJklSCQRRJkiRJkqQSDKJIkiRJkiSVYBBFkiRJkiSpBIMokiRJkiRJJRhEkSRJkiRJKsEgiiRJkiRJUgkGUSRJkiRJkkowiCJJkiRJklSCQRRJkiRJkqQSDKJIkiRJkiSVYBBFkiRJkiSpBIMokiRJkiRJJRhEkSRJkiRJKsEgiiRJkiRJUgkGUSRJkiRJkkowiCJJkiRJklSCQRRJkiRJkqQSDKJIkiRJkiSVYBBFkiRJkiSpBIMokiRJkiRJJRhEkSRJkiRJKsEgiiRJkiRJUgkGUSRJkiRJkkowiCJJkiRJklSCQRRJkiRJkqQSDKJIkiRJkiSVYBBFkiRJkiSpBIMokiRJkiRJJRhEkSRJkiRJKsEgiiRJkiRJUgkGUSRJkiRJkkowiCJJkiRJklSCQRRJkiRJkqQSDKJIkiRJkiSVYBBFkiRJkiSpBIMokiRJkiRJJRhEkSRJkiRJKsEgiiRJkiRJUgkGUSRJkiRJkkowiCJJkiRJklSCQRRJkiRJkqQSDKJIkiRJkiSVYBBFkiRJkiSpBIMokiRJkiRJJRhEkSRJkiRJKsEgiiRJkiRJUgkGUSRJkiRJkkowiCJJkiRJklSCQRRJkiRJkqQSDKJIkiRJkiSVYBBFkiRJkiSpBIMokiRJkiRJJRhEkSRJkiRJKsEgiiRJkiRJUgkGUSRJkiRJkkowiCJJkiRJklSCQRRJkiRJkqQSDKJIkiRJkiSVYBBFkiRJkiSpBIMokiRJkiRJJRhEkSRJkiRJKsEgiiRJkiRJUgkGUSRJkiRJkkowiCJJkiRJklSCQRRJkiRJkqQSDKJIkiRJkiSVYBBFkiRJkiSpBIMokiRJkiRJJRhEkSRJkiRJKsEgiiRJkiRJUgkGUSRJkiRJkkowiCJJkiRJklSCQRRJkiRJkqQSDKJIkiRJkiSVYBBFkiRJkiSpBIMokiRJkiRJJRhEkSRJkiRJKsEgiiRJkiRJUgkGUSRJkiRJkkowiCJJkiRJklSCQRRJkiRJkqQSDKJIkiRJkiSVYBBFkiRJkiSpBIMokiRJkiRJJRhEkSRJkiRJKsEgiiRJkiRJUgkGUSRJkiRJkkowiCJJkiRJklSCQRRJkiRJkqQSDKJIkiRJkiSVYBBFkiRJkiSpBIMokiRJkiRJJRhEkSRJkiRJKsEgiiRJkiRJUgkGUSRJkiRJkkowiCJJkiRJklSCQRRJkiRJkqQSDKJIkiRJkiSVYBBFkiRJkiSpBIMokiRJkiRJJRhEkSRJkiRJKsEgiiRJkiRJUgkGUSRJkiRJkkowiCJJkiRJklSCQRRJkiRJkqQSDKJIkiRJkiSVYBBFkiRJkiSpBIMokiRJkiRJJRhEkSRJkiRJKsEgiiRJkiRJkkEUSZIkSZKk9mEmiiRJkiRJUgkGUSRJkiRJkkowiCJJkiRJklSCQRRJkiRJkqQSDKJIkiRJkiSVYBBFkiRJkiSpBIMokiRJkiRJJRhEkSRJkiRJKsEgiiRJkiRJUgkGUSRJkiRJkkowiCJJkiRJklSCQRRJkiRJkqQSDKJIkiRJkiSVYBBFkiRJkiSpBIMokiRJkiRJJRhEkSRJkiRJKsEgiiRJkiRJUgkGUSRJkiRJkkowiCJJkiRJklSCQRRJkiRJkqQSDKJIkiRJkiSVYBBFkiRJkiSpBIMokiRJkiRJJRhEkSRJktTSarVa7kuQ1EUYRJEkSZLUcsaOHVv/9bhx47Jei6SuwyCKJEmSpJbyxRdfhD/+8Y/hgw8+CPfff3/YeuutDaRIaoqezflrJEmSJKl9TDnllKFbt25httlmC/PNN18YMWJE6N7d9WFJHc+WRpIkSVLLGTx4cJh22mnD22+/HWaaaab4tS+//LL+527xkdQRDKJIkiRJaqkCsg8//HDMRrn33nvDvvvuG5ZYYonw+OOPh+9973vx5zFjxpiZIqlDGESRJEmS1BLYwnPTTTeFjTfeOGagzD333GHvvfcOP//5z8PKK68czj333LDWWmuFxx57LPelSuqkutU8D0ySJElSC3j22WfDJptsEs4555ywwgorxMwUAis47bTTwt133x223377sMYaa+S+VEmdlEEUSZIkSS3htttuC2eeeWa44oor4u+/+uqrMMUUU8RTeqabbrq4jad37965L1NSJ+Z2HkmSJEktgQKyr732Wsw4AQGUkSNHht133z189tlnBlAkdTiPOJYkSZJUOWmrDkVkP/300/j7VVZZJSy77LLh6quvDg888EBYcsklw5577hmGDx8epppqqtyXLKkLcDuPJEmSpEoGUG699daw7bbbhrXXXjtcfvnl4aCDDgrrr79+3NZz7bXXhtlnnz3+ft111x2vPookdRSDKJIkSZIqh0wT6p/ssssuYbnllgv/+te/wmqrrRYOPvjg+DWCJtRE4VhjAyiSmsUgiiRJkqRK+fjjj8Nmm20WHn/88fDcc8+FqaeeOn6d7BMCK9ddd13o1atX7suU1AVZWFaSJElSVi+88ELYY489YoCEQEnfvn1jxsk000wTfvOb39S/7+uvvw49evQI3bs7jZESMrHUPLY+kiRJkrIh02SNNdYIPXv2DG+88UYMphx44IFh+eWXDxdccEF48sknw6qrrhouueSScOqpp4btt98+buGRujoKLsNaQM3ldh5JkiRJ2Zx11lnh3XffDYccckj8/bPPPht++MMfhl133TUcddRR4aGHHoqBE44z/uMf/xgWXXTRWAuF30td1Q033BDOP//88N///jf86le/Cj/+8Y894rtJzESRJEmSlM0nn3wS/v73v9d//4Mf/CAWlb344ovDZZddFpZZZplwzjnnhDnmmCOcdtpp8XsMoKgru/nmm8P+++8fhg0bFuaff/5w3HHHhffeey/+mVt7Op5BFEmSJElNR30T7LTTTvGo4hNOOKH+ZwsuuGDYbbfdwvPPPx9/v/TSS4e99947ThTffvttPy11WbfffnvYZpttYkBxnXXWiXWECDASiPSUquYwiCJJkiSpad555534MzVQCKRMOeWUcTJI7ZOTTjqp/n29e/cOL774Yhg7dmz83hVWWCGMGDEizDrrrH5a6pIIIpKF9cUXX4T333+/nnVCNheZW0sssUQ47LDDwhVXXBG+/PLL3JfbafXMfQGSJEmSuoann346ZpWstNJK4ZZbbonBEWy44YZh3LhxcTV9yJAhYeutt46ZKWeffXY8jQecyOOxxurKNVD22muvWDOI7W177rlnDJRwDPgHH3wQTjzxxHDTTTeF0aNHh3333TesssoqYaaZZsp92Z2ShWUlSZIkdbgPP/wwrL322vGknYcffjgGR2688cb6n7O6zmTw5JNPDlNPPXVYaqml4ve7RUFdHcHFgw46KNY+4f3BlVdeGY//5hhwAikpIInPPvssTDXVVBmvuHMziCJJkiSpKf7617+GFVdcMW7RWX/99eMEsBhIaWQARV0dAZShQ4fG7JIjjzwyZmxxpDE//va3v4VddtklBlfWXXfd+uk8vjcdy5ookiRJkpqCyeB0000XZpxxxnDdddeFjz76KKy11lrxz5555plwzTXXjPf9TBSlrp6B8stf/jJcffXV4dJLL60HUMC7Qx2hHXbYIVx11VX1/873pmNZE0WSJElSU7FSTiCFieFWW20VBg0aFD7++ONw7rnn+klIIYTXX389nHLKKTFIMnjw4LDwwguHAw44INYG2nTTTev3iHpCbOXhz9UcbueRJEmS1BRtbTO49tprw2abbRZPFGFLgtTV3XbbbTFYssACC8Tjv9nCw+/PP//8uKXnmGOOGS+QouYyE0WSpMmUBjOSpPKog0IxWY5m/fTTT8Occ84ZXnvttbDrrruGSy65JAZQrOWgru7NN98MG2ywQZhhhhliZtb3v//9euBx2LBh8eeDDz44FmImi0vN5whQkiYTAzx1Tf/85z/Df//7XwMoklQiYNKIAMobb7wRtybQnjIx7N+/f7jjjjvilgT7VynEoAkBRQKNe+yxRzy2uJi9RSDlV7/6VdzmwxY4NZ/beSSppLQ6RuR/yimn9L51Me+8807YbrvtwplnnhnmmmuuUNXn8+uvvx7vmENJaqbi0aoUxbznnntioOSnP/1prIHCtp2VVlop7LbbbvV2y+wTaXxPPPFE3NLD+8QWnlNPPTWss846430PRZk53UrNZyaKJJXEQI8BIQPBo48+OnZu6tyKq6IM/j///PPw/PPPx+08SD9X5fm8/fbbw2WXXRav2xVdSc02evTo8POf/zzce++9MdNkr732Ch9++GG488474zGsrJqfdtppMYCC1E55koi6urvuuiucfvrpMWiCmWeeOR79zZhz+PDhMfOE44yL+vXrl+lq5VKVJJX08MMPxwEhA8Qnn3wyPPfcc3FwSAqyOicG9iNHjgzPPvtsPJJz3nnnjZlIqR5K7oE/afH33Xdf2GSTTeLvR4wYEdZYY43s1yWpa2JlnKyTk08+OWbFUeeEU3foM5kgcgzr7373u/i91paS/r///Oc/sR/nZxboVl999Zittffee4fDDjssXH755TFASf0T+nn+HPb1+RhEkaSJSCnGFMHjZyqir7/++jEbgRX/66+/Pg4UrZDeOX311VfhpZdeigG03r17x4HMVVddFQcwY8aMiUXfzj777PC9732v6YMZJiDsk/7Tn/4UM2QYXDEAS1t5ihMUU+UldXR7RHszzzzzhP/5n/+JWZvHH398eOyxx2IQZeDAgWH33XcPJ554Ythpp53ClVde6bZD6X9NO+20cSzBth3qrs0666yxT1966aXDM888E956663wy1/+MkwxxRTxHVN+BlEkaSKYGJNOSRole7wp9jVkyJAwYMCAsMUWW4TzzjsvXHfddeFHP/pRmGWWWbyXnUAx4MCAZccdd4w/MNNMM8UMpGOPPTY8+OCDYf75589WH4cJC1knX375ZVyZIq13ySWXjKddkDJPYIWfCf7w3EpSR2D7AcESJnxkxo0aNSqst956ceX80ksvjcez/vjHP46BFIpk0r7Stqp1ffDBB7GvnH766XNfSqfA+/CTn/wk/vzb3/42vPfee7GW0J///Oe4SEMQhX6cLXGqBgvLStJEPPLII+Hw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j6HOHveTQvaMQayB0kJqUgcYN25cS1zjoYceWuvWrVttjTXWqN1///25L0lNduONN9YWWWSR2siRI+tf+9e//lXbc889a0suuWRtwIABtWuvvdbPpZN58cUXa9tuu23toYceqn322We1xRdfvLblllvWxowZU/+et956q1YVb775Zu2VV16prbzyyrW33347fm2DDTaoLbvssrV//vOfuS+v0saOHRt/pq3/xz/+URsxYkStR48etT/84Q+1/fffv/bRRx/lvkSpsj788MPaLrvsUjv++ONjP0lbecYZZ9ROPvnk2g9+8IPaSSedFNvQyy+/vHbuuefW7rvvvmxjwBdeeCGO5/r371979NFHs11Hq3v11VdrgwYNql1yySXx9zfffHPse1577bX69zz55JPxXh944IEZr7TaeBf+/ve/16655pr61372s5/V1ltvvdozzzwTf3/DDTfUHn/88YxXqe/CXFZ1RGCuvmrLNggKHlYR10gKHen6rEhSOPS+++7LfVlqkn//+9+xBgpHL6622moxI4V6EzwLFBRltZqCo6RWWkOhc+BzfPHFF+PnzedMoVhWflgJYoWIlTeKy4IspPTfNPsa8cwzz8TrIivm+9//fvjkk09iVkqq78Gq1ogRI+IJCZqwtGWHVfNbb7011rxhpZwMRDJ72L7l+y21bcoppwwLLLBAeP755+NqOvXBOP3r17/+dazfQBtEQVkyvcjmS1kJzc7cY6sRdTto2xl3sk0TaZuuynv77bfjiUupQOzqq69e75NAe0nfSe2blVZayVvbBrKgUvF3xpXbbbddzGq+9NJLY1YX842XX345bhlfbLHFvIctyiCK2l3qPNkCwXnnnINOqif70quACuIJhdFIT+QEFo5uYxBgIKVroOAde1JJqySYxpaud999N3Z2HHfL0bZpC4/bvDoHPsf5558/1jzhPWcQyCCbQQ3FWQlOkMZcnHw3+7NPNVCYDDDZZ2LAZJ9BK0WNSZ2n6C1BAI8NbRvFDpkIpKBTQv0YBq6cIEKNFLbuMdj1/Za+ickyR7oTNFlyySVjjQwmgQR2scgii8TtjgR0c0kB0FTjiEApR74zSWXbBDVcHnjggbjNSOXQ16QASvqsU80TEFBjcXSJJZaItcQMQo+PbU5sfeKEKorTc/QzdVDYYsbWOPodtp0xN6I/UusyiKIOwZF3HNnFYJWBP/v+KDaWG0UFWZEkqMOAgFVnVk441YKVlZ/97GfhF7/4RSyepc6N55FCaazwUx/hD3/4Q9h3333DHHPMYWX0ToiBHgNtcCQjg0Sq4rO/m0AK+5DZo0xQNdf1pQEYp8iQBUV7xSB1xRVXjIPWs846Kw7KLrvssjgAc/D6TayWcm/4fKl5Q0E/sGJKcJTAKQEUJoPcTwMoUtvSSVYEUlgQY7GBGkLUYKItZQxF0DnnSSJcI2NNsggZvz3++OMx4MN7T6Yp18zJZbSrKj82IvuxuJjA507AjD6ShSYK9xY/A/0fslv79+8fF+IwwwwzxKwt3pe99torfo1AytJLL+1Ys8UZRFGHZXuwws/ElG0TFHGkoWWFICeCOUyYuB4KNTIwoJOgqBNHiB566KFxsE2R0ZyrK+pYrK7Q0e26666xGB2ndTCZZnDAiSxVCPjpuyHrgJRuPlfQ/pDyDd7t3/72t3EyTcptapdyDgb5uwme0G6y5ShlmdCGEkhh2w7/JtonVgBzX28VPffcc3EyRSCc4pcExu688876SipbtPjcWREEwXTSqQ1GSW2jjWHrI+8P2bo/+clP4uIYwWbeM7I9UnvUTOmdJYhz5JFHhqFDh8aJK+0jgRQy+WjXmfhfeeWVsaC0Jh+ZPJhrrrliwX2K7zM+JlClthFwIruVzPaEE44InJDxSrZU6tu5r2ph36miivS/hcf++9//xntBocYvvvgiFuyba665amuvvXb9Hp111lm1oUOH1j7++OOm37enn3669u6779Y+//zz2pVXXllbYYUVakceeWTt3nvvra200krxWvfee+/69/O96hy+/vrrNgtN/vvf/67ttddetdGjR8cCYDyr1113Xfwzi9G1NooDL7roorX111+/NvPMM9dOOOGE+p+NGjUqfv2RRx6Jv99nn31qd911V7ZrTc/anXfeGa+VdoiCfRRvLKIQ7q233prpKqvvyy+/rB1++OG14447rv41PleK79JHpe9pbAck1Sb4XqT+k/7yzDPPjL/m5y222KL22GOPZb11tJk/+clPan/729/qX6OAdL9+/ert+1dffZXxCjvP88A4uVevXrXbbrst/t4x0vg+/fTT8X7PPIhxxsYbbzze17fbbrvaPffc08GfmprFIIq+ExoKKk+feOKJteHDh9c22mijGFDhFITVV1+9dthhh8XvO++882pLLLFE7amnnmr6Hac69hxzzFH7y1/+En/PpPniiy+Okyz+DFzXAw880PRrU8cpVpJvDKS88847tRVXXLE+4SK4xiARDg5aGwHT5ZZbLr7vTJoJjBEkffbZZ+NJLHzunDTRKOfn/uCDD9YOOeSQ2h133BF/f9NNN9Vmmmmm2tlnn/2N7/X5nDDa8TfeeKPeN/EsLLPMMvXA/fvvv99Bn6DU2t57770JBlLoS2k3jz322Ph7TuNJ/WWzFdu/W265pda9e/fazjvvPN737LfffrWePXvGsajB0va536eccko8zVDfdPXVV8eFWfqboueff7627rrrxkA+84vf//73tQUWWKD28ssvexs7CYMo+tbSUZtkcwwcODAGKvg1WPkjUMEqwSqrrBJ/5AigMBlZeumlYyPGMcbJJ598UrvgggtiIIXrVOfr+NdZZ504gUqKK1LXX3997cILL4y/dpDVeTBxZtDC0ZvJf/7zn9pPf/rTeGwjGWbFo8yr8NnzrBJwJguF5zIF/AjwsvLHwEsT74foW/ici/c0TQw5mhN33313bdiwYTGAKqk23nhoq622ill5bbWNTKBTNl8V2kwyzNJRyrSTZJ5w1HLjBFYTl9pJ+s1JIXA2oUWprozgyIABA2qLLbZYbbXVVouLNUXcq1133TX2PT/+8Y/j0dDqPAyi6Funyx900EFxpZfMju233z6mrZFKWey8aJxZ5c+xhYdVioUXXrie1slgOm3XSOl3BFBYpR4xYkTTr08dNyAEkyo6LQJ5STGdP32fK/udw5gxY+qDGrYN0ibhkksuqa266qr1LR1JlT532kmumTb0rbfeql8bK3+0Y2rbE088UVtyySVjkJ4syN13373+Z9xDAiabb755vI+DBg2q/fWvf/VWSgVpK/af//zn2qabbhozipO2tsLkbjf5+4866qgYdE4BcbbzfP/7349fb+v7NWEsfDJOevPNNyf4PSlowlhe4xs5cmRckMWWW24Ztz0RSGl87niX0hhFnYdBFH0rNAakRj/88MOx82Vyyj4/9u2zp58Axc0331z7wx/+kOUOE7Q54IAD6pkxDBRYadl3333H+z4m0pdeemntpZdeynKdav/g3v/8z//EoBifLc/o4MGDxwuk4Pbbb68tssgiMU3ZQVbre+6552o77bRTnCSzUkq7tNZaa8UthKShp1T1KqyiNkoTFQIpBHt+9rOf1V5//fXxnkuf0W/i/f7Rj35Uu+yyy2L/Q6YJ7/kGG2xQ/x4C+n369KkttNBCMStR0v+hfyRwQoCZH1dccUVtww03HC+QkmqPUMuhuOWnmRrbPybzbMWdb7756hkptP3TTjtt3Cphe1nufrJ9lPqFU089dczaTdnlbQVQPvjgg1jjo7hNWrVvbBNlnpECKbBMQOdmEEWTLU1E+JmVPzrhtMJH4ITOlqwPBq45UypToafUCdDZzjjjjHGwrc7nmWeeqS211FIxcEcwJUmBFLZMgAn2nHPOGQsMq3N87mzZo3B1CpqmQApBiU022aSSacjFgX66LgIpbD1k+1GZFOuujPd6+eWXrw9WuZ8En3jPd9xxx3qg5Yc//GHthhtuyHy1UjWDz/POO299Ysz7kgIpZBWnSeAss8xSu/baa7NeK1knxVpWtI/HHHNM3LqZJqrFLX2aOMbBs846awyQvfDCCzETku3tZEKm9rQYQKEvzVmAveqKCzQEUgjo8w5NP/309XuqzscgikorpnamCQCBigMPPDCmsaWtMq+88kostETDXKUGjh+77LJL7dRTT41fs2p758EKyuKLL15Pq2zs2JhwMQhg5YrtWzyfyLFilf5OBq7sM07bTFw9m3xMmskoSvVtGu8zgRQGh5tttlktt/T5TqgmRzGQQmFuTdoee+wRT9gqrgTymZONQv/D+2/B6Ek/k+q6CJgU2xsCKZdffnncBkcAOnd/Cd5jroHaE8XaJ9SXoBbF3HPPHfvRKmYaVhVjpXTwQ8KWRwqkFuufcF+p9WEAZdKKzx+lBNhixpZTdV7dcx+xrPHPvK+qBx98MJ5p/p///Cf+vlu3bmHs2LFhqqmmCgcccECYc845w9VXXx0uvfTSMMccc4Sf/vSnYf755w9V0b179/hjscUWC0cffXT49NNPQ8+ePXNfltrJ66+/Huaaa66w9dZbx3cpvU985ujTp0+4/PLLw3zzzRdOOOGE+HzyPTzHzcbfeeONN4bNN988HHjggWHLLbcMb7/9dpZraXXPP/98WHzxxcNWW20Vxo0bN147yv3kzw455JDYbj355JPZrjM9azfffHP8zEePHv2N7+nRo0dsU7/3ve+F5ZZbLst1Vh2fMb7++uv486qrrho/W/qdDz74IH5tySWXDJ988kl499134/tPuwDfr/Hddttt4aijjgqnnHJKkz9F5W4zjzjiiHDxxReHUaNGxb7zkUceqf85feWaa64Z1ltvvfi9J554Ypb+stiW8x5zDccdd1wYMWJEvCZ8+eWXYe211w7XX399mGaaaer9vcrd3/PPPz+8+uqr9a/tsssu4Y033gibbLJJ/D3j5FVWWSUMHz48DB482Ns6kT7pnXfeCZ9//nn8Nf38F198EX9edNFFvW+dmC1ORkyc6LzoyKo+wHvxxRfDueeeG6699trw/vvv1wf9dGIEUg4//PDQr1+/cPfdd4cxY8Y0/fpScGdSwajtt98+DrzTv0GdA58/kyYU3yWehzfffDPccMMNYcYZZ4w/b7zxxtkCKOAdOfjgg8MFF1wQB31M/nr16pXlWlrdyy+/HD9fFAfQfL7PPPNMuPXWW8MyyywTA7yDBg3Kdp08aw8//HC46aabYqBv+umnn+j3M3hN/y79f88++2wMOHJfUgB86NChMeD0wAMPhJNPPjm88sor4bnnnovfM/XUU3vrJoDB/c477xwnnr/5zW/C6aef7r3qIugnCY7QJv7iF7+Ivz/yyCPDfvvtF6644orYbn722Wdhs802i8/JRhttlCWAkoLO2267bdhwww1joHT99dcPBx10UDjppJNiUIXfr7TSSmHgwIFNu7ZWlMbFL7zwQnjqqadisH6bbbYJ6667bthjjz3CE088Ee699944vj/77LNj28k4fsoppwx/+tOf4j3u6lKwpIhgPuOOu+66K+y2227hvffei1/na3/72998LruC3KkwXRX7uNnLyTGsU001Ve2qq66qfHotBVipdULtgcYCYxSVffDBB7McH8nezm7durWZctrW/bRCduczatSo2nTTTRefzSRt16J6Oun9uY82TameFMTjKNvbbrstFnNj+1t6jotptJq0p59+uta/f//6u188gYmjL7fYYotvnMqTA3vKOQKe1PN0fY31WYr7zymIaxrw/6HwN/eP+8K2PLZxFVHf6Je//GVMoaYuEsVm1TbaHk40SnUkzjvvvNrBBx8ct3BYYL1roQD/RRddVFtuueVifTuKsrNlhtoYuY8I5sQdtpew7eSMM86ItSVOP/30+GcvvvhifG6Lx9Vr4qgLNdtss9XWXHPN2I7S37AN+te//nXcEksBdrZCUt+QLT2cuqlvYp5Dzb00B2IuR59z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OlETyuSowntDe0m2Hs9i4/g4ZVJowoqBePoXFkDpBxmz82sK2afTdqhjSHCgCu037RGLI2TIpWA4zyhtFAuiOQrDc2+KdXfIyOdaUiDl4osvjgsjLCpbn0cGUZrwQm699db137e16khHQXQ415FyxU60eAwsKxVEsYlap/S1XB1uCqCwopauI10Lq74Uw2KwUMzoqcLgQN9d+hxJ+fzLX/4S037TKirFRH/zm99M8L9ppvR38h4z+ee55Lmddtpp42CAwsZ0umzf23LLLetp9PomBnwcb1lcPeUeUiC6rTa0eLoNRa9zFElk8EqwPE1ayTriWjgVgRo9TBJMR/9uBTAn9N+YzdO2NHEikEchXorGEpzkOaVvZ6su706OwKM69h0iKEYGHBkJBCUI6FOoNQXvi99bBQT8CO6lrIR77rknbu8pZk7pm0aNGhUXj9LWF8ZI1BhJCKRQkJdJfzEglfOz59lcf/31Y5YUnzcnlFJYtphVWFy0bdbCF88f2TrpBKj0d6dACgs6KG4vVtfWPahD/fe//w3vvvtu/PXXX38dunf/v1v+1ltvhRtuuCEstthiYfTo0WGBBRZo+qdBNlK3bt3ir3//+9+HHXfcMey6667hd7/7Xdhnn33CwgsvHE4//fRw8803hy+//LL+vc3097//Pay33nqhV69eYZlllqnfS67l/vvvD4MHDw5jxowJCy64YPyzHj16xD/Lca3qmOfz9ttvD+uvv3649dZbw1ZbbRUOPvjg0L9//3DdddeFCy64IPzyl78c77/L8dnzd95xxx1hiy22CJtttlmYZ555whprrBFOPPHEsNdee4VtttkmbLLJJvHd4s95nr/66qum3UeMGzcuVB1t4SeffBI23XTT8Omnn4bLL788fv31118PY8eODV988cV438/XeOc//PDDsPHGG4ejjjoqzDXXXE295rvvvjv+3Y899lhYc801wxlnnBGfg+OPPz688cYb4c0334y/Hjp0aP2zUNvvEH0N7zfoL9t6ZvnMwZ/x3xT7Vf2f6aabLv489dRTh/POOy/e1x/84AdhyJAh8eu8N/yYcsopvW2dBO8D40r6m4svvjgsv/zy4fnnnw/bb799mH322cOf/vSncOONN9a/tyrWWWed2B/SZx555JFxLHr00UeHhRZaKPelVdrjjz8eTjnllHD11VeHjz76KLz8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8ed33nknzDPPPGGvvfYKs88+e8jptddeC++99179Oj///POwxBJLhHvvvTf84he/CF9++WVYaKGFwl//+tcwaNCgynzmfNZXXnll/OzXXXfdMNdcc4U99tgjfPHFF6FPnz6Veod+//vfh+233z689dZb8fdHHHFE2GyzzUJO3bp1C9dee218t/mceaeHDBkSttpqq7D66quHn/3sZ+HRRx8d7/ubiUB1Uffu3cMTTzwRPvjgg7DBBhvEr51++ulhwQUXDPvss098TnNppbaTz/Hvf/97OOyww+LnPXjw4HDNNdeE2267LQwfPjzeX2yxxRZhxRVXzHKNd999dzj//PPDDTfcEPr27RtWWmml+L5zfVzXCSecEEaMGBFmmWWWkPteXnfddWGFFVYIv/3tb8PZZ59d/7Nhw4bFr/H5097zTDRTK70/Ve8vR48eHXbaaaf43J177rmxn9loo43CXXfdFe8rn236Gbvvvnv2fp13h+vlOp577rmwyy671P9su+22CxdddFGYf/75s10f1zRgwIBw0kknxbEFuKfzzjtv2HbbbcN8880XevfuHWacccZwySWXhMsuuyyst95633ium2HllVeOn/+FF14Yr41rYpz50ksvxbETP/Ou03fOOeecoSrvT3oen3766fiecw9vuummbGPiyWk3N9100zjeS/+GZo4/nn/++bDxxhuHHXbYIY7baIdWWWWV2I9vvvnm4de//nX4yU9+Erbccsv4Lh111FFh6NChTX82q9xmtlp7VHTLLbeEP//5z/FamQ/x608//TQsvvjice7BnCnNMZo9Li77rl9++eVxDLrooouGyqp1IkSyWO1nNT+lqoJVwBQxpNATBf5yHhdbjLQRUWdlgCyKdP41e+pY7UVxVTUHIpWkqaWaLcViVKQEUpisCvumSelnf3zxOlNGChFiimnlvpeT+sxTwa+chdLKvEPsVSU7IVdaZRHvMdXu05YDsEpAwUTSl8lSSKutObEyVdzPyb2jSn56z5Gz/k2rtZ0gSy/t4U4FGrnHvF+s/OZOp2V/N6cLgHuW2s2UVl2s25QTKb477bRTLDpHSj+rfqSfF3HaWU5Vf39apb9MxxentpttHKyYp7oDVWjTiyu+ZD2mjAMKS/Jus/2kKth+y2rp0KFD44ovWRxkFJMtxUo/bRBZe2SgViGrh9VnsmESxky0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JEmSJEmSOmsQ5YMPPgjrr79+mHPOOUOvXr1C//79w4EHHhjGjRsX/v3vf4du3bp948eHH36Y+7Ir6/PPPw8bbLBBGDBgQFhsscXC6quvHl588cX4Z9ttt11YdNFFw+KLLx6WWWaZMHLkyNyX27L38qijjgoLLrhg6N69e7j22mtzX2pL+OUvfxnmnnvu+A4//vjj9a/fdNNNYemll47P5vLLLx+eeOKJrNfZyvfz/fffj+93+sGz27NnzzB69Ojcl9uSzyZf4z1P9/Oyyy7Lep2tfj9/8pOf1PugwYMHh8ceeyzrdbbyvZzQ1zV57H/ah31P+7P/aT/2Pe3n2GOPjWNL5j/0P3fccUfoFGot6JVXXqktsMACtcMPP7x25pln1vr371/jn3LqqafGP+PXG220UW3EiBH1H19++WXuy66sMWPG1G644YbauHHj4u9PO+202o9+9KP46w8++KD+fY8++mhtuummq40dOzbbtbbyvXzggQdqL730Uvz9Nddck/lKW8Odd95Ze+2112pzzTVX7bHHHotfGz16dG366aevPfXUU/H3d911V22RRRbJfKWtez8bHX/88bWhQ4c2/do6y72c2L3V5N/PYh909dVX1xZddFFv47d8Nsu8/5o4+5+OY9/z3flutx/7nvZz2GGH1X7zm9/E55M5+u23317rDHqGFjTHHHOEZ599NvTo0SP+/osvvgi//vWv48rKuuuuG7+2yCKLhHXWWSf07ds389VWH9k8a6+9dv33rOyfcMIJ8dfTTjtt/esfffRRluvrLPdy2WWXzXhlrWnllVf+xtdeeumlMMMMM8R3HKxOjxo1Kjz66KNhySWXzHCVrX0/G5133nnh6KOPbsr1dPZ7qe9+Pxv7IFax9O3upc/sd2f/03Hse1Ql9j3t59BDD40/33fffeHVV18NnUVLbuch1TwFUNjCc8MNN8RfDxkypP49RxxxROjXr198CfbZZ5/4fSrnlFNOidulkv322y/MN998YcMNNwxXXXVVTMfSt7uX+u4WWGCBmAZMY4y//OUv4eOPP45b+fTdcE/ZLjl06FBv5Xew9dZbh0GDBoXtt98+/Oc///FetsP9ZPvuwQcfHC666CLvp7Kx/+kY9j3tx/6nfe+lfY8mpKVnw2SgbLHFFuGWW26Je30333zz0KdPnzjQYrJ/8cUXh5lmmikcf/zx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QRRJkiRJkqQSepb5JkmSJLWuMWPGhHvuuSe0wnVKUhW0SrvZCsZ0srbdIIokSVInN2rUqDB48OBQdQMGDMh9CZLUUu1mKxjQydp2t/NIkiR1cgMHDgytoFWuU1LnZ3vkvZwQgyiSJEmd3KBBg0IraJXrlNT52R55LyekW61Wq03wTyVJktQp0tL5UXX9+/ePPyQpt1ZpN1tB/07WthtEkSRJkiRJKsHtPJIkSZIkSSUYRJEkSZIkSSrBIIokSZIkSVIJBlEkSZIkSZJKMIgiSZIkSZJUgkEUaTJ89NFHYYYZZgiTezL42LFjw29/+9vxvnbVVVeFJ5980vsvSZIkSS2iywdRmNxKZT388MNhqaWWCt26dZusm0aw5Iorrhjva0ccccS3ev58ZiVJkiSpiwZRvv7667D33nuH0aNHl/r+l156KSy88MLx11999VXo379/2G+//eLv77777rDKKqvEX7///vthq622CosttlhYaKGFwsknn1z//1hxxRXDgQceGFZaaaWw/vrrhw8++CBsuummcXLM//fQoUPj933xxRfhgAMOCD/84Q/DIossEjbbbLP4NXXtIMrMM88c1l577bDAAgvEZ2XMmDETfV6eeeaZsM4664Q333wzLL744uGQQw4JG2ywQXjqqafCtttuG5ZZZpn4HvzrX/+Kz+Nyyy0XfvCDH4QLLrig/vfOPvvsMejC//fOO+88yev88MMPw5577hnfEUmSJElSJwiiMHFkovnxxx+H6aabrtR/w/fx/bj44ovDvPPOG4MgOPHEE8O+++4bf73eeuuF1VZbLTzxxBPhoYceCscff3x47rnn4ir+448/Hn8m6PLXv/41Tk6ZyD7yyCNxwnvhhRfG/49ddtklTpTvv//+8PTTT4devXqFSy65pMPuh6qPZ+ntt9+OWSU8TwRJJvW8EJj76U9/GoMnPHuHH354OOigg2JAhd/z/8kzvN1224WTTjopPPDAA+G+++4L++yzT/z/5+8jAMM2Iv6/zz333Ele5zTTTBM+//zzGBw0kCJJkiRJ7aNnyOiMM86IdSGmn376cOWVV37jzzfZZJNw5plnjve1aaedNnz66aexJsXpp58ejjvuuHDWWWeFF198Mbz66qthrbXWCiNHjgzjxo2Lk1L07ds3zDXXXOHdd9+NgZt+/frF+hRpSwar/Oecc07o0aNH2HDDDcPcc88dM14uu+yy8Oijj4ZTTjklft8nn3xSz3RR181E+dvf/hb69OkTf0/20jvvvDPJ54X/bssttxzv/2fppZeu//60004Lr732Wthoo43qX5tiiilisI/gHkG+XXfdtc1r2n333cOIESPa/DMysvj/3mOPPdrpDkiSJElS15U1iLLDDjuEv/zlL2H55ZcPRx55ZKn/pnv37vHHtddeG1ZYYYW48s8q/u9+97u4fSHVn1hyySXr/81nn30WXn755bjF4oYbbogZKgRMEv47tmVcf/31cYvP2WefHVfx2YJx+eWXd8C/XK3oP//5T9x2xlabhIwRtpORUTKh54VMEDJTyDxJyD5ha07y2GOPxUAe24QaEZhZffXVJ3hdBEn40ejQQw8Nd911V6ntP5IkSZKkim/n6d27d9xO88Ybb4T33nuv9H9HJglBl7322ituW3jllVfC7bffHrcGYc4554yTVrJRWMlnFZ6sFjJeWNUvZgC89dZbcWK84IILhl//+tdh4MCBMYAyxxxzhHvuuSdmr4AMFrYGqesie4QsKLJOwDaeL7/8Mqy55poTfV54vsmGYntPwv8H9XwS/vtrrrmmfuoP2S2vv/56/HXjM1sGgcV///vfMWg41VRTtcO/XpIkSZKUvbAsE8s//vGPYcYZZyz931AXhToTbNFhckoQZccddww9e/7/xBrqTxAUISBC9glBl1RYtnFCysR48ODBYdCgQfHrFPVkSw8/U+OCbBeK0/J7MgLUdZE9QvbUsGHD4nNFFhXZS2RGTex5IUCy6KKLxv+GwrPYfPPN4/YctgNR94R6KQRbeG7JWOHPUy2TbxNE4R2hMK0BFEmSJElqP91qaelbkiRJkiRJ1c1EkSRJkiRJagUGUSRJkiRJkkowiCJJkiRJklSCQRRJkiRJkqQSDKJIkiRJkiSVYBBFkiRJkiSpBIMokiRJkiRJJRhEkSRJkiRJKsEgiiRJkiRJUgkGUSRJkiRJkkowiCJJkiRJklSCQRRJkiRJkqQSDKJIkiRJkiSVYBBFkiRJkiSpBIMokiRJkiRJJRhEkSRJkiRJKsEgiiRJkiRJUgkGUSRJkiRJkkowiCJJkiRJklSCQRRJkiRJkqQSDKJIkiRJkiSVYBBFkiRJkiSpBIMokiRJkiRJJRhEkSRJkiRJKsEgiiRJkiRJUgkGUSRJkiRJkkowiCJJkiRJklSCQRRJkiRJkqQSDKJIkiRJkiSVYBBFkiRJkiSpBIMokiRJkiRJJRhEkSRJkiRJKsEgiiRJkiRJUgkGUSRJkiRJkkowiCJJkiRJkmQQRZIkSZIkqX2YiSJJkiRJklSCQRRJkiRJkqQSDKJIkiRJkiSVYBBFkiRJkiSpBIMokiRJkiRJYdL+HyXYEhFswB/hAAAAAElFTkSuQmCC",
+      "text/plain": [
+       "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "fig, ax = plt.subplots()\n", + "\n", + "model.plot_cdd_diagram(\n", + " rope_value=(0.35, 0.65), interpretation=\"weak\", ax=ax, xlabel_spacing=2\n", + ")\n", + "\n", + "fig.set_size_inches(14, 8)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a95b041e", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "py-bbt (3.11.13)", + "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.11.13" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/pyproject.toml b/pyproject.toml index 696c55a..64f989b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -36,6 +36,7 @@ dev = [ "pre-commit", "ruff", "seaborn", + "graphviz", ] test = [ "pre-commit", diff --git a/tests/bbt/test__utils.py b/tests/bbt/test__utils.py index ccd3531..b5efdbd 100644 --- a/tests/bbt/test__utils.py +++ b/tests/bbt/test__utils.py @@ -11,6 +11,10 @@ def mock_fun(param_lit: MockLiteralType, param_str: str): ... +@_validate_params +def mock_fun_with_kwargs(param_lit: MockLiteralType, **kwargs): ... + + class TestLiteralValidation: """Tests _validate_params decorator for validating parameters.""" @@ -37,3 +41,11 @@ def test_unexpected_kwarg(self): ValueError, match="Unexpected keyword argument 'unexpected_kwarg'" ): mock_fun(param_lit="option1", param_str="valid string", unexpected_kwarg=42) + + def test_unexpected_kwarg_ignored_with_var_keyword(self): + """Test if _validate_params ignores extra kwargs when **kwargs is declared.""" + mock_fun_with_kwargs( + param_lit="option1", + unexpected_kwarg=42, + another_kwarg="value", + ) diff --git a/tests/bbt/test_alg.py b/tests/bbt/test_alg.py index 5ce9f0c..b742d19 100644 --- a/tests/bbt/test_alg.py +++ b/tests/bbt/test_alg.py @@ -82,6 +82,61 @@ def test_construct_win_table( # Then np.testing.assert_array_almost_equal(result_table, expected_table) + def test_construct_win_table_paired_local_rope(self): + """Test paired-path win/tie/loss construction for repeated datasets.""" + data = pd.DataFrame( + { + "dataset": ["d1", "d1", "d2", "d2"], + "alg1": [0.8, 0.9, 0.3, 0.2], + "alg2": [0.7, 0.8, 0.4, 0.5], + } + ) + + result_table, _ = _construct_win_table( + data=data, + data_sd=None, + dataset_col="dataset", + local_rope_value=0.1, + tie_solver="davidson", + maximize=True, + ) + + expected_table = np.array( + [ + [0, 1, 1, 1, 0], + ] + ) + np.testing.assert_array_equal(result_table, expected_table) + + def test_construct_win_table_paired_local_rope_three_algorithms(self): + """Test paired local-ROPE construction with dataset column and 3 algorithms.""" + data = pd.DataFrame( + { + "dataset": ["d1", "d1", "d2", "d2"], + "alg1": [0.9, 0.8, 0.4, 0.3], + "alg2": [0.8, 0.7, 0.3, 0.2], + "alg3": [0.2, 0.1, 0.5, 0.4], + } + ) + + result_table, _ = _construct_win_table( + data=data, + data_sd=None, + dataset_col="dataset", + local_rope_value=0.1, + tie_solver="davidson", + maximize=True, + ) + + expected_table = np.array( + [ + [0, 1, 2, 0, 0], # alg1 > alg2 on both datasets + [0, 2, 1, 1, 0], # split decisions across datasets + [1, 2, 1, 1, 0], # split decisions across datasets + ] + ) + np.testing.assert_array_equal(result_table, expected_table) + class TestUserWarnings: """Test whether the correct warnings are raised.""" @@ -111,3 +166,70 @@ def test_unnamed_columns(self): tie_solver="davidson", maximize=True, ) + + +class TestTieSolvers: + """Test tie solver semantics for spread/add/forget strategies.""" + + def test_spread_solver_assigns_half_point_per_tie(self): + """Each tie contributes 0.5 win to both algorithms in spread mode.""" + data = pd.DataFrame( + { + "alg1": [0.7], + "alg2": [0.7], + } + ) + + spread_table, _ = _construct_win_table( + data=data, + data_sd=None, + dataset_col=None, + local_rope_value=0.01, + tie_solver="spread", + maximize=True, + ) + + expected = np.array([[0, 1, 0.5, 0.5, 1]]) + np.testing.assert_array_almost_equal(spread_table, expected) + + def test_add_solver_assigns_full_point_per_tie(self): + """Each tie contributes 1 win to both algorithms in add mode.""" + data = pd.DataFrame( + { + "alg1": [0.7], + "alg2": [0.7], + } + ) + + add_table, _ = _construct_win_table( + data=data, + data_sd=None, + dataset_col=None, + local_rope_value=0.01, + tie_solver="add", + maximize=True, + ) + + expected = np.array([[0, 1, 1, 1, 1]]) + np.testing.assert_array_almost_equal(add_table, expected) + + def test_forget_solver_ignores_ties(self): + """Forget mode should leave tie counts out of win totals.""" + data = pd.DataFrame( + { + "alg1": [0.7], + "alg2": [0.7], + } + ) + + forget_table, _ = _construct_win_table( + data=data, + data_sd=None, + dataset_col=None, + local_rope_value=0.01, + tie_solver="forget", + maximize=True, + ) + + expected = np.array([[0, 1, 0, 0, 1]]) + np.testing.assert_array_almost_equal(forget_table, expected) diff --git a/uv.lock b/uv.lock index d802741..30e3b1a 100644 --- a/uv.lock +++ b/uv.lock @@ -157,6 +157,7 @@ dependencies = [ [package.dev-dependencies] dev = [ + { name = "graphviz" }, { name = "jupyter" }, { name = "mypy" }, { name = "pre-commit" }, @@ -182,6 +183,7 @@ requires-dist = [ [package.metadata.requires-dev] dev = [ + { name = "graphviz" }, { name = "jupyter" }, { name = "mypy" }, { name = "pre-commit" }, @@ -768,6 +770,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl", hash = "sha256:3a179af3761e4df6eb2e026ff9e1a3033d3587bf980a0b1b2e1e5d08d7358014", size = 9121, upload-time = "2021-03-11T07:16:28.351Z" }, ] +[[package]] +name = "graphviz" +version = "0.21" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f8/b3/3ac91e9be6b761a4b30d66ff165e54439dcd48b83f4e20d644867215f6ca/graphviz-0.21.tar.gz", hash = "sha256:20743e7183be82aaaa8ad6c93f8893c923bd6658a04c32ee115edb3c8a835f78", size = 200434, upload-time = "2025-06-15T09:35:05.824Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/91/4c/e0ce1ef95d4000ebc1c11801f9b944fa5910ecc15b5e351865763d8657f8/graphviz-0.21-py3-none-any.whl", hash = "sha256:54f33de9f4f911d7e84e4191749cac8cc5653f815b06738c54db9a15ab8b1e42", size = 47300, upload-time = "2025-06-15T09:35:04.433Z" }, +] + [[package]] name = "h11" version = "0.16.0" From 00236f0b4dd187c1e0f5f6436335cf09b92de1f9 Mon Sep 17 00:00:00 2001 From: Mateusz Praski Date: Thu, 7 May 2026 23:55:51 +0200 Subject: [PATCH 2/2] Fixed spread issues --- bbttest/bbt/alg.py | 6 +++--- bbttest/bbt/model.py | 8 +++----- bbttest/bbt/py_bbt.py | 2 +- tests/bbt/test_alg.py | 21 --------------------- tests/bbt/test_py_bbt.py | 2 +- tests/regression/test_benchmarking_mol.py | 7 ++++--- 6 files changed, 12 insertions(+), 34 deletions(-) diff --git a/bbttest/bbt/alg.py b/bbttest/bbt/alg.py index 98c973a..a79e970 100644 --- a/bbttest/bbt/alg.py +++ b/bbttest/bbt/alg.py @@ -46,7 +46,7 @@ def _construct_no_paired( no_pairs = no_algs * (no_algs - 1) // 2 out_array = -1 * np.ones( (no_pairs, 5), # alg_1, alg_2, 1_wins, 2_wins, ties - dtype=np.float32, + dtype=np.int32, ) for i, j, k in tqdm( _gen_pairs(no_algs), @@ -83,7 +83,7 @@ def _construct_lrope( no_pairs = no_algs * (no_algs - 1) // 2 out_array = np.zeros( (no_pairs, 5), # alg_1, alg_2, 1_wins, 2_wins, ties - dtype=np.float32, + dtype=np.int32, ) for dataset_name in data[dataset_col].unique(): data_subset = data[data[dataset_col] == dataset_name] @@ -114,7 +114,7 @@ def _solve_ties(table: np.ndarray, tie_solver: str) -> np.ndarray: if tie_solver == "davidson": return table if tie_solver == "spread": - tie_val = table[:, TIE_COL] / 2 + tie_val = np.ceil(table[:, TIE_COL] / 2).astype(int) elif tie_solver == "add": tie_val = table[:, TIE_COL] else: diff --git a/bbttest/bbt/model.py b/bbttest/bbt/model.py index 0fcce2c..685cc33 100644 --- a/bbttest/bbt/model.py +++ b/bbttest/bbt/model.py @@ -9,8 +9,8 @@ def _build_bbt_model( player1: list[int], player2: list[int], - win1: list[float], - win2: list[float], + win1: list[int], + win2: list[int], ties: list[int] | None, hyp: str, scale: float, @@ -46,9 +46,7 @@ def _build_bbt_model( K = len(np.unique(np.concatenate((p1_idx, p2_idx)))) # Transformed data - n = (w1 + w2).astype(np.int32) - w1 = w1.astype(np.int32) - w2 = w2.astype(np.int32) + n = w1 + w2 if use_davidson: ties_arr = np.array(ties, dtype=int) nn = n + ties_arr diff --git a/bbttest/bbt/py_bbt.py b/bbttest/bbt/py_bbt.py index ae3b32b..fadd0f8 100644 --- a/bbttest/bbt/py_bbt.py +++ b/bbttest/bbt/py_bbt.py @@ -96,7 +96,7 @@ class PyBBT: def __init__( self, local_rope_value: float | None = None, - tie_solver: TieSolverType = "spread", + tie_solver: TieSolverType = "add", hyper_prior: HyperPriorType = "log_normal", maximize: bool = True, scale: float = 1.0, diff --git a/tests/bbt/test_alg.py b/tests/bbt/test_alg.py index b742d19..2b924fe 100644 --- a/tests/bbt/test_alg.py +++ b/tests/bbt/test_alg.py @@ -171,27 +171,6 @@ def test_unnamed_columns(self): class TestTieSolvers: """Test tie solver semantics for spread/add/forget strategies.""" - def test_spread_solver_assigns_half_point_per_tie(self): - """Each tie contributes 0.5 win to both algorithms in spread mode.""" - data = pd.DataFrame( - { - "alg1": [0.7], - "alg2": [0.7], - } - ) - - spread_table, _ = _construct_win_table( - data=data, - data_sd=None, - dataset_col=None, - local_rope_value=0.01, - tie_solver="spread", - maximize=True, - ) - - expected = np.array([[0, 1, 0.5, 0.5, 1]]) - np.testing.assert_array_almost_equal(spread_table, expected) - def test_add_solver_assigns_full_point_per_tie(self): """Each tie contributes 1 win to both algorithms in add mode.""" data = pd.DataFrame( diff --git a/tests/bbt/test_py_bbt.py b/tests/bbt/test_py_bbt.py index 06d78a9..f72e22c 100644 --- a/tests/bbt/test_py_bbt.py +++ b/tests/bbt/test_py_bbt.py @@ -83,7 +83,7 @@ def test_init_defaults(self): """Test that default initialization values are set correctly.""" model = PyBBT() assert model._local_rope_value is None - assert model._tie_solver == "spread" + assert model._tie_solver == "add" assert model._hyper_prior == "log_normal" assert model._scale == 1.0 assert model._maximize diff --git a/tests/regression/test_benchmarking_mol.py b/tests/regression/test_benchmarking_mol.py index dcd1d46..7e7a8d2 100644 --- a/tests/regression/test_benchmarking_mol.py +++ b/tests/regression/test_benchmarking_mol.py @@ -67,7 +67,7 @@ def fitted_model(benchmarking_data): PyBBT Fitted PyBBT model instance. """ - model = PyBBT(local_rope_value=0.01, tie_solver="spread") + model = PyBBT(local_rope_value=0.01, tie_solver="add") model.fit( benchmarking_data, dataset_col="dataset", @@ -140,12 +140,13 @@ def _extract_interpretations(results): ROPE_2_VALUE = (0.4, 0.6) ROPE_2_BETTER_MODELS = ["CLAMP", "rmat_4M"] ROPE_2_EQUIVALENT_MODELS = [ + "AtomPair_count", "CDDD", "ChemBERTa-10M-MTR", "mat_masking_2M", "molbert", ] -ROPE_2_UNKNOWN_MODELS = ["AtomPair_count"] +ROPE_2_UNKNOWN_MODELS = [] ROPE_2_WORSE_MODELS = [ "ChemFM-3B", "ChemGPT-4.7M", @@ -172,6 +173,7 @@ def _extract_interpretations(results): "AtomPair_count", "CDDD", "ChemBERTa-10M-MTR", + "MoLFormer-XL-both-10pct", "mat_masking_2M", "molbert", "rmat_4M", @@ -184,7 +186,6 @@ def _extract_interpretations(results): "GNN-GraphCL-sum", "GraphFP-CP", "GraphMVP_CP-max", - "MoLFormer-XL-both-10pct", "SELFormer-Lite", "SimSon", "TT",