{
    "file_diffs": [
        {
            "old_file_content": "from functools import partial\n\nfrom concurrent import futures\nfrom concurrent.futures import Future\n\nfrom types import SimpleNamespace as namespace\nfrom typing import Optional, Callable, Tuple, Any\n\nimport numpy as np\nimport scipy.sparse as sp\nimport networkx as nx\n\nfrom AnyQt.QtCore import (\n    Qt, QObject, QTimer, pyqtSignal as Signal, pyqtSlot as Slot\n)\nfrom AnyQt.QtWidgets import QSlider, QCheckBox, QWidget, QLabel\n\nfrom Orange.clustering.louvain import matrix_to_knn_graph, Louvain\nfrom Orange.data import Table, DiscreteVariable\nfrom Orange.data.util import get_unique_names, array_equal\nfrom Orange import preprocess\nfrom Orange.projection import PCA\nfrom Orange.widgets import widget, gui, report\nfrom Orange.widgets.settings import Setting\nfrom Orange.widgets.utils.annotated_data import add_columns, \\\n    ANNOTATED_DATA_SIGNAL_NAME\nfrom Orange.widgets.utils.concurrent import FutureWatcher\nfrom Orange.widgets.utils.signals import Input, Output\nfrom Orange.widgets.utils.widgetpreview import WidgetPreview\nfrom Orange.widgets.widget import Msg\n\ntry:\n    from orangecontrib.network.network import Network\nexcept ImportError:\n    Network = None\n\n\n_MAX_PCA_COMPONENTS = 50\n_DEFAULT_PCA_COMPONENTS = 25\n_MAX_K_NEIGBOURS = 200\n_DEFAULT_K_NEIGHBORS = 30\n\n\nMETRICS = [(\"Euclidean\", \"l2\"), (\"Manhattan\", \"l1\")]\n\n\nclass OWLouvainClustering(widget.OWWidget):\n    name = \"Louvain Clustering\"\n    description = \"Detects communities in a network of nearest neighbors.\"\n    icon = \"icons/LouvainClustering.svg\"\n    priority = 2110\n\n    want_main_area = False\n    resizing_enabled = False\n\n    class Inputs:\n        data = Input(\"Data\", Table, default=True)\n\n    class Outputs:\n        annotated_data = Output(ANNOTATED_DATA_SIGNAL_NAME, Table, default=True)\n        if Network is not None:\n            graph = Output(\"Network\", Network)\n\n    apply_pca = Setting(True)\n    pca_components = Setting(_DEFAULT_PCA_COMPONENTS)\n    normalize = Setting(True)\n    metric_idx = Setting(0)\n    k_neighbors = Setting(_DEFAULT_K_NEIGHBORS)\n    resolution = Setting(1.)\n    auto_commit = Setting(False)\n\n    class Information(widget.OWWidget.Information):\n        modified = Msg(\"Press commit to recompute clusters and send new data\")\n\n    class Error(widget.OWWidget.Error):\n        empty_dataset = Msg(\"No features in data\")\n\n    def __init__(self):\n        super().__init__()\n\n        self.data = None  # type: Optional[Table]\n        self.preprocessed_data = None  # type: Optional[Table]\n        self.pca_projection = None  # type: Optional[Table]\n        self.graph = None  # type: Optional[nx.Graph]\n        self.partition = None  # type: Optional[np.array]\n        # Use a executor with a single worker, to limit CPU overcommitment for\n        # cancelled tasks. The method does not have a fine cancellation\n        # granularity so we assure that there are not N - 1 jobs executing\n        # for no reason only to be thrown away. It would be better to use the\n        # global pool but implement a limit on jobs from this source.\n        self.__executor = futures.ThreadPoolExecutor(max_workers=1)\n        self.__task = None   # type: Optional[TaskState]\n        self.__invalidated = False\n        # coalescing commit timer\n        self.__commit_timer = QTimer(self, singleShot=True)\n        self.__commit_timer.timeout.connect(self.commit)\n\n        # Set up UI\n        info_box = gui.vBox(self.controlArea, \"Info\")\n        self.info_label = gui.widgetLabel(info_box, \"No data on input.\")  # type: QLabel\n\n        preprocessing_box = gui.vBox(self.controlArea, \"Preprocessing\")\n        self.normalize_cbx = gui.checkBox(\n            preprocessing_box, self, \"normalize\", label=\"Normalize data\",\n            callback=self._invalidate_preprocessed_data,\n        )  # type: QCheckBox\n        self.apply_pca_cbx = gui.checkBox(\n            preprocessing_box, self, \"apply_pca\", label=\"Apply PCA preprocessing\",\n            callback=self._apply_pca_changed,\n        )  # type: QCheckBox\n        self.pca_components_slider = gui.hSlider(\n            preprocessing_box, self, \"pca_components\", label=\"PCA Components: \", minValue=2,\n            maxValue=_MAX_PCA_COMPONENTS,\n            callback=self._invalidate_pca_projection, tracking=False\n        )  # type: QSlider\n\n        graph_box = gui.vBox(self.controlArea, \"Graph parameters\")\n        self.metric_combo = gui.comboBox(\n            graph_box, self, \"metric_idx\", label=\"Distance metric\",\n            items=[m[0] for m in METRICS], callback=self._invalidate_graph,\n            orientation=Qt.Horizontal,\n        )  # type: gui.OrangeComboBox\n        self.k_neighbors_spin = gui.spin(\n            graph_box, self, \"k_neighbors\", minv=1, maxv=_MAX_K_NEIGBOURS,\n            label=\"k neighbors\", controlWidth=80, alignment=Qt.AlignRight,\n            callback=self._invalidate_graph,\n        )  # type: gui.SpinBoxWFocusOut\n        self.resolution_spin = gui.hSlider(\n            graph_box, self, \"resolution\", minValue=0, maxValue=5., step=1e-1,\n            label=\"Resolution\", intOnly=False, labelFormat=\"%.1f\",\n            callback=self._invalidate_partition, tracking=False,\n        )  # type: QSlider\n        self.resolution_spin.parent().setToolTip(\n            \"The resolution parameter affects the number of clusters to find. \"\n            \"Smaller values tend to produce more clusters and larger values \"\n            \"retrieve less clusters.\"\n        )\n        self.apply_button = gui.auto_apply(\n            self.controlArea, self, \"auto_commit\", box=None,\n            commit=lambda: self.commit(), callback=lambda: self._on_auto_commit_changed()\n        )  # type: QWidget\n\n    def _preprocess_data(self):\n        if self.preprocessed_data is None:\n            if self.normalize:\n                normalizer = preprocess.Normalize(center=False)\n                self.preprocessed_data = normalizer(self.data)\n            else:\n                self.preprocessed_data = self.data\n\n    def _apply_pca_changed(self):\n        self.controls.pca_components.setEnabled(self.apply_pca)\n        self._invalidate_graph()\n\n    def _invalidate_preprocessed_data(self):\n        self.preprocessed_data = None\n        self._invalidate_pca_projection()\n        # If we don't apply PCA, this still invalidates the graph, otherwise\n        # this change won't be propagated further\n        if not self.apply_pca:\n            self._invalidate_graph()\n\n    def _invalidate_pca_projection(self):\n        self.pca_projection = None\n        if not self.apply_pca:\n            return\n\n        self._invalidate_graph()\n        self._set_modified(True)\n\n    def _invalidate_graph(self):\n        self.graph = None\n        self._invalidate_partition()\n        self._set_modified(True)\n\n    def _invalidate_partition(self):\n        self.partition = None\n        self._invalidate_output()\n        self.Information.modified()\n        self._set_modified(True)\n\n    def _invalidate_output(self):\n        self.__invalidated = True\n        if self.__task is not None:\n            self.__cancel_task(wait=False)\n\n        if self.auto_commit:\n            self.__commit_timer.start()\n        else:\n            self.__set_state_ready()\n\n    def _set_modified(self, state):\n        \"\"\"\n        Mark the widget (GUI) as containing modified state.\n        \"\"\"\n        if self.data is None:\n            # does not apply when we have no data\n            state = False\n        elif self.auto_commit:\n            # does not apply when auto commit is on\n            state = False\n        self.Information.modified(shown=state)\n\n    def _on_auto_commit_changed(self):\n        if self.auto_commit and self.__invalidated:\n            self.commit()\n\n    def cancel(self):\n        \"\"\"Cancel any running jobs.\"\"\"\n        self.__cancel_task(wait=False)\n        self.__set_state_ready()\n\n    def commit(self):\n        self.__commit_timer.stop()\n        self.__invalidated = False\n        self._set_modified(False)\n\n        # Cancel current running task\n        self.__cancel_task(wait=False)\n\n        if self.data is None:\n            self.__set_state_ready()\n            return\n\n        self.Error.clear()\n\n        if self.partition is not None:\n            self.__set_state_ready()\n            self._send_data()\n            return\n\n        self._preprocess_data()\n\n        state = TaskState(self)\n\n        # Prepare/assemble the task(s) to run; reuse partial results\n        if self.apply_pca:\n            if self.pca_projection is not None:\n                data = self.pca_projection\n                pca_components = None\n            else:\n                data = self.preprocessed_data\n                pca_components = self.pca_components\n        else:\n            data = self.preprocessed_data\n            pca_components = None\n\n        if self.graph is not None:\n            # run on graph only; no need to do PCA and k-nn search ...\n            graph = self.graph\n            k_neighbors = metric = None\n        else:\n            k_neighbors, metric = self.k_neighbors, METRICS[self.metric_idx][1]\n            graph = None\n\n        if graph is None:\n            task = partial(\n                run_on_data, data, pca_components=pca_components,\n                normalize=self.normalize, k_neighbors=k_neighbors,\n                metric=metric, resolution=self.resolution, state=state,\n            )\n        else:\n            task = partial(\n                run_on_graph, graph, resolution=self.resolution, state=state\n            )\n\n        self.info_label.setText(\"Running...\")\n        self.__set_state_busy()\n        self.__start_task(task, state)\n\n    @Slot(object)\n    def __set_partial_results(self, result):\n        # type: (Tuple[str, Any]) -> None\n        which, res = result\n        if which == \"pca_projection\":\n            assert isinstance(res, Table) and len(res) == len(self.data)\n            self.pca_projection = res\n        elif which == \"graph\":\n            assert isinstance(res, nx.Graph)\n            self.graph = res\n        elif which == \"partition\":\n            assert isinstance(res, np.ndarray)\n            self.partition = res\n        else:\n            assert False, which\n\n    @Slot(object)\n    def __on_done(self, future):\n        # type: (Future[\"Results\"]) -> None\n        assert future.done()\n        assert self.__task is not None\n        assert self.__task.future is future\n        assert self.__task.watcher.future() is future\n        self.__task, task = None, self.__task\n        task.deleteLater()\n\n        self.__set_state_ready()\n\n        result = future.result()\n        self.__set_results(result)\n\n    @Slot(str)\n    def setStatusMessage(self, text):\n        super().setStatusMessage(text)\n\n    @Slot(float)\n    def progressBarSet(self, value, *a, **kw):\n        super().progressBarSet(value, *a, **kw)\n\n    def __set_state_ready(self):\n        self.progressBarFinished()\n        self.setInvalidated(False)\n        self.setStatusMessage(\"\")\n\n    def __set_state_busy(self):\n        self.progressBarInit()\n        self.setInvalidated(True)\n\n    def __start_task(self, task, state):\n        # type: (Callable[[], Any], TaskState) -> None\n        assert self.__task is None\n        state.status_changed.connect(self.setStatusMessage)\n        state.progress_changed.connect(self.progressBarSet)\n        state.partial_result_ready.connect(self.__set_partial_results)\n        state.watcher.done.connect(self.__on_done)\n        state.start(self.__executor, task)\n        state.setParent(self)\n        self.__task = state\n\n    def __cancel_task(self, wait=True):\n        # Cancel and dispose of the current task\n        if self.__task is not None:\n            state, self.__task = self.__task, None\n            state.cancel()\n            state.partial_result_ready.disconnect(self.__set_partial_results)\n            state.status_changed.disconnect(self.setStatusMessage)\n            state.progress_changed.disconnect(self.progressBarSet)\n            state.watcher.done.disconnect(self.__on_done)\n            if wait:\n                futures.wait([state.future])\n                state.deleteLater()\n            else:\n                w = FutureWatcher(state.future, parent=state)\n                w.done.connect(state.deleteLater)\n\n    def __set_results(self, results):\n        # type: (\"Results\") -> None\n        # NOTE: All of these have already been set by __set_partial_results,\n        # we double check that they are aliases\n        if results.pca_projection is not None:\n            assert self.pca_components == results.pca_components\n            assert self.pca_projection is results.pca_projection\n            self.pca_projection = results.pca_projection\n        if results.graph is not None:\n            assert results.metric == METRICS[self.metric_idx][1]\n            assert results.k_neighbors == self.k_neighbors\n            assert self.graph is results.graph\n            self.graph = results.graph\n        if results.partition is not None:\n            assert results.resolution == self.resolution\n            assert self.partition is results.partition\n            self.partition = results.partition\n\n        # Display the number of found clusters in the UI\n        num_clusters = len(np.unique(self.partition))\n        self.info_label.setText(\"%d clusters found.\" % num_clusters)\n\n        self._send_data()\n\n    def _send_data(self):\n        if self.partition is None or self.data is None:\n            return\n        domain = self.data.domain\n        # Compute the frequency of each cluster index\n        counts = np.bincount(self.partition)\n        indices = np.argsort(counts)[::-1]\n        index_map = {n: o for n, o in zip(indices, range(len(indices)))}\n        new_partition = list(map(index_map.get, self.partition))\n\n        cluster_var = DiscreteVariable(\n            get_unique_names(domain, \"Cluster\"),\n            values=[\"C%d\" % (i + 1) for i, _ in enumerate(np.unique(new_partition))]\n        )\n\n        new_domain = add_columns(domain, metas=[cluster_var])\n        new_table = self.data.transform(new_domain)\n        new_table.get_column_view(cluster_var)[0][:] = new_partition\n        self.Outputs.annotated_data.send(new_table)\n\n        if Network is not None:\n            n_edges = self.graph.number_of_edges()\n            edges = sp.coo_matrix(\n                (np.ones(n_edges), np.array(self.graph.edges()).T),\n                shape=(n_edges, n_edges))\n            graph = Network(new_table, edges)\n            self.Outputs.graph.send(graph)\n\n    @Inputs.data\n    def set_data(self, data):\n        self.Error.clear()\n\n        prev_data, self.data = self.data, data\n        # Make sure to properly enable/disable slider based on `apply_pca` setting\n        self.controls.pca_components.setEnabled(self.apply_pca)\n\n        if prev_data and self.data and array_equal(prev_data.X, self.data.X):\n            if self.auto_commit and not self.isInvalidated():\n                self._send_data()\n            return\n\n        self.cancel()\n        # Clear the outputs\n        self.Outputs.annotated_data.send(None)\n        if Network is not None:\n            self.Outputs.graph.send(None)\n\n        # Clear internal state\n        self.clear()\n        self._invalidate_pca_projection()\n\n        # Make sure the dataset is ok\n        if self.data is not None and len(self.data.domain.attributes) < 1:\n            self.Error.empty_dataset()\n            self.data = None\n\n        if self.data is None:\n            return\n\n        # Can't have more PCA components than the number of attributes\n        n_attrs = len(data.domain.attributes)\n        self.pca_components_slider.setMaximum(min(_MAX_PCA_COMPONENTS, n_attrs))\n        # Can't have more k neighbors than there are data points\n        self.k_neighbors_spin.setMaximum(min(_MAX_K_NEIGBOURS, len(data) - 1))\n\n        self.info_label.setText(\"Clustering not yet run.\")\n\n        self.commit()\n\n    def clear(self):\n        self.__cancel_task(wait=False)\n        self.preprocessed_data = None\n        self.pca_projection = None\n        self.graph = None\n        self.partition = None\n        self.Error.clear()\n        self.Information.modified.clear()\n        self.info_label.setText(\"No data on input.\")\n\n    def onDeleteWidget(self):\n        self.__cancel_task(wait=True)\n        self.__executor.shutdown(True)\n        self.clear()\n        self.data = None\n        super().onDeleteWidget()\n\n    def send_report(self):\n        pca = report.bool_str(self.apply_pca)\n        if self.apply_pca:\n            pca += report.plural(\", {number} component{s}\", self.pca_components)\n\n        self.report_items((\n            (\"Normalize data\", report.bool_str(self.normalize)),\n            (\"PCA preprocessing\", pca),\n            (\"Metric\", METRICS[self.metric_idx][0]),\n            (\"k neighbors\", self.k_neighbors),\n            (\"Resolution\", self.resolution),\n        ))\n\n\nclass TaskState(QObject):\n\n    status_changed = Signal(str)\n    _p_status_changed = Signal(str)\n\n    progress_changed = Signal(float)\n    _p_progress_changed = Signal(float)\n\n    partial_result_ready = Signal(object)\n    _p_partial_result_ready = Signal(object)\n\n    def __init__(self, *args):\n        super().__init__(*args)\n        self.__future = None\n        self.watcher = FutureWatcher()\n        self.__interuption_requested = False\n        self.__progress = 0\n        # Helpers to route the signal emits via a this object's queue.\n        # This ensures 'atomic' disconnect from signals for targets/slots\n        # in the same thread. Requires that the event loop is running in this\n        # object's thread.\n        self._p_status_changed.connect(\n            self.status_changed, Qt.QueuedConnection)\n        self._p_progress_changed.connect(\n            self.progress_changed, Qt.QueuedConnection)\n        self._p_partial_result_ready.connect(\n            self.partial_result_ready, Qt.QueuedConnection)\n\n    @property\n    def future(self):\n        # type: () -> Future\n        return self.__future\n\n    def set_status(self, text):\n        self._p_status_changed.emit(text)\n\n    def set_progress_value(self, value):\n        if round(value, 1) > round(self.__progress, 1):\n            # Only emit progress when it has changed sufficiently\n            self._p_progress_changed.emit(value)\n            self.__progress = value\n\n    def set_partial_results(self, value):\n        self._p_partial_result_ready.emit(value)\n\n    def is_interuption_requested(self):\n        return self.__interuption_requested\n\n    def start(self, executor, func=None):\n        # type: (futures.Executor, Callable[[], Any]) -> Future\n        assert self.future is None\n        assert not self.__interuption_requested\n        self.__future = executor.submit(func)\n        self.watcher.setFuture(self.future)\n        return self.future\n\n    def cancel(self):\n        assert not self.__interuption_requested\n        self.__interuption_requested = True\n        if self.future is not None:\n            rval = self.future.cancel()\n        else:\n            # not even scheduled yet\n            rval = True\n        return rval\n\n\nclass InteruptRequested(BaseException):\n    pass\n\n\nclass Results(namespace):\n    pca_projection = None    # type: Optional[Table]\n    pca_components = None    # type: Optional[int]\n    normalize = None         # type: Optional[bool]\n    k_neighbors = None       # type: Optional[int]\n    metric = None            # type: Optional[str]\n    graph = None             # type: Optional[nx.Graph]\n    resolution = None        # type: Optional[float]\n    partition = None         # type: Optional[np.ndarray]\n\n\ndef run_on_data(data, normalize, pca_components, k_neighbors, metric, resolution, state):\n    # type: (Table, Optional[int], int, str, float, bool, TaskState) -> Results\n    \"\"\"\n    Run the louvain clustering on `data`.\n\n    state is used to report progress and partial results. Returns early if\n    `task.is_interuption_requested()` returns true.\n\n    Parameters\n    ----------\n    data : Table\n        Data table\n    normalize : bool\n        If `True`, the data is first normalized before computing PCA.\n    pca_components : Optional[int]\n        If not `None` then the data is first projected onto first\n        `pca_components` principal components.\n    k_neighbors : int\n        Passed to `table_to_knn_graph`\n    metric : str\n        Passed to `table_to_knn_graph`\n    resolution : float\n        Passed to `Louvain`\n    state : TaskState\n\n    Returns\n    -------\n    res : Results\n    \"\"\"\n    state = state  # type: TaskState\n    res = Results(\n        normalize=normalize, pca_components=pca_components,\n        k_neighbors=k_neighbors, metric=metric, resolution=resolution,\n    )\n    step = 0\n    if state.is_interuption_requested():\n        return res\n\n    if pca_components is not None:\n        steps = 3\n        state.set_status(\"Computing PCA...\")\n        pca = PCA(n_components=pca_components, random_state=0)\n\n        data = res.pca_projection = pca(data)(data)\n        assert isinstance(data, Table)\n        state.set_partial_results((\"pca_projection\", res.pca_projection))\n        step += 1\n    else:\n        steps = 2\n\n    if state.is_interuption_requested():\n        return res\n\n    state.set_progress_value(100. * step / steps)\n    state.set_status(\"Building graph...\")\n\n    # Apply Louvain preprocessing before converting the table into a graph\n    louvain = Louvain(resolution=resolution, random_state=0)\n    data = louvain.preprocess(data)\n\n    if state.is_interuption_requested():\n        return res\n\n    def pcallback(val):\n        state.set_progress_value((100. * step + 100 * val) / steps)\n        if state.is_interuption_requested():\n            raise InteruptRequested()\n\n    try:\n        res.graph = graph = matrix_to_knn_graph(\n            data.X, k_neighbors=k_neighbors, metric=metric,\n            progress_callback=pcallback)\n    except InteruptRequested:\n        return res\n\n    state.set_partial_results((\"graph\", res.graph))\n\n    step += 1\n    state.set_progress_value(100 * step / steps)\n    state.set_status(\"Detecting communities...\")\n    if state.is_interuption_requested():\n        return res\n\n    res.partition = louvain(graph)\n    state.set_partial_results((\"partition\", res.partition))\n    return res\n\n\ndef run_on_graph(graph, resolution, state):\n    # type: (nx.Graph, float, TaskState) -> Results\n    \"\"\"\n    Run the louvain clustering on `graph`.\n    \"\"\"\n    state = state  # type: TaskState\n    res = Results(resolution=resolution)\n    louvain = Louvain(resolution=resolution, random_state=0)\n    state.set_status(\"Detecting communities...\")\n    if state.is_interuption_requested():\n        return res\n    partition = louvain(graph)\n    res.partition = partition\n    state.set_partial_results((\"partition\", res.partition))\n    return res\n\n\nif __name__ == \"__main__\":  # pragma: no cover\n    WidgetPreview(OWLouvainClustering).run(Table(\"iris\"))\n",
            "new_file_content": "from functools import partial\n\nfrom concurrent import futures\nfrom concurrent.futures import Future\n\nfrom types import SimpleNamespace as namespace\nfrom typing import Optional, Callable, Tuple, Any\n\nimport numpy as np\nimport scipy.sparse as sp\nimport networkx as nx\n\nfrom AnyQt.QtCore import (\n    Qt, QObject, QTimer, pyqtSignal as Signal, pyqtSlot as Slot\n)\nfrom AnyQt.QtWidgets import QSlider, QCheckBox, QWidget, QLabel\n\nfrom Orange.clustering.louvain import matrix_to_knn_graph, Louvain\nfrom Orange.data import Table, DiscreteVariable\nfrom Orange.data.util import get_unique_names, array_equal\nfrom Orange import preprocess\nfrom Orange.projection import PCA\nfrom Orange.widgets import widget, gui, report\nfrom Orange.widgets.settings import Setting\nfrom Orange.widgets.utils.annotated_data import add_columns, \\\n    ANNOTATED_DATA_SIGNAL_NAME\nfrom Orange.widgets.utils.concurrent import FutureWatcher\nfrom Orange.widgets.utils.signals import Input, Output\nfrom Orange.widgets.utils.widgetpreview import WidgetPreview\nfrom Orange.widgets.widget import Msg\n\ntry:\n    from orangecontrib.network.network import Network\nexcept ImportError:\n    Network = None\n\n\n_MAX_PCA_COMPONENTS = 50\n_DEFAULT_PCA_COMPONENTS = 25\n_MAX_K_NEIGBOURS = 200\n_DEFAULT_K_NEIGHBORS = 30\n\n\nMETRICS = [(\"Euclidean\", \"l2\"), (\"Manhattan\", \"l1\")]\n\n\nclass OWLouvainClustering(widget.OWWidget):\n    name = \"Louvain Clustering\"\n    description = \"Detects communities in a network of nearest neighbors.\"\n    icon = \"icons/LouvainClustering.svg\"\n    priority = 2110\n\n    settings_version = 2\n\n    want_main_area = False\n    resizing_enabled = False\n\n    class Inputs:\n        data = Input(\"Data\", Table, default=True)\n\n    class Outputs:\n        annotated_data = Output(ANNOTATED_DATA_SIGNAL_NAME, Table, default=True)\n        if Network is not None:\n            graph = Output(\"Network\", Network)\n\n    apply_pca = Setting(True)\n    pca_components = Setting(_DEFAULT_PCA_COMPONENTS)\n    normalize = Setting(True)\n    metric_idx = Setting(0)\n    k_neighbors = Setting(_DEFAULT_K_NEIGHBORS)\n    resolution = Setting(1.)\n    auto_commit = Setting(False)\n\n    class Information(widget.OWWidget.Information):\n        modified = Msg(\"Press commit to recompute clusters and send new data\")\n\n    class Error(widget.OWWidget.Error):\n        empty_dataset = Msg(\"No features in data\")\n\n    def __init__(self):\n        super().__init__()\n\n        self.data = None  # type: Optional[Table]\n        self.preprocessed_data = None  # type: Optional[Table]\n        self.pca_projection = None  # type: Optional[Table]\n        self.graph = None  # type: Optional[nx.Graph]\n        self.partition = None  # type: Optional[np.array]\n        # Use a executor with a single worker, to limit CPU overcommitment for\n        # cancelled tasks. The method does not have a fine cancellation\n        # granularity so we assure that there are not N - 1 jobs executing\n        # for no reason only to be thrown away. It would be better to use the\n        # global pool but implement a limit on jobs from this source.\n        self.__executor = futures.ThreadPoolExecutor(max_workers=1)\n        self.__task = None   # type: Optional[TaskState]\n        self.__invalidated = False\n        # coalescing commit timer\n        self.__commit_timer = QTimer(self, singleShot=True)\n        self.__commit_timer.timeout.connect(self.commit)\n\n        # Set up UI\n        info_box = gui.vBox(self.controlArea, \"Info\")\n        self.info_label = gui.widgetLabel(info_box, \"No data on input.\")  # type: QLabel\n\n        preprocessing_box = gui.vBox(self.controlArea, \"Preprocessing\")\n        self.normalize_cbx = gui.checkBox(\n            preprocessing_box, self, \"normalize\", label=\"Normalize data\",\n            callback=self._invalidate_preprocessed_data,\n        )  # type: QCheckBox\n        self.apply_pca_cbx = gui.checkBox(\n            preprocessing_box, self, \"apply_pca\", label=\"Apply PCA preprocessing\",\n            callback=self._apply_pca_changed,\n        )  # type: QCheckBox\n        self.pca_components_slider = gui.hSlider(\n            preprocessing_box, self, \"pca_components\", label=\"PCA Components: \", minValue=2,\n            maxValue=_MAX_PCA_COMPONENTS,\n            callback=self._invalidate_pca_projection, tracking=False\n        )  # type: QSlider\n\n        graph_box = gui.vBox(self.controlArea, \"Graph parameters\")\n        self.metric_combo = gui.comboBox(\n            graph_box, self, \"metric_idx\", label=\"Distance metric\",\n            items=[m[0] for m in METRICS], callback=self._invalidate_graph,\n            orientation=Qt.Horizontal,\n        )  # type: gui.OrangeComboBox\n        self.k_neighbors_spin = gui.spin(\n            graph_box, self, \"k_neighbors\", minv=1, maxv=_MAX_K_NEIGBOURS,\n            label=\"k neighbors\", controlWidth=80, alignment=Qt.AlignRight,\n            callback=self._invalidate_graph,\n        )  # type: gui.SpinBoxWFocusOut\n        self.resolution_spin = gui.hSlider(\n            graph_box, self, \"resolution\", minValue=0, maxValue=5., step=1e-1,\n            label=\"Resolution\", intOnly=False, labelFormat=\"%.1f\",\n            callback=self._invalidate_partition, tracking=False,\n        )  # type: QSlider\n        self.resolution_spin.parent().setToolTip(\n            \"The resolution parameter affects the number of clusters to find. \"\n            \"Smaller values tend to produce more clusters and larger values \"\n            \"retrieve less clusters.\"\n        )\n        self.apply_button = gui.auto_apply(\n            self.controlArea, self, \"auto_commit\", box=None,\n            commit=lambda: self.commit(), callback=lambda: self._on_auto_commit_changed()\n        )  # type: QWidget\n\n    def _preprocess_data(self):\n        if self.preprocessed_data is None:\n            if self.normalize:\n                normalizer = preprocess.Normalize(center=False)\n                self.preprocessed_data = normalizer(self.data)\n            else:\n                self.preprocessed_data = self.data\n\n    def _apply_pca_changed(self):\n        self.controls.pca_components.setEnabled(self.apply_pca)\n        self._invalidate_graph()\n\n    def _invalidate_preprocessed_data(self):\n        self.preprocessed_data = None\n        self._invalidate_pca_projection()\n        # If we don't apply PCA, this still invalidates the graph, otherwise\n        # this change won't be propagated further\n        if not self.apply_pca:\n            self._invalidate_graph()\n\n    def _invalidate_pca_projection(self):\n        self.pca_projection = None\n        if not self.apply_pca:\n            return\n\n        self._invalidate_graph()\n        self._set_modified(True)\n\n    def _invalidate_graph(self):\n        self.graph = None\n        self._invalidate_partition()\n        self._set_modified(True)\n\n    def _invalidate_partition(self):\n        self.partition = None\n        self._invalidate_output()\n        self.Information.modified()\n        self._set_modified(True)\n\n    def _invalidate_output(self):\n        self.__invalidated = True\n        if self.__task is not None:\n            self.__cancel_task(wait=False)\n\n        if self.auto_commit:\n            self.__commit_timer.start()\n        else:\n            self.__set_state_ready()\n\n    def _set_modified(self, state):\n        \"\"\"\n        Mark the widget (GUI) as containing modified state.\n        \"\"\"\n        if self.data is None:\n            # does not apply when we have no data\n            state = False\n        elif self.auto_commit:\n            # does not apply when auto commit is on\n            state = False\n        self.Information.modified(shown=state)\n\n    def _on_auto_commit_changed(self):\n        if self.auto_commit and self.__invalidated:\n            self.commit()\n\n    def cancel(self):\n        \"\"\"Cancel any running jobs.\"\"\"\n        self.__cancel_task(wait=False)\n        self.__set_state_ready()\n\n    def commit(self):\n        self.__commit_timer.stop()\n        self.__invalidated = False\n        self._set_modified(False)\n\n        # Cancel current running task\n        self.__cancel_task(wait=False)\n\n        if self.data is None:\n            self.__set_state_ready()\n            return\n\n        self.Error.clear()\n\n        if self.partition is not None:\n            self.__set_state_ready()\n            self._send_data()\n            return\n\n        self._preprocess_data()\n\n        state = TaskState(self)\n\n        # Prepare/assemble the task(s) to run; reuse partial results\n        if self.apply_pca:\n            if self.pca_projection is not None:\n                data = self.pca_projection\n                pca_components = None\n            else:\n                data = self.preprocessed_data\n                pca_components = self.pca_components\n        else:\n            data = self.preprocessed_data\n            pca_components = None\n\n        if self.graph is not None:\n            # run on graph only; no need to do PCA and k-nn search ...\n            graph = self.graph\n            k_neighbors = metric = None\n        else:\n            k_neighbors, metric = self.k_neighbors, METRICS[self.metric_idx][1]\n            graph = None\n\n        if graph is None:\n            task = partial(\n                run_on_data, data, pca_components=pca_components,\n                normalize=self.normalize, k_neighbors=k_neighbors,\n                metric=metric, resolution=self.resolution, state=state,\n            )\n        else:\n            task = partial(\n                run_on_graph, graph, resolution=self.resolution, state=state\n            )\n\n        self.info_label.setText(\"Running...\")\n        self.__set_state_busy()\n        self.__start_task(task, state)\n\n    @Slot(object)\n    def __set_partial_results(self, result):\n        # type: (Tuple[str, Any]) -> None\n        which, res = result\n        if which == \"pca_projection\":\n            assert isinstance(res, Table) and len(res) == len(self.data)\n            self.pca_projection = res\n        elif which == \"graph\":\n            assert isinstance(res, nx.Graph)\n            self.graph = res\n        elif which == \"partition\":\n            assert isinstance(res, np.ndarray)\n            self.partition = res\n        else:\n            assert False, which\n\n    @Slot(object)\n    def __on_done(self, future):\n        # type: (Future[\"Results\"]) -> None\n        assert future.done()\n        assert self.__task is not None\n        assert self.__task.future is future\n        assert self.__task.watcher.future() is future\n        self.__task, task = None, self.__task\n        task.deleteLater()\n\n        self.__set_state_ready()\n\n        result = future.result()\n        self.__set_results(result)\n\n    @Slot(str)\n    def setStatusMessage(self, text):\n        super().setStatusMessage(text)\n\n    @Slot(float)\n    def progressBarSet(self, value, *a, **kw):\n        super().progressBarSet(value, *a, **kw)\n\n    def __set_state_ready(self):\n        self.progressBarFinished()\n        self.setInvalidated(False)\n        self.setStatusMessage(\"\")\n\n    def __set_state_busy(self):\n        self.progressBarInit()\n        self.setInvalidated(True)\n\n    def __start_task(self, task, state):\n        # type: (Callable[[], Any], TaskState) -> None\n        assert self.__task is None\n        state.status_changed.connect(self.setStatusMessage)\n        state.progress_changed.connect(self.progressBarSet)\n        state.partial_result_ready.connect(self.__set_partial_results)\n        state.watcher.done.connect(self.__on_done)\n        state.start(self.__executor, task)\n        state.setParent(self)\n        self.__task = state\n\n    def __cancel_task(self, wait=True):\n        # Cancel and dispose of the current task\n        if self.__task is not None:\n            state, self.__task = self.__task, None\n            state.cancel()\n            state.partial_result_ready.disconnect(self.__set_partial_results)\n            state.status_changed.disconnect(self.setStatusMessage)\n            state.progress_changed.disconnect(self.progressBarSet)\n            state.watcher.done.disconnect(self.__on_done)\n            if wait:\n                futures.wait([state.future])\n                state.deleteLater()\n            else:\n                w = FutureWatcher(state.future, parent=state)\n                w.done.connect(state.deleteLater)\n\n    def __set_results(self, results):\n        # type: (\"Results\") -> None\n        # NOTE: All of these have already been set by __set_partial_results,\n        # we double check that they are aliases\n        if results.pca_projection is not None:\n            assert self.pca_components == results.pca_components\n            assert self.pca_projection is results.pca_projection\n            self.pca_projection = results.pca_projection\n        if results.graph is not None:\n            assert results.metric == METRICS[self.metric_idx][1]\n            assert results.k_neighbors == self.k_neighbors\n            assert self.graph is results.graph\n            self.graph = results.graph\n        if results.partition is not None:\n            assert results.resolution == self.resolution\n            assert self.partition is results.partition\n            self.partition = results.partition\n\n        # Display the number of found clusters in the UI\n        num_clusters = len(np.unique(self.partition))\n        self.info_label.setText(\"%d clusters found.\" % num_clusters)\n\n        self._send_data()\n\n    def _send_data(self):\n        if self.partition is None or self.data is None:\n            return\n        domain = self.data.domain\n        # Compute the frequency of each cluster index\n        counts = np.bincount(self.partition)\n        indices = np.argsort(counts)[::-1]\n        index_map = {n: o for n, o in zip(indices, range(len(indices)))}\n        new_partition = list(map(index_map.get, self.partition))\n\n        cluster_var = DiscreteVariable(\n            get_unique_names(domain, \"Cluster\"),\n            values=[\"C%d\" % (i + 1) for i, _ in enumerate(np.unique(new_partition))]\n        )\n\n        new_domain = add_columns(domain, metas=[cluster_var])\n        new_table = self.data.transform(new_domain)\n        new_table.get_column_view(cluster_var)[0][:] = new_partition\n        self.Outputs.annotated_data.send(new_table)\n\n        if Network is not None:\n            n_edges = self.graph.number_of_edges()\n            edges = sp.coo_matrix(\n                (np.ones(n_edges), np.array(self.graph.edges()).T),\n                shape=(n_edges, n_edges))\n            graph = Network(new_table, edges)\n            self.Outputs.graph.send(graph)\n\n    @Inputs.data\n    def set_data(self, data):\n        self.Error.clear()\n\n        prev_data, self.data = self.data, data\n        # Make sure to properly enable/disable slider based on `apply_pca` setting\n        self.controls.pca_components.setEnabled(self.apply_pca)\n\n        if prev_data and self.data and array_equal(prev_data.X, self.data.X):\n            if self.auto_commit and not self.isInvalidated():\n                self._send_data()\n            return\n\n        self.cancel()\n        # Clear the outputs\n        self.Outputs.annotated_data.send(None)\n        if Network is not None:\n            self.Outputs.graph.send(None)\n\n        # Clear internal state\n        self.clear()\n        self._invalidate_pca_projection()\n\n        # Make sure the dataset is ok\n        if self.data is not None and len(self.data.domain.attributes) < 1:\n            self.Error.empty_dataset()\n            self.data = None\n\n        if self.data is None:\n            return\n\n        # Can't have more PCA components than the number of attributes\n        n_attrs = len(data.domain.attributes)\n        self.pca_components_slider.setMaximum(min(_MAX_PCA_COMPONENTS, n_attrs))\n        # Can't have more k neighbors than there are data points\n        self.k_neighbors_spin.setMaximum(min(_MAX_K_NEIGBOURS, len(data) - 1))\n\n        self.info_label.setText(\"Clustering not yet run.\")\n\n        self.commit()\n\n    def clear(self):\n        self.__cancel_task(wait=False)\n        self.preprocessed_data = None\n        self.pca_projection = None\n        self.graph = None\n        self.partition = None\n        self.Error.clear()\n        self.Information.modified.clear()\n        self.info_label.setText(\"No data on input.\")\n\n    def onDeleteWidget(self):\n        self.__cancel_task(wait=True)\n        self.__executor.shutdown(True)\n        self.clear()\n        self.data = None\n        super().onDeleteWidget()\n\n    def send_report(self):\n        pca = report.bool_str(self.apply_pca)\n        if self.apply_pca:\n            pca += report.plural(\", {number} component{s}\", self.pca_components)\n\n        self.report_items((\n            (\"Normalize data\", report.bool_str(self.normalize)),\n            (\"PCA preprocessing\", pca),\n            (\"Metric\", METRICS[self.metric_idx][0]),\n            (\"k neighbors\", self.k_neighbors),\n            (\"Resolution\", self.resolution),\n        ))\n\n    @classmethod\n    def migrate_settings(cls, settings, version):\n        if version < 2 and \"context_settings\" in settings:\n            try:\n                current_context = settings[\"context_settings\"][0]\n                for n in ['apply_pca', 'k_neighbors', 'metric_idx',\n                          'normalize', 'pca_components', 'resolution']:\n                    if n in current_context.values:\n                        settings[n] = current_context.values[n][0]\n            except:  # pylint: disable=bare-except\n                pass\n            finally:\n                del settings[\"context_settings\"]\n\n\nclass TaskState(QObject):\n\n    status_changed = Signal(str)\n    _p_status_changed = Signal(str)\n\n    progress_changed = Signal(float)\n    _p_progress_changed = Signal(float)\n\n    partial_result_ready = Signal(object)\n    _p_partial_result_ready = Signal(object)\n\n    def __init__(self, *args):\n        super().__init__(*args)\n        self.__future = None\n        self.watcher = FutureWatcher()\n        self.__interuption_requested = False\n        self.__progress = 0\n        # Helpers to route the signal emits via a this object's queue.\n        # This ensures 'atomic' disconnect from signals for targets/slots\n        # in the same thread. Requires that the event loop is running in this\n        # object's thread.\n        self._p_status_changed.connect(\n            self.status_changed, Qt.QueuedConnection)\n        self._p_progress_changed.connect(\n            self.progress_changed, Qt.QueuedConnection)\n        self._p_partial_result_ready.connect(\n            self.partial_result_ready, Qt.QueuedConnection)\n\n    @property\n    def future(self):\n        # type: () -> Future\n        return self.__future\n\n    def set_status(self, text):\n        self._p_status_changed.emit(text)\n\n    def set_progress_value(self, value):\n        if round(value, 1) > round(self.__progress, 1):\n            # Only emit progress when it has changed sufficiently\n            self._p_progress_changed.emit(value)\n            self.__progress = value\n\n    def set_partial_results(self, value):\n        self._p_partial_result_ready.emit(value)\n\n    def is_interuption_requested(self):\n        return self.__interuption_requested\n\n    def start(self, executor, func=None):\n        # type: (futures.Executor, Callable[[], Any]) -> Future\n        assert self.future is None\n        assert not self.__interuption_requested\n        self.__future = executor.submit(func)\n        self.watcher.setFuture(self.future)\n        return self.future\n\n    def cancel(self):\n        assert not self.__interuption_requested\n        self.__interuption_requested = True\n        if self.future is not None:\n            rval = self.future.cancel()\n        else:\n            # not even scheduled yet\n            rval = True\n        return rval\n\n\nclass InteruptRequested(BaseException):\n    pass\n\n\nclass Results(namespace):\n    pca_projection = None    # type: Optional[Table]\n    pca_components = None    # type: Optional[int]\n    normalize = None         # type: Optional[bool]\n    k_neighbors = None       # type: Optional[int]\n    metric = None            # type: Optional[str]\n    graph = None             # type: Optional[nx.Graph]\n    resolution = None        # type: Optional[float]\n    partition = None         # type: Optional[np.ndarray]\n\n\ndef run_on_data(data, normalize, pca_components, k_neighbors, metric, resolution, state):\n    # type: (Table, Optional[int], int, str, float, bool, TaskState) -> Results\n    \"\"\"\n    Run the louvain clustering on `data`.\n\n    state is used to report progress and partial results. Returns early if\n    `task.is_interuption_requested()` returns true.\n\n    Parameters\n    ----------\n    data : Table\n        Data table\n    normalize : bool\n        If `True`, the data is first normalized before computing PCA.\n    pca_components : Optional[int]\n        If not `None` then the data is first projected onto first\n        `pca_components` principal components.\n    k_neighbors : int\n        Passed to `table_to_knn_graph`\n    metric : str\n        Passed to `table_to_knn_graph`\n    resolution : float\n        Passed to `Louvain`\n    state : TaskState\n\n    Returns\n    -------\n    res : Results\n    \"\"\"\n    state = state  # type: TaskState\n    res = Results(\n        normalize=normalize, pca_components=pca_components,\n        k_neighbors=k_neighbors, metric=metric, resolution=resolution,\n    )\n    step = 0\n    if state.is_interuption_requested():\n        return res\n\n    if pca_components is not None:\n        steps = 3\n        state.set_status(\"Computing PCA...\")\n        pca = PCA(n_components=pca_components, random_state=0)\n\n        data = res.pca_projection = pca(data)(data)\n        assert isinstance(data, Table)\n        state.set_partial_results((\"pca_projection\", res.pca_projection))\n        step += 1\n    else:\n        steps = 2\n\n    if state.is_interuption_requested():\n        return res\n\n    state.set_progress_value(100. * step / steps)\n    state.set_status(\"Building graph...\")\n\n    # Apply Louvain preprocessing before converting the table into a graph\n    louvain = Louvain(resolution=resolution, random_state=0)\n    data = louvain.preprocess(data)\n\n    if state.is_interuption_requested():\n        return res\n\n    def pcallback(val):\n        state.set_progress_value((100. * step + 100 * val) / steps)\n        if state.is_interuption_requested():\n            raise InteruptRequested()\n\n    try:\n        res.graph = graph = matrix_to_knn_graph(\n            data.X, k_neighbors=k_neighbors, metric=metric,\n            progress_callback=pcallback)\n    except InteruptRequested:\n        return res\n\n    state.set_partial_results((\"graph\", res.graph))\n\n    step += 1\n    state.set_progress_value(100 * step / steps)\n    state.set_status(\"Detecting communities...\")\n    if state.is_interuption_requested():\n        return res\n\n    res.partition = louvain(graph)\n    state.set_partial_results((\"partition\", res.partition))\n    return res\n\n\ndef run_on_graph(graph, resolution, state):\n    # type: (nx.Graph, float, TaskState) -> Results\n    \"\"\"\n    Run the louvain clustering on `graph`.\n    \"\"\"\n    state = state  # type: TaskState\n    res = Results(resolution=resolution)\n    louvain = Louvain(resolution=resolution, random_state=0)\n    state.set_status(\"Detecting communities...\")\n    if state.is_interuption_requested():\n        return res\n    partition = louvain(graph)\n    res.partition = partition\n    state.set_partial_results((\"partition\", res.partition))\n    return res\n\n\nif __name__ == \"__main__\":  # pragma: no cover\n    WidgetPreview(OWLouvainClustering).run(Table(\"iris\"))\n",
            "header": {
                "file": {
                    "path": "Orange/widgets/unsupervised/owlouvainclustering.py"
                },
                "misc_line": null
            },
            "index_line": {
                "old_commit_hash": "18f322a6f",
                "new_commit_hash": "5b9fdc571",
                "mode": "100644"
            },
            "is_binary_file": false,
            "binary_line": null,
            "minus_file": {
                "path": "a/Orange/widgets/unsupervised/owlouvainclustering.py"
            },
            "plus_file": {
                "path": "b/Orange/widgets/unsupervised/owlouvainclustering.py"
            },
            "hunks": [
                {
                    "descriptor": {
                        "old_range": {
                            "start": 50,
                            "length": 6
                        },
                        "new_range": {
                            "start": 50,
                            "length": 8
                        },
                        "section": "class OWLouvainClustering(widget.OWWidget):"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "    icon = \"icons/LouvainClustering.svg\"",
                                "type": "context"
                            },
                            {
                                "content": "    priority = 2110",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "    settings_version = 2",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "    want_main_area = False",
                                "type": "context"
                            },
                            {
                                "content": "    resizing_enabled = False",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [
                        {
                            "file_name": "Orange/widgets/unsupervised/owlouvainclustering.py",
                            "type": "class",
                            "name": "OWLouvainClustering",
                            "content": "class OWLouvainClustering(widget.OWWidget):\n    name = \"Louvain Clustering\"\n    description = \"Detects communities in a network of nearest neighbors.\"\n    icon = \"icons/LouvainClustering.svg\"\n    priority = 2110\n\n    settings_version = 2\n\n    want_main_area = False\n    resizing_enabled = False\n\n    class Inputs:\n        data = Input(\"Data\", Table, default=True)\n\n    class Outputs:\n        annotated_data = Output(ANNOTATED_DATA_SIGNAL_NAME, Table, default=True)\n        if Network is not None:\n            graph = Output(\"Network\", Network)\n\n    apply_pca = Setting(True)\n    pca_components = Setting(_DEFAULT_PCA_COMPONENTS)\n    normalize = Setting(True)\n    metric_idx = Setting(0)\n    k_neighbors = Setting(_DEFAULT_K_NEIGHBORS)\n    resolution = Setting(1.)\n    auto_commit = Setting(False)\n\n    class Information(widget.OWWidget.Information):\n        modified = Msg(\"Press commit to recompute clusters and send new data\")\n\n    class Error(widget.OWWidget.Error):\n        empty_dataset = Msg(\"No features in data\")\n\n    def __init__(self):\n        super().__init__()\n\n        self.data = None  # type: Optional[Table]\n        self.preprocessed_data = None  # type: Optional[Table]\n        self.pca_projection = None  # type: Optional[Table]\n        self.graph = None  # type: Optional[nx.Graph]\n        self.partition = None  # type: Optional[np.array]\n        # Use a executor with a single worker, to limit CPU overcommitment for\n        # cancelled tasks. The method does not have a fine cancellation\n        # granularity so we assure that there are not N - 1 jobs executing\n        # for no reason only to be thrown away. It would be better to use the\n        # global pool but implement a limit on jobs from this source.\n        self.__executor = futures.ThreadPoolExecutor(max_workers=1)\n        self.__task = None   # type: Optional[TaskState]\n        self.__invalidated = False\n        # coalescing commit timer\n        self.__commit_timer = QTimer(self, singleShot=True)\n        self.__commit_timer.timeout.connect(self.commit)\n\n        # Set up UI\n        info_box = gui.vBox(self.controlArea, \"Info\")\n        self.info_label = gui.widgetLabel(info_box, \"No data on input.\")  # type: QLabel\n\n        preprocessing_box = gui.vBox(self.controlArea, \"Preprocessing\")\n        self.normalize_cbx = gui.checkBox(\n            preprocessing_box, self, \"normalize\", label=\"Normalize data\",\n            callback=self._invalidate_preprocessed_data,\n        )  # type: QCheckBox\n        self.apply_pca_cbx = gui.checkBox(\n            preprocessing_box, self, \"apply_pca\", label=\"Apply PCA preprocessing\",\n            callback=self._apply_pca_changed,\n        )  # type: QCheckBox\n        self.pca_components_slider = gui.hSlider(\n            preprocessing_box, self, \"pca_components\", label=\"PCA Components: \", minValue=2,\n            maxValue=_MAX_PCA_COMPONENTS,\n            callback=self._invalidate_pca_projection, tracking=False\n        )  # type: QSlider\n\n        graph_box = gui.vBox(self.controlArea, \"Graph parameters\")\n        self.metric_combo = gui.comboBox(\n            graph_box, self, \"metric_idx\", label=\"Distance metric\",\n            items=[m[0] for m in METRICS], callback=self._invalidate_graph,\n            orientation=Qt.Horizontal,\n        )  # type: gui.OrangeComboBox\n        self.k_neighbors_spin = gui.spin(\n            graph_box, self, \"k_neighbors\", minv=1, maxv=_MAX_K_NEIGBOURS,\n            label=\"k neighbors\", controlWidth=80, alignment=Qt.AlignRight,\n            callback=self._invalidate_graph,\n        )  # type: gui.SpinBoxWFocusOut\n        self.resolution_spin = gui.hSlider(\n            graph_box, self, \"resolution\", minValue=0, maxValue=5., step=1e-1,\n            label=\"Resolution\", intOnly=False, labelFormat=\"%.1f\",\n            callback=self._invalidate_partition, tracking=False,\n        )  # type: QSlider\n        self.resolution_spin.parent().setToolTip(\n            \"The resolution parameter affects the number of clusters to find. \"\n            \"Smaller values tend to produce more clusters and larger values \"\n            \"retrieve less clusters.\"\n        )\n        self.apply_button = gui.auto_apply(\n            self.controlArea, self, \"auto_commit\", box=None,\n            commit=lambda: self.commit(), callback=lambda: self._on_auto_commit_changed()\n        )  # type: QWidget\n\n    def _preprocess_data(self):\n        if self.preprocessed_data is None:\n            if self.normalize:\n                normalizer = preprocess.Normalize(center=False)\n                self.preprocessed_data = normalizer(self.data)\n            else:\n                self.preprocessed_data = self.data\n\n    def _apply_pca_changed(self):\n        self.controls.pca_components.setEnabled(self.apply_pca)\n        self._invalidate_graph()\n\n    def _invalidate_preprocessed_data(self):\n        self.preprocessed_data = None\n        self._invalidate_pca_projection()\n        # If we don't apply PCA, this still invalidates the graph, otherwise\n        # this change won't be propagated further\n        if not self.apply_pca:\n            self._invalidate_graph()\n\n    def _invalidate_pca_projection(self):\n        self.pca_projection = None\n        if not self.apply_pca:\n            return\n\n        self._invalidate_graph()\n        self._set_modified(True)\n\n    def _invalidate_graph(self):\n        self.graph = None\n        self._invalidate_partition()\n        self._set_modified(True)\n\n    def _invalidate_partition(self):\n        self.partition = None\n        self._invalidate_output()\n        self.Information.modified()\n        self._set_modified(True)\n\n    def _invalidate_output(self):\n        self.__invalidated = True\n        if self.__task is not None:\n            self.__cancel_task(wait=False)\n\n        if self.auto_commit:\n            self.__commit_timer.start()\n        else:\n            self.__set_state_ready()\n\n    def _set_modified(self, state):\n        \"\"\"\n        Mark the widget (GUI) as containing modified state.\n        \"\"\"\n        if self.data is None:\n            # does not apply when we have no data\n            state = False\n        elif self.auto_commit:\n            # does not apply when auto commit is on\n            state = False\n        self.Information.modified(shown=state)\n\n    def _on_auto_commit_changed(self):\n        if self.auto_commit and self.__invalidated:\n            self.commit()\n\n    def cancel(self):\n        \"\"\"Cancel any running jobs.\"\"\"\n        self.__cancel_task(wait=False)\n        self.__set_state_ready()\n\n    def commit(self):\n        self.__commit_timer.stop()\n        self.__invalidated = False\n        self._set_modified(False)\n\n        # Cancel current running task\n        self.__cancel_task(wait=False)\n\n        if self.data is None:\n            self.__set_state_ready()\n            return\n\n        self.Error.clear()\n\n        if self.partition is not None:\n            self.__set_state_ready()\n            self._send_data()\n            return\n\n        self._preprocess_data()\n\n        state = TaskState(self)\n\n        # Prepare/assemble the task(s) to run; reuse partial results\n        if self.apply_pca:\n            if self.pca_projection is not None:\n                data = self.pca_projection\n                pca_components = None\n            else:\n                data = self.preprocessed_data\n                pca_components = self.pca_components\n        else:\n            data = self.preprocessed_data\n            pca_components = None\n\n        if self.graph is not None:\n            # run on graph only; no need to do PCA and k-nn search ...\n            graph = self.graph\n            k_neighbors = metric = None\n        else:\n            k_neighbors, metric = self.k_neighbors, METRICS[self.metric_idx][1]\n            graph = None\n\n        if graph is None:\n            task = partial(\n                run_on_data, data, pca_components=pca_components,\n                normalize=self.normalize, k_neighbors=k_neighbors,\n                metric=metric, resolution=self.resolution, state=state,\n            )\n        else:\n            task = partial(\n                run_on_graph, graph, resolution=self.resolution, state=state\n            )\n\n        self.info_label.setText(\"Running...\")\n        self.__set_state_busy()\n        self.__start_task(task, state)\n\n    @Slot(object)\n    def __set_partial_results(self, result):\n        # type: (Tuple[str, Any]) -> None\n        which, res = result\n        if which == \"pca_projection\":\n            assert isinstance(res, Table) and len(res) == len(self.data)\n            self.pca_projection = res\n        elif which == \"graph\":\n            assert isinstance(res, nx.Graph)\n            self.graph = res\n        elif which == \"partition\":\n            assert isinstance(res, np.ndarray)\n            self.partition = res\n        else:\n            assert False, which\n\n    @Slot(object)\n    def __on_done(self, future):\n        # type: (Future[\"Results\"]) -> None\n        assert future.done()\n        assert self.__task is not None\n        assert self.__task.future is future\n        assert self.__task.watcher.future() is future\n        self.__task, task = None, self.__task\n        task.deleteLater()\n\n        self.__set_state_ready()\n\n        result = future.result()\n        self.__set_results(result)\n\n    @Slot(str)\n    def setStatusMessage(self, text):\n        super().setStatusMessage(text)\n\n    @Slot(float)\n    def progressBarSet(self, value, *a, **kw):\n        super().progressBarSet(value, *a, **kw)\n\n    def __set_state_ready(self):\n        self.progressBarFinished()\n        self.setInvalidated(False)\n        self.setStatusMessage(\"\")\n\n    def __set_state_busy(self):\n        self.progressBarInit()\n        self.setInvalidated(True)\n\n    def __start_task(self, task, state):\n        # type: (Callable[[], Any], TaskState) -> None\n        assert self.__task is None\n        state.status_changed.connect(self.setStatusMessage)\n        state.progress_changed.connect(self.progressBarSet)\n        state.partial_result_ready.connect(self.__set_partial_results)\n        state.watcher.done.connect(self.__on_done)\n        state.start(self.__executor, task)\n        state.setParent(self)\n        self.__task = state\n\n    def __cancel_task(self, wait=True):\n        # Cancel and dispose of the current task\n        if self.__task is not None:\n            state, self.__task = self.__task, None\n            state.cancel()\n            state.partial_result_ready.disconnect(self.__set_partial_results)\n            state.status_changed.disconnect(self.setStatusMessage)\n            state.progress_changed.disconnect(self.progressBarSet)\n            state.watcher.done.disconnect(self.__on_done)\n            if wait:\n                futures.wait([state.future])\n                state.deleteLater()\n            else:\n                w = FutureWatcher(state.future, parent=state)\n                w.done.connect(state.deleteLater)\n\n    def __set_results(self, results):\n        # type: (\"Results\") -> None\n        # NOTE: All of these have already been set by __set_partial_results,\n        # we double check that they are aliases\n        if results.pca_projection is not None:\n            assert self.pca_components == results.pca_components\n            assert self.pca_projection is results.pca_projection\n            self.pca_projection = results.pca_projection\n        if results.graph is not None:\n            assert results.metric == METRICS[self.metric_idx][1]\n            assert results.k_neighbors == self.k_neighbors\n            assert self.graph is results.graph\n            self.graph = results.graph\n        if results.partition is not None:\n            assert results.resolution == self.resolution\n            assert self.partition is results.partition\n            self.partition = results.partition\n\n        # Display the number of found clusters in the UI\n        num_clusters = len(np.unique(self.partition))\n        self.info_label.setText(\"%d clusters found.\" % num_clusters)\n\n        self._send_data()\n\n    def _send_data(self):\n        if self.partition is None or self.data is None:\n            return\n        domain = self.data.domain\n        # Compute the frequency of each cluster index\n        counts = np.bincount(self.partition)\n        indices = np.argsort(counts)[::-1]\n        index_map = {n: o for n, o in zip(indices, range(len(indices)))}\n        new_partition = list(map(index_map.get, self.partition))\n\n        cluster_var = DiscreteVariable(\n            get_unique_names(domain, \"Cluster\"),\n            values=[\"C%d\" % (i + 1) for i, _ in enumerate(np.unique(new_partition))]\n        )\n\n        new_domain = add_columns(domain, metas=[cluster_var])\n        new_table = self.data.transform(new_domain)\n        new_table.get_column_view(cluster_var)[0][:] = new_partition\n        self.Outputs.annotated_data.send(new_table)\n\n        if Network is not None:\n            n_edges = self.graph.number_of_edges()\n            edges = sp.coo_matrix(\n                (np.ones(n_edges), np.array(self.graph.edges()).T),\n                shape=(n_edges, n_edges))\n            graph = Network(new_table, edges)\n            self.Outputs.graph.send(graph)\n\n    @Inputs.data\n    def set_data(self, data):\n        self.Error.clear()\n\n        prev_data, self.data = self.data, data\n        # Make sure to properly enable/disable slider based on `apply_pca` setting\n        self.controls.pca_components.setEnabled(self.apply_pca)\n\n        if prev_data and self.data and array_equal(prev_data.X, self.data.X):\n            if self.auto_commit and not self.isInvalidated():\n                self._send_data()\n            return\n\n        self.cancel()\n        # Clear the outputs\n        self.Outputs.annotated_data.send(None)\n        if Network is not None:\n            self.Outputs.graph.send(None)\n\n        # Clear internal state\n        self.clear()\n        self._invalidate_pca_projection()\n\n        # Make sure the dataset is ok\n        if self.data is not None and len(self.data.domain.attributes) < 1:\n            self.Error.empty_dataset()\n            self.data = None\n\n        if self.data is None:\n            return\n\n        # Can't have more PCA components than the number of attributes\n        n_attrs = len(data.domain.attributes)\n        self.pca_components_slider.setMaximum(min(_MAX_PCA_COMPONENTS, n_attrs))\n        # Can't have more k neighbors than there are data points\n        self.k_neighbors_spin.setMaximum(min(_MAX_K_NEIGBOURS, len(data) - 1))\n\n        self.info_label.setText(\"Clustering not yet run.\")\n\n        self.commit()\n\n    def clear(self):\n        self.__cancel_task(wait=False)\n        self.preprocessed_data = None\n        self.pca_projection = None\n        self.graph = None\n        self.partition = None\n        self.Error.clear()\n        self.Information.modified.clear()\n        self.info_label.setText(\"No data on input.\")\n\n    def onDeleteWidget(self):\n        self.__cancel_task(wait=True)\n        self.__executor.shutdown(True)\n        self.clear()\n        self.data = None\n        super().onDeleteWidget()\n\n    def send_report(self):\n        pca = report.bool_str(self.apply_pca)\n        if self.apply_pca:\n            pca += report.plural(\", {number} component{s}\", self.pca_components)\n\n        self.report_items((\n            (\"Normalize data\", report.bool_str(self.normalize)),\n            (\"PCA preprocessing\", pca),\n            (\"Metric\", METRICS[self.metric_idx][0]),\n            (\"k neighbors\", self.k_neighbors),\n            (\"Resolution\", self.resolution),\n        ))\n\n    @classmethod\n    def migrate_settings(cls, settings, version):\n        if version < 2 and \"context_settings\" in settings:\n            try:\n                current_context = settings[\"context_settings\"][0]\n                for n in ['apply_pca', 'k_neighbors', 'metric_idx',\n                          'normalize', 'pca_components', 'resolution']:\n                    if n in current_context.values:\n                        settings[n] = current_context.values[n][0]\n            except:  # pylint: disable=bare-except\n                pass\n            finally:\n                del settings[\"context_settings\"]",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 47,
                            "end_lineno": 483,
                            "parent": null
                        }
                    ],
                    "added_entities": [],
                    "deleted_entities": []
                },
                {
                    "descriptor": {
                        "old_range": {
                            "start": 466,
                            "length": 6
                        },
                        "new_range": {
                            "start": 468,
                            "length": 20
                        },
                        "section": "class OWLouvainClustering(widget.OWWidget):"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "            (\"Resolution\", self.resolution),",
                                "type": "context"
                            },
                            {
                                "content": "        ))",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "    @classmethod",
                                "type": "added"
                            },
                            {
                                "content": "    def migrate_settings(cls, settings, version):",
                                "type": "added"
                            },
                            {
                                "content": "        if version < 2 and \"context_settings\" in settings:",
                                "type": "added"
                            },
                            {
                                "content": "            try:",
                                "type": "added"
                            },
                            {
                                "content": "                current_context = settings[\"context_settings\"][0]",
                                "type": "added"
                            },
                            {
                                "content": "                for n in ['apply_pca', 'k_neighbors', 'metric_idx',",
                                "type": "added"
                            },
                            {
                                "content": "                          'normalize', 'pca_components', 'resolution']:",
                                "type": "added"
                            },
                            {
                                "content": "                    if n in current_context.values:",
                                "type": "added"
                            },
                            {
                                "content": "                        settings[n] = current_context.values[n][0]",
                                "type": "added"
                            },
                            {
                                "content": "            except:  # pylint: disable=bare-except",
                                "type": "added"
                            },
                            {
                                "content": "                pass",
                                "type": "added"
                            },
                            {
                                "content": "            finally:",
                                "type": "added"
                            },
                            {
                                "content": "                del settings[\"context_settings\"]",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "class TaskState(QObject):",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [
                        {
                            "file_name": "Orange/widgets/unsupervised/owlouvainclustering.py",
                            "type": "class",
                            "name": "OWLouvainClustering",
                            "content": "class OWLouvainClustering(widget.OWWidget):\n    name = \"Louvain Clustering\"\n    description = \"Detects communities in a network of nearest neighbors.\"\n    icon = \"icons/LouvainClustering.svg\"\n    priority = 2110\n\n    settings_version = 2\n\n    want_main_area = False\n    resizing_enabled = False\n\n    class Inputs:\n        data = Input(\"Data\", Table, default=True)\n\n    class Outputs:\n        annotated_data = Output(ANNOTATED_DATA_SIGNAL_NAME, Table, default=True)\n        if Network is not None:\n            graph = Output(\"Network\", Network)\n\n    apply_pca = Setting(True)\n    pca_components = Setting(_DEFAULT_PCA_COMPONENTS)\n    normalize = Setting(True)\n    metric_idx = Setting(0)\n    k_neighbors = Setting(_DEFAULT_K_NEIGHBORS)\n    resolution = Setting(1.)\n    auto_commit = Setting(False)\n\n    class Information(widget.OWWidget.Information):\n        modified = Msg(\"Press commit to recompute clusters and send new data\")\n\n    class Error(widget.OWWidget.Error):\n        empty_dataset = Msg(\"No features in data\")\n\n    def __init__(self):\n        super().__init__()\n\n        self.data = None  # type: Optional[Table]\n        self.preprocessed_data = None  # type: Optional[Table]\n        self.pca_projection = None  # type: Optional[Table]\n        self.graph = None  # type: Optional[nx.Graph]\n        self.partition = None  # type: Optional[np.array]\n        # Use a executor with a single worker, to limit CPU overcommitment for\n        # cancelled tasks. The method does not have a fine cancellation\n        # granularity so we assure that there are not N - 1 jobs executing\n        # for no reason only to be thrown away. It would be better to use the\n        # global pool but implement a limit on jobs from this source.\n        self.__executor = futures.ThreadPoolExecutor(max_workers=1)\n        self.__task = None   # type: Optional[TaskState]\n        self.__invalidated = False\n        # coalescing commit timer\n        self.__commit_timer = QTimer(self, singleShot=True)\n        self.__commit_timer.timeout.connect(self.commit)\n\n        # Set up UI\n        info_box = gui.vBox(self.controlArea, \"Info\")\n        self.info_label = gui.widgetLabel(info_box, \"No data on input.\")  # type: QLabel\n\n        preprocessing_box = gui.vBox(self.controlArea, \"Preprocessing\")\n        self.normalize_cbx = gui.checkBox(\n            preprocessing_box, self, \"normalize\", label=\"Normalize data\",\n            callback=self._invalidate_preprocessed_data,\n        )  # type: QCheckBox\n        self.apply_pca_cbx = gui.checkBox(\n            preprocessing_box, self, \"apply_pca\", label=\"Apply PCA preprocessing\",\n            callback=self._apply_pca_changed,\n        )  # type: QCheckBox\n        self.pca_components_slider = gui.hSlider(\n            preprocessing_box, self, \"pca_components\", label=\"PCA Components: \", minValue=2,\n            maxValue=_MAX_PCA_COMPONENTS,\n            callback=self._invalidate_pca_projection, tracking=False\n        )  # type: QSlider\n\n        graph_box = gui.vBox(self.controlArea, \"Graph parameters\")\n        self.metric_combo = gui.comboBox(\n            graph_box, self, \"metric_idx\", label=\"Distance metric\",\n            items=[m[0] for m in METRICS], callback=self._invalidate_graph,\n            orientation=Qt.Horizontal,\n        )  # type: gui.OrangeComboBox\n        self.k_neighbors_spin = gui.spin(\n            graph_box, self, \"k_neighbors\", minv=1, maxv=_MAX_K_NEIGBOURS,\n            label=\"k neighbors\", controlWidth=80, alignment=Qt.AlignRight,\n            callback=self._invalidate_graph,\n        )  # type: gui.SpinBoxWFocusOut\n        self.resolution_spin = gui.hSlider(\n            graph_box, self, \"resolution\", minValue=0, maxValue=5., step=1e-1,\n            label=\"Resolution\", intOnly=False, labelFormat=\"%.1f\",\n            callback=self._invalidate_partition, tracking=False,\n        )  # type: QSlider\n        self.resolution_spin.parent().setToolTip(\n            \"The resolution parameter affects the number of clusters to find. \"\n            \"Smaller values tend to produce more clusters and larger values \"\n            \"retrieve less clusters.\"\n        )\n        self.apply_button = gui.auto_apply(\n            self.controlArea, self, \"auto_commit\", box=None,\n            commit=lambda: self.commit(), callback=lambda: self._on_auto_commit_changed()\n        )  # type: QWidget\n\n    def _preprocess_data(self):\n        if self.preprocessed_data is None:\n            if self.normalize:\n                normalizer = preprocess.Normalize(center=False)\n                self.preprocessed_data = normalizer(self.data)\n            else:\n                self.preprocessed_data = self.data\n\n    def _apply_pca_changed(self):\n        self.controls.pca_components.setEnabled(self.apply_pca)\n        self._invalidate_graph()\n\n    def _invalidate_preprocessed_data(self):\n        self.preprocessed_data = None\n        self._invalidate_pca_projection()\n        # If we don't apply PCA, this still invalidates the graph, otherwise\n        # this change won't be propagated further\n        if not self.apply_pca:\n            self._invalidate_graph()\n\n    def _invalidate_pca_projection(self):\n        self.pca_projection = None\n        if not self.apply_pca:\n            return\n\n        self._invalidate_graph()\n        self._set_modified(True)\n\n    def _invalidate_graph(self):\n        self.graph = None\n        self._invalidate_partition()\n        self._set_modified(True)\n\n    def _invalidate_partition(self):\n        self.partition = None\n        self._invalidate_output()\n        self.Information.modified()\n        self._set_modified(True)\n\n    def _invalidate_output(self):\n        self.__invalidated = True\n        if self.__task is not None:\n            self.__cancel_task(wait=False)\n\n        if self.auto_commit:\n            self.__commit_timer.start()\n        else:\n            self.__set_state_ready()\n\n    def _set_modified(self, state):\n        \"\"\"\n        Mark the widget (GUI) as containing modified state.\n        \"\"\"\n        if self.data is None:\n            # does not apply when we have no data\n            state = False\n        elif self.auto_commit:\n            # does not apply when auto commit is on\n            state = False\n        self.Information.modified(shown=state)\n\n    def _on_auto_commit_changed(self):\n        if self.auto_commit and self.__invalidated:\n            self.commit()\n\n    def cancel(self):\n        \"\"\"Cancel any running jobs.\"\"\"\n        self.__cancel_task(wait=False)\n        self.__set_state_ready()\n\n    def commit(self):\n        self.__commit_timer.stop()\n        self.__invalidated = False\n        self._set_modified(False)\n\n        # Cancel current running task\n        self.__cancel_task(wait=False)\n\n        if self.data is None:\n            self.__set_state_ready()\n            return\n\n        self.Error.clear()\n\n        if self.partition is not None:\n            self.__set_state_ready()\n            self._send_data()\n            return\n\n        self._preprocess_data()\n\n        state = TaskState(self)\n\n        # Prepare/assemble the task(s) to run; reuse partial results\n        if self.apply_pca:\n            if self.pca_projection is not None:\n                data = self.pca_projection\n                pca_components = None\n            else:\n                data = self.preprocessed_data\n                pca_components = self.pca_components\n        else:\n            data = self.preprocessed_data\n            pca_components = None\n\n        if self.graph is not None:\n            # run on graph only; no need to do PCA and k-nn search ...\n            graph = self.graph\n            k_neighbors = metric = None\n        else:\n            k_neighbors, metric = self.k_neighbors, METRICS[self.metric_idx][1]\n            graph = None\n\n        if graph is None:\n            task = partial(\n                run_on_data, data, pca_components=pca_components,\n                normalize=self.normalize, k_neighbors=k_neighbors,\n                metric=metric, resolution=self.resolution, state=state,\n            )\n        else:\n            task = partial(\n                run_on_graph, graph, resolution=self.resolution, state=state\n            )\n\n        self.info_label.setText(\"Running...\")\n        self.__set_state_busy()\n        self.__start_task(task, state)\n\n    @Slot(object)\n    def __set_partial_results(self, result):\n        # type: (Tuple[str, Any]) -> None\n        which, res = result\n        if which == \"pca_projection\":\n            assert isinstance(res, Table) and len(res) == len(self.data)\n            self.pca_projection = res\n        elif which == \"graph\":\n            assert isinstance(res, nx.Graph)\n            self.graph = res\n        elif which == \"partition\":\n            assert isinstance(res, np.ndarray)\n            self.partition = res\n        else:\n            assert False, which\n\n    @Slot(object)\n    def __on_done(self, future):\n        # type: (Future[\"Results\"]) -> None\n        assert future.done()\n        assert self.__task is not None\n        assert self.__task.future is future\n        assert self.__task.watcher.future() is future\n        self.__task, task = None, self.__task\n        task.deleteLater()\n\n        self.__set_state_ready()\n\n        result = future.result()\n        self.__set_results(result)\n\n    @Slot(str)\n    def setStatusMessage(self, text):\n        super().setStatusMessage(text)\n\n    @Slot(float)\n    def progressBarSet(self, value, *a, **kw):\n        super().progressBarSet(value, *a, **kw)\n\n    def __set_state_ready(self):\n        self.progressBarFinished()\n        self.setInvalidated(False)\n        self.setStatusMessage(\"\")\n\n    def __set_state_busy(self):\n        self.progressBarInit()\n        self.setInvalidated(True)\n\n    def __start_task(self, task, state):\n        # type: (Callable[[], Any], TaskState) -> None\n        assert self.__task is None\n        state.status_changed.connect(self.setStatusMessage)\n        state.progress_changed.connect(self.progressBarSet)\n        state.partial_result_ready.connect(self.__set_partial_results)\n        state.watcher.done.connect(self.__on_done)\n        state.start(self.__executor, task)\n        state.setParent(self)\n        self.__task = state\n\n    def __cancel_task(self, wait=True):\n        # Cancel and dispose of the current task\n        if self.__task is not None:\n            state, self.__task = self.__task, None\n            state.cancel()\n            state.partial_result_ready.disconnect(self.__set_partial_results)\n            state.status_changed.disconnect(self.setStatusMessage)\n            state.progress_changed.disconnect(self.progressBarSet)\n            state.watcher.done.disconnect(self.__on_done)\n            if wait:\n                futures.wait([state.future])\n                state.deleteLater()\n            else:\n                w = FutureWatcher(state.future, parent=state)\n                w.done.connect(state.deleteLater)\n\n    def __set_results(self, results):\n        # type: (\"Results\") -> None\n        # NOTE: All of these have already been set by __set_partial_results,\n        # we double check that they are aliases\n        if results.pca_projection is not None:\n            assert self.pca_components == results.pca_components\n            assert self.pca_projection is results.pca_projection\n            self.pca_projection = results.pca_projection\n        if results.graph is not None:\n            assert results.metric == METRICS[self.metric_idx][1]\n            assert results.k_neighbors == self.k_neighbors\n            assert self.graph is results.graph\n            self.graph = results.graph\n        if results.partition is not None:\n            assert results.resolution == self.resolution\n            assert self.partition is results.partition\n            self.partition = results.partition\n\n        # Display the number of found clusters in the UI\n        num_clusters = len(np.unique(self.partition))\n        self.info_label.setText(\"%d clusters found.\" % num_clusters)\n\n        self._send_data()\n\n    def _send_data(self):\n        if self.partition is None or self.data is None:\n            return\n        domain = self.data.domain\n        # Compute the frequency of each cluster index\n        counts = np.bincount(self.partition)\n        indices = np.argsort(counts)[::-1]\n        index_map = {n: o for n, o in zip(indices, range(len(indices)))}\n        new_partition = list(map(index_map.get, self.partition))\n\n        cluster_var = DiscreteVariable(\n            get_unique_names(domain, \"Cluster\"),\n            values=[\"C%d\" % (i + 1) for i, _ in enumerate(np.unique(new_partition))]\n        )\n\n        new_domain = add_columns(domain, metas=[cluster_var])\n        new_table = self.data.transform(new_domain)\n        new_table.get_column_view(cluster_var)[0][:] = new_partition\n        self.Outputs.annotated_data.send(new_table)\n\n        if Network is not None:\n            n_edges = self.graph.number_of_edges()\n            edges = sp.coo_matrix(\n                (np.ones(n_edges), np.array(self.graph.edges()).T),\n                shape=(n_edges, n_edges))\n            graph = Network(new_table, edges)\n            self.Outputs.graph.send(graph)\n\n    @Inputs.data\n    def set_data(self, data):\n        self.Error.clear()\n\n        prev_data, self.data = self.data, data\n        # Make sure to properly enable/disable slider based on `apply_pca` setting\n        self.controls.pca_components.setEnabled(self.apply_pca)\n\n        if prev_data and self.data and array_equal(prev_data.X, self.data.X):\n            if self.auto_commit and not self.isInvalidated():\n                self._send_data()\n            return\n\n        self.cancel()\n        # Clear the outputs\n        self.Outputs.annotated_data.send(None)\n        if Network is not None:\n            self.Outputs.graph.send(None)\n\n        # Clear internal state\n        self.clear()\n        self._invalidate_pca_projection()\n\n        # Make sure the dataset is ok\n        if self.data is not None and len(self.data.domain.attributes) < 1:\n            self.Error.empty_dataset()\n            self.data = None\n\n        if self.data is None:\n            return\n\n        # Can't have more PCA components than the number of attributes\n        n_attrs = len(data.domain.attributes)\n        self.pca_components_slider.setMaximum(min(_MAX_PCA_COMPONENTS, n_attrs))\n        # Can't have more k neighbors than there are data points\n        self.k_neighbors_spin.setMaximum(min(_MAX_K_NEIGBOURS, len(data) - 1))\n\n        self.info_label.setText(\"Clustering not yet run.\")\n\n        self.commit()\n\n    def clear(self):\n        self.__cancel_task(wait=False)\n        self.preprocessed_data = None\n        self.pca_projection = None\n        self.graph = None\n        self.partition = None\n        self.Error.clear()\n        self.Information.modified.clear()\n        self.info_label.setText(\"No data on input.\")\n\n    def onDeleteWidget(self):\n        self.__cancel_task(wait=True)\n        self.__executor.shutdown(True)\n        self.clear()\n        self.data = None\n        super().onDeleteWidget()\n\n    def send_report(self):\n        pca = report.bool_str(self.apply_pca)\n        if self.apply_pca:\n            pca += report.plural(\", {number} component{s}\", self.pca_components)\n\n        self.report_items((\n            (\"Normalize data\", report.bool_str(self.normalize)),\n            (\"PCA preprocessing\", pca),\n            (\"Metric\", METRICS[self.metric_idx][0]),\n            (\"k neighbors\", self.k_neighbors),\n            (\"Resolution\", self.resolution),\n        ))\n\n    @classmethod\n    def migrate_settings(cls, settings, version):\n        if version < 2 and \"context_settings\" in settings:\n            try:\n                current_context = settings[\"context_settings\"][0]\n                for n in ['apply_pca', 'k_neighbors', 'metric_idx',\n                          'normalize', 'pca_components', 'resolution']:\n                    if n in current_context.values:\n                        settings[n] = current_context.values[n][0]\n            except:  # pylint: disable=bare-except\n                pass\n            finally:\n                del settings[\"context_settings\"]",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 47,
                            "end_lineno": 483,
                            "parent": null
                        }
                    ],
                    "added_entities": [
                        {
                            "file_name": "Orange/widgets/unsupervised/owlouvainclustering.py",
                            "type": "method",
                            "name": "OWLouvainClustering.migrate_settings",
                            "content": "class OWLouvainClustering(widget.OWWidget):\n    name = \"Louvain Clustering\"\n    description = \"Detects communities in a network of nearest neighbors.\"\n    icon = \"icons/LouvainClustering.svg\"\n    priority = 2110\n\n    settings_version = 2\n\n    want_main_area = False\n    resizing_enabled = False\n\n    class Inputs:\n        data = Input(\"Data\", Table, default=True)\n\n    class Outputs:\n        annotated_data = Output(ANNOTATED_DATA_SIGNAL_NAME, Table, default=True)\n        if Network is not None:\n            graph = Output(\"Network\", Network)\n\n    apply_pca = Setting(True)\n    pca_components = Setting(_DEFAULT_PCA_COMPONENTS)\n    normalize = Setting(True)\n    metric_idx = Setting(0)\n    k_neighbors = Setting(_DEFAULT_K_NEIGHBORS)\n    resolution = Setting(1.)\n    auto_commit = Setting(False)\n\n    class Information(widget.OWWidget.Information):\n        modified = Msg(\"Press commit to recompute clusters and send new data\")\n\n    class Error(widget.OWWidget.Error):\n        empty_dataset = Msg(\"No features in data\")\n\n    def __init__(self):\n        super().__init__()\n\n        self.data = None  # type: Optional[Table]\n        self.preprocessed_data = None  # type: Optional[Table]\n        self.pca_projection = None  # type: Optional[Table]\n        self.graph = None  # type: Optional[nx.Graph]\n        self.partition = None  # type: Optional[np.array]\n        # Use a executor with a single worker, to limit CPU overcommitment for\n        # cancelled tasks. The method does not have a fine cancellation\n        # granularity so we assure that there are not N - 1 jobs executing\n        # for no reason only to be thrown away. It would be better to use the\n        # global pool but implement a limit on jobs from this source.\n        self.__executor = futures.ThreadPoolExecutor(max_workers=1)\n        self.__task = None   # type: Optional[TaskState]\n        self.__invalidated = False\n        # coalescing commit timer\n        self.__commit_timer = QTimer(self, singleShot=True)\n        self.__commit_timer.timeout.connect(self.commit)\n\n        # Set up UI\n        info_box = gui.vBox(self.controlArea, \"Info\")\n        self.info_label = gui.widgetLabel(info_box, \"No data on input.\")  # type: QLabel\n\n        preprocessing_box = gui.vBox(self.controlArea, \"Preprocessing\")\n        self.normalize_cbx = gui.checkBox(\n            preprocessing_box, self, \"normalize\", label=\"Normalize data\",\n            callback=self._invalidate_preprocessed_data,\n        )  # type: QCheckBox\n        self.apply_pca_cbx = gui.checkBox(\n            preprocessing_box, self, \"apply_pca\", label=\"Apply PCA preprocessing\",\n            callback=self._apply_pca_changed,\n        )  # type: QCheckBox\n        self.pca_components_slider = gui.hSlider(\n            preprocessing_box, self, \"pca_components\", label=\"PCA Components: \", minValue=2,\n            maxValue=_MAX_PCA_COMPONENTS,\n            callback=self._invalidate_pca_projection, tracking=False\n        )  # type: QSlider\n\n        graph_box = gui.vBox(self.controlArea, \"Graph parameters\")\n        self.metric_combo = gui.comboBox(\n            graph_box, self, \"metric_idx\", label=\"Distance metric\",\n            items=[m[0] for m in METRICS], callback=self._invalidate_graph,\n            orientation=Qt.Horizontal,\n        )  # type: gui.OrangeComboBox\n        self.k_neighbors_spin = gui.spin(\n            graph_box, self, \"k_neighbors\", minv=1, maxv=_MAX_K_NEIGBOURS,\n            label=\"k neighbors\", controlWidth=80, alignment=Qt.AlignRight,\n            callback=self._invalidate_graph,\n        )  # type: gui.SpinBoxWFocusOut\n        self.resolution_spin = gui.hSlider(\n            graph_box, self, \"resolution\", minValue=0, maxValue=5., step=1e-1,\n            label=\"Resolution\", intOnly=False, labelFormat=\"%.1f\",\n            callback=self._invalidate_partition, tracking=False,\n        )  # type: QSlider\n        self.resolution_spin.parent().setToolTip(\n            \"The resolution parameter affects the number of clusters to find. \"\n            \"Smaller values tend to produce more clusters and larger values \"\n            \"retrieve less clusters.\"\n        )\n        self.apply_button = gui.auto_apply(\n            self.controlArea, self, \"auto_commit\", box=None,\n            commit=lambda: self.commit(), callback=lambda: self._on_auto_commit_changed()\n        )  # type: QWidget\n\n    def _preprocess_data(self):\n        if self.preprocessed_data is None:\n            if self.normalize:\n                normalizer = preprocess.Normalize(center=False)\n                self.preprocessed_data = normalizer(self.data)\n            else:\n                self.preprocessed_data = self.data\n\n    def _apply_pca_changed(self):\n        self.controls.pca_components.setEnabled(self.apply_pca)\n        self._invalidate_graph()\n\n    def _invalidate_preprocessed_data(self):\n        self.preprocessed_data = None\n        self._invalidate_pca_projection()\n        # If we don't apply PCA, this still invalidates the graph, otherwise\n        # this change won't be propagated further\n        if not self.apply_pca:\n            self._invalidate_graph()\n\n    def _invalidate_pca_projection(self):\n        self.pca_projection = None\n        if not self.apply_pca:\n            return\n\n        self._invalidate_graph()\n        self._set_modified(True)\n\n    def _invalidate_graph(self):\n        self.graph = None\n        self._invalidate_partition()\n        self._set_modified(True)\n\n    def _invalidate_partition(self):\n        self.partition = None\n        self._invalidate_output()\n        self.Information.modified()\n        self._set_modified(True)\n\n    def _invalidate_output(self):\n        self.__invalidated = True\n        if self.__task is not None:\n            self.__cancel_task(wait=False)\n\n        if self.auto_commit:\n            self.__commit_timer.start()\n        else:\n            self.__set_state_ready()\n\n    def _set_modified(self, state):\n        \"\"\"\n        Mark the widget (GUI) as containing modified state.\n        \"\"\"\n        if self.data is None:\n            # does not apply when we have no data\n            state = False\n        elif self.auto_commit:\n            # does not apply when auto commit is on\n            state = False\n        self.Information.modified(shown=state)\n\n    def _on_auto_commit_changed(self):\n        if self.auto_commit and self.__invalidated:\n            self.commit()\n\n    def cancel(self):\n        \"\"\"Cancel any running jobs.\"\"\"\n        self.__cancel_task(wait=False)\n        self.__set_state_ready()\n\n    def commit(self):\n        self.__commit_timer.stop()\n        self.__invalidated = False\n        self._set_modified(False)\n\n        # Cancel current running task\n        self.__cancel_task(wait=False)\n\n        if self.data is None:\n            self.__set_state_ready()\n            return\n\n        self.Error.clear()\n\n        if self.partition is not None:\n            self.__set_state_ready()\n            self._send_data()\n            return\n\n        self._preprocess_data()\n\n        state = TaskState(self)\n\n        # Prepare/assemble the task(s) to run; reuse partial results\n        if self.apply_pca:\n            if self.pca_projection is not None:\n                data = self.pca_projection\n                pca_components = None\n            else:\n                data = self.preprocessed_data\n                pca_components = self.pca_components\n        else:\n            data = self.preprocessed_data\n            pca_components = None\n\n        if self.graph is not None:\n            # run on graph only; no need to do PCA and k-nn search ...\n            graph = self.graph\n            k_neighbors = metric = None\n        else:\n            k_neighbors, metric = self.k_neighbors, METRICS[self.metric_idx][1]\n            graph = None\n\n        if graph is None:\n            task = partial(\n                run_on_data, data, pca_components=pca_components,\n                normalize=self.normalize, k_neighbors=k_neighbors,\n                metric=metric, resolution=self.resolution, state=state,\n            )\n        else:\n            task = partial(\n                run_on_graph, graph, resolution=self.resolution, state=state\n            )\n\n        self.info_label.setText(\"Running...\")\n        self.__set_state_busy()\n        self.__start_task(task, state)\n\n    @Slot(object)\n    def __set_partial_results(self, result):\n        # type: (Tuple[str, Any]) -> None\n        which, res = result\n        if which == \"pca_projection\":\n            assert isinstance(res, Table) and len(res) == len(self.data)\n            self.pca_projection = res\n        elif which == \"graph\":\n            assert isinstance(res, nx.Graph)\n            self.graph = res\n        elif which == \"partition\":\n            assert isinstance(res, np.ndarray)\n            self.partition = res\n        else:\n            assert False, which\n\n    @Slot(object)\n    def __on_done(self, future):\n        # type: (Future[\"Results\"]) -> None\n        assert future.done()\n        assert self.__task is not None\n        assert self.__task.future is future\n        assert self.__task.watcher.future() is future\n        self.__task, task = None, self.__task\n        task.deleteLater()\n\n        self.__set_state_ready()\n\n        result = future.result()\n        self.__set_results(result)\n\n    @Slot(str)\n    def setStatusMessage(self, text):\n        super().setStatusMessage(text)\n\n    @Slot(float)\n    def progressBarSet(self, value, *a, **kw):\n        super().progressBarSet(value, *a, **kw)\n\n    def __set_state_ready(self):\n        self.progressBarFinished()\n        self.setInvalidated(False)\n        self.setStatusMessage(\"\")\n\n    def __set_state_busy(self):\n        self.progressBarInit()\n        self.setInvalidated(True)\n\n    def __start_task(self, task, state):\n        # type: (Callable[[], Any], TaskState) -> None\n        assert self.__task is None\n        state.status_changed.connect(self.setStatusMessage)\n        state.progress_changed.connect(self.progressBarSet)\n        state.partial_result_ready.connect(self.__set_partial_results)\n        state.watcher.done.connect(self.__on_done)\n        state.start(self.__executor, task)\n        state.setParent(self)\n        self.__task = state\n\n    def __cancel_task(self, wait=True):\n        # Cancel and dispose of the current task\n        if self.__task is not None:\n            state, self.__task = self.__task, None\n            state.cancel()\n            state.partial_result_ready.disconnect(self.__set_partial_results)\n            state.status_changed.disconnect(self.setStatusMessage)\n            state.progress_changed.disconnect(self.progressBarSet)\n            state.watcher.done.disconnect(self.__on_done)\n            if wait:\n                futures.wait([state.future])\n                state.deleteLater()\n            else:\n                w = FutureWatcher(state.future, parent=state)\n                w.done.connect(state.deleteLater)\n\n    def __set_results(self, results):\n        # type: (\"Results\") -> None\n        # NOTE: All of these have already been set by __set_partial_results,\n        # we double check that they are aliases\n        if results.pca_projection is not None:\n            assert self.pca_components == results.pca_components\n            assert self.pca_projection is results.pca_projection\n            self.pca_projection = results.pca_projection\n        if results.graph is not None:\n            assert results.metric == METRICS[self.metric_idx][1]\n            assert results.k_neighbors == self.k_neighbors\n            assert self.graph is results.graph\n            self.graph = results.graph\n        if results.partition is not None:\n            assert results.resolution == self.resolution\n            assert self.partition is results.partition\n            self.partition = results.partition\n\n        # Display the number of found clusters in the UI\n        num_clusters = len(np.unique(self.partition))\n        self.info_label.setText(\"%d clusters found.\" % num_clusters)\n\n        self._send_data()\n\n    def _send_data(self):\n        if self.partition is None or self.data is None:\n            return\n        domain = self.data.domain\n        # Compute the frequency of each cluster index\n        counts = np.bincount(self.partition)\n        indices = np.argsort(counts)[::-1]\n        index_map = {n: o for n, o in zip(indices, range(len(indices)))}\n        new_partition = list(map(index_map.get, self.partition))\n\n        cluster_var = DiscreteVariable(\n            get_unique_names(domain, \"Cluster\"),\n            values=[\"C%d\" % (i + 1) for i, _ in enumerate(np.unique(new_partition))]\n        )\n\n        new_domain = add_columns(domain, metas=[cluster_var])\n        new_table = self.data.transform(new_domain)\n        new_table.get_column_view(cluster_var)[0][:] = new_partition\n        self.Outputs.annotated_data.send(new_table)\n\n        if Network is not None:\n            n_edges = self.graph.number_of_edges()\n            edges = sp.coo_matrix(\n                (np.ones(n_edges), np.array(self.graph.edges()).T),\n                shape=(n_edges, n_edges))\n            graph = Network(new_table, edges)\n            self.Outputs.graph.send(graph)\n\n    @Inputs.data\n    def set_data(self, data):\n        self.Error.clear()\n\n        prev_data, self.data = self.data, data\n        # Make sure to properly enable/disable slider based on `apply_pca` setting\n        self.controls.pca_components.setEnabled(self.apply_pca)\n\n        if prev_data and self.data and array_equal(prev_data.X, self.data.X):\n            if self.auto_commit and not self.isInvalidated():\n                self._send_data()\n            return\n\n        self.cancel()\n        # Clear the outputs\n        self.Outputs.annotated_data.send(None)\n        if Network is not None:\n            self.Outputs.graph.send(None)\n\n        # Clear internal state\n        self.clear()\n        self._invalidate_pca_projection()\n\n        # Make sure the dataset is ok\n        if self.data is not None and len(self.data.domain.attributes) < 1:\n            self.Error.empty_dataset()\n            self.data = None\n\n        if self.data is None:\n            return\n\n        # Can't have more PCA components than the number of attributes\n        n_attrs = len(data.domain.attributes)\n        self.pca_components_slider.setMaximum(min(_MAX_PCA_COMPONENTS, n_attrs))\n        # Can't have more k neighbors than there are data points\n        self.k_neighbors_spin.setMaximum(min(_MAX_K_NEIGBOURS, len(data) - 1))\n\n        self.info_label.setText(\"Clustering not yet run.\")\n\n        self.commit()\n\n    def clear(self):\n        self.__cancel_task(wait=False)\n        self.preprocessed_data = None\n        self.pca_projection = None\n        self.graph = None\n        self.partition = None\n        self.Error.clear()\n        self.Information.modified.clear()\n        self.info_label.setText(\"No data on input.\")\n\n    def onDeleteWidget(self):\n        self.__cancel_task(wait=True)\n        self.__executor.shutdown(True)\n        self.clear()\n        self.data = None\n        super().onDeleteWidget()\n\n    def send_report(self):\n        pca = report.bool_str(self.apply_pca)\n        if self.apply_pca:\n            pca += report.plural(\", {number} component{s}\", self.pca_components)\n\n        self.report_items((\n            (\"Normalize data\", report.bool_str(self.normalize)),\n            (\"PCA preprocessing\", pca),\n            (\"Metric\", METRICS[self.metric_idx][0]),\n            (\"k neighbors\", self.k_neighbors),\n            (\"Resolution\", self.resolution),\n        ))\n\n    @classmethod\n    def migrate_settings(cls, settings, version):\n        if version < 2 and \"context_settings\" in settings:\n            try:\n                current_context = settings[\"context_settings\"][0]\n                for n in ['apply_pca', 'k_neighbors', 'metric_idx',\n                          'normalize', 'pca_components', 'resolution']:\n                    if n in current_context.values:\n                        settings[n] = current_context.values[n][0]\n            except:  # pylint: disable=bare-except\n                pass\n            finally:\n                del settings[\"context_settings\"]",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 472,
                            "end_lineno": 483,
                            "parent": {
                                "file_name": "Orange/widgets/unsupervised/owlouvainclustering.py",
                                "type": "class",
                                "name": "OWLouvainClustering",
                                "content": "class OWLouvainClustering(widget.OWWidget):\n    name = \"Louvain Clustering\"\n    description = \"Detects communities in a network of nearest neighbors.\"\n    icon = \"icons/LouvainClustering.svg\"\n    priority = 2110\n\n    settings_version = 2\n\n    want_main_area = False\n    resizing_enabled = False\n\n    class Inputs:\n        data = Input(\"Data\", Table, default=True)\n\n    class Outputs:\n        annotated_data = Output(ANNOTATED_DATA_SIGNAL_NAME, Table, default=True)\n        if Network is not None:\n            graph = Output(\"Network\", Network)\n\n    apply_pca = Setting(True)\n    pca_components = Setting(_DEFAULT_PCA_COMPONENTS)\n    normalize = Setting(True)\n    metric_idx = Setting(0)\n    k_neighbors = Setting(_DEFAULT_K_NEIGHBORS)\n    resolution = Setting(1.)\n    auto_commit = Setting(False)\n\n    class Information(widget.OWWidget.Information):\n        modified = Msg(\"Press commit to recompute clusters and send new data\")\n\n    class Error(widget.OWWidget.Error):\n        empty_dataset = Msg(\"No features in data\")\n\n    def __init__(self):\n        super().__init__()\n\n        self.data = None  # type: Optional[Table]\n        self.preprocessed_data = None  # type: Optional[Table]\n        self.pca_projection = None  # type: Optional[Table]\n        self.graph = None  # type: Optional[nx.Graph]\n        self.partition = None  # type: Optional[np.array]\n        # Use a executor with a single worker, to limit CPU overcommitment for\n        # cancelled tasks. The method does not have a fine cancellation\n        # granularity so we assure that there are not N - 1 jobs executing\n        # for no reason only to be thrown away. It would be better to use the\n        # global pool but implement a limit on jobs from this source.\n        self.__executor = futures.ThreadPoolExecutor(max_workers=1)\n        self.__task = None   # type: Optional[TaskState]\n        self.__invalidated = False\n        # coalescing commit timer\n        self.__commit_timer = QTimer(self, singleShot=True)\n        self.__commit_timer.timeout.connect(self.commit)\n\n        # Set up UI\n        info_box = gui.vBox(self.controlArea, \"Info\")\n        self.info_label = gui.widgetLabel(info_box, \"No data on input.\")  # type: QLabel\n\n        preprocessing_box = gui.vBox(self.controlArea, \"Preprocessing\")\n        self.normalize_cbx = gui.checkBox(\n            preprocessing_box, self, \"normalize\", label=\"Normalize data\",\n            callback=self._invalidate_preprocessed_data,\n        )  # type: QCheckBox\n        self.apply_pca_cbx = gui.checkBox(\n            preprocessing_box, self, \"apply_pca\", label=\"Apply PCA preprocessing\",\n            callback=self._apply_pca_changed,\n        )  # type: QCheckBox\n        self.pca_components_slider = gui.hSlider(\n            preprocessing_box, self, \"pca_components\", label=\"PCA Components: \", minValue=2,\n            maxValue=_MAX_PCA_COMPONENTS,\n            callback=self._invalidate_pca_projection, tracking=False\n        )  # type: QSlider\n\n        graph_box = gui.vBox(self.controlArea, \"Graph parameters\")\n        self.metric_combo = gui.comboBox(\n            graph_box, self, \"metric_idx\", label=\"Distance metric\",\n            items=[m[0] for m in METRICS], callback=self._invalidate_graph,\n            orientation=Qt.Horizontal,\n        )  # type: gui.OrangeComboBox\n        self.k_neighbors_spin = gui.spin(\n            graph_box, self, \"k_neighbors\", minv=1, maxv=_MAX_K_NEIGBOURS,\n            label=\"k neighbors\", controlWidth=80, alignment=Qt.AlignRight,\n            callback=self._invalidate_graph,\n        )  # type: gui.SpinBoxWFocusOut\n        self.resolution_spin = gui.hSlider(\n            graph_box, self, \"resolution\", minValue=0, maxValue=5., step=1e-1,\n            label=\"Resolution\", intOnly=False, labelFormat=\"%.1f\",\n            callback=self._invalidate_partition, tracking=False,\n        )  # type: QSlider\n        self.resolution_spin.parent().setToolTip(\n            \"The resolution parameter affects the number of clusters to find. \"\n            \"Smaller values tend to produce more clusters and larger values \"\n            \"retrieve less clusters.\"\n        )\n        self.apply_button = gui.auto_apply(\n            self.controlArea, self, \"auto_commit\", box=None,\n            commit=lambda: self.commit(), callback=lambda: self._on_auto_commit_changed()\n        )  # type: QWidget\n\n    def _preprocess_data(self):\n        if self.preprocessed_data is None:\n            if self.normalize:\n                normalizer = preprocess.Normalize(center=False)\n                self.preprocessed_data = normalizer(self.data)\n            else:\n                self.preprocessed_data = self.data\n\n    def _apply_pca_changed(self):\n        self.controls.pca_components.setEnabled(self.apply_pca)\n        self._invalidate_graph()\n\n    def _invalidate_preprocessed_data(self):\n        self.preprocessed_data = None\n        self._invalidate_pca_projection()\n        # If we don't apply PCA, this still invalidates the graph, otherwise\n        # this change won't be propagated further\n        if not self.apply_pca:\n            self._invalidate_graph()\n\n    def _invalidate_pca_projection(self):\n        self.pca_projection = None\n        if not self.apply_pca:\n            return\n\n        self._invalidate_graph()\n        self._set_modified(True)\n\n    def _invalidate_graph(self):\n        self.graph = None\n        self._invalidate_partition()\n        self._set_modified(True)\n\n    def _invalidate_partition(self):\n        self.partition = None\n        self._invalidate_output()\n        self.Information.modified()\n        self._set_modified(True)\n\n    def _invalidate_output(self):\n        self.__invalidated = True\n        if self.__task is not None:\n            self.__cancel_task(wait=False)\n\n        if self.auto_commit:\n            self.__commit_timer.start()\n        else:\n            self.__set_state_ready()\n\n    def _set_modified(self, state):\n        \"\"\"\n        Mark the widget (GUI) as containing modified state.\n        \"\"\"\n        if self.data is None:\n            # does not apply when we have no data\n            state = False\n        elif self.auto_commit:\n            # does not apply when auto commit is on\n            state = False\n        self.Information.modified(shown=state)\n\n    def _on_auto_commit_changed(self):\n        if self.auto_commit and self.__invalidated:\n            self.commit()\n\n    def cancel(self):\n        \"\"\"Cancel any running jobs.\"\"\"\n        self.__cancel_task(wait=False)\n        self.__set_state_ready()\n\n    def commit(self):\n        self.__commit_timer.stop()\n        self.__invalidated = False\n        self._set_modified(False)\n\n        # Cancel current running task\n        self.__cancel_task(wait=False)\n\n        if self.data is None:\n            self.__set_state_ready()\n            return\n\n        self.Error.clear()\n\n        if self.partition is not None:\n            self.__set_state_ready()\n            self._send_data()\n            return\n\n        self._preprocess_data()\n\n        state = TaskState(self)\n\n        # Prepare/assemble the task(s) to run; reuse partial results\n        if self.apply_pca:\n            if self.pca_projection is not None:\n                data = self.pca_projection\n                pca_components = None\n            else:\n                data = self.preprocessed_data\n                pca_components = self.pca_components\n        else:\n            data = self.preprocessed_data\n            pca_components = None\n\n        if self.graph is not None:\n            # run on graph only; no need to do PCA and k-nn search ...\n            graph = self.graph\n            k_neighbors = metric = None\n        else:\n            k_neighbors, metric = self.k_neighbors, METRICS[self.metric_idx][1]\n            graph = None\n\n        if graph is None:\n            task = partial(\n                run_on_data, data, pca_components=pca_components,\n                normalize=self.normalize, k_neighbors=k_neighbors,\n                metric=metric, resolution=self.resolution, state=state,\n            )\n        else:\n            task = partial(\n                run_on_graph, graph, resolution=self.resolution, state=state\n            )\n\n        self.info_label.setText(\"Running...\")\n        self.__set_state_busy()\n        self.__start_task(task, state)\n\n    @Slot(object)\n    def __set_partial_results(self, result):\n        # type: (Tuple[str, Any]) -> None\n        which, res = result\n        if which == \"pca_projection\":\n            assert isinstance(res, Table) and len(res) == len(self.data)\n            self.pca_projection = res\n        elif which == \"graph\":\n            assert isinstance(res, nx.Graph)\n            self.graph = res\n        elif which == \"partition\":\n            assert isinstance(res, np.ndarray)\n            self.partition = res\n        else:\n            assert False, which\n\n    @Slot(object)\n    def __on_done(self, future):\n        # type: (Future[\"Results\"]) -> None\n        assert future.done()\n        assert self.__task is not None\n        assert self.__task.future is future\n        assert self.__task.watcher.future() is future\n        self.__task, task = None, self.__task\n        task.deleteLater()\n\n        self.__set_state_ready()\n\n        result = future.result()\n        self.__set_results(result)\n\n    @Slot(str)\n    def setStatusMessage(self, text):\n        super().setStatusMessage(text)\n\n    @Slot(float)\n    def progressBarSet(self, value, *a, **kw):\n        super().progressBarSet(value, *a, **kw)\n\n    def __set_state_ready(self):\n        self.progressBarFinished()\n        self.setInvalidated(False)\n        self.setStatusMessage(\"\")\n\n    def __set_state_busy(self):\n        self.progressBarInit()\n        self.setInvalidated(True)\n\n    def __start_task(self, task, state):\n        # type: (Callable[[], Any], TaskState) -> None\n        assert self.__task is None\n        state.status_changed.connect(self.setStatusMessage)\n        state.progress_changed.connect(self.progressBarSet)\n        state.partial_result_ready.connect(self.__set_partial_results)\n        state.watcher.done.connect(self.__on_done)\n        state.start(self.__executor, task)\n        state.setParent(self)\n        self.__task = state\n\n    def __cancel_task(self, wait=True):\n        # Cancel and dispose of the current task\n        if self.__task is not None:\n            state, self.__task = self.__task, None\n            state.cancel()\n            state.partial_result_ready.disconnect(self.__set_partial_results)\n            state.status_changed.disconnect(self.setStatusMessage)\n            state.progress_changed.disconnect(self.progressBarSet)\n            state.watcher.done.disconnect(self.__on_done)\n            if wait:\n                futures.wait([state.future])\n                state.deleteLater()\n            else:\n                w = FutureWatcher(state.future, parent=state)\n                w.done.connect(state.deleteLater)\n\n    def __set_results(self, results):\n        # type: (\"Results\") -> None\n        # NOTE: All of these have already been set by __set_partial_results,\n        # we double check that they are aliases\n        if results.pca_projection is not None:\n            assert self.pca_components == results.pca_components\n            assert self.pca_projection is results.pca_projection\n            self.pca_projection = results.pca_projection\n        if results.graph is not None:\n            assert results.metric == METRICS[self.metric_idx][1]\n            assert results.k_neighbors == self.k_neighbors\n            assert self.graph is results.graph\n            self.graph = results.graph\n        if results.partition is not None:\n            assert results.resolution == self.resolution\n            assert self.partition is results.partition\n            self.partition = results.partition\n\n        # Display the number of found clusters in the UI\n        num_clusters = len(np.unique(self.partition))\n        self.info_label.setText(\"%d clusters found.\" % num_clusters)\n\n        self._send_data()\n\n    def _send_data(self):\n        if self.partition is None or self.data is None:\n            return\n        domain = self.data.domain\n        # Compute the frequency of each cluster index\n        counts = np.bincount(self.partition)\n        indices = np.argsort(counts)[::-1]\n        index_map = {n: o for n, o in zip(indices, range(len(indices)))}\n        new_partition = list(map(index_map.get, self.partition))\n\n        cluster_var = DiscreteVariable(\n            get_unique_names(domain, \"Cluster\"),\n            values=[\"C%d\" % (i + 1) for i, _ in enumerate(np.unique(new_partition))]\n        )\n\n        new_domain = add_columns(domain, metas=[cluster_var])\n        new_table = self.data.transform(new_domain)\n        new_table.get_column_view(cluster_var)[0][:] = new_partition\n        self.Outputs.annotated_data.send(new_table)\n\n        if Network is not None:\n            n_edges = self.graph.number_of_edges()\n            edges = sp.coo_matrix(\n                (np.ones(n_edges), np.array(self.graph.edges()).T),\n                shape=(n_edges, n_edges))\n            graph = Network(new_table, edges)\n            self.Outputs.graph.send(graph)\n\n    @Inputs.data\n    def set_data(self, data):\n        self.Error.clear()\n\n        prev_data, self.data = self.data, data\n        # Make sure to properly enable/disable slider based on `apply_pca` setting\n        self.controls.pca_components.setEnabled(self.apply_pca)\n\n        if prev_data and self.data and array_equal(prev_data.X, self.data.X):\n            if self.auto_commit and not self.isInvalidated():\n                self._send_data()\n            return\n\n        self.cancel()\n        # Clear the outputs\n        self.Outputs.annotated_data.send(None)\n        if Network is not None:\n            self.Outputs.graph.send(None)\n\n        # Clear internal state\n        self.clear()\n        self._invalidate_pca_projection()\n\n        # Make sure the dataset is ok\n        if self.data is not None and len(self.data.domain.attributes) < 1:\n            self.Error.empty_dataset()\n            self.data = None\n\n        if self.data is None:\n            return\n\n        # Can't have more PCA components than the number of attributes\n        n_attrs = len(data.domain.attributes)\n        self.pca_components_slider.setMaximum(min(_MAX_PCA_COMPONENTS, n_attrs))\n        # Can't have more k neighbors than there are data points\n        self.k_neighbors_spin.setMaximum(min(_MAX_K_NEIGBOURS, len(data) - 1))\n\n        self.info_label.setText(\"Clustering not yet run.\")\n\n        self.commit()\n\n    def clear(self):\n        self.__cancel_task(wait=False)\n        self.preprocessed_data = None\n        self.pca_projection = None\n        self.graph = None\n        self.partition = None\n        self.Error.clear()\n        self.Information.modified.clear()\n        self.info_label.setText(\"No data on input.\")\n\n    def onDeleteWidget(self):\n        self.__cancel_task(wait=True)\n        self.__executor.shutdown(True)\n        self.clear()\n        self.data = None\n        super().onDeleteWidget()\n\n    def send_report(self):\n        pca = report.bool_str(self.apply_pca)\n        if self.apply_pca:\n            pca += report.plural(\", {number} component{s}\", self.pca_components)\n\n        self.report_items((\n            (\"Normalize data\", report.bool_str(self.normalize)),\n            (\"PCA preprocessing\", pca),\n            (\"Metric\", METRICS[self.metric_idx][0]),\n            (\"k neighbors\", self.k_neighbors),\n            (\"Resolution\", self.resolution),\n        ))\n\n    @classmethod\n    def migrate_settings(cls, settings, version):\n        if version < 2 and \"context_settings\" in settings:\n            try:\n                current_context = settings[\"context_settings\"][0]\n                for n in ['apply_pca', 'k_neighbors', 'metric_idx',\n                          'normalize', 'pca_components', 'resolution']:\n                    if n in current_context.values:\n                        settings[n] = current_context.values[n][0]\n            except:  # pylint: disable=bare-except\n                pass\n            finally:\n                del settings[\"context_settings\"]",
                                "ast_type_str": "ClassDef",
                                "start_lineno": 47,
                                "end_lineno": 483,
                                "parent": null
                            }
                        }
                    ],
                    "deleted_entities": []
                }
            ]
        },
        {
            "old_file_content": "from unittest.mock import patch\n\nimport numpy as np\n\nfrom Orange.data import Table, Domain, ContinuousVariable\nfrom Orange.preprocess import Normalize\nfrom Orange.widgets.tests.base import WidgetTest\nfrom Orange.widgets.tests.utils import table_dense_sparse\nfrom Orange.widgets.unsupervised.owlouvainclustering import OWLouvainClustering\nfrom sklearn.utils import check_random_state\n\n# Deterministic tests\nnp.random.seed(42)\n\n\nclass TestOWLouvain(WidgetTest):\n    def setUp(self):\n        self.widget = self.create_widget(\n            OWLouvainClustering, stored_settings={'auto_commit': False}\n        )\n        self.iris = Table('iris')[::5]\n\n    def tearDown(self):\n        self.widget.onDeleteWidget()\n        super().tearDown()\n\n    def commit_and_wait(self, widget=None):\n        widget = self.widget if widget is None else widget\n        widget.commit()\n        self.wait_until_stop_blocking(widget)\n\n    def test_removing_data(self):\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        self.commit_and_wait(self.widget)\n        self.send_signal(self.widget.Inputs.data, None)\n        self.commit_and_wait(self.widget)\n\n    def test_clusters_ordered_by_size(self):\n        \"\"\"Cluster names should be sorted based on the number of instances.\"\"\"\n        x1 = np.array([[0, 0]] * 20)\n        x2 = np.array([[1, 0]] * 15)\n        x3 = np.array([[0, 1]] * 10)\n        x4 = np.array([[1, 1]] * 5)\n        data = np.vstack((x1, x2, x3, x4))\n        # Remove any order depencence in data, not that this should affect it\n        np.random.shuffle(data)\n\n        table = Table.from_numpy(domain=Domain.from_numpy(X=data), X=data)\n\n        self.send_signal(self.widget.Inputs.data, table)\n        self.widget.k_neighbors = 4\n        self.commit_and_wait()\n        output = self.get_output(self.widget.Outputs.annotated_data)\n\n        clustering = output.get_column_view('Cluster')[0].astype(int)\n        counts = np.bincount(clustering)\n        np.testing.assert_equal(counts, sorted(counts, reverse=True))\n\n    def test_empty_dataset(self):\n        # Prepare a table with 5 rows with only meta attributes\n        meta = np.array([0] * 5)\n        meta_var = ContinuousVariable(name='meta_var')\n        table = Table.from_domain(domain=Domain([], metas=[meta_var]), n_rows=5)\n        table.get_column_view(meta_var)[0][:] = meta\n\n        self.send_signal(self.widget.Inputs.data, table)\n        self.commit_and_wait()\n        self.assertTrue(self.widget.Error.empty_dataset.is_shown())\n\n    def test_do_not_recluster_on_same_data(self):\n        \"\"\"Do not recluster data points when targets or metas change.\"\"\"\n\n        # Prepare some dummy data\n        x = np.eye(5)\n        y1, y2 = np.ones((5, 1)), np.ones((5, 2))\n        meta1, meta2 = np.ones((5, 1)), np.ones((5, 2))\n\n        table1 = Table.from_numpy(\n            domain=Domain.from_numpy(X=x, Y=y1, metas=meta1),\n            X=x, Y=y1, metas=meta1,\n        )\n        # X is same, should not cause update\n        table2 = Table.from_numpy(\n            domain=Domain.from_numpy(X=x, Y=y2, metas=meta2),\n            X=x, Y=y2, metas=meta2,\n        )\n        # X is different, should cause update\n        table3 = table1.copy()\n        table3.X[:, 0] = 1\n\n        with patch.object(self.widget, '_invalidate_output') as commit:\n            self.send_signal(self.widget.Inputs.data, table1)\n            self.commit_and_wait()\n            call_count = commit.call_count\n\n            # Sending data with same X should not recompute the clustering\n            self.send_signal(self.widget.Inputs.data, table2)\n            self.commit_and_wait()\n            self.assertEqual(call_count, commit.call_count)\n\n            # Sending data with different X should recompute the clustering\n            self.send_signal(self.widget.Inputs.data, table3)\n            self.commit_and_wait()\n            self.assertEqual(call_count + 1, commit.call_count)\n\n    def test_only_recluster_when_necessary_pca_components_change(self):\n        # Compute clustering on some data\n        self.send_signal(self.widget.Inputs.data, self.iris)\n\n        # When PCA checkbox is ticked, any update to slider should invalidate results\n        self.widget.apply_pca_cbx.setChecked(True)\n        self.widget.pca_components_slider.setValue(2)\n        self.commit_and_wait()\n\n        with patch.object(self.widget, '_invalidate_output') as invalidate:\n            # Change slider value, this should invalidate output\n            self.widget.pca_components_slider.setValue(4)\n            self.commit_and_wait()\n            self.assertEqual(invalidate.call_count, 1)\n\n        with patch.object(self.widget, '_invalidate_output') as invalidate:\n            # Don't change slider value, this shouldn't do anything\n            self.widget.pca_components_slider.setValue(4)\n            self.commit_and_wait()\n            invalidate.assert_not_called()\n\n        # When PCA checkbox is not ticked updating the slider should have no effect\n        self.widget.apply_pca_cbx.setChecked(False)\n        self.widget.pca_components_slider.setValue(2)\n        self.commit_and_wait()\n\n        with patch.object(self.widget, '_invalidate_output') as invalidate:\n            # Change slider value, this should invalidate output\n            self.widget.pca_components_slider.setValue(4)\n            self.commit_and_wait()\n            invalidate.assert_not_called()\n\n    def test_invalidate(self):\n        # pylint: disable=protected-access\n        data = self.iris\n        self.send_signal(self.widget.Inputs.data, data)\n        self.widget.commit()\n        out = self.get_output(self.widget.Outputs.annotated_data)\n        self.assertIsNotNone(out)\n        # invalidate the partitioning\n        p = self.widget.partition\n        g = self.widget.graph\n        self.widget._invalidate_partition()\n        # not in auto commit mode.\n        self.assertTrue(self.widget.Information.modified.is_shown())\n        self.widget.commit()\n        out = self.get_output(self.widget.Outputs.annotated_data)\n        self.assertIsNotNone(out)\n        self.assertIs(self.widget.graph, g)\n        self.assertIsNot(self.widget.partition, p)\n\n        self.assertFalse(self.widget.Information.modified.is_shown())\n        self.widget._invalidate_graph()\n        self.assertIsNone(self.widget.graph)\n        self.assertIsNotNone(self.widget.pca_projection)\n        self.assertTrue(self.widget.Information.modified.is_shown())\n        self.widget._invalidate_pca_projection()\n        self.assertIsNone(self.widget.pca_projection)\n        self.widget.commit()\n        self.get_output(self.widget.Outputs.annotated_data)\n        self.assertFalse(self.widget.Information.modified.is_shown())\n\n    def test_deterministic_clustering(self):\n        # Compute clustering on iris\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        self.commit_and_wait()\n        result1 = self.get_output(self.widget.Outputs.annotated_data)\n\n        # Reset widget state\n        self.send_signal(self.widget.Inputs.data, None)\n\n        # Compute clustering on iris again\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        self.commit_and_wait()\n        result2 = self.get_output(self.widget.Outputs.annotated_data)\n\n        # Ensure that clustering was the same in both instances\n        np.testing.assert_equal(result1.metas, result2.metas)\n\n    @table_dense_sparse\n    def test_normalize_data(self, prepare_table):\n        \"\"\"Check that normalization is called at the proper times.\"\"\"\n        data = prepare_table(self.iris)\n\n        # Enable checkbox\n        self.widget.controls.normalize.setChecked(True)\n        self.assertTrue(self.widget.controls.normalize.isChecked())\n        with patch(\"Orange.preprocess.Normalize\", wraps=Normalize) as normalize:\n            self.send_signal(self.widget.Inputs.data, data)\n            self.wait_until_stop_blocking()\n            self.assertTrue(self.widget.controls.normalize.isEnabled())\n            normalize.assert_called_once()\n\n        # Disable checkbox\n        self.widget.controls.normalize.setChecked(False)\n        self.assertFalse(self.widget.controls.normalize.isChecked())\n        with patch(\"Orange.preprocess.Normalize\", wraps=Normalize) as normalize:\n            self.send_signal(self.widget.Inputs.data, data)\n            self.wait_until_stop_blocking()\n            self.assertTrue(self.widget.controls.normalize.isEnabled())\n            normalize.assert_not_called()\n\n    def test_dense_and_sparse_return_same_result(self):\n        \"\"\"Check that Louvain clustering returns identical results for both\n        dense and sparse data.\"\"\"\n        random_state = check_random_state(42)\n\n        # Randomly set some values to zero\n        dense_data = self.iris\n        mask = random_state.beta(1, 2, size=self.iris.X.shape) > 0.5\n        dense_data.X[mask] = 0\n        sparse_data = dense_data.to_sparse()\n\n        def _compute_clustering(data):\n            self.send_signal(self.widget.Inputs.data, data)\n            self.wait_until_stop_blocking()\n            result = self.get_output(self.widget.Outputs.annotated_data)\n            self.send_signal(self.widget.Inputs.data, None)\n            return result\n\n        # Disable normalization\n        self.widget.controls.normalize.setChecked(False)\n        dense_result = _compute_clustering(dense_data)\n        sparse_result = _compute_clustering(sparse_data)\n        np.testing.assert_equal(dense_result.metas, sparse_result.metas)\n\n        # Enable normalization\n        self.widget.controls.normalize.setChecked(True)\n        dense_result = _compute_clustering(dense_data)\n        sparse_result = _compute_clustering(sparse_data)\n        np.testing.assert_equal(dense_result.metas, sparse_result.metas)\n\n    def test_graph_output(self):\n        w = self.widget\n\n        # This test executes only if network add-on is installed\n        if not hasattr(w.Outputs, \"graph\"):\n            return\n\n        self.send_signal(w.Inputs.data, self.iris)\n        graph = self.get_output(w.Outputs.graph)\n        self.assertEqual(len(graph.nodes), len(self.iris))\n\n        self.send_signal(w.Inputs.data, None)\n        graph = self.get_output(w.Outputs.graph)\n        self.assertIsNone(graph)\n",
            "new_file_content": "from unittest.mock import patch\n\nimport numpy as np\nfrom sklearn.utils import check_random_state\n\nfrom orangewidget.settings import Context\n\nfrom Orange.data import Table, Domain, ContinuousVariable\nfrom Orange.preprocess import Normalize\nfrom Orange.widgets.tests.base import WidgetTest\nfrom Orange.widgets.tests.utils import table_dense_sparse\nfrom Orange.widgets.unsupervised.owlouvainclustering import OWLouvainClustering\n\n# Deterministic tests\nnp.random.seed(42)\n\n\nclass TestOWLouvain(WidgetTest):\n    def setUp(self):\n        self.widget = self.create_widget(\n            OWLouvainClustering, stored_settings={'auto_commit': False}\n        )\n        self.iris = Table('iris')[::5]\n\n    def tearDown(self):\n        self.widget.onDeleteWidget()\n        super().tearDown()\n\n    def commit_and_wait(self, widget=None):\n        widget = self.widget if widget is None else widget\n        widget.commit()\n        self.wait_until_stop_blocking(widget)\n\n    def test_removing_data(self):\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        self.commit_and_wait(self.widget)\n        self.send_signal(self.widget.Inputs.data, None)\n        self.commit_and_wait(self.widget)\n\n    def test_clusters_ordered_by_size(self):\n        \"\"\"Cluster names should be sorted based on the number of instances.\"\"\"\n        x1 = np.array([[0, 0]] * 20)\n        x2 = np.array([[1, 0]] * 15)\n        x3 = np.array([[0, 1]] * 10)\n        x4 = np.array([[1, 1]] * 5)\n        data = np.vstack((x1, x2, x3, x4))\n        # Remove any order depencence in data, not that this should affect it\n        np.random.shuffle(data)\n\n        table = Table.from_numpy(domain=Domain.from_numpy(X=data), X=data)\n\n        self.send_signal(self.widget.Inputs.data, table)\n        self.widget.k_neighbors = 4\n        self.commit_and_wait()\n        output = self.get_output(self.widget.Outputs.annotated_data)\n\n        clustering = output.get_column_view('Cluster')[0].astype(int)\n        counts = np.bincount(clustering)\n        np.testing.assert_equal(counts, sorted(counts, reverse=True))\n\n    def test_empty_dataset(self):\n        # Prepare a table with 5 rows with only meta attributes\n        meta = np.array([0] * 5)\n        meta_var = ContinuousVariable(name='meta_var')\n        table = Table.from_domain(domain=Domain([], metas=[meta_var]), n_rows=5)\n        table.get_column_view(meta_var)[0][:] = meta\n\n        self.send_signal(self.widget.Inputs.data, table)\n        self.commit_and_wait()\n        self.assertTrue(self.widget.Error.empty_dataset.is_shown())\n\n    def test_do_not_recluster_on_same_data(self):\n        \"\"\"Do not recluster data points when targets or metas change.\"\"\"\n\n        # Prepare some dummy data\n        x = np.eye(5)\n        y1, y2 = np.ones((5, 1)), np.ones((5, 2))\n        meta1, meta2 = np.ones((5, 1)), np.ones((5, 2))\n\n        table1 = Table.from_numpy(\n            domain=Domain.from_numpy(X=x, Y=y1, metas=meta1),\n            X=x, Y=y1, metas=meta1,\n        )\n        # X is same, should not cause update\n        table2 = Table.from_numpy(\n            domain=Domain.from_numpy(X=x, Y=y2, metas=meta2),\n            X=x, Y=y2, metas=meta2,\n        )\n        # X is different, should cause update\n        table3 = table1.copy()\n        table3.X[:, 0] = 1\n\n        with patch.object(self.widget, '_invalidate_output') as commit:\n            self.send_signal(self.widget.Inputs.data, table1)\n            self.commit_and_wait()\n            call_count = commit.call_count\n\n            # Sending data with same X should not recompute the clustering\n            self.send_signal(self.widget.Inputs.data, table2)\n            self.commit_and_wait()\n            self.assertEqual(call_count, commit.call_count)\n\n            # Sending data with different X should recompute the clustering\n            self.send_signal(self.widget.Inputs.data, table3)\n            self.commit_and_wait()\n            self.assertEqual(call_count + 1, commit.call_count)\n\n    def test_only_recluster_when_necessary_pca_components_change(self):\n        # Compute clustering on some data\n        self.send_signal(self.widget.Inputs.data, self.iris)\n\n        # When PCA checkbox is ticked, any update to slider should invalidate results\n        self.widget.apply_pca_cbx.setChecked(True)\n        self.widget.pca_components_slider.setValue(2)\n        self.commit_and_wait()\n\n        with patch.object(self.widget, '_invalidate_output') as invalidate:\n            # Change slider value, this should invalidate output\n            self.widget.pca_components_slider.setValue(4)\n            self.commit_and_wait()\n            self.assertEqual(invalidate.call_count, 1)\n\n        with patch.object(self.widget, '_invalidate_output') as invalidate:\n            # Don't change slider value, this shouldn't do anything\n            self.widget.pca_components_slider.setValue(4)\n            self.commit_and_wait()\n            invalidate.assert_not_called()\n\n        # When PCA checkbox is not ticked updating the slider should have no effect\n        self.widget.apply_pca_cbx.setChecked(False)\n        self.widget.pca_components_slider.setValue(2)\n        self.commit_and_wait()\n\n        with patch.object(self.widget, '_invalidate_output') as invalidate:\n            # Change slider value, this should invalidate output\n            self.widget.pca_components_slider.setValue(4)\n            self.commit_and_wait()\n            invalidate.assert_not_called()\n\n    def test_invalidate(self):\n        # pylint: disable=protected-access\n        data = self.iris\n        self.send_signal(self.widget.Inputs.data, data)\n        self.widget.commit()\n        out = self.get_output(self.widget.Outputs.annotated_data)\n        self.assertIsNotNone(out)\n        # invalidate the partitioning\n        p = self.widget.partition\n        g = self.widget.graph\n        self.widget._invalidate_partition()\n        # not in auto commit mode.\n        self.assertTrue(self.widget.Information.modified.is_shown())\n        self.widget.commit()\n        out = self.get_output(self.widget.Outputs.annotated_data)\n        self.assertIsNotNone(out)\n        self.assertIs(self.widget.graph, g)\n        self.assertIsNot(self.widget.partition, p)\n\n        self.assertFalse(self.widget.Information.modified.is_shown())\n        self.widget._invalidate_graph()\n        self.assertIsNone(self.widget.graph)\n        self.assertIsNotNone(self.widget.pca_projection)\n        self.assertTrue(self.widget.Information.modified.is_shown())\n        self.widget._invalidate_pca_projection()\n        self.assertIsNone(self.widget.pca_projection)\n        self.widget.commit()\n        self.get_output(self.widget.Outputs.annotated_data)\n        self.assertFalse(self.widget.Information.modified.is_shown())\n\n    def test_deterministic_clustering(self):\n        # Compute clustering on iris\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        self.commit_and_wait()\n        result1 = self.get_output(self.widget.Outputs.annotated_data)\n\n        # Reset widget state\n        self.send_signal(self.widget.Inputs.data, None)\n\n        # Compute clustering on iris again\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        self.commit_and_wait()\n        result2 = self.get_output(self.widget.Outputs.annotated_data)\n\n        # Ensure that clustering was the same in both instances\n        np.testing.assert_equal(result1.metas, result2.metas)\n\n    @table_dense_sparse\n    def test_normalize_data(self, prepare_table):\n        \"\"\"Check that normalization is called at the proper times.\"\"\"\n        data = prepare_table(self.iris)\n\n        # Enable checkbox\n        self.widget.controls.normalize.setChecked(True)\n        self.assertTrue(self.widget.controls.normalize.isChecked())\n        with patch(\"Orange.preprocess.Normalize\", wraps=Normalize) as normalize:\n            self.send_signal(self.widget.Inputs.data, data)\n            self.wait_until_stop_blocking()\n            self.assertTrue(self.widget.controls.normalize.isEnabled())\n            normalize.assert_called_once()\n\n        # Disable checkbox\n        self.widget.controls.normalize.setChecked(False)\n        self.assertFalse(self.widget.controls.normalize.isChecked())\n        with patch(\"Orange.preprocess.Normalize\", wraps=Normalize) as normalize:\n            self.send_signal(self.widget.Inputs.data, data)\n            self.wait_until_stop_blocking()\n            self.assertTrue(self.widget.controls.normalize.isEnabled())\n            normalize.assert_not_called()\n\n    def test_dense_and_sparse_return_same_result(self):\n        \"\"\"Check that Louvain clustering returns identical results for both\n        dense and sparse data.\"\"\"\n        random_state = check_random_state(42)\n\n        # Randomly set some values to zero\n        dense_data = self.iris\n        mask = random_state.beta(1, 2, size=self.iris.X.shape) > 0.5\n        dense_data.X[mask] = 0\n        sparse_data = dense_data.to_sparse()\n\n        def _compute_clustering(data):\n            self.send_signal(self.widget.Inputs.data, data)\n            self.wait_until_stop_blocking()\n            result = self.get_output(self.widget.Outputs.annotated_data)\n            self.send_signal(self.widget.Inputs.data, None)\n            return result\n\n        # Disable normalization\n        self.widget.controls.normalize.setChecked(False)\n        dense_result = _compute_clustering(dense_data)\n        sparse_result = _compute_clustering(sparse_data)\n        np.testing.assert_equal(dense_result.metas, sparse_result.metas)\n\n        # Enable normalization\n        self.widget.controls.normalize.setChecked(True)\n        dense_result = _compute_clustering(dense_data)\n        sparse_result = _compute_clustering(sparse_data)\n        np.testing.assert_equal(dense_result.metas, sparse_result.metas)\n\n    def test_graph_output(self):\n        w = self.widget\n\n        # This test executes only if network add-on is installed\n        if not hasattr(w.Outputs, \"graph\"):\n            return\n\n        self.send_signal(w.Inputs.data, self.iris)\n        graph = self.get_output(w.Outputs.graph)\n        self.assertEqual(len(graph.nodes), len(self.iris))\n\n        self.send_signal(w.Inputs.data, None)\n        graph = self.get_output(w.Outputs.graph)\n        self.assertIsNone(graph)\n\n    def test_migrate_settings(self):\n        # any context settings are removed\n        settings = {\"context_settings\": []}\n        self.widget.migrate_settings(settings, 1)\n        self.assertEqual(len(settings), 0)\n\n        # context settings become ordinary settings\n        settings = {\"context_settings\": [Context(values={'__version__': 1,\n                                                         'apply_pca': (True, -2),\n                                                         'k_neighbors': (29, -2),\n                                                         'metric_idx': (1, -2),\n                                                         'normalize': (False, -2),\n                                                         'pca_components': (10, -2),\n                                                         'resolution': (1.0, -2)})]}\n        self.widget.migrate_settings(settings, 1)\n        correct = {'apply_pca': True, 'k_neighbors': 29, 'metric_idx': 1,\n                   'normalize': False, 'pca_components': 10, 'resolution': 1.0}\n        self.assertEqual(sorted(settings.items()), sorted(correct.items()))\n",
            "header": {
                "file": {
                    "path": "Orange/widgets/unsupervised/tests/test_owlouvain.py"
                },
                "misc_line": null
            },
            "index_line": {
                "old_commit_hash": "397e1f597",
                "new_commit_hash": "44e82b959",
                "mode": "100644"
            },
            "is_binary_file": false,
            "binary_line": null,
            "minus_file": {
                "path": "a/Orange/widgets/unsupervised/tests/test_owlouvain.py"
            },
            "plus_file": {
                "path": "b/Orange/widgets/unsupervised/tests/test_owlouvain.py"
            },
            "hunks": [
                {
                    "descriptor": {
                        "old_range": {
                            "start": 1,
                            "length": 13
                        },
                        "new_range": {
                            "start": 1,
                            "length": 15
                        },
                        "section": ""
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "from unittest.mock import patch",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "import numpy as np",
                                "type": "context"
                            },
                            {
                                "content": "from sklearn.utils import check_random_state",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "from orangewidget.settings import Context",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "from Orange.data import Table, Domain, ContinuousVariable",
                                "type": "context"
                            },
                            {
                                "content": "from Orange.preprocess import Normalize",
                                "type": "context"
                            },
                            {
                                "content": "from Orange.widgets.tests.base import WidgetTest",
                                "type": "context"
                            },
                            {
                                "content": "from Orange.widgets.tests.utils import table_dense_sparse",
                                "type": "context"
                            },
                            {
                                "content": "from Orange.widgets.unsupervised.owlouvainclustering import OWLouvainClustering",
                                "type": "context"
                            },
                            {
                                "content": "from sklearn.utils import check_random_state",
                                "type": "deleted"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "# Deterministic tests",
                                "type": "context"
                            },
                            {
                                "content": "np.random.seed(42)",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [
                        {
                            "file_name": "Orange/widgets/unsupervised/tests/test_owlouvain.py",
                            "type": "statement",
                            "name": "",
                            "content": "from sklearn.utils import check_random_state",
                            "ast_type_str": "ImportFrom",
                            "start_lineno": 10,
                            "end_lineno": 10,
                            "parent": null
                        }
                    ],
                    "added_entities": [
                        {
                            "file_name": "Orange/widgets/unsupervised/tests/test_owlouvain.py",
                            "type": "statement",
                            "name": "",
                            "content": "from orangewidget.settings import Context",
                            "ast_type_str": "ImportFrom",
                            "start_lineno": 6,
                            "end_lineno": 6,
                            "parent": null
                        }
                    ],
                    "deleted_entities": []
                },
                {
                    "descriptor": {
                        "old_range": {
                            "start": 249,
                            "length": 3
                        },
                        "new_range": {
                            "start": 251,
                            "length": 22
                        },
                        "section": "class TestOWLouvain(WidgetTest):"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "        self.send_signal(w.Inputs.data, None)",
                                "type": "context"
                            },
                            {
                                "content": "        graph = self.get_output(w.Outputs.graph)",
                                "type": "context"
                            },
                            {
                                "content": "        self.assertIsNone(graph)",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "    def test_migrate_settings(self):",
                                "type": "added"
                            },
                            {
                                "content": "        # any context settings are removed",
                                "type": "added"
                            },
                            {
                                "content": "        settings = {\"context_settings\": []}",
                                "type": "added"
                            },
                            {
                                "content": "        self.widget.migrate_settings(settings, 1)",
                                "type": "added"
                            },
                            {
                                "content": "        self.assertEqual(len(settings), 0)",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "        # context settings become ordinary settings",
                                "type": "added"
                            },
                            {
                                "content": "        settings = {\"context_settings\": [Context(values={'__version__': 1,",
                                "type": "added"
                            },
                            {
                                "content": "                                                         'apply_pca': (True, -2),",
                                "type": "added"
                            },
                            {
                                "content": "                                                         'k_neighbors': (29, -2),",
                                "type": "added"
                            },
                            {
                                "content": "                                                         'metric_idx': (1, -2),",
                                "type": "added"
                            },
                            {
                                "content": "                                                         'normalize': (False, -2),",
                                "type": "added"
                            },
                            {
                                "content": "                                                         'pca_components': (10, -2),",
                                "type": "added"
                            },
                            {
                                "content": "                                                         'resolution': (1.0, -2)})]}",
                                "type": "added"
                            },
                            {
                                "content": "        self.widget.migrate_settings(settings, 1)",
                                "type": "added"
                            },
                            {
                                "content": "        correct = {'apply_pca': True, 'k_neighbors': 29, 'metric_idx': 1,",
                                "type": "added"
                            },
                            {
                                "content": "                   'normalize': False, 'pca_components': 10, 'resolution': 1.0}",
                                "type": "added"
                            },
                            {
                                "content": "        self.assertEqual(sorted(settings.items()), sorted(correct.items()))",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [
                        {
                            "file_name": "Orange/widgets/unsupervised/tests/test_owlouvain.py",
                            "type": "class",
                            "name": "TestOWLouvain",
                            "content": "class TestOWLouvain(WidgetTest):\n    def setUp(self):\n        self.widget = self.create_widget(\n            OWLouvainClustering, stored_settings={'auto_commit': False}\n        )\n        self.iris = Table('iris')[::5]\n\n    def tearDown(self):\n        self.widget.onDeleteWidget()\n        super().tearDown()\n\n    def commit_and_wait(self, widget=None):\n        widget = self.widget if widget is None else widget\n        widget.commit()\n        self.wait_until_stop_blocking(widget)\n\n    def test_removing_data(self):\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        self.commit_and_wait(self.widget)\n        self.send_signal(self.widget.Inputs.data, None)\n        self.commit_and_wait(self.widget)\n\n    def test_clusters_ordered_by_size(self):\n        \"\"\"Cluster names should be sorted based on the number of instances.\"\"\"\n        x1 = np.array([[0, 0]] * 20)\n        x2 = np.array([[1, 0]] * 15)\n        x3 = np.array([[0, 1]] * 10)\n        x4 = np.array([[1, 1]] * 5)\n        data = np.vstack((x1, x2, x3, x4))\n        # Remove any order depencence in data, not that this should affect it\n        np.random.shuffle(data)\n\n        table = Table.from_numpy(domain=Domain.from_numpy(X=data), X=data)\n\n        self.send_signal(self.widget.Inputs.data, table)\n        self.widget.k_neighbors = 4\n        self.commit_and_wait()\n        output = self.get_output(self.widget.Outputs.annotated_data)\n\n        clustering = output.get_column_view('Cluster')[0].astype(int)\n        counts = np.bincount(clustering)\n        np.testing.assert_equal(counts, sorted(counts, reverse=True))\n\n    def test_empty_dataset(self):\n        # Prepare a table with 5 rows with only meta attributes\n        meta = np.array([0] * 5)\n        meta_var = ContinuousVariable(name='meta_var')\n        table = Table.from_domain(domain=Domain([], metas=[meta_var]), n_rows=5)\n        table.get_column_view(meta_var)[0][:] = meta\n\n        self.send_signal(self.widget.Inputs.data, table)\n        self.commit_and_wait()\n        self.assertTrue(self.widget.Error.empty_dataset.is_shown())\n\n    def test_do_not_recluster_on_same_data(self):\n        \"\"\"Do not recluster data points when targets or metas change.\"\"\"\n\n        # Prepare some dummy data\n        x = np.eye(5)\n        y1, y2 = np.ones((5, 1)), np.ones((5, 2))\n        meta1, meta2 = np.ones((5, 1)), np.ones((5, 2))\n\n        table1 = Table.from_numpy(\n            domain=Domain.from_numpy(X=x, Y=y1, metas=meta1),\n            X=x, Y=y1, metas=meta1,\n        )\n        # X is same, should not cause update\n        table2 = Table.from_numpy(\n            domain=Domain.from_numpy(X=x, Y=y2, metas=meta2),\n            X=x, Y=y2, metas=meta2,\n        )\n        # X is different, should cause update\n        table3 = table1.copy()\n        table3.X[:, 0] = 1\n\n        with patch.object(self.widget, '_invalidate_output') as commit:\n            self.send_signal(self.widget.Inputs.data, table1)\n            self.commit_and_wait()\n            call_count = commit.call_count\n\n            # Sending data with same X should not recompute the clustering\n            self.send_signal(self.widget.Inputs.data, table2)\n            self.commit_and_wait()\n            self.assertEqual(call_count, commit.call_count)\n\n            # Sending data with different X should recompute the clustering\n            self.send_signal(self.widget.Inputs.data, table3)\n            self.commit_and_wait()\n            self.assertEqual(call_count + 1, commit.call_count)\n\n    def test_only_recluster_when_necessary_pca_components_change(self):\n        # Compute clustering on some data\n        self.send_signal(self.widget.Inputs.data, self.iris)\n\n        # When PCA checkbox is ticked, any update to slider should invalidate results\n        self.widget.apply_pca_cbx.setChecked(True)\n        self.widget.pca_components_slider.setValue(2)\n        self.commit_and_wait()\n\n        with patch.object(self.widget, '_invalidate_output') as invalidate:\n            # Change slider value, this should invalidate output\n            self.widget.pca_components_slider.setValue(4)\n            self.commit_and_wait()\n            self.assertEqual(invalidate.call_count, 1)\n\n        with patch.object(self.widget, '_invalidate_output') as invalidate:\n            # Don't change slider value, this shouldn't do anything\n            self.widget.pca_components_slider.setValue(4)\n            self.commit_and_wait()\n            invalidate.assert_not_called()\n\n        # When PCA checkbox is not ticked updating the slider should have no effect\n        self.widget.apply_pca_cbx.setChecked(False)\n        self.widget.pca_components_slider.setValue(2)\n        self.commit_and_wait()\n\n        with patch.object(self.widget, '_invalidate_output') as invalidate:\n            # Change slider value, this should invalidate output\n            self.widget.pca_components_slider.setValue(4)\n            self.commit_and_wait()\n            invalidate.assert_not_called()\n\n    def test_invalidate(self):\n        # pylint: disable=protected-access\n        data = self.iris\n        self.send_signal(self.widget.Inputs.data, data)\n        self.widget.commit()\n        out = self.get_output(self.widget.Outputs.annotated_data)\n        self.assertIsNotNone(out)\n        # invalidate the partitioning\n        p = self.widget.partition\n        g = self.widget.graph\n        self.widget._invalidate_partition()\n        # not in auto commit mode.\n        self.assertTrue(self.widget.Information.modified.is_shown())\n        self.widget.commit()\n        out = self.get_output(self.widget.Outputs.annotated_data)\n        self.assertIsNotNone(out)\n        self.assertIs(self.widget.graph, g)\n        self.assertIsNot(self.widget.partition, p)\n\n        self.assertFalse(self.widget.Information.modified.is_shown())\n        self.widget._invalidate_graph()\n        self.assertIsNone(self.widget.graph)\n        self.assertIsNotNone(self.widget.pca_projection)\n        self.assertTrue(self.widget.Information.modified.is_shown())\n        self.widget._invalidate_pca_projection()\n        self.assertIsNone(self.widget.pca_projection)\n        self.widget.commit()\n        self.get_output(self.widget.Outputs.annotated_data)\n        self.assertFalse(self.widget.Information.modified.is_shown())\n\n    def test_deterministic_clustering(self):\n        # Compute clustering on iris\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        self.commit_and_wait()\n        result1 = self.get_output(self.widget.Outputs.annotated_data)\n\n        # Reset widget state\n        self.send_signal(self.widget.Inputs.data, None)\n\n        # Compute clustering on iris again\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        self.commit_and_wait()\n        result2 = self.get_output(self.widget.Outputs.annotated_data)\n\n        # Ensure that clustering was the same in both instances\n        np.testing.assert_equal(result1.metas, result2.metas)\n\n    @table_dense_sparse\n    def test_normalize_data(self, prepare_table):\n        \"\"\"Check that normalization is called at the proper times.\"\"\"\n        data = prepare_table(self.iris)\n\n        # Enable checkbox\n        self.widget.controls.normalize.setChecked(True)\n        self.assertTrue(self.widget.controls.normalize.isChecked())\n        with patch(\"Orange.preprocess.Normalize\", wraps=Normalize) as normalize:\n            self.send_signal(self.widget.Inputs.data, data)\n            self.wait_until_stop_blocking()\n            self.assertTrue(self.widget.controls.normalize.isEnabled())\n            normalize.assert_called_once()\n\n        # Disable checkbox\n        self.widget.controls.normalize.setChecked(False)\n        self.assertFalse(self.widget.controls.normalize.isChecked())\n        with patch(\"Orange.preprocess.Normalize\", wraps=Normalize) as normalize:\n            self.send_signal(self.widget.Inputs.data, data)\n            self.wait_until_stop_blocking()\n            self.assertTrue(self.widget.controls.normalize.isEnabled())\n            normalize.assert_not_called()\n\n    def test_dense_and_sparse_return_same_result(self):\n        \"\"\"Check that Louvain clustering returns identical results for both\n        dense and sparse data.\"\"\"\n        random_state = check_random_state(42)\n\n        # Randomly set some values to zero\n        dense_data = self.iris\n        mask = random_state.beta(1, 2, size=self.iris.X.shape) > 0.5\n        dense_data.X[mask] = 0\n        sparse_data = dense_data.to_sparse()\n\n        def _compute_clustering(data):\n            self.send_signal(self.widget.Inputs.data, data)\n            self.wait_until_stop_blocking()\n            result = self.get_output(self.widget.Outputs.annotated_data)\n            self.send_signal(self.widget.Inputs.data, None)\n            return result\n\n        # Disable normalization\n        self.widget.controls.normalize.setChecked(False)\n        dense_result = _compute_clustering(dense_data)\n        sparse_result = _compute_clustering(sparse_data)\n        np.testing.assert_equal(dense_result.metas, sparse_result.metas)\n\n        # Enable normalization\n        self.widget.controls.normalize.setChecked(True)\n        dense_result = _compute_clustering(dense_data)\n        sparse_result = _compute_clustering(sparse_data)\n        np.testing.assert_equal(dense_result.metas, sparse_result.metas)\n\n    def test_graph_output(self):\n        w = self.widget\n\n        # This test executes only if network add-on is installed\n        if not hasattr(w.Outputs, \"graph\"):\n            return\n\n        self.send_signal(w.Inputs.data, self.iris)\n        graph = self.get_output(w.Outputs.graph)\n        self.assertEqual(len(graph.nodes), len(self.iris))\n\n        self.send_signal(w.Inputs.data, None)\n        graph = self.get_output(w.Outputs.graph)\n        self.assertIsNone(graph)\n\n    def test_migrate_settings(self):\n        # any context settings are removed\n        settings = {\"context_settings\": []}\n        self.widget.migrate_settings(settings, 1)\n        self.assertEqual(len(settings), 0)\n\n        # context settings become ordinary settings\n        settings = {\"context_settings\": [Context(values={'__version__': 1,\n                                                         'apply_pca': (True, -2),\n                                                         'k_neighbors': (29, -2),\n                                                         'metric_idx': (1, -2),\n                                                         'normalize': (False, -2),\n                                                         'pca_components': (10, -2),\n                                                         'resolution': (1.0, -2)})]}\n        self.widget.migrate_settings(settings, 1)\n        correct = {'apply_pca': True, 'k_neighbors': 29, 'metric_idx': 1,\n                   'normalize': False, 'pca_components': 10, 'resolution': 1.0}\n        self.assertEqual(sorted(settings.items()), sorted(correct.items()))",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 18,
                            "end_lineno": 272,
                            "parent": null
                        }
                    ],
                    "added_entities": [
                        {
                            "file_name": "Orange/widgets/unsupervised/tests/test_owlouvain.py",
                            "type": "method",
                            "name": "TestOWLouvain.test_migrate_settings",
                            "content": "class TestOWLouvain(WidgetTest):\n    def setUp(self):\n        self.widget = self.create_widget(\n            OWLouvainClustering, stored_settings={'auto_commit': False}\n        )\n        self.iris = Table('iris')[::5]\n\n    def tearDown(self):\n        self.widget.onDeleteWidget()\n        super().tearDown()\n\n    def commit_and_wait(self, widget=None):\n        widget = self.widget if widget is None else widget\n        widget.commit()\n        self.wait_until_stop_blocking(widget)\n\n    def test_removing_data(self):\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        self.commit_and_wait(self.widget)\n        self.send_signal(self.widget.Inputs.data, None)\n        self.commit_and_wait(self.widget)\n\n    def test_clusters_ordered_by_size(self):\n        \"\"\"Cluster names should be sorted based on the number of instances.\"\"\"\n        x1 = np.array([[0, 0]] * 20)\n        x2 = np.array([[1, 0]] * 15)\n        x3 = np.array([[0, 1]] * 10)\n        x4 = np.array([[1, 1]] * 5)\n        data = np.vstack((x1, x2, x3, x4))\n        # Remove any order depencence in data, not that this should affect it\n        np.random.shuffle(data)\n\n        table = Table.from_numpy(domain=Domain.from_numpy(X=data), X=data)\n\n        self.send_signal(self.widget.Inputs.data, table)\n        self.widget.k_neighbors = 4\n        self.commit_and_wait()\n        output = self.get_output(self.widget.Outputs.annotated_data)\n\n        clustering = output.get_column_view('Cluster')[0].astype(int)\n        counts = np.bincount(clustering)\n        np.testing.assert_equal(counts, sorted(counts, reverse=True))\n\n    def test_empty_dataset(self):\n        # Prepare a table with 5 rows with only meta attributes\n        meta = np.array([0] * 5)\n        meta_var = ContinuousVariable(name='meta_var')\n        table = Table.from_domain(domain=Domain([], metas=[meta_var]), n_rows=5)\n        table.get_column_view(meta_var)[0][:] = meta\n\n        self.send_signal(self.widget.Inputs.data, table)\n        self.commit_and_wait()\n        self.assertTrue(self.widget.Error.empty_dataset.is_shown())\n\n    def test_do_not_recluster_on_same_data(self):\n        \"\"\"Do not recluster data points when targets or metas change.\"\"\"\n\n        # Prepare some dummy data\n        x = np.eye(5)\n        y1, y2 = np.ones((5, 1)), np.ones((5, 2))\n        meta1, meta2 = np.ones((5, 1)), np.ones((5, 2))\n\n        table1 = Table.from_numpy(\n            domain=Domain.from_numpy(X=x, Y=y1, metas=meta1),\n            X=x, Y=y1, metas=meta1,\n        )\n        # X is same, should not cause update\n        table2 = Table.from_numpy(\n            domain=Domain.from_numpy(X=x, Y=y2, metas=meta2),\n            X=x, Y=y2, metas=meta2,\n        )\n        # X is different, should cause update\n        table3 = table1.copy()\n        table3.X[:, 0] = 1\n\n        with patch.object(self.widget, '_invalidate_output') as commit:\n            self.send_signal(self.widget.Inputs.data, table1)\n            self.commit_and_wait()\n            call_count = commit.call_count\n\n            # Sending data with same X should not recompute the clustering\n            self.send_signal(self.widget.Inputs.data, table2)\n            self.commit_and_wait()\n            self.assertEqual(call_count, commit.call_count)\n\n            # Sending data with different X should recompute the clustering\n            self.send_signal(self.widget.Inputs.data, table3)\n            self.commit_and_wait()\n            self.assertEqual(call_count + 1, commit.call_count)\n\n    def test_only_recluster_when_necessary_pca_components_change(self):\n        # Compute clustering on some data\n        self.send_signal(self.widget.Inputs.data, self.iris)\n\n        # When PCA checkbox is ticked, any update to slider should invalidate results\n        self.widget.apply_pca_cbx.setChecked(True)\n        self.widget.pca_components_slider.setValue(2)\n        self.commit_and_wait()\n\n        with patch.object(self.widget, '_invalidate_output') as invalidate:\n            # Change slider value, this should invalidate output\n            self.widget.pca_components_slider.setValue(4)\n            self.commit_and_wait()\n            self.assertEqual(invalidate.call_count, 1)\n\n        with patch.object(self.widget, '_invalidate_output') as invalidate:\n            # Don't change slider value, this shouldn't do anything\n            self.widget.pca_components_slider.setValue(4)\n            self.commit_and_wait()\n            invalidate.assert_not_called()\n\n        # When PCA checkbox is not ticked updating the slider should have no effect\n        self.widget.apply_pca_cbx.setChecked(False)\n        self.widget.pca_components_slider.setValue(2)\n        self.commit_and_wait()\n\n        with patch.object(self.widget, '_invalidate_output') as invalidate:\n            # Change slider value, this should invalidate output\n            self.widget.pca_components_slider.setValue(4)\n            self.commit_and_wait()\n            invalidate.assert_not_called()\n\n    def test_invalidate(self):\n        # pylint: disable=protected-access\n        data = self.iris\n        self.send_signal(self.widget.Inputs.data, data)\n        self.widget.commit()\n        out = self.get_output(self.widget.Outputs.annotated_data)\n        self.assertIsNotNone(out)\n        # invalidate the partitioning\n        p = self.widget.partition\n        g = self.widget.graph\n        self.widget._invalidate_partition()\n        # not in auto commit mode.\n        self.assertTrue(self.widget.Information.modified.is_shown())\n        self.widget.commit()\n        out = self.get_output(self.widget.Outputs.annotated_data)\n        self.assertIsNotNone(out)\n        self.assertIs(self.widget.graph, g)\n        self.assertIsNot(self.widget.partition, p)\n\n        self.assertFalse(self.widget.Information.modified.is_shown())\n        self.widget._invalidate_graph()\n        self.assertIsNone(self.widget.graph)\n        self.assertIsNotNone(self.widget.pca_projection)\n        self.assertTrue(self.widget.Information.modified.is_shown())\n        self.widget._invalidate_pca_projection()\n        self.assertIsNone(self.widget.pca_projection)\n        self.widget.commit()\n        self.get_output(self.widget.Outputs.annotated_data)\n        self.assertFalse(self.widget.Information.modified.is_shown())\n\n    def test_deterministic_clustering(self):\n        # Compute clustering on iris\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        self.commit_and_wait()\n        result1 = self.get_output(self.widget.Outputs.annotated_data)\n\n        # Reset widget state\n        self.send_signal(self.widget.Inputs.data, None)\n\n        # Compute clustering on iris again\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        self.commit_and_wait()\n        result2 = self.get_output(self.widget.Outputs.annotated_data)\n\n        # Ensure that clustering was the same in both instances\n        np.testing.assert_equal(result1.metas, result2.metas)\n\n    @table_dense_sparse\n    def test_normalize_data(self, prepare_table):\n        \"\"\"Check that normalization is called at the proper times.\"\"\"\n        data = prepare_table(self.iris)\n\n        # Enable checkbox\n        self.widget.controls.normalize.setChecked(True)\n        self.assertTrue(self.widget.controls.normalize.isChecked())\n        with patch(\"Orange.preprocess.Normalize\", wraps=Normalize) as normalize:\n            self.send_signal(self.widget.Inputs.data, data)\n            self.wait_until_stop_blocking()\n            self.assertTrue(self.widget.controls.normalize.isEnabled())\n            normalize.assert_called_once()\n\n        # Disable checkbox\n        self.widget.controls.normalize.setChecked(False)\n        self.assertFalse(self.widget.controls.normalize.isChecked())\n        with patch(\"Orange.preprocess.Normalize\", wraps=Normalize) as normalize:\n            self.send_signal(self.widget.Inputs.data, data)\n            self.wait_until_stop_blocking()\n            self.assertTrue(self.widget.controls.normalize.isEnabled())\n            normalize.assert_not_called()\n\n    def test_dense_and_sparse_return_same_result(self):\n        \"\"\"Check that Louvain clustering returns identical results for both\n        dense and sparse data.\"\"\"\n        random_state = check_random_state(42)\n\n        # Randomly set some values to zero\n        dense_data = self.iris\n        mask = random_state.beta(1, 2, size=self.iris.X.shape) > 0.5\n        dense_data.X[mask] = 0\n        sparse_data = dense_data.to_sparse()\n\n        def _compute_clustering(data):\n            self.send_signal(self.widget.Inputs.data, data)\n            self.wait_until_stop_blocking()\n            result = self.get_output(self.widget.Outputs.annotated_data)\n            self.send_signal(self.widget.Inputs.data, None)\n            return result\n\n        # Disable normalization\n        self.widget.controls.normalize.setChecked(False)\n        dense_result = _compute_clustering(dense_data)\n        sparse_result = _compute_clustering(sparse_data)\n        np.testing.assert_equal(dense_result.metas, sparse_result.metas)\n\n        # Enable normalization\n        self.widget.controls.normalize.setChecked(True)\n        dense_result = _compute_clustering(dense_data)\n        sparse_result = _compute_clustering(sparse_data)\n        np.testing.assert_equal(dense_result.metas, sparse_result.metas)\n\n    def test_graph_output(self):\n        w = self.widget\n\n        # This test executes only if network add-on is installed\n        if not hasattr(w.Outputs, \"graph\"):\n            return\n\n        self.send_signal(w.Inputs.data, self.iris)\n        graph = self.get_output(w.Outputs.graph)\n        self.assertEqual(len(graph.nodes), len(self.iris))\n\n        self.send_signal(w.Inputs.data, None)\n        graph = self.get_output(w.Outputs.graph)\n        self.assertIsNone(graph)\n\n    def test_migrate_settings(self):\n        # any context settings are removed\n        settings = {\"context_settings\": []}\n        self.widget.migrate_settings(settings, 1)\n        self.assertEqual(len(settings), 0)\n\n        # context settings become ordinary settings\n        settings = {\"context_settings\": [Context(values={'__version__': 1,\n                                                         'apply_pca': (True, -2),\n                                                         'k_neighbors': (29, -2),\n                                                         'metric_idx': (1, -2),\n                                                         'normalize': (False, -2),\n                                                         'pca_components': (10, -2),\n                                                         'resolution': (1.0, -2)})]}\n        self.widget.migrate_settings(settings, 1)\n        correct = {'apply_pca': True, 'k_neighbors': 29, 'metric_idx': 1,\n                   'normalize': False, 'pca_components': 10, 'resolution': 1.0}\n        self.assertEqual(sorted(settings.items()), sorted(correct.items()))",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 255,
                            "end_lineno": 272,
                            "parent": {
                                "file_name": "Orange/widgets/unsupervised/tests/test_owlouvain.py",
                                "type": "class",
                                "name": "TestOWLouvain",
                                "content": "class TestOWLouvain(WidgetTest):\n    def setUp(self):\n        self.widget = self.create_widget(\n            OWLouvainClustering, stored_settings={'auto_commit': False}\n        )\n        self.iris = Table('iris')[::5]\n\n    def tearDown(self):\n        self.widget.onDeleteWidget()\n        super().tearDown()\n\n    def commit_and_wait(self, widget=None):\n        widget = self.widget if widget is None else widget\n        widget.commit()\n        self.wait_until_stop_blocking(widget)\n\n    def test_removing_data(self):\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        self.commit_and_wait(self.widget)\n        self.send_signal(self.widget.Inputs.data, None)\n        self.commit_and_wait(self.widget)\n\n    def test_clusters_ordered_by_size(self):\n        \"\"\"Cluster names should be sorted based on the number of instances.\"\"\"\n        x1 = np.array([[0, 0]] * 20)\n        x2 = np.array([[1, 0]] * 15)\n        x3 = np.array([[0, 1]] * 10)\n        x4 = np.array([[1, 1]] * 5)\n        data = np.vstack((x1, x2, x3, x4))\n        # Remove any order depencence in data, not that this should affect it\n        np.random.shuffle(data)\n\n        table = Table.from_numpy(domain=Domain.from_numpy(X=data), X=data)\n\n        self.send_signal(self.widget.Inputs.data, table)\n        self.widget.k_neighbors = 4\n        self.commit_and_wait()\n        output = self.get_output(self.widget.Outputs.annotated_data)\n\n        clustering = output.get_column_view('Cluster')[0].astype(int)\n        counts = np.bincount(clustering)\n        np.testing.assert_equal(counts, sorted(counts, reverse=True))\n\n    def test_empty_dataset(self):\n        # Prepare a table with 5 rows with only meta attributes\n        meta = np.array([0] * 5)\n        meta_var = ContinuousVariable(name='meta_var')\n        table = Table.from_domain(domain=Domain([], metas=[meta_var]), n_rows=5)\n        table.get_column_view(meta_var)[0][:] = meta\n\n        self.send_signal(self.widget.Inputs.data, table)\n        self.commit_and_wait()\n        self.assertTrue(self.widget.Error.empty_dataset.is_shown())\n\n    def test_do_not_recluster_on_same_data(self):\n        \"\"\"Do not recluster data points when targets or metas change.\"\"\"\n\n        # Prepare some dummy data\n        x = np.eye(5)\n        y1, y2 = np.ones((5, 1)), np.ones((5, 2))\n        meta1, meta2 = np.ones((5, 1)), np.ones((5, 2))\n\n        table1 = Table.from_numpy(\n            domain=Domain.from_numpy(X=x, Y=y1, metas=meta1),\n            X=x, Y=y1, metas=meta1,\n        )\n        # X is same, should not cause update\n        table2 = Table.from_numpy(\n            domain=Domain.from_numpy(X=x, Y=y2, metas=meta2),\n            X=x, Y=y2, metas=meta2,\n        )\n        # X is different, should cause update\n        table3 = table1.copy()\n        table3.X[:, 0] = 1\n\n        with patch.object(self.widget, '_invalidate_output') as commit:\n            self.send_signal(self.widget.Inputs.data, table1)\n            self.commit_and_wait()\n            call_count = commit.call_count\n\n            # Sending data with same X should not recompute the clustering\n            self.send_signal(self.widget.Inputs.data, table2)\n            self.commit_and_wait()\n            self.assertEqual(call_count, commit.call_count)\n\n            # Sending data with different X should recompute the clustering\n            self.send_signal(self.widget.Inputs.data, table3)\n            self.commit_and_wait()\n            self.assertEqual(call_count + 1, commit.call_count)\n\n    def test_only_recluster_when_necessary_pca_components_change(self):\n        # Compute clustering on some data\n        self.send_signal(self.widget.Inputs.data, self.iris)\n\n        # When PCA checkbox is ticked, any update to slider should invalidate results\n        self.widget.apply_pca_cbx.setChecked(True)\n        self.widget.pca_components_slider.setValue(2)\n        self.commit_and_wait()\n\n        with patch.object(self.widget, '_invalidate_output') as invalidate:\n            # Change slider value, this should invalidate output\n            self.widget.pca_components_slider.setValue(4)\n            self.commit_and_wait()\n            self.assertEqual(invalidate.call_count, 1)\n\n        with patch.object(self.widget, '_invalidate_output') as invalidate:\n            # Don't change slider value, this shouldn't do anything\n            self.widget.pca_components_slider.setValue(4)\n            self.commit_and_wait()\n            invalidate.assert_not_called()\n\n        # When PCA checkbox is not ticked updating the slider should have no effect\n        self.widget.apply_pca_cbx.setChecked(False)\n        self.widget.pca_components_slider.setValue(2)\n        self.commit_and_wait()\n\n        with patch.object(self.widget, '_invalidate_output') as invalidate:\n            # Change slider value, this should invalidate output\n            self.widget.pca_components_slider.setValue(4)\n            self.commit_and_wait()\n            invalidate.assert_not_called()\n\n    def test_invalidate(self):\n        # pylint: disable=protected-access\n        data = self.iris\n        self.send_signal(self.widget.Inputs.data, data)\n        self.widget.commit()\n        out = self.get_output(self.widget.Outputs.annotated_data)\n        self.assertIsNotNone(out)\n        # invalidate the partitioning\n        p = self.widget.partition\n        g = self.widget.graph\n        self.widget._invalidate_partition()\n        # not in auto commit mode.\n        self.assertTrue(self.widget.Information.modified.is_shown())\n        self.widget.commit()\n        out = self.get_output(self.widget.Outputs.annotated_data)\n        self.assertIsNotNone(out)\n        self.assertIs(self.widget.graph, g)\n        self.assertIsNot(self.widget.partition, p)\n\n        self.assertFalse(self.widget.Information.modified.is_shown())\n        self.widget._invalidate_graph()\n        self.assertIsNone(self.widget.graph)\n        self.assertIsNotNone(self.widget.pca_projection)\n        self.assertTrue(self.widget.Information.modified.is_shown())\n        self.widget._invalidate_pca_projection()\n        self.assertIsNone(self.widget.pca_projection)\n        self.widget.commit()\n        self.get_output(self.widget.Outputs.annotated_data)\n        self.assertFalse(self.widget.Information.modified.is_shown())\n\n    def test_deterministic_clustering(self):\n        # Compute clustering on iris\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        self.commit_and_wait()\n        result1 = self.get_output(self.widget.Outputs.annotated_data)\n\n        # Reset widget state\n        self.send_signal(self.widget.Inputs.data, None)\n\n        # Compute clustering on iris again\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        self.commit_and_wait()\n        result2 = self.get_output(self.widget.Outputs.annotated_data)\n\n        # Ensure that clustering was the same in both instances\n        np.testing.assert_equal(result1.metas, result2.metas)\n\n    @table_dense_sparse\n    def test_normalize_data(self, prepare_table):\n        \"\"\"Check that normalization is called at the proper times.\"\"\"\n        data = prepare_table(self.iris)\n\n        # Enable checkbox\n        self.widget.controls.normalize.setChecked(True)\n        self.assertTrue(self.widget.controls.normalize.isChecked())\n        with patch(\"Orange.preprocess.Normalize\", wraps=Normalize) as normalize:\n            self.send_signal(self.widget.Inputs.data, data)\n            self.wait_until_stop_blocking()\n            self.assertTrue(self.widget.controls.normalize.isEnabled())\n            normalize.assert_called_once()\n\n        # Disable checkbox\n        self.widget.controls.normalize.setChecked(False)\n        self.assertFalse(self.widget.controls.normalize.isChecked())\n        with patch(\"Orange.preprocess.Normalize\", wraps=Normalize) as normalize:\n            self.send_signal(self.widget.Inputs.data, data)\n            self.wait_until_stop_blocking()\n            self.assertTrue(self.widget.controls.normalize.isEnabled())\n            normalize.assert_not_called()\n\n    def test_dense_and_sparse_return_same_result(self):\n        \"\"\"Check that Louvain clustering returns identical results for both\n        dense and sparse data.\"\"\"\n        random_state = check_random_state(42)\n\n        # Randomly set some values to zero\n        dense_data = self.iris\n        mask = random_state.beta(1, 2, size=self.iris.X.shape) > 0.5\n        dense_data.X[mask] = 0\n        sparse_data = dense_data.to_sparse()\n\n        def _compute_clustering(data):\n            self.send_signal(self.widget.Inputs.data, data)\n            self.wait_until_stop_blocking()\n            result = self.get_output(self.widget.Outputs.annotated_data)\n            self.send_signal(self.widget.Inputs.data, None)\n            return result\n\n        # Disable normalization\n        self.widget.controls.normalize.setChecked(False)\n        dense_result = _compute_clustering(dense_data)\n        sparse_result = _compute_clustering(sparse_data)\n        np.testing.assert_equal(dense_result.metas, sparse_result.metas)\n\n        # Enable normalization\n        self.widget.controls.normalize.setChecked(True)\n        dense_result = _compute_clustering(dense_data)\n        sparse_result = _compute_clustering(sparse_data)\n        np.testing.assert_equal(dense_result.metas, sparse_result.metas)\n\n    def test_graph_output(self):\n        w = self.widget\n\n        # This test executes only if network add-on is installed\n        if not hasattr(w.Outputs, \"graph\"):\n            return\n\n        self.send_signal(w.Inputs.data, self.iris)\n        graph = self.get_output(w.Outputs.graph)\n        self.assertEqual(len(graph.nodes), len(self.iris))\n\n        self.send_signal(w.Inputs.data, None)\n        graph = self.get_output(w.Outputs.graph)\n        self.assertIsNone(graph)\n\n    def test_migrate_settings(self):\n        # any context settings are removed\n        settings = {\"context_settings\": []}\n        self.widget.migrate_settings(settings, 1)\n        self.assertEqual(len(settings), 0)\n\n        # context settings become ordinary settings\n        settings = {\"context_settings\": [Context(values={'__version__': 1,\n                                                         'apply_pca': (True, -2),\n                                                         'k_neighbors': (29, -2),\n                                                         'metric_idx': (1, -2),\n                                                         'normalize': (False, -2),\n                                                         'pca_components': (10, -2),\n                                                         'resolution': (1.0, -2)})]}\n        self.widget.migrate_settings(settings, 1)\n        correct = {'apply_pca': True, 'k_neighbors': 29, 'metric_idx': 1,\n                   'normalize': False, 'pca_components': 10, 'resolution': 1.0}\n        self.assertEqual(sorted(settings.items()), sorted(correct.items()))",
                                "ast_type_str": "ClassDef",
                                "start_lineno": 18,
                                "end_lineno": 272,
                                "parent": null
                            }
                        }
                    ],
                    "deleted_entities": []
                }
            ]
        }
    ],
    "old_commit_hash": "3b2c6c87051a852fdb6884847464f16d1d526d81^",
    "new_commit_hash": "3b2c6c87051a852fdb6884847464f16d1d526d81",
    "commit_message": "OWLouvain: migrate context settings",
    "commit_date": "2020-03-05T16:26:54+01:00",
    "metadata": {}
}
{
    "repo_name": "orange3",
    "new_commit_hash": "3b2c6c87051a852fdb6884847464f16d1d526d81",
    "test_file_codes": [
        "from unittest.mock import patch\n\nimport numpy as np\nfrom sklearn.utils import check_random_state\n\nfrom orangewidget.settings import Context\n\nfrom Orange.data import Table, Domain, ContinuousVariable\nfrom Orange.preprocess import Normalize\nfrom Orange.widgets.tests.base import WidgetTest\nfrom Orange.widgets.tests.utils import table_dense_sparse\nfrom Orange.widgets.unsupervised.owlouvainclustering import OWLouvainClustering\n\n# Deterministic tests\nnp.random.seed(42)\n\n\nclass TestOWLouvain(WidgetTest):\n    def setUp(self):\n        self.widget = self.create_widget(\n            OWLouvainClustering, stored_settings={'auto_commit': False}\n        )\n        self.iris = Table('iris')[::5]\n\n    def tearDown(self):\n        self.widget.onDeleteWidget()\n        super().tearDown()\n\n    def commit_and_wait(self, widget=None):\n        widget = self.widget if widget is None else widget\n        widget.commit()\n        self.wait_until_stop_blocking(widget)\n\n    def test_removing_data(self):\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        self.commit_and_wait(self.widget)\n        self.send_signal(self.widget.Inputs.data, None)\n        self.commit_and_wait(self.widget)\n\n    def test_clusters_ordered_by_size(self):\n        \"\"\"Cluster names should be sorted based on the number of instances.\"\"\"\n        x1 = np.array([[0, 0]] * 20)\n        x2 = np.array([[1, 0]] * 15)\n        x3 = np.array([[0, 1]] * 10)\n        x4 = np.array([[1, 1]] * 5)\n        data = np.vstack((x1, x2, x3, x4))\n        # Remove any order depencence in data, not that this should affect it\n        np.random.shuffle(data)\n\n        table = Table.from_numpy(domain=Domain.from_numpy(X=data), X=data)\n\n        self.send_signal(self.widget.Inputs.data, table)\n        self.widget.k_neighbors = 4\n        self.commit_and_wait()\n        output = self.get_output(self.widget.Outputs.annotated_data)\n\n        clustering = output.get_column_view('Cluster')[0].astype(int)\n        counts = np.bincount(clustering)\n        np.testing.assert_equal(counts, sorted(counts, reverse=True))\n\n    def test_empty_dataset(self):\n        # Prepare a table with 5 rows with only meta attributes\n        meta = np.array([0] * 5)\n        meta_var = ContinuousVariable(name='meta_var')\n        table = Table.from_domain(domain=Domain([], metas=[meta_var]), n_rows=5)\n        table.get_column_view(meta_var)[0][:] = meta\n\n        self.send_signal(self.widget.Inputs.data, table)\n        self.commit_and_wait()\n        self.assertTrue(self.widget.Error.empty_dataset.is_shown())\n\n    def test_do_not_recluster_on_same_data(self):\n        \"\"\"Do not recluster data points when targets or metas change.\"\"\"\n\n        # Prepare some dummy data\n        x = np.eye(5)\n        y1, y2 = np.ones((5, 1)), np.ones((5, 2))\n        meta1, meta2 = np.ones((5, 1)), np.ones((5, 2))\n\n        table1 = Table.from_numpy(\n            domain=Domain.from_numpy(X=x, Y=y1, metas=meta1),\n            X=x, Y=y1, metas=meta1,\n        )\n        # X is same, should not cause update\n        table2 = Table.from_numpy(\n            domain=Domain.from_numpy(X=x, Y=y2, metas=meta2),\n            X=x, Y=y2, metas=meta2,\n        )\n        # X is different, should cause update\n        table3 = table1.copy()\n        table3.X[:, 0] = 1\n\n        with patch.object(self.widget, '_invalidate_output') as commit:\n            self.send_signal(self.widget.Inputs.data, table1)\n            self.commit_and_wait()\n            call_count = commit.call_count\n\n            # Sending data with same X should not recompute the clustering\n            self.send_signal(self.widget.Inputs.data, table2)\n            self.commit_and_wait()\n            self.assertEqual(call_count, commit.call_count)\n\n            # Sending data with different X should recompute the clustering\n            self.send_signal(self.widget.Inputs.data, table3)\n            self.commit_and_wait()\n            self.assertEqual(call_count + 1, commit.call_count)\n\n    def test_only_recluster_when_necessary_pca_components_change(self):\n        # Compute clustering on some data\n        self.send_signal(self.widget.Inputs.data, self.iris)\n\n        # When PCA checkbox is ticked, any update to slider should invalidate results\n        self.widget.apply_pca_cbx.setChecked(True)\n        self.widget.pca_components_slider.setValue(2)\n        self.commit_and_wait()\n\n        with patch.object(self.widget, '_invalidate_output') as invalidate:\n            # Change slider value, this should invalidate output\n            self.widget.pca_components_slider.setValue(4)\n            self.commit_and_wait()\n            self.assertEqual(invalidate.call_count, 1)\n\n        with patch.object(self.widget, '_invalidate_output') as invalidate:\n            # Don't change slider value, this shouldn't do anything\n            self.widget.pca_components_slider.setValue(4)\n            self.commit_and_wait()\n            invalidate.assert_not_called()\n\n        # When PCA checkbox is not ticked updating the slider should have no effect\n        self.widget.apply_pca_cbx.setChecked(False)\n        self.widget.pca_components_slider.setValue(2)\n        self.commit_and_wait()\n\n        with patch.object(self.widget, '_invalidate_output') as invalidate:\n            # Change slider value, this should invalidate output\n            self.widget.pca_components_slider.setValue(4)\n            self.commit_and_wait()\n            invalidate.assert_not_called()\n\n    def test_invalidate(self):\n        # pylint: disable=protected-access\n        data = self.iris\n        self.send_signal(self.widget.Inputs.data, data)\n        self.widget.commit()\n        out = self.get_output(self.widget.Outputs.annotated_data)\n        self.assertIsNotNone(out)\n        # invalidate the partitioning\n        p = self.widget.partition\n        g = self.widget.graph\n        self.widget._invalidate_partition()\n        # not in auto commit mode.\n        self.assertTrue(self.widget.Information.modified.is_shown())\n        self.widget.commit()\n        out = self.get_output(self.widget.Outputs.annotated_data)\n        self.assertIsNotNone(out)\n        self.assertIs(self.widget.graph, g)\n        self.assertIsNot(self.widget.partition, p)\n\n        self.assertFalse(self.widget.Information.modified.is_shown())\n        self.widget._invalidate_graph()\n        self.assertIsNone(self.widget.graph)\n        self.assertIsNotNone(self.widget.pca_projection)\n        self.assertTrue(self.widget.Information.modified.is_shown())\n        self.widget._invalidate_pca_projection()\n        self.assertIsNone(self.widget.pca_projection)\n        self.widget.commit()\n        self.get_output(self.widget.Outputs.annotated_data)\n        self.assertFalse(self.widget.Information.modified.is_shown())\n\n    def test_deterministic_clustering(self):\n        # Compute clustering on iris\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        self.commit_and_wait()\n        result1 = self.get_output(self.widget.Outputs.annotated_data)\n\n        # Reset widget state\n        self.send_signal(self.widget.Inputs.data, None)\n\n        # Compute clustering on iris again\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        self.commit_and_wait()\n        result2 = self.get_output(self.widget.Outputs.annotated_data)\n\n        # Ensure that clustering was the same in both instances\n        np.testing.assert_equal(result1.metas, result2.metas)\n\n    @table_dense_sparse\n    def test_normalize_data(self, prepare_table):\n        \"\"\"Check that normalization is called at the proper times.\"\"\"\n        data = prepare_table(self.iris)\n\n        # Enable checkbox\n        self.widget.controls.normalize.setChecked(True)\n        self.assertTrue(self.widget.controls.normalize.isChecked())\n        with patch(\"Orange.preprocess.Normalize\", wraps=Normalize) as normalize:\n            self.send_signal(self.widget.Inputs.data, data)\n            self.wait_until_stop_blocking()\n            self.assertTrue(self.widget.controls.normalize.isEnabled())\n            normalize.assert_called_once()\n\n        # Disable checkbox\n        self.widget.controls.normalize.setChecked(False)\n        self.assertFalse(self.widget.controls.normalize.isChecked())\n        with patch(\"Orange.preprocess.Normalize\", wraps=Normalize) as normalize:\n            self.send_signal(self.widget.Inputs.data, data)\n            self.wait_until_stop_blocking()\n            self.assertTrue(self.widget.controls.normalize.isEnabled())\n            normalize.assert_not_called()\n\n    def test_dense_and_sparse_return_same_result(self):\n        \"\"\"Check that Louvain clustering returns identical results for both\n        dense and sparse data.\"\"\"\n        random_state = check_random_state(42)\n\n        # Randomly set some values to zero\n        dense_data = self.iris\n        mask = random_state.beta(1, 2, size=self.iris.X.shape) > 0.5\n        dense_data.X[mask] = 0\n        sparse_data = dense_data.to_sparse()\n\n        def _compute_clustering(data):\n            self.send_signal(self.widget.Inputs.data, data)\n            self.wait_until_stop_blocking()\n            result = self.get_output(self.widget.Outputs.annotated_data)\n            self.send_signal(self.widget.Inputs.data, None)\n            return result\n\n        # Disable normalization\n        self.widget.controls.normalize.setChecked(False)\n        dense_result = _compute_clustering(dense_data)\n        sparse_result = _compute_clustering(sparse_data)\n        np.testing.assert_equal(dense_result.metas, sparse_result.metas)\n\n        # Enable normalization\n        self.widget.controls.normalize.setChecked(True)\n        dense_result = _compute_clustering(dense_data)\n        sparse_result = _compute_clustering(sparse_data)\n        np.testing.assert_equal(dense_result.metas, sparse_result.metas)\n\n    def test_graph_output(self):\n        w = self.widget\n\n        # This test executes only if network add-on is installed\n        if not hasattr(w.Outputs, \"graph\"):\n            return\n\n        self.send_signal(w.Inputs.data, self.iris)\n        graph = self.get_output(w.Outputs.graph)\n        self.assertEqual(len(graph.nodes), len(self.iris))\n\n        self.send_signal(w.Inputs.data, None)\n        graph = self.get_output(w.Outputs.graph)\n        self.assertIsNone(graph)\n\n    def test_migrate_settings(self):\n        # any context settings are removed\n        settings = {\"context_settings\": []}\n        self.widget.migrate_settings(settings, 1)\n        self.assertEqual(len(settings), 0)\n\n        # context settings become ordinary settings\n        settings = {\"context_settings\": [Context(values={'__version__': 1,\n                                                         'apply_pca': (True, -2),\n                                                         'k_neighbors': (29, -2),\n                                                         'metric_idx': (1, -2),\n                                                         'normalize': (False, -2),\n                                                         'pca_components': (10, -2),\n                                                         'resolution': (1.0, -2)})]}\n        self.widget.migrate_settings(settings, 1)\n        correct = {'apply_pca': True, 'k_neighbors': 29, 'metric_idx': 1,\n                   'normalize': False, 'pca_components': 10, 'resolution': 1.0}\n        self.assertEqual(sorted(settings.items()), sorted(correct.items()))\n"
    ],
    "test_file_names": [
        "test_1.py"
    ],
    "setup_res_code": 0,
    "setup_res_stdout": "Starting Orange installation attempts...\nAttempting installation with Python 3.7...\nrunning build_ext\nrunning build_src\nbuild_src\nbuilding extension \"Orange.classification._simple_tree\" sources\nbuilding extension \"Orange.classification._tree_scorers\" sources\nbuilding extension \"Orange.data._valuecount\" sources\nbuilding extension \"Orange.data._contingency\" sources\nbuilding extension \"Orange.data._io\" sources\nbuilding extension \"Orange.data._variable\" sources\nbuilding extension \"Orange.distance._distance\" sources\nbuilding extension \"Orange.preprocess._discretize\" sources\nbuilding extension \"Orange.preprocess._relieff\" sources\nbuilding extension \"Orange.projection._som\" sources\nbuilding extension \"Orange.widgets.utils._grid_density\" sources\nbuild_src: building npy-pkg config files\ncustomize UnixCCompiler\nC compiler: cc -pthread -Wno-unused-result -Wsign-compare -Wunreachable-code -DNDEBUG -g -fwrapv -O3 -Wall -fPIC -I/tools/deps/include -I/tools/deps/include/ncursesw -fPIC\n\ncreating /tmp/tmph0c0b50o/tmp\ncreating /tmp/tmph0c0b50o/tmp/tmph0c0b50o\ncompile options: '-MMD -MF /tmp/tmph0c0b50o/file.c.d -c'\ncc: /tmp/tmph0c0b50o/file.c\ncustomize UnixCCompiler using build_ext\ncustomize UnixCCompiler\nC compiler: cc -pthread -Wno-unused-result -Wsign-compare -Wunreachable-code -DNDEBUG -g -fwrapv -O3 -Wall -fPIC -I/tools/deps/include -I/tools/deps/include/ncursesw -fPIC\n\ncreating /tmp/tmphe_4p0ik/tmp\ncreating /tmp/tmphe_4p0ik/tmp/tmphe_4p0ik\ncompile options: '-MMD -MF /tmp/tmphe_4p0ik/file.c.d -c'\ncc: /tmp/tmphe_4p0ik/file.c\ncustomize UnixCCompiler using build_ext\nbuilding 'Orange.classification._simple_tree' extension\ncompiling C sources\nC compiler: cc -pthread -Wno-unused-result -Wsign-compare -Wunreachable-code -DNDEBUG -g -fwrapv -O3 -Wall -fPIC -I/tools/deps/include -I/tools/deps/include/ncursesw -fPIC\n\ncreating build/temp.linux-x86_64-3.7/Orange\ncreating build/temp.linux-x86_64-3.7/Orange/classification\ncompile options: '-I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/include -I/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/include/python3.7m -c'\ncc: Orange/classification/_simple_tree.c\ncc -pthread -shared -L/tools/deps/lib build/temp.linux-x86_64-3.7/Orange/classification/_simple_tree.o -L/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/lib -lm -lpython3.7m -o Orange/classification/_simple_tree.cpython-37m-x86_64-linux-gnu.so\nbuilding 'Orange.classification._tree_scorers' extension\ncompiling C sources\nC compiler: cc -pthread -Wno-unused-result -Wsign-compare -Wunreachable-code -DNDEBUG -g -fwrapv -O3 -Wall -fPIC -I/tools/deps/include -I/tools/deps/include/ncursesw -fPIC\n\ncompile options: '-I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/include -I/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/include/python3.7m -c'\ncc: Orange/classification/_tree_scorers.c\ncc -pthread -shared -L/tools/deps/lib build/temp.linux-x86_64-3.7/Orange/classification/_tree_scorers.o -L/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/lib -lm -lpython3.7m -o Orange/classification/_tree_scorers.cpython-37m-x86_64-linux-gnu.so\nbuilding 'Orange.data._valuecount' extension\ncompiling C sources\nC compiler: cc -pthread -Wno-unused-result -Wsign-compare -Wunreachable-code -DNDEBUG -g -fwrapv -O3 -Wall -fPIC -I/tools/deps/include -I/tools/deps/include/ncursesw -fPIC\n\ncreating build/temp.linux-x86_64-3.7/Orange/data\ncompile options: '-I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/include -I/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/include/python3.7m -c'\ncc: Orange/data/_valuecount.c\ncc -pthread -shared -L/tools/deps/lib build/temp.linux-x86_64-3.7/Orange/data/_valuecount.o -L/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/lib -lm -lpython3.7m -o Orange/data/_valuecount.cpython-37m-x86_64-linux-gnu.so\nbuilding 'Orange.data._contingency' extension\ncompiling C sources\nC compiler: cc -pthread -Wno-unused-result -Wsign-compare -Wunreachable-code -DNDEBUG -g -fwrapv -O3 -Wall -fPIC -I/tools/deps/include -I/tools/deps/include/ncursesw -fPIC\n\ncompile options: '-I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/include -I/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/include/python3.7m -c'\ncc: Orange/data/_contingency.c\ncc -pthread -shared -L/tools/deps/lib build/temp.linux-x86_64-3.7/Orange/data/_contingency.o -L/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/lib -lm -lpython3.7m -o Orange/data/_contingency.cpython-37m-x86_64-linux-gnu.so\nbuilding 'Orange.data._io' extension\ncompiling C sources\nC compiler: cc -pthread -Wno-unused-result -Wsign-compare -Wunreachable-code -DNDEBUG -g -fwrapv -O3 -Wall -fPIC -I/tools/deps/include -I/tools/deps/include/ncursesw -fPIC\n\ncompile options: '-I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/include -I/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/include/python3.7m -c'\ncc: Orange/data/_io.c\ncc -pthread -shared -L/tools/deps/lib build/temp.linux-x86_64-3.7/Orange/data/_io.o -L/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/lib -lm -lpython3.7m -o Orange/data/_io.cpython-37m-x86_64-linux-gnu.so\nbuilding 'Orange.data._variable' extension\ncompiling C sources\nC compiler: cc -pthread -Wno-unused-result -Wsign-compare -Wunreachable-code -DNDEBUG -g -fwrapv -O3 -Wall -fPIC -I/tools/deps/include -I/tools/deps/include/ncursesw -fPIC\n\ncompile options: '-I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/include -I/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/include/python3.7m -c'\ncc: Orange/data/_variable.c\ncc -pthread -shared -L/tools/deps/lib build/temp.linux-x86_64-3.7/Orange/data/_variable.o -L/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/lib -lm -lpython3.7m -o Orange/data/_variable.cpython-37m-x86_64-linux-gnu.so\nbuilding 'Orange.distance._distance' extension\ncompiling C sources\nC compiler: cc -pthread -Wno-unused-result -Wsign-compare -Wunreachable-code -DNDEBUG -g -fwrapv -O3 -Wall -fPIC -I/tools/deps/include -I/tools/deps/include/ncursesw -fPIC\n\ncreating build/temp.linux-x86_64-3.7/Orange/distance\ncompile options: '-I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/include -I/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/include/python3.7m -c'\ncc: Orange/distance/_distance.c\ncc -pthread -shared -L/tools/deps/lib build/temp.linux-x86_64-3.7/Orange/distance/_distance.o -L/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/lib -lm -lpython3.7m -o Orange/distance/_distance.cpython-37m-x86_64-linux-gnu.so\nbuilding 'Orange.preprocess._discretize' extension\ncompiling C sources\nC compiler: cc -pthread -Wno-unused-result -Wsign-compare -Wunreachable-code -DNDEBUG -g -fwrapv -O3 -Wall -fPIC -I/tools/deps/include -I/tools/deps/include/ncursesw -fPIC\n\ncreating build/temp.linux-x86_64-3.7/Orange/preprocess\ncompile options: '-I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/include -I/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/include/python3.7m -c'\ncc: Orange/preprocess/_discretize.c\ncc -pthread -shared -L/tools/deps/lib build/temp.linux-x86_64-3.7/Orange/preprocess/_discretize.o -L/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/lib -lm -lpython3.7m -o Orange/preprocess/_discretize.cpython-37m-x86_64-linux-gnu.so\nbuilding 'Orange.preprocess._relieff' extension\ncompiling C++ sources\nC compiler: c++ -pthread -Wno-unused-result -Wsign-compare -Wunreachable-code -DNDEBUG -g -fwrapv -O3 -Wall -fPIC -I/tools/deps/include -I/tools/deps/include/ncursesw -fPIC\n\ncompile options: '-I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/include -I/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/include/python3.7m -c'\nc++: Orange/preprocess/_relieff.cpp\nc++ -pthread -shared -L/tools/deps/lib build/temp.linux-x86_64-3.7/Orange/preprocess/_relieff.o -L/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/lib -lm -lpython3.7m -o Orange/preprocess/_relieff.cpython-37m-x86_64-linux-gnu.so\nbuilding 'Orange.projection._som' extension\ncompiling C sources\nC compiler: cc -pthread -Wno-unused-result -Wsign-compare -Wunreachable-code -DNDEBUG -g -fwrapv -O3 -Wall -fPIC -I/tools/deps/include -I/tools/deps/include/ncursesw -fPIC\n\ncreating build/temp.linux-x86_64-3.7/Orange/projection\ncompile options: '-I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/include -I/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/include/python3.7m -c'\ncc: Orange/projection/_som.c\ncc -pthread -shared -L/tools/deps/lib build/temp.linux-x86_64-3.7/Orange/projection/_som.o -L/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/lib -lm -lpython3.7m -o Orange/projection/_som.cpython-37m-x86_64-linux-gnu.so\nbuilding 'Orange.widgets.utils._grid_density' extension\ncompiling C++ sources\nC compiler: c++ -pthread -Wno-unused-result -Wsign-compare -Wunreachable-code -DNDEBUG -g -fwrapv -O3 -Wall -fPIC -I/tools/deps/include -I/tools/deps/include/ncursesw -fPIC\n\ncreating build/temp.linux-x86_64-3.7/Orange/widgets\ncreating build/temp.linux-x86_64-3.7/Orange/widgets/utils\ncompile options: '-I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/include -I/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/include/python3.7m -c'\nc++: Orange/widgets/utils/_grid_density.cpp\nc++ -pthread -shared -L/tools/deps/lib build/temp.linux-x86_64-3.7/Orange/widgets/utils/_grid_density.o -L/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/lib -lm -lpython3.7m -o Orange/widgets/utils/_grid_density.cpython-37m-x86_64-linux-gnu.so\nrunning develop\nrunning build_scripts\nrunning egg_info\nrunning build_src\nbuild_src\nbuilding extension \"Orange.classification._simple_tree\" sources\nbuilding extension \"Orange.classification._tree_scorers\" sources\nbuilding extension \"Orange.data._valuecount\" sources\nbuilding extension \"Orange.data._contingency\" sources\nbuilding extension \"Orange.data._io\" sources\nbuilding extension \"Orange.data._variable\" sources\nbuilding extension \"Orange.distance._distance\" sources\nbuilding extension \"Orange.preprocess._discretize\" sources\nbuilding extension \"Orange.preprocess._relieff\" sources\nbuilding extension \"Orange.projection._som\" sources\nbuilding extension \"Orange.widgets.utils._grid_density\" sources\nbuild_src: building npy-pkg config files\ncreating Orange3.egg-info\nwriting Orange3.egg-info/PKG-INFO\nwriting dependency_links to Orange3.egg-info/dependency_links.txt\nwriting entry points to Orange3.egg-info/entry_points.txt\nwriting requirements to Orange3.egg-info/requires.txt\nwriting top-level names to Orange3.egg-info/top_level.txt\nwriting manifest file 'Orange3.egg-info/SOURCES.txt'\nreading manifest file 'Orange3.egg-info/SOURCES.txt'\nreading manifest template 'MANIFEST.in'\nwarning: no files found matching 'README*' under directory 'Orange'\nwarning: no files found matching 'LICENSE' under directory 'Orange'\nwarning: no files found matching '*.basket' under directory 'Orange/datasets'\nwarning: no files found matching '*.js' under directory 'Orange/widgets'\nwarning: no files found matching '*.css' under directory 'Orange/widgets'\nwarning: no files found matching '*.html' under directory 'Orange/widgets'\nwarning: no files found matching 'COPYING'\nadding license file 'LICENSE'\nwriting manifest file 'Orange3.egg-info/SOURCES.txt'\nrunning build_ext\ncustomize UnixCCompiler\nC compiler: cc -pthread -Wno-unused-result -Wsign-compare -Wunreachable-code -DNDEBUG -g -fwrapv -O3 -Wall -fPIC -I/tools/deps/include -I/tools/deps/include/ncursesw -fPIC\n\ncreating /tmp/tmpbl7tph84/tmp\ncreating /tmp/tmpbl7tph84/tmp/tmpbl7tph84\ncompile options: '-MMD -MF /tmp/tmpbl7tph84/file.c.d -c'\ncc: /tmp/tmpbl7tph84/file.c\ncustomize UnixCCompiler using build_ext\ncustomize UnixCCompiler\nC compiler: cc -pthread -Wno-unused-result -Wsign-compare -Wunreachable-code -DNDEBUG -g -fwrapv -O3 -Wall -fPIC -I/tools/deps/include -I/tools/deps/include/ncursesw -fPIC\n\ncreating /tmp/tmp7q10ye_t/tmp\ncreating /tmp/tmp7q10ye_t/tmp/tmp7q10ye_t\ncompile options: '-MMD -MF /tmp/tmp7q10ye_t/file.c.d -c'\ncc: /tmp/tmp7q10ye_t/file.c\ncustomize UnixCCompiler using build_ext\nCreating /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages/Orange3.egg-link (link to .)\nAdding Orange3 3.25.0.dev0+3b2c6c8 to easy-install.pth file\nInstalling orange-canvas script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/bin\n\nInstalled /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81\nProcessing dependencies for Orange3==3.25.0.dev0+3b2c6c8\nSearching for XlsxWriter==3.2.0\nBest match: XlsxWriter 3.2.0\nAdding XlsxWriter 3.2.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages\nSearching for xlrd==2.0.1\nBest match: xlrd 2.0.1\nAdding xlrd 2.0.1 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages\nSearching for setuptools==59.8.0\nBest match: setuptools 59.8.0\nAdding setuptools 59.8.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages\nSearching for serverfiles==0.3.1\nBest match: serverfiles 0.3.1\nAdding serverfiles 0.3.1 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages\nSearching for scipy==1.7.3\nBest match: scipy 1.7.3\nAdding scipy 1.7.3 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages\nSearching for scikit-learn==1.0.2\nBest match: scikit-learn 1.0.2\nAdding scikit-learn 1.0.2 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages\nSearching for requests==2.31.0\nBest match: requests 2.31.0\nAdding requests 2.31.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages\nSearching for PyYAML==6.0.1\nBest match: PyYAML 6.0.1\nAdding PyYAML 6.0.1 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages\nSearching for python-louvain==0.16\nBest match: python-louvain 0.16\nAdding python-louvain 0.16 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages\nSearching for pyqtgraph==0.10.0\nBest match: pyqtgraph 0.10.0\nAdding pyqtgraph 0.10.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages\nSearching for pip==24.0\nBest match: pip 24.0\nAdding pip 24.0 to easy-install.pth file\nInstalling pip script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/bin\nInstalling pip3 script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/bin\nInstalling pip3.10 script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/bin\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages\nSearching for pandas==1.3.5\nBest match: pandas 1.3.5\nAdding pandas 1.3.5 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages\nSearching for orange-widget-base==4.22.0\nBest match: orange-widget-base 4.22.0\nAdding orange-widget-base 4.22.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages\nSearching for orange-canvas-core==0.1.35\nBest match: orange-canvas-core 0.1.35\nAdding orange-canvas-core 0.1.35 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages\nSearching for openpyxl==3.1.3\nBest match: openpyxl 3.1.3\nAdding openpyxl 3.1.3 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages\nSearching for openTSNE==1.0.1\nBest match: openTSNE 1.0.1\nAdding openTSNE 1.0.1 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages\nSearching for numpy==1.17.5\nBest match: numpy 1.17.5\nAdding numpy 1.17.5 to easy-install.pth file\nInstalling f2py script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/bin\nInstalling f2py3 script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/bin\nInstalling f2py3.7 script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/bin\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages\nSearching for networkx==2.6.3\nBest match: networkx 2.6.3\nAdding networkx 2.6.3 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages\nSearching for matplotlib==3.5.3\nBest match: matplotlib 3.5.3\nAdding matplotlib 3.5.3 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages\nSearching for keyrings.alt==4.2.0\nBest match: keyrings.alt 4.2.0\nAdding keyrings.alt 4.2.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages\nSearching for keyring==24.1.1\nBest match: keyring 24.1.1\nAdding keyring 24.1.1 to easy-install.pth file\nInstalling keyring script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/bin\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages\nSearching for joblib==1.3.2\nBest match: joblib 1.3.2\nAdding joblib 1.3.2 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages\nSearching for docutils==0.20.1\nBest match: docutils 0.20.1\nAdding docutils 0.20.1 to easy-install.pth file\nInstalling docutils script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/bin\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages\nSearching for chardet==5.2.0\nBest match: chardet 5.2.0\nAdding chardet 5.2.0 to easy-install.pth file\nInstalling chardetect script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/bin\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages\nSearching for Bottleneck==1.4.0\nBest match: Bottleneck 1.4.0\nAdding Bottleneck 1.4.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages\nSearching for baycomp==1.0.3\nBest match: baycomp 1.0.3\nAdding baycomp 1.0.3 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages\nSearching for AnyQt==0.2.0\nBest match: AnyQt 0.2.0\nAdding AnyQt 0.2.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages\nSearching for threadpoolctl==3.1.0\nBest match: threadpoolctl 3.1.0\nAdding threadpoolctl 3.1.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages\nSearching for idna==3.10\nBest match: idna 3.10\nAdding idna 3.10 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages\nSearching for urllib3==2.0.7\nBest match: urllib3 2.0.7\nAdding urllib3 2.0.7 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages\nSearching for certifi==2024.12.14\nBest match: certifi 2024.12.14\nAdding certifi 2024.12.14 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages\nSearching for charset-normalizer==3.4.1\nBest match: charset-normalizer 3.4.1\nAdding charset-normalizer 3.4.1 to easy-install.pth file\nInstalling normalizer script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/bin\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages\nSearching for pytz==2024.2\nBest match: pytz 2024.2\nAdding pytz 2024.2 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages\nSearching for python-dateutil==2.9.0.post0\nBest match: python-dateutil 2.9.0.post0\nAdding python-dateutil 2.9.0.post0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages\nSearching for typing-extensions==4.7.1\nBest match: typing-extensions 4.7.1\nAdding typing-extensions 4.7.1 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages\nSearching for requests-cache==1.1.1\nBest match: requests-cache 1.1.1\nAdding requests-cache 1.1.1 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages\nSearching for commonmark==0.9.1\nBest match: commonmark 0.9.1\nAdding commonmark 0.9.1 to easy-install.pth file\nInstalling cmark script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/bin\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages\nSearching for packaging==24.0\nBest match: packaging 24.0\nAdding packaging 24.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages\nSearching for qasync==0.25.0\nBest match: qasync 0.25.0\nAdding qasync 0.25.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages\nSearching for importlib-metadata==6.7.0\nBest match: importlib-metadata 6.7.0\nAdding importlib-metadata 6.7.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages\nSearching for dictdiffer==0.9.0\nBest match: dictdiffer 0.9.0\nAdding dictdiffer 0.9.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages\nSearching for et-xmlfile==1.1.0\nBest match: et-xmlfile 1.1.0\nAdding et-xmlfile 1.1.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages\nSearching for cycler==0.11.0\nBest match: cycler 0.11.0\nAdding cycler 0.11.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages\nSearching for kiwisolver==1.4.5\nBest match: kiwisolver 1.4.5\nAdding kiwisolver 1.4.5 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages\nSearching for pyparsing==3.1.4\nBest match: pyparsing 3.1.4\nAdding pyparsing 3.1.4 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages\nSearching for Pillow==9.5.0\nBest match: Pillow 9.5.0\nAdding Pillow 9.5.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages\nSearching for fonttools==4.38.0\nBest match: fonttools 4.38.0\nAdding fonttools 4.38.0 to easy-install.pth file\nInstalling fonttools script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/bin\nInstalling pyftmerge script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/bin\nInstalling pyftsubset script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/bin\nInstalling ttx script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/bin\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages\nSearching for jaraco.classes==3.2.3\nBest match: jaraco.classes 3.2.3\nAdding jaraco.classes 3.2.3 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages\nSearching for importlib-resources==5.12.0\nBest match: importlib-resources 5.12.0\nAdding importlib-resources 5.12.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages\nSearching for SecretStorage==3.3.3\nBest match: SecretStorage 3.3.3\nAdding SecretStorage 3.3.3 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages\nSearching for jeepney==0.8.0\nBest match: jeepney 0.8.0\nAdding jeepney 0.8.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages\nSearching for six==1.17.0\nBest match: six 1.17.0\nAdding six 1.17.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages\nSearching for cattrs==23.1.2\nBest match: cattrs 23.1.2\nAdding cattrs 23.1.2 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages\nSearching for platformdirs==4.0.0\nBest match: platformdirs 4.0.0\nAdding platformdirs 4.0.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages\nSearching for url-normalize==1.4.3\nBest match: url-normalize 1.4.3\nAdding url-normalize 1.4.3 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages\nSearching for attrs==24.2.0\nBest match: attrs 24.2.0\nAdding attrs 24.2.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages\nSearching for zipp==3.15.0\nBest match: zipp 3.15.0\nAdding zipp 3.15.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages\nSearching for more-itertools==9.1.0\nBest match: more-itertools 9.1.0\nAdding more-itertools 9.1.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages\nSearching for cryptography==44.0.0\nBest match: cryptography 44.0.0\nAdding cryptography 44.0.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages\nSearching for exceptiongroup==1.2.2\nBest match: exceptiongroup 1.2.2\nAdding exceptiongroup 1.2.2 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages\nSearching for cffi==1.15.1\nBest match: cffi 1.15.1\nAdding cffi 1.15.1 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages\nSearching for pycparser==2.21\nBest match: pycparser 2.21\nAdding pycparser 2.21 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages\nFinished processing dependencies for Orange3==3.25.0.dev0+3b2c6c8\nVerifying Orange installation...\n✅ Orange installation successful!\nSuccessfully installed orange using Python 3.7\n",
    "setup_res_stderr": "Using CPython 3.7.9\nCreating virtual environment at: .venv\nActivate with: source .venv/bin/activate\nResolved 18 packages in 102ms\nPrepared 18 packages in 9ms\nInstalled 18 packages in 119ms\n + cython==0.29.37\n + exceptiongroup==1.2.2\n + importlib-metadata==6.7.0\n + iniconfig==2.0.0\n + numpy==1.17.5\n + packaging==24.0\n + pluggy==1.2.0\n + pyqt5==5.15.10\n + pyqt5-qt5==5.15.16\n + pyqt5-sip==12.13.0\n + pyqtwebengine==5.15.6\n + pyqtwebengine-qt5==5.15.16\n + pytest==7.4.4\n + setuptools==59.8.0\n + tomli==2.0.1\n + typing-extensions==4.7.1\n + wheel==0.42.0\n + zipp==3.15.0\nResolved 48 packages in 265ms\nInstalled 42 packages in 556ms\n + baycomp==1.0.3\n + bottleneck==1.4.0\n + certifi==2024.12.14\n + cffi==1.15.1\n + chardet==5.2.0\n + charset-normalizer==3.4.1\n + cryptography==44.0.0\n + cycler==0.11.0\n + et-xmlfile==1.1.0\n + fonttools==4.38.0\n + idna==3.10\n + importlib-resources==5.12.0\n + jaraco-classes==3.2.3\n + jeepney==0.8.0\n + joblib==1.3.2\n + keyring==24.1.1\n + keyrings-alt==4.2.0\n + kiwisolver==1.4.5\n + matplotlib==3.5.3\n + more-itertools==9.1.0\n + networkx==2.6.3\n + openpyxl==3.1.3\n + opentsne==1.0.1\n + pandas==1.3.5\n + pillow==9.5.0\n + pip==24.0\n + pycparser==2.21\n + pyparsing==3.1.4\n + python-dateutil==2.9.0.post0\n + python-louvain==0.16\n + pytz==2024.2\n + pyyaml==6.0.1\n + requests==2.31.0\n + scikit-learn==1.0.2\n + scipy==1.7.3\n + secretstorage==3.3.3\n + serverfiles==0.3.1\n + six==1.17.0\n + threadpoolctl==3.1.0\n + urllib3==2.0.7\n + xlrd==2.0.1\n + xlsxwriter==3.2.0\nResolved 34 packages in 82ms\nInstalled 13 packages in 299ms\n + anyqt==0.2.0\n + attrs==24.2.0\n + cattrs==23.1.2\n + commonmark==0.9.1\n + dictdiffer==0.9.0\n + docutils==0.20.1\n + orange-canvas-core==0.1.35\n + orange-widget-base==4.22.0\n + platformdirs==4.0.0\n + pyqtgraph==0.10.0\n + qasync==0.25.0\n + requests-cache==1.1.1\n + url-normalize==1.4.3\nResolved 2 packages in 5ms\nInstalled 2 packages in 39ms\n + psycopg2==2.9.9\n + pymssql==2.3.2\nAudited 1 package in 7ms\nAudited 2 packages in 14ms\nOrange/classification/_simple_tree.c: In function ‘gain_ratio_c’:\nOrange/classification/_simple_tree.c:63:9: warning: implicit declaration of function ‘qsort_r’; did you mean ‘qsort’? [-Wimplicit-function-declaration]\n   63 |         qsort_r(base, nel, size, compar, thunk)\n      |         ^~~~~~~\nOrange/classification/_simple_tree.c:147:9: note: in expansion of macro ‘QSORT_R_FUNC’\n  147 |         QSORT_R_FUNC(examples, size, sizeof(struct Example), (void*) &compar_attr, compar_examples);\n      |         ^~~~~~~~~~~~\nIn file included from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/ndarraytypes.h:1830,\n                 from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/ndarrayobject.h:12,\n                 from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/arrayobject.h:4,\n                 from Orange/classification/_tree_scorers.c:567:\n/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/npy_1_7_deprecated_api.h:17:2: warning: #warning \"Using deprecated NumPy API, disable it with \" \"#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\" [-Wcpp]\n   17 | #warning \"Using deprecated NumPy API, disable it with \" \\\n      |  ^~~~~~~\nOrange/classification/_tree_scorers.c: In function ‘__pyx_pf_6Orange_14classification_13_tree_scorers_2find_threshold_entropy’:\nOrange/classification/_tree_scorers.c:3115:27: warning: comparison of integer expressions of different signedness: ‘unsigned int’ and ‘int’ [-Wsign-compare]\n 3115 |   __pyx_t_7 = ((__pyx_v_N <= __pyx_v_min_leaf) != 0);\n      |                           ^~\nOrange/classification/_tree_scorers.c: In function ‘__pyx_pf_6Orange_14classification_13_tree_scorers_6find_threshold_MSE’:\nOrange/classification/_tree_scorers.c:4619:27: warning: comparison of integer expressions of different signedness: ‘unsigned int’ and ‘int’ [-Wsign-compare]\n 4619 |   __pyx_t_1 = ((__pyx_v_N <= __pyx_v_min_leaf) != 0);\n      |                           ^~\nOrange/classification/_tree_scorers.c: In function ‘__pyx_pf_6Orange_14classification_13_tree_scorers_8find_binarization_MSE’:\nOrange/classification/_tree_scorers.c:5396:43: warning: comparison of integer expressions of different signedness: ‘unsigned int’ and ‘int’ [-Wsign-compare]\n 5396 |           for (__pyx_t_22 = 0; __pyx_t_22 < __pyx_t_21; __pyx_t_22+=1) {\n      |                                           ^\nOrange/classification/_tree_scorers.c:5517:39: warning: comparison of integer expressions of different signedness: ‘unsigned int’ and ‘int’ [-Wsign-compare]\n 5517 |           __pyx_t_27 = ((__pyx_v_left >= __pyx_v_min_leaf) != 0);\n      |                                       ^~\nOrange/classification/_tree_scorers.c:5523:53: warning: comparison of integer expressions of different signedness: ‘unsigned int’ and ‘int’ [-Wsign-compare]\n 5523 |           __pyx_t_27 = (((__pyx_v_N - __pyx_v_left) >= __pyx_v_min_leaf) != 0);\n      |                                                     ^~\nOrange/classification/_tree_scorers.c: In function ‘__pyx_pf_6Orange_14classification_13_tree_scorers_14compute_predictions_csr’:\nOrange/classification/_tree_scorers.c:7051:41: warning: comparison of integer expressions of different signedness: ‘unsigned int’ and ‘int’ [-Wsign-compare]\n 7051 |         for (__pyx_t_11 = 0; __pyx_t_11 < __pyx_t_10; __pyx_t_11+=1) {\n      |                                         ^\nOrange/classification/_tree_scorers.c: In function ‘__pyx_pf_6Orange_14classification_13_tree_scorers_16compute_predictions_csc’:\nOrange/classification/_tree_scorers.c:7741:41: warning: comparison of integer expressions of different signedness: ‘unsigned int’ and ‘int’ [-Wsign-compare]\n 7741 |         for (__pyx_t_11 = 0; __pyx_t_11 < __pyx_t_10; __pyx_t_11+=1) {\n      |                                         ^\nOrange/classification/_tree_scorers.c:7801:143: warning: comparison of integer expressions of different signedness: ‘__pyx_t_5numpy_int32_t’ {aka ‘int’} and ‘unsigned int’ [-Wsign-compare]\n 7801 |               __pyx_t_17 = (((*((__pyx_t_5numpy_int32_t *) ( /* dim=0 */ (__pyx_v_indices.data + __pyx_t_18 * __pyx_v_indices.strides[0]) ))) != __pyx_v_i) != 0);\n      |                                                                                                                                               ^~\nIn file included from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/ndarraytypes.h:1830,\n                 from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/ndarrayobject.h:12,\n                 from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/arrayobject.h:4,\n                 from Orange/data/_valuecount.c:567:\n/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/npy_1_7_deprecated_api.h:17:2: warning: #warning \"Using deprecated NumPy API, disable it with \" \"#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\" [-Wcpp]\n   17 | #warning \"Using deprecated NumPy API, disable it with \" \\\n      |  ^~~~~~~\nIn file included from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/ndarraytypes.h:1830,\n                 from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/ndarrayobject.h:12,\n                 from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/arrayobject.h:4,\n                 from Orange/data/_contingency.c:598:\n/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/npy_1_7_deprecated_api.h:17:2: warning: #warning \"Using deprecated NumPy API, disable it with \" \"#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\" [-Wcpp]\n   17 | #warning \"Using deprecated NumPy API, disable it with \" \\\n      |  ^~~~~~~\nOrange/data/_io.c: In function ‘__pyx_pf_6Orange_4data_3_io_sparse_read_float.constprop’:\nOrange/data/_io.c:3881:24: warning: ‘__pyx_v_decs’ may be used uninitialized in this function [-Wmaybe-uninitialized]\n 3881 |           __pyx_v_decs = (__pyx_v_decs / 10.0);\n      |           ~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~\nOrange/data/_io.c:4070:24: warning: ‘__pyx_v_value’ may be used uninitialized in this function [-Wmaybe-uninitialized]\n 4070 |           __pyx_t_10 = PyFloat_FromDouble(__pyx_v_value); if (unlikely(!__pyx_t_10)) __PYX_ERR(0, 221, __pyx_L5_error)\n      |                        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\nOrange/data/_io.c:480:40: warning: ‘__pyx_v_attr_index’ may be used uninitialized in this function [-Wmaybe-uninitialized]\n  480 |   #define PyInt_FromLong               PyLong_FromLong\n      |                                        ^~~~~~~~~~~~~~~\nOrange/data/_io.c:1301:7: note: ‘__pyx_v_attr_index’ was declared here\n 1301 |   int __pyx_v_attr_index;\n      |       ^~~~~~~~~~~~~~~~~~\nOrange/data/_io.c:2935:25: warning: ‘__pyx_v_atomp’ may be used uninitialized in this function [-Wmaybe-uninitialized]\n 2935 |           __pyx_v_atomp = (__pyx_v_atomp + 1);\n      |           ~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~\nIn file included from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/ndarraytypes.h:1830,\n                 from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/ndarrayobject.h:12,\n                 from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/arrayobject.h:4,\n                 from Orange/distance/_distance.c:598:\n/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/npy_1_7_deprecated_api.h:17:2: warning: #warning \"Using deprecated NumPy API, disable it with \" \"#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\" [-Wcpp]\n   17 | #warning \"Using deprecated NumPy API, disable it with \" \\\n      |  ^~~~~~~\nIn file included from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/ndarraytypes.h:1830,\n                 from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/ndarrayobject.h:12,\n                 from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/arrayobject.h:4,\n                 from Orange/preprocess/_discretize.c:567:\n/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/npy_1_7_deprecated_api.h:17:2: warning: #warning \"Using deprecated NumPy API, disable it with \" \"#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\" [-Wcpp]\n   17 | #warning \"Using deprecated NumPy API, disable it with \" \\\n      |  ^~~~~~~\nIn file included from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/ndarraytypes.h:1830,\n                 from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/ndarrayobject.h:12,\n                 from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/arrayobject.h:4,\n                 from Orange/preprocess/_relieff.cpp:581:\n/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/npy_1_7_deprecated_api.h:17:2: warning: #warning \"Using deprecated NumPy API, disable it with \" \"#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\" [-Wcpp]\n   17 | #warning \"Using deprecated NumPy API, disable it with \" \\\n      |  ^~~~~~~\nIn file included from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/ndarraytypes.h:1830,\n                 from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/ndarrayobject.h:12,\n                 from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/arrayobject.h:4,\n                 from Orange/projection/_som.c:598:\n/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/npy_1_7_deprecated_api.h:17:2: warning: #warning \"Using deprecated NumPy API, disable it with \" \"#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\" [-Wcpp]\n   17 | #warning \"Using deprecated NumPy API, disable it with \" \\\n      |  ^~~~~~~\nOrange/projection/_som.c: In function ‘__pyx_pw_6Orange_10projection_4_som_5update_hex’:\nOrange/projection/_som.c:3655:7: warning: ‘__pyx_v_win_y’ may be used uninitialized in this function [-Wmaybe-uninitialized]\n 3655 |   int __pyx_v_win_y;\n      |       ^~~~~~~~~~~~~\nOrange/projection/_som.c:3654:7: warning: ‘__pyx_v_win_x’ may be used uninitialized in this function [-Wmaybe-uninitialized]\n 3654 |   int __pyx_v_win_x;\n      |       ^~~~~~~~~~~~~\nOrange/projection/_som.c: In function ‘__pyx_pw_6Orange_10projection_4_som_7get_winners_sparse’:\nOrange/projection/_som.c:4547:223: warning: ‘__pyx_v_win_y’ may be used uninitialized in this function [-Wmaybe-uninitialized]\n 4547 |           *__Pyx_BufPtrStrided2d(__pyx_t_5numpy_int16_t *, __pyx_pybuffernd_winners.rcbuffer->pybuffer.buf, __pyx_t_38, __pyx_pybuffernd_winners.diminfo[0].strides, __pyx_t_39, __pyx_pybuffernd_winners.diminfo[1].strides) = __pyx_v_win_y;\n      |           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~\nOrange/projection/_som.c:4114:7: note: ‘__pyx_v_win_y’ was declared here\n 4114 |   int __pyx_v_win_y;\n      |       ^~~~~~~~~~~~~\nOrange/projection/_som.c:4536:223: warning: ‘__pyx_v_win_x’ may be used uninitialized in this function [-Wmaybe-uninitialized]\n 4536 |           *__Pyx_BufPtrStrided2d(__pyx_t_5numpy_int16_t *, __pyx_pybuffernd_winners.rcbuffer->pybuffer.buf, __pyx_t_36, __pyx_pybuffernd_winners.diminfo[0].strides, __pyx_t_37, __pyx_pybuffernd_winners.diminfo[1].strides) = __pyx_v_win_x;\n      |           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~\nOrange/projection/_som.c:4113:7: note: ‘__pyx_v_win_x’ was declared here\n 4113 |   int __pyx_v_win_x;\n      |       ^~~~~~~~~~~~~\nOrange/projection/_som.c: In function ‘__pyx_pf_6Orange_10projection_4_som_8update_sparse.constprop’:\nOrange/projection/_som.c:4746:7: warning: ‘__pyx_v_win_y’ may be used uninitialized in this function [-Wmaybe-uninitialized]\n 4746 |   int __pyx_v_win_y;\n      |       ^~~~~~~~~~~~~\nOrange/projection/_som.c:4745:7: warning: ‘__pyx_v_win_x’ may be used uninitialized in this function [-Wmaybe-uninitialized]\n 4745 |   int __pyx_v_win_x;\n      |       ^~~~~~~~~~~~~\nOrange/projection/_som.c: In function ‘__pyx_pw_6Orange_10projection_4_som_3update’:\nOrange/projection/_som.c:3141:7: warning: ‘__pyx_v_win_y’ may be used uninitialized in this function [-Wmaybe-uninitialized]\n 3141 |   int __pyx_v_win_y;\n      |       ^~~~~~~~~~~~~\nOrange/projection/_som.c: In function ‘__pyx_pw_6Orange_10projection_4_som_1get_winners’:\nOrange/projection/_som.c:2964:223: warning: ‘__pyx_v_win_y’ may be used uninitialized in this function [-Wmaybe-uninitialized]\n 2964 |           *__Pyx_BufPtrStrided2d(__pyx_t_5numpy_int16_t *, __pyx_pybuffernd_winners.rcbuffer->pybuffer.buf, __pyx_t_27, __pyx_pybuffernd_winners.diminfo[0].strides, __pyx_t_28, __pyx_pybuffernd_winners.diminfo[1].strides) = __pyx_v_win_y;\n      |           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~\nOrange/projection/_som.c:2665:7: note: ‘__pyx_v_win_y’ was declared here\n 2665 |   int __pyx_v_win_y;\n      |       ^~~~~~~~~~~~~\nOrange/projection/_som.c:2953:223: warning: ‘__pyx_v_win_x’ may be used uninitialized in this function [-Wmaybe-uninitialized]\n 2953 |           *__Pyx_BufPtrStrided2d(__pyx_t_5numpy_int16_t *, __pyx_pybuffernd_winners.rcbuffer->pybuffer.buf, __pyx_t_25, __pyx_pybuffernd_winners.diminfo[0].strides, __pyx_t_26, __pyx_pybuffernd_winners.diminfo[1].strides) = __pyx_v_win_x;\n      |           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~\nOrange/projection/_som.c:2664:7: note: ‘__pyx_v_win_x’ was declared here\n 2664 |   int __pyx_v_win_x;\n      |       ^~~~~~~~~~~~~\nOrange/projection/_som.c: In function ‘__pyx_pf_6Orange_10projection_4_som_10update_sparse_hex.constprop’:\nOrange/projection/_som.c:5515:7: warning: ‘__pyx_v_win_y’ may be used uninitialized in this function [-Wmaybe-uninitialized]\n 5515 |   int __pyx_v_win_y;\n      |       ^~~~~~~~~~~~~\nOrange/projection/_som.c:5514:7: warning: ‘__pyx_v_win_x’ may be used uninitialized in this function [-Wmaybe-uninitialized]\n 5514 |   int __pyx_v_win_x;\n      |       ^~~~~~~~~~~~~\n/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages/setuptools/command/install.py:37: SetuptoolsDeprecationWarning: setup.py install is deprecated. Use build and pip and other standards-based tools.\n  setuptools.SetuptoolsDeprecationWarning,\n/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages/setuptools/command/easy_install.py:159: EasyInstallDeprecationWarning: easy_install command is deprecated. Use build and pip and other standards-based tools.\n  EasyInstallDeprecationWarning,\n/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages/setuptools/command/install.py:37: SetuptoolsDeprecationWarning: setup.py install is deprecated. Use build and pip and other standards-based tools.\n  setuptools.SetuptoolsDeprecationWarning,\n/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81/.venv/lib/python3.7/site-packages/setuptools/command/egg_info.py:628: SetuptoolsDeprecationWarning: Custom 'build_py' does not implement 'get_data_files_without_manifest'.\nPlease extend command classes from setuptools instead of distutils.\n  SetuptoolsDeprecationWarning\n",
    "new_commit_res_code": 0,
    "new_commit_res_stdout": "============================= test session starts ==============================\nplatform linux -- Python 3.7.9, pytest-7.4.4, pluggy-1.2.0\nrootdir: /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81\ncollected 17 items\n\nr2e_tests/test_1.py sss..............                                    [100%]\n\n==================================== PASSES ====================================\n=========================== short test summary info ============================\nPASSED r2e_tests/test_1.py::TestOWLouvain::test_clusters_ordered_by_size\nPASSED r2e_tests/test_1.py::TestOWLouvain::test_dense_and_sparse_return_same_result\nPASSED r2e_tests/test_1.py::TestOWLouvain::test_deterministic_clustering\nPASSED r2e_tests/test_1.py::TestOWLouvain::test_do_not_recluster_on_same_data\nPASSED r2e_tests/test_1.py::TestOWLouvain::test_empty_dataset\nPASSED r2e_tests/test_1.py::TestOWLouvain::test_graph_output\nPASSED r2e_tests/test_1.py::TestOWLouvain::test_image_export\nPASSED r2e_tests/test_1.py::TestOWLouvain::test_invalidate\nPASSED r2e_tests/test_1.py::TestOWLouvain::test_migrate_settings\nPASSED r2e_tests/test_1.py::TestOWLouvain::test_minimum_size\nPASSED r2e_tests/test_1.py::TestOWLouvain::test_msg_base_class\nPASSED r2e_tests/test_1.py::TestOWLouvain::test_normalize_data\nPASSED r2e_tests/test_1.py::TestOWLouvain::test_only_recluster_when_necessary_pca_components_change\nPASSED r2e_tests/test_1.py::TestOWLouvain::test_removing_data\nSKIPPED [1] .venv/lib/python3.7/site-packages/orangewidget/tests/base.py:244: image exporting not tested as .widget was not set\nSKIPPED [1] .venv/lib/python3.7/site-packages/orangewidget/tests/base.py:238: minimum size not tested as .widget was not set\nSKIPPED [1] .venv/lib/python3.7/site-packages/orangewidget/tests/base.py:250: msg base class not tested as .widget was not set\n======================== 14 passed, 3 skipped in 5.50s =========================\nWidgets left: 0    Max widgets: 1020 \n\n",
    "new_commit_res_stderr": "",
    "old_commit_res_code": 1,
    "old_commit_res_stdout": "============================= test session starts ==============================\nplatform linux -- Python 3.7.9, pytest-7.4.4, pluggy-1.2.0\nrootdir: /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_3b2c6c87051a852fdb6884847464f16d1d526d81\ncollected 17 items\n\nr2e_tests/test_1.py sss........F.....                                    [100%]\n\n=================================== FAILURES ===================================\n_____________________ TestOWLouvain.test_migrate_settings ______________________\n\nself = <r2e_tests.test_1.TestOWLouvain testMethod=test_migrate_settings>\n\n    def test_migrate_settings(self):\n        # any context settings are removed\n        settings = {\"context_settings\": []}\n        self.widget.migrate_settings(settings, 1)\n>       self.assertEqual(len(settings), 0)\nE       AssertionError: 1 != 0\n\nr2e_tests/test_1.py:259: AssertionError\n==================================== PASSES ====================================\n=========================== short test summary info ============================\nPASSED r2e_tests/test_1.py::TestOWLouvain::test_clusters_ordered_by_size\nPASSED r2e_tests/test_1.py::TestOWLouvain::test_dense_and_sparse_return_same_result\nPASSED r2e_tests/test_1.py::TestOWLouvain::test_deterministic_clustering\nPASSED r2e_tests/test_1.py::TestOWLouvain::test_do_not_recluster_on_same_data\nPASSED r2e_tests/test_1.py::TestOWLouvain::test_empty_dataset\nPASSED r2e_tests/test_1.py::TestOWLouvain::test_graph_output\nPASSED r2e_tests/test_1.py::TestOWLouvain::test_image_export\nPASSED r2e_tests/test_1.py::TestOWLouvain::test_invalidate\nPASSED r2e_tests/test_1.py::TestOWLouvain::test_minimum_size\nPASSED r2e_tests/test_1.py::TestOWLouvain::test_msg_base_class\nPASSED r2e_tests/test_1.py::TestOWLouvain::test_normalize_data\nPASSED r2e_tests/test_1.py::TestOWLouvain::test_only_recluster_when_necessary_pca_components_change\nPASSED r2e_tests/test_1.py::TestOWLouvain::test_removing_data\nSKIPPED [1] .venv/lib/python3.7/site-packages/orangewidget/tests/base.py:244: image exporting not tested as .widget was not set\nSKIPPED [1] .venv/lib/python3.7/site-packages/orangewidget/tests/base.py:238: minimum size not tested as .widget was not set\nSKIPPED [1] .venv/lib/python3.7/site-packages/orangewidget/tests/base.py:250: msg base class not tested as .widget was not set\nFAILED r2e_tests/test_1.py::TestOWLouvain::test_migrate_settings - AssertionE...\n=================== 1 failed, 13 passed, 3 skipped in 3.52s ====================\nWidgets left: 0    Max widgets: 1020 \n\n",
    "old_commit_res_stderr": ""
}
Orange/widgets/unsupervised/owlouvainclustering.py Orange/widgets/unsupervised/tests/test_owlouvain.py
We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[ISSUE]

**Title:** Incorrect Handling of `context_settings` During Settings Migration

**Description:**
When migrating settings from older versions, the `context_settings` are not properly handled. Specifically, empty `context_settings` are not removed, and existing `context_settings` entries are not correctly migrated into individual settings. This results in leftover `context_settings` in the settings and improperly migrated configuration values.

**Example Code:**
```python
# Migrating with empty context_settings
settings = {"context_settings": []}
OWLouvainClustering.migrate_settings(settings, 1)
# Expected: settings should be empty

# Migrating with existing context_settings
settings = {
    "context_settings": [Context(values={
        "__version__": 1,
        "apply_pca": (True, -2),
        "k_neighbors": (29, -2),
        "metric_idx": (1, -2),
        "normalize": (False, -2),
        "pca_components": (10, -2),
        "resolution": (1.0, -2)
    })]
}
OWLouvainClustering.migrate_settings(settings, 1)
# Expected: settings contains individual entries like 'apply_pca': True, 'k_neighbors': 29, etc.
```

**Expected Behavior:**
- If `context_settings` is empty, it should be removed from the `settings`.
- If `context_settings` contains values, each setting should be migrated into individual entries within `settings`.

**Actual Behavior:**
- Empty `context_settings` are not removed, leaving `settings` with an empty `context_settings`.
- Existing `context_settings` are not correctly migrated, resulting in improper settings configuration.

[/ISSUE]
--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.

--- BEGIN FILE ---
```
### Orange/widgets/unsupervised/owlouvainclustering.py
from functools import partial

from concurrent import futures
from concurrent.futures import Future

from types import SimpleNamespace as namespace
from typing import Optional, Callable, Tuple, Any

import numpy as np
import scipy.sparse as sp
import networkx as nx

from AnyQt.QtCore import (
    Qt, QObject, QTimer, pyqtSignal as Signal, pyqtSlot as Slot
)
from AnyQt.QtWidgets import QSlider, QCheckBox, QWidget, QLabel

from Orange.clustering.louvain import matrix_to_knn_graph, Louvain
from Orange.data import Table, DiscreteVariable
from Orange.data.util import get_unique_names, array_equal
from Orange import preprocess
from Orange.projection import PCA
from Orange.widgets import widget, gui, report
from Orange.widgets.settings import Setting
from Orange.widgets.utils.annotated_data import add_columns, \
    ANNOTATED_DATA_SIGNAL_NAME
from Orange.widgets.utils.concurrent import FutureWatcher
from Orange.widgets.utils.signals import Input, Output
from Orange.widgets.utils.widgetpreview import WidgetPreview
from Orange.widgets.widget import Msg

try:
    from orangecontrib.network.network import Network
except ImportError:
    Network = None


_MAX_PCA_COMPONENTS = 50
_DEFAULT_PCA_COMPONENTS = 25
_MAX_K_NEIGBOURS = 200
_DEFAULT_K_NEIGHBORS = 30


METRICS = [("Euclidean", "l2"), ("Manhattan", "l1")]


class OWLouvainClustering(widget.OWWidget):
    name = "Louvain Clustering"
    description = "Detects communities in a network of nearest neighbors."
    icon = "icons/LouvainClustering.svg"
    priority = 2110

    want_main_area = False
    resizing_enabled = False

    class Inputs:
        data = Input("Data", Table, default=True)

    class Outputs:
        annotated_data = Output(ANNOTATED_DATA_SIGNAL_NAME, Table, default=True)
        if Network is not None:
            graph = Output("Network", Network)

    apply_pca = Setting(True)
    pca_components = Setting(_DEFAULT_PCA_COMPONENTS)
    normalize = Setting(True)
    metric_idx = Setting(0)
    k_neighbors = Setting(_DEFAULT_K_NEIGHBORS)
    resolution = Setting(1.)
    auto_commit = Setting(False)

    class Information(widget.OWWidget.Information):
        modified = Msg("Press commit to recompute clusters and send new data")

    class Error(widget.OWWidget.Error):
        empty_dataset = Msg("No features in data")

    def __init__(self):
        super().__init__()

        self.data = None  # type: Optional[Table]
        self.preprocessed_data = None  # type: Optional[Table]
        self.pca_projection = None  # type: Optional[Table]
        self.graph = None  # type: Optional[nx.Graph]
        self.partition = None  # type: Optional[np.array]
        # Use a executor with a single worker, to limit CPU overcommitment for
        # cancelled tasks. The method does not have a fine cancellation
        # granularity so we assure that there are not N - 1 jobs executing
        # for no reason only to be thrown away. It would be better to use the
        # global pool but implement a limit on jobs from this source.
        self.__executor = futures.ThreadPoolExecutor(max_workers=1)
        self.__task = None   # type: Optional[TaskState]
        self.__invalidated = False
        # coalescing commit timer
        self.__commit_timer = QTimer(self, singleShot=True)
        self.__commit_timer.timeout.connect(self.commit)

        # Set up UI
        info_box = gui.vBox(self.controlArea, "Info")
        self.info_label = gui.widgetLabel(info_box, "No data on input.")  # type: QLabel

        preprocessing_box = gui.vBox(self.controlArea, "Preprocessing")
        self.normalize_cbx = gui.checkBox(
            preprocessing_box, self, "normalize", label="Normalize data",
            callback=self._invalidate_preprocessed_data,
        )  # type: QCheckBox
        self.apply_pca_cbx = gui.checkBox(
            preprocessing_box, self, "apply_pca", label="Apply PCA preprocessing",
            callback=self._apply_pca_changed,
        )  # type: QCheckBox
        self.pca_components_slider = gui.hSlider(
            preprocessing_box, self, "pca_components", label="PCA Components: ", minValue=2,
            maxValue=_MAX_PCA_COMPONENTS,
            callback=self._invalidate_pca_projection, tracking=False
        )  # type: QSlider

        graph_box = gui.vBox(self.controlArea, "Graph parameters")
        self.metric_combo = gui.comboBox(
            graph_box, self, "metric_idx", label="Distance metric",
            items=[m[0] for m in METRICS], callback=self._invalidate_graph,
            orientation=Qt.Horizontal,
        )  # type: gui.OrangeComboBox
        self.k_neighbors_spin = gui.spin(
            graph_box, self, "k_neighbors", minv=1, maxv=_MAX_K_NEIGBOURS,
            label="k neighbors", controlWidth=80, alignment=Qt.AlignRight,
            callback=self._invalidate_graph,
        )  # type: gui.SpinBoxWFocusOut
        self.resolution_spin = gui.hSlider(
            graph_box, self, "resolution", minValue=0, maxValue=5., step=1e-1,
            label="Resolution", intOnly=False, labelFormat="%.1f",
            callback=self._invalidate_partition, tracking=False,
        )  # type: QSlider
        self.resolution_spin.parent().setToolTip(
            "The resolution parameter affects the number of clusters to find. "
            "Smaller values tend to produce more clusters and larger values "
            "retrieve less clusters."
        )
        self.apply_button = gui.auto_apply(
            self.controlArea, self, "auto_commit", box=None,
            commit=lambda: self.commit(), callback=lambda: self._on_auto_commit_changed()
        )  # type: QWidget

    def _preprocess_data(self):
        if self.preprocessed_data is None:
            if self.normalize:
                normalizer = preprocess.Normalize(center=False)
                self.preprocessed_data = normalizer(self.data)
            else:
                self.preprocessed_data = self.data

    def _apply_pca_changed(self):
        self.controls.pca_components.setEnabled(self.apply_pca)
        self._invalidate_graph()

    def _invalidate_preprocessed_data(self):
        self.preprocessed_data = None
        self._invalidate_pca_projection()
        # If we don't apply PCA, this still invalidates the graph, otherwise
        # this change won't be propagated further
        if not self.apply_pca:
            self._invalidate_graph()

    def _invalidate_pca_projection(self):
        self.pca_projection = None
        if not self.apply_pca:
            return

        self._invalidate_graph()
        self._set_modified(True)

    def _invalidate_graph(self):
        self.graph = None
        self._invalidate_partition()
        self._set_modified(True)

    def _invalidate_partition(self):
        self.partition = None
        self._invalidate_output()
        self.Information.modified()
        self._set_modified(True)

    def _invalidate_output(self):
        self.__invalidated = True
        if self.__task is not None:
            self.__cancel_task(wait=False)

        if self.auto_commit:
            self.__commit_timer.start()
        else:
            self.__set_state_ready()

    def _set_modified(self, state):
        """
        Mark the widget (GUI) as containing modified state.
        """
        if self.data is None:
            # does not apply when we have no data
            state = False
        elif self.auto_commit:
            # does not apply when auto commit is on
            state = False
        self.Information.modified(shown=state)

    def _on_auto_commit_changed(self):
        if self.auto_commit and self.__invalidated:
            self.commit()

    def cancel(self):
        """Cancel any running jobs."""
        self.__cancel_task(wait=False)
        self.__set_state_ready()

    def commit(self):
        self.__commit_timer.stop()
        self.__invalidated = False
        self._set_modified(False)

        # Cancel current running task
        self.__cancel_task(wait=False)

        if self.data is None:
            self.__set_state_ready()
            return

        self.Error.clear()

        if self.partition is not None:
            self.__set_state_ready()
            self._send_data()
            return

        self._preprocess_data()

        state = TaskState(self)

        # Prepare/assemble the task(s) to run; reuse partial results
        if self.apply_pca:
            if self.pca_projection is not None:
                data = self.pca_projection
                pca_components = None
            else:
                data = self.preprocessed_data
                pca_components = self.pca_components
        else:
            data = self.preprocessed_data
            pca_components = None

        if self.graph is not None:
            # run on graph only; no need to do PCA and k-nn search ...
            graph = self.graph
            k_neighbors = metric = None
        else:
            k_neighbors, metric = self.k_neighbors, METRICS[self.metric_idx][1]
            graph = None

        if graph is None:
            task = partial(
                run_on_data, data, pca_components=pca_components,
                normalize=self.normalize, k_neighbors=k_neighbors,
                metric=metric, resolution=self.resolution, state=state,
            )
        else:
            task = partial(
                run_on_graph, graph, resolution=self.resolution, state=state
            )

        self.info_label.setText("Running...")
        self.__set_state_busy()
        self.__start_task(task, state)

    @Slot(object)
    def __set_partial_results(self, result):
        # type: (Tuple[str, Any]) -> None
        which, res = result
        if which == "pca_projection":
            assert isinstance(res, Table) and len(res) == len(self.data)
            self.pca_projection = res
        elif which == "graph":
            assert isinstance(res, nx.Graph)
            self.graph = res
        elif which == "partition":
            assert isinstance(res, np.ndarray)
            self.partition = res
        else:
            assert False, which

    @Slot(object)
    def __on_done(self, future):
        # type: (Future["Results"]) -> None
        assert future.done()
        assert self.__task is not None
        assert self.__task.future is future
        assert self.__task.watcher.future() is future
        self.__task, task = None, self.__task
        task.deleteLater()

        self.__set_state_ready()

        result = future.result()
        self.__set_results(result)

    @Slot(str)
    def setStatusMessage(self, text):
        super().setStatusMessage(text)

    @Slot(float)
    def progressBarSet(self, value, *a, **kw):
        super().progressBarSet(value, *a, **kw)

    def __set_state_ready(self):
        self.progressBarFinished()
        self.setInvalidated(False)
        self.setStatusMessage("")

    def __set_state_busy(self):
        self.progressBarInit()
        self.setInvalidated(True)

    def __start_task(self, task, state):
        # type: (Callable[[], Any], TaskState) -> None
        assert self.__task is None
        state.status_changed.connect(self.setStatusMessage)
        state.progress_changed.connect(self.progressBarSet)
        state.partial_result_ready.connect(self.__set_partial_results)
        state.watcher.done.connect(self.__on_done)
        state.start(self.__executor, task)
        state.setParent(self)
        self.__task = state

    def __cancel_task(self, wait=True):
        # Cancel and dispose of the current task
        if self.__task is not None:
            state, self.__task = self.__task, None
            state.cancel()
            state.partial_result_ready.disconnect(self.__set_partial_results)
            state.status_changed.disconnect(self.setStatusMessage)
            state.progress_changed.disconnect(self.progressBarSet)
            state.watcher.done.disconnect(self.__on_done)
            if wait:
                futures.wait([state.future])
                state.deleteLater()
            else:
                w = FutureWatcher(state.future, parent=state)
                w.done.connect(state.deleteLater)

    def __set_results(self, results):
        # type: ("Results") -> None
        # NOTE: All of these have already been set by __set_partial_results,
        # we double check that they are aliases
        if results.pca_projection is not None:
            assert self.pca_components == results.pca_components
            assert self.pca_projection is results.pca_projection
            self.pca_projection = results.pca_projection
        if results.graph is not None:
            assert results.metric == METRICS[self.metric_idx][1]
            assert results.k_neighbors == self.k_neighbors
            assert self.graph is results.graph
            self.graph = results.graph
        if results.partition is not None:
            assert results.resolution == self.resolution
            assert self.partition is results.partition
            self.partition = results.partition

        # Display the number of found clusters in the UI
        num_clusters = len(np.unique(self.partition))
        self.info_label.setText("%d clusters found." % num_clusters)

        self._send_data()

    def _send_data(self):
        if self.partition is None or self.data is None:
            return
        domain = self.data.domain
        # Compute the frequency of each cluster index
        counts = np.bincount(self.partition)
        indices = np.argsort(counts)[::-1]
        index_map = {n: o for n, o in zip(indices, range(len(indices)))}
        new_partition = list(map(index_map.get, self.partition))

        cluster_var = DiscreteVariable(
            get_unique_names(domain, "Cluster"),
            values=["C%d" % (i + 1) for i, _ in enumerate(np.unique(new_partition))]
        )

        new_domain = add_columns(domain, metas=[cluster_var])
        new_table = self.data.transform(new_domain)
        new_table.get_column_view(cluster_var)[0][:] = new_partition
        self.Outputs.annotated_data.send(new_table)

        if Network is not None:
            n_edges = self.graph.number_of_edges()
            edges = sp.coo_matrix(
                (np.ones(n_edges), np.array(self.graph.edges()).T),
                shape=(n_edges, n_edges))
            graph = Network(new_table, edges)
            self.Outputs.graph.send(graph)

    @Inputs.data
    def set_data(self, data):
        self.Error.clear()

        prev_data, self.data = self.data, data
        # Make sure to properly enable/disable slider based on `apply_pca` setting
        self.controls.pca_components.setEnabled(self.apply_pca)

        if prev_data and self.data and array_equal(prev_data.X, self.data.X):
            if self.auto_commit and not self.isInvalidated():
                self._send_data()
            return

        self.cancel()
        # Clear the outputs
        self.Outputs.annotated_data.send(None)
        if Network is not None:
            self.Outputs.graph.send(None)

        # Clear internal state
        self.clear()
        self._invalidate_pca_projection()

        # Make sure the dataset is ok
        if self.data is not None and len(self.data.domain.attributes) < 1:
            self.Error.empty_dataset()
            self.data = None

        if self.data is None:
            return

        # Can't have more PCA components than the number of attributes
        n_attrs = len(data.domain.attributes)
        self.pca_components_slider.setMaximum(min(_MAX_PCA_COMPONENTS, n_attrs))
        # Can't have more k neighbors than there are data points
        self.k_neighbors_spin.setMaximum(min(_MAX_K_NEIGBOURS, len(data) - 1))

        self.info_label.setText("Clustering not yet run.")

        self.commit()

    def clear(self):
        self.__cancel_task(wait=False)
        self.preprocessed_data = None
        self.pca_projection = None
        self.graph = None
        self.partition = None
        self.Error.clear()
        self.Information.modified.clear()
        self.info_label.setText("No data on input.")

    def onDeleteWidget(self):
        self.__cancel_task(wait=True)
        self.__executor.shutdown(True)
        self.clear()
        self.data = None
        super().onDeleteWidget()

    def send_report(self):
        pca = report.bool_str(self.apply_pca)
        if self.apply_pca:
            pca += report.plural(", {number} component{s}", self.pca_components)

        self.report_items((
            ("Normalize data", report.bool_str(self.normalize)),
            ("PCA preprocessing", pca),
            ("Metric", METRICS[self.metric_idx][0]),
            ("k neighbors", self.k_neighbors),
            ("Resolution", self.resolution),
        ))


class TaskState(QObject):

    status_changed = Signal(str)
    _p_status_changed = Signal(str)

    progress_changed = Signal(float)
    _p_progress_changed = Signal(float)

    partial_result_ready = Signal(object)
    _p_partial_result_ready = Signal(object)

    def __init__(self, *args):
        super().__init__(*args)
        self.__future = None
        self.watcher = FutureWatcher()
        self.__interuption_requested = False
        self.__progress = 0
        # Helpers to route the signal emits via a this object's queue.
        # This ensures 'atomic' disconnect from signals for targets/slots
        # in the same thread. Requires that the event loop is running in this
        # object's thread.
        self._p_status_changed.connect(
            self.status_changed, Qt.QueuedConnection)
        self._p_progress_changed.connect(
            self.progress_changed, Qt.QueuedConnection)
        self._p_partial_result_ready.connect(
            self.partial_result_ready, Qt.QueuedConnection)

    @property
    def future(self):
        # type: () -> Future
        return self.__future

    def set_status(self, text):
        self._p_status_changed.emit(text)

    def set_progress_value(self, value):
        if round(value, 1) > round(self.__progress, 1):
            # Only emit progress when it has changed sufficiently
            self._p_progress_changed.emit(value)
            self.__progress = value

    def set_partial_results(self, value):
        self._p_partial_result_ready.emit(value)

    def is_interuption_requested(self):
        return self.__interuption_requested

    def start(self, executor, func=None):
        # type: (futures.Executor, Callable[[], Any]) -> Future
        assert self.future is None
        assert not self.__interuption_requested
        self.__future = executor.submit(func)
        self.watcher.setFuture(self.future)
        return self.future

    def cancel(self):
        assert not self.__interuption_requested
        self.__interuption_requested = True
        if self.future is not None:
            rval = self.future.cancel()
        else:
            # not even scheduled yet
            rval = True
        return rval


class InteruptRequested(BaseException):
    pass


class Results(namespace):
    pca_projection = None    # type: Optional[Table]
    pca_components = None    # type: Optional[int]
    normalize = None         # type: Optional[bool]
    k_neighbors = None       # type: Optional[int]
    metric = None            # type: Optional[str]
    graph = None             # type: Optional[nx.Graph]
    resolution = None        # type: Optional[float]
    partition = None         # type: Optional[np.ndarray]


def run_on_data(data, normalize, pca_components, k_neighbors, metric, resolution, state):
    # type: (Table, Optional[int], int, str, float, bool, TaskState) -> Results
    """
    Run the louvain clustering on `data`.

    state is used to report progress and partial results. Returns early if
    `task.is_interuption_requested()` returns true.

    Parameters
    ----------
    data : Table
        Data table
    normalize : bool
        If `True`, the data is first normalized before computing PCA.
    pca_components : Optional[int]
        If not `None` then the data is first projected onto first
        `pca_components` principal components.
    k_neighbors : int
        Passed to `table_to_knn_graph`
    metric : str
        Passed to `table_to_knn_graph`
    resolution : float
        Passed to `Louvain`
    state : TaskState

    Returns
    -------
    res : Results
    """
    state = state  # type: TaskState
    res = Results(
        normalize=normalize, pca_components=pca_components,
        k_neighbors=k_neighbors, metric=metric, resolution=resolution,
    )
    step = 0
    if state.is_interuption_requested():
        return res

    if pca_components is not None:
        steps = 3
        state.set_status("Computing PCA...")
        pca = PCA(n_components=pca_components, random_state=0)

        data = res.pca_projection = pca(data)(data)
        assert isinstance(data, Table)
        state.set_partial_results(("pca_projection", res.pca_projection))
        step += 1
    else:
        steps = 2

    if state.is_interuption_requested():
        return res

    state.set_progress_value(100. * step / steps)
    state.set_status("Building graph...")

    # Apply Louvain preprocessing before converting the table into a graph
    louvain = Louvain(resolution=resolution, random_state=0)
    data = louvain.preprocess(data)

    if state.is_interuption_requested():
        return res

    def pcallback(val):
        state.set_progress_value((100. * step + 100 * val) / steps)
        if state.is_interuption_requested():
            raise InteruptRequested()

    try:
        res.graph = graph = matrix_to_knn_graph(
            data.X, k_neighbors=k_neighbors, metric=metric,
            progress_callback=pcallback)
    except InteruptRequested:
        return res

    state.set_partial_results(("graph", res.graph))

    step += 1
    state.set_progress_value(100 * step / steps)
    state.set_status("Detecting communities...")
    if state.is_interuption_requested():
        return res

    res.partition = louvain(graph)
    state.set_partial_results(("partition", res.partition))
    return res


def run_on_graph(graph, resolution, state):
    # type: (nx.Graph, float, TaskState) -> Results
    """
    Run the louvain clustering on `graph`.
    """
    state = state  # type: TaskState
    res = Results(resolution=resolution)
    louvain = Louvain(resolution=resolution, random_state=0)
    state.set_status("Detecting communities...")
    if state.is_interuption_requested():
        return res
    partition = louvain(graph)
    res.partition = partition
    state.set_partial_results(("partition", res.partition))
    return res


if __name__ == "__main__":  # pragma: no cover
    WidgetPreview(OWLouvainClustering).run(Table("iris"))

```
--- END FILE ---

Please first localize the bug based on the issue statement, and then generate *SEARCH/REPLACE* edits to fix the issue.

Every *SEARCH/REPLACE* edit must use this format:
1. The file path
2. The start of search block: <<<<<<< SEARCH
3. A contiguous chunk of lines to search for in the existing source code
4. The dividing line: =======
5. The lines to replace into the source code
6. The end of the replace block: >>>>>>> REPLACE

Here is an example:

```python
### mathweb/flask/app.py
<<<<<<< SEARCH
from flask import Flask
=======
import math
from flask import Flask
>>>>>>> REPLACE
```
</solution>

Please note that the *SEARCH/REPLACE* edit REQUIRES PROPER INDENTATION. If you would like to add the line '        print(x)', you must fully write that out, with all those spaces before the code!
Wrap each *SEARCH/REPLACE* edit in a code block as shown in the example above. If you have multiple *SEARCH/REPLACE* edits, use a separate code block for each one.
[ISSUE]

**Title:** Incorrect Handling of `context_settings` During Settings Migration

**Description:**
When migrating settings from older versions, the `context_settings` are not properly handled. Specifically, empty `context_settings` are not removed, and existing `context_settings` entries are not correctly migrated into individual settings. This results in leftover `context_settings` in the settings and improperly migrated configuration values.

**Example Code:**
```python
# Migrating with empty context_settings
settings = {"context_settings": []}
OWLouvainClustering.migrate_settings(settings, 1)
# Expected: settings should be empty

# Migrating with existing context_settings
settings = {
    "context_settings": [Context(values={
        "__version__": 1,
        "apply_pca": (True, -2),
        "k_neighbors": (29, -2),
        "metric_idx": (1, -2),
        "normalize": (False, -2),
        "pca_components": (10, -2),
        "resolution": (1.0, -2)
    })]
}
OWLouvainClustering.migrate_settings(settings, 1)
# Expected: settings contains individual entries like 'apply_pca': True, 'k_neighbors': 29, etc.
```

**Expected Behavior:**
- If `context_settings` is empty, it should be removed from the `settings`.
- If `context_settings` contains values, each setting should be migrated into individual entries within `settings`.

**Actual Behavior:**
- Empty `context_settings` are not removed, leaving `settings` with an empty `context_settings`.
- Existing `context_settings` are not correctly migrated, resulting in improper settings configuration.

[/ISSUE]
{
    "TestOWLouvain.test_clusters_ordered_by_size": "PASSED",
    "TestOWLouvain.test_dense_and_sparse_return_same_result": "PASSED",
    "TestOWLouvain.test_deterministic_clustering": "PASSED",
    "TestOWLouvain.test_do_not_recluster_on_same_data": "PASSED",
    "TestOWLouvain.test_empty_dataset": "PASSED",
    "TestOWLouvain.test_graph_output": "PASSED",
    "TestOWLouvain.test_image_export": "PASSED",
    "TestOWLouvain.test_invalidate": "PASSED",
    "TestOWLouvain.test_migrate_settings": "PASSED",
    "TestOWLouvain.test_minimum_size": "PASSED",
    "TestOWLouvain.test_msg_base_class": "PASSED",
    "TestOWLouvain.test_normalize_data": "PASSED",
    "TestOWLouvain.test_only_recluster_when_necessary_pca_components_change": "PASSED",
    "TestOWLouvain.test_removing_data": "PASSED"
}
diff --git a/Orange/widgets/unsupervised/owlouvainclustering.py b/Orange/widgets/unsupervised/owlouvainclustering.py
index 18f322a6f..5b9fdc571 100644
--- a/Orange/widgets/unsupervised/owlouvainclustering.py
+++ b/Orange/widgets/unsupervised/owlouvainclustering.py
@@ -50,6 +50,8 @@ class OWLouvainClustering(widget.OWWidget):
     icon = "icons/LouvainClustering.svg"
     priority = 2110
 
+    settings_version = 2
+
     want_main_area = False
     resizing_enabled = False
 
@@ -466,6 +468,20 @@ class OWLouvainClustering(widget.OWWidget):
             ("Resolution", self.resolution),
         ))
 
+    @classmethod
+    def migrate_settings(cls, settings, version):
+        if version < 2 and "context_settings" in settings:
+            try:
+                current_context = settings["context_settings"][0]
+                for n in ['apply_pca', 'k_neighbors', 'metric_idx',
+                          'normalize', 'pca_components', 'resolution']:
+                    if n in current_context.values:
+                        settings[n] = current_context.values[n][0]
+            except:  # pylint: disable=bare-except
+                pass
+            finally:
+                del settings["context_settings"]
+
 
 class TaskState(QObject):
 

{"Orange/widgets/unsupervised/owlouvainclustering.py": "from functools import partial\n\nfrom concurrent import futures\nfrom concurrent.futures import Future\n\nfrom types import SimpleNamespace as namespace\nfrom typing import Optional, Callable, Tuple, Any\n\nimport numpy as np\nimport scipy.sparse as sp\nimport networkx as nx\n\nfrom AnyQt.QtCore import (\n    Qt, QObject, QTimer, pyqtSignal as Signal, pyqtSlot as Slot\n)\nfrom AnyQt.QtWidgets import QSlider, QCheckBox, QWidget, QLabel\n\nfrom Orange.clustering.louvain import matrix_to_knn_graph, Louvain\nfrom Orange.data import Table, DiscreteVariable\nfrom Orange.data.util import get_unique_names, array_equal\nfrom Orange import preprocess\nfrom Orange.projection import PCA\nfrom Orange.widgets import widget, gui, report\nfrom Orange.widgets.settings import Setting\nfrom Orange.widgets.utils.annotated_data import add_columns, \\\n    ANNOTATED_DATA_SIGNAL_NAME\nfrom Orange.widgets.utils.concurrent import FutureWatcher\nfrom Orange.widgets.utils.signals import Input, Output\nfrom Orange.widgets.utils.widgetpreview import WidgetPreview\nfrom Orange.widgets.widget import Msg\n\ntry:\n    from orangecontrib.network.network import Network\nexcept ImportError:\n    Network = None\n\n\n_MAX_PCA_COMPONENTS = 50\n_DEFAULT_PCA_COMPONENTS = 25\n_MAX_K_NEIGBOURS = 200\n_DEFAULT_K_NEIGHBORS = 30\n\n\nMETRICS = [(\"Euclidean\", \"l2\"), (\"Manhattan\", \"l1\")]\n\n\nclass OWLouvainClustering(widget.OWWidget):\n    name = \"Louvain Clustering\"\n    description = \"Detects communities in a network of nearest neighbors.\"\n    icon = \"icons/LouvainClustering.svg\"\n    priority = 2110\n\n    want_main_area = False\n    resizing_enabled = False\n\n    class Inputs:\n        data = Input(\"Data\", Table, default=True)\n\n    class Outputs:\n        annotated_data = Output(ANNOTATED_DATA_SIGNAL_NAME, Table, default=True)\n        if Network is not None:\n            graph = Output(\"Network\", Network)\n\n    apply_pca = Setting(True)\n    pca_components = Setting(_DEFAULT_PCA_COMPONENTS)\n    normalize = Setting(True)\n    metric_idx = Setting(0)\n    k_neighbors = Setting(_DEFAULT_K_NEIGHBORS)\n    resolution = Setting(1.)\n    auto_commit = Setting(False)\n\n    class Information(widget.OWWidget.Information):\n        modified = Msg(\"Press commit to recompute clusters and send new data\")\n\n    class Error(widget.OWWidget.Error):\n        empty_dataset = Msg(\"No features in data\")\n\n    def __init__(self):\n        super().__init__()\n\n        self.data = None  # type: Optional[Table]\n        self.preprocessed_data = None  # type: Optional[Table]\n        self.pca_projection = None  # type: Optional[Table]\n        self.graph = None  # type: Optional[nx.Graph]\n        self.partition = None  # type: Optional[np.array]\n        # Use a executor with a single worker, to limit CPU overcommitment for\n        # cancelled tasks. The method does not have a fine cancellation\n        # granularity so we assure that there are not N - 1 jobs executing\n        # for no reason only to be thrown away. It would be better to use the\n        # global pool but implement a limit on jobs from this source.\n        self.__executor = futures.ThreadPoolExecutor(max_workers=1)\n        self.__task = None   # type: Optional[TaskState]\n        self.__invalidated = False\n        # coalescing commit timer\n        self.__commit_timer = QTimer(self, singleShot=True)\n        self.__commit_timer.timeout.connect(self.commit)\n\n        # Set up UI\n        info_box = gui.vBox(self.controlArea, \"Info\")\n        self.info_label = gui.widgetLabel(info_box, \"No data on input.\")  # type: QLabel\n\n        preprocessing_box = gui.vBox(self.controlArea, \"Preprocessing\")\n        self.normalize_cbx = gui.checkBox(\n            preprocessing_box, self, \"normalize\", label=\"Normalize data\",\n            callback=self._invalidate_preprocessed_data,\n        )  # type: QCheckBox\n        self.apply_pca_cbx = gui.checkBox(\n            preprocessing_box, self, \"apply_pca\", label=\"Apply PCA preprocessing\",\n            callback=self._apply_pca_changed,\n        )  # type: QCheckBox\n        self.pca_components_slider = gui.hSlider(\n            preprocessing_box, self, \"pca_components\", label=\"PCA Components: \", minValue=2,\n            maxValue=_MAX_PCA_COMPONENTS,\n            callback=self._invalidate_pca_projection, tracking=False\n        )  # type: QSlider\n\n        graph_box = gui.vBox(self.controlArea, \"Graph parameters\")\n        self.metric_combo = gui.comboBox(\n            graph_box, self, \"metric_idx\", label=\"Distance metric\",\n            items=[m[0] for m in METRICS], callback=self._invalidate_graph,\n            orientation=Qt.Horizontal,\n        )  # type: gui.OrangeComboBox\n        self.k_neighbors_spin = gui.spin(\n            graph_box, self, \"k_neighbors\", minv=1, maxv=_MAX_K_NEIGBOURS,\n            label=\"k neighbors\", controlWidth=80, alignment=Qt.AlignRight,\n            callback=self._invalidate_graph,\n        )  # type: gui.SpinBoxWFocusOut\n        self.resolution_spin = gui.hSlider(\n            graph_box, self, \"resolution\", minValue=0, maxValue=5., step=1e-1,\n            label=\"Resolution\", intOnly=False, labelFormat=\"%.1f\",\n            callback=self._invalidate_partition, tracking=False,\n        )  # type: QSlider\n        self.resolution_spin.parent().setToolTip(\n            \"The resolution parameter affects the number of clusters to find. \"\n            \"Smaller values tend to produce more clusters and larger values \"\n            \"retrieve less clusters.\"\n        )\n        self.apply_button = gui.auto_apply(\n            self.controlArea, self, \"auto_commit\", box=None,\n            commit=lambda: self.commit(), callback=lambda: self._on_auto_commit_changed()\n        )  # type: QWidget\n\n    def _preprocess_data(self):\n        if self.preprocessed_data is None:\n            if self.normalize:\n                normalizer = preprocess.Normalize(center=False)\n                self.preprocessed_data = normalizer(self.data)\n            else:\n                self.preprocessed_data = self.data\n\n    def _apply_pca_changed(self):\n        self.controls.pca_components.setEnabled(self.apply_pca)\n        self._invalidate_graph()\n\n    def _invalidate_preprocessed_data(self):\n        self.preprocessed_data = None\n        self._invalidate_pca_projection()\n        # If we don't apply PCA, this still invalidates the graph, otherwise\n        # this change won't be propagated further\n        if not self.apply_pca:\n            self._invalidate_graph()\n\n    def _invalidate_pca_projection(self):\n        self.pca_projection = None\n        if not self.apply_pca:\n            return\n\n        self._invalidate_graph()\n        self._set_modified(True)\n\n    def _invalidate_graph(self):\n        self.graph = None\n        self._invalidate_partition()\n        self._set_modified(True)\n\n    def _invalidate_partition(self):\n        self.partition = None\n        self._invalidate_output()\n        self.Information.modified()\n        self._set_modified(True)\n\n    def _invalidate_output(self):\n        self.__invalidated = True\n        if self.__task is not None:\n            self.__cancel_task(wait=False)\n\n        if self.auto_commit:\n            self.__commit_timer.start()\n        else:\n            self.__set_state_ready()\n\n    def _set_modified(self, state):\n        \"\"\"\n        Mark the widget (GUI) as containing modified state.\n        \"\"\"\n        if self.data is None:\n            # does not apply when we have no data\n            state = False\n        elif self.auto_commit:\n            # does not apply when auto commit is on\n            state = False\n        self.Information.modified(shown=state)\n\n    def _on_auto_commit_changed(self):\n        if self.auto_commit and self.__invalidated:\n            self.commit()\n\n    def cancel(self):\n        \"\"\"Cancel any running jobs.\"\"\"\n        self.__cancel_task(wait=False)\n        self.__set_state_ready()\n\n    def commit(self):\n        self.__commit_timer.stop()\n        self.__invalidated = False\n        self._set_modified(False)\n\n        # Cancel current running task\n        self.__cancel_task(wait=False)\n\n        if self.data is None:\n            self.__set_state_ready()\n            return\n\n        self.Error.clear()\n\n        if self.partition is not None:\n            self.__set_state_ready()\n            self._send_data()\n            return\n\n        self._preprocess_data()\n\n        state = TaskState(self)\n\n        # Prepare/assemble the task(s) to run; reuse partial results\n        if self.apply_pca:\n            if self.pca_projection is not None:\n                data = self.pca_projection\n                pca_components = None\n            else:\n                data = self.preprocessed_data\n                pca_components = self.pca_components\n        else:\n            data = self.preprocessed_data\n            pca_components = None\n\n        if self.graph is not None:\n            # run on graph only; no need to do PCA and k-nn search ...\n            graph = self.graph\n            k_neighbors = metric = None\n        else:\n            k_neighbors, metric = self.k_neighbors, METRICS[self.metric_idx][1]\n            graph = None\n\n        if graph is None:\n            task = partial(\n                run_on_data, data, pca_components=pca_components,\n                normalize=self.normalize, k_neighbors=k_neighbors,\n                metric=metric, resolution=self.resolution, state=state,\n            )\n        else:\n            task = partial(\n                run_on_graph, graph, resolution=self.resolution, state=state\n            )\n\n        self.info_label.setText(\"Running...\")\n        self.__set_state_busy()\n        self.__start_task(task, state)\n\n    @Slot(object)\n    def __set_partial_results(self, result):\n        # type: (Tuple[str, Any]) -> None\n        which, res = result\n        if which == \"pca_projection\":\n            assert isinstance(res, Table) and len(res) == len(self.data)\n            self.pca_projection = res\n        elif which == \"graph\":\n            assert isinstance(res, nx.Graph)\n            self.graph = res\n        elif which == \"partition\":\n            assert isinstance(res, np.ndarray)\n            self.partition = res\n        else:\n            assert False, which\n\n    @Slot(object)\n    def __on_done(self, future):\n        # type: (Future[\"Results\"]) -> None\n        assert future.done()\n        assert self.__task is not None\n        assert self.__task.future is future\n        assert self.__task.watcher.future() is future\n        self.__task, task = None, self.__task\n        task.deleteLater()\n\n        self.__set_state_ready()\n\n        result = future.result()\n        self.__set_results(result)\n\n    @Slot(str)\n    def setStatusMessage(self, text):\n        super().setStatusMessage(text)\n\n    @Slot(float)\n    def progressBarSet(self, value, *a, **kw):\n        super().progressBarSet(value, *a, **kw)\n\n    def __set_state_ready(self):\n        self.progressBarFinished()\n        self.setInvalidated(False)\n        self.setStatusMessage(\"\")\n\n    def __set_state_busy(self):\n        self.progressBarInit()\n        self.setInvalidated(True)\n\n    def __start_task(self, task, state):\n        # type: (Callable[[], Any], TaskState) -> None\n        assert self.__task is None\n        state.status_changed.connect(self.setStatusMessage)\n        state.progress_changed.connect(self.progressBarSet)\n        state.partial_result_ready.connect(self.__set_partial_results)\n        state.watcher.done.connect(self.__on_done)\n        state.start(self.__executor, task)\n        state.setParent(self)\n        self.__task = state\n\n    def __cancel_task(self, wait=True):\n        # Cancel and dispose of the current task\n        if self.__task is not None:\n            state, self.__task = self.__task, None\n            state.cancel()\n            state.partial_result_ready.disconnect(self.__set_partial_results)\n            state.status_changed.disconnect(self.setStatusMessage)\n            state.progress_changed.disconnect(self.progressBarSet)\n            state.watcher.done.disconnect(self.__on_done)\n            if wait:\n                futures.wait([state.future])\n                state.deleteLater()\n            else:\n                w = FutureWatcher(state.future, parent=state)\n                w.done.connect(state.deleteLater)\n\n    def __set_results(self, results):\n        # type: (\"Results\") -> None\n        # NOTE: All of these have already been set by __set_partial_results,\n        # we double check that they are aliases\n        if results.pca_projection is not None:\n            assert self.pca_components == results.pca_components\n            assert self.pca_projection is results.pca_projection\n            self.pca_projection = results.pca_projection\n        if results.graph is not None:\n            assert results.metric == METRICS[self.metric_idx][1]\n            assert results.k_neighbors == self.k_neighbors\n            assert self.graph is results.graph\n            self.graph = results.graph\n        if results.partition is not None:\n            assert results.resolution == self.resolution\n            assert self.partition is results.partition\n            self.partition = results.partition\n\n        # Display the number of found clusters in the UI\n        num_clusters = len(np.unique(self.partition))\n        self.info_label.setText(\"%d clusters found.\" % num_clusters)\n\n        self._send_data()\n\n    def _send_data(self):\n        if self.partition is None or self.data is None:\n            return\n        domain = self.data.domain\n        # Compute the frequency of each cluster index\n        counts = np.bincount(self.partition)\n        indices = np.argsort(counts)[::-1]\n        index_map = {n: o for n, o in zip(indices, range(len(indices)))}\n        new_partition = list(map(index_map.get, self.partition))\n\n        cluster_var = DiscreteVariable(\n            get_unique_names(domain, \"Cluster\"),\n            values=[\"C%d\" % (i + 1) for i, _ in enumerate(np.unique(new_partition))]\n        )\n\n        new_domain = add_columns(domain, metas=[cluster_var])\n        new_table = self.data.transform(new_domain)\n        new_table.get_column_view(cluster_var)[0][:] = new_partition\n        self.Outputs.annotated_data.send(new_table)\n\n        if Network is not None:\n            n_edges = self.graph.number_of_edges()\n            edges = sp.coo_matrix(\n                (np.ones(n_edges), np.array(self.graph.edges()).T),\n                shape=(n_edges, n_edges))\n            graph = Network(new_table, edges)\n            self.Outputs.graph.send(graph)\n\n    @Inputs.data\n    def set_data(self, data):\n        self.Error.clear()\n\n        prev_data, self.data = self.data, data\n        # Make sure to properly enable/disable slider based on `apply_pca` setting\n        self.controls.pca_components.setEnabled(self.apply_pca)\n\n        if prev_data and self.data and array_equal(prev_data.X, self.data.X):\n            if self.auto_commit and not self.isInvalidated():\n                self._send_data()\n            return\n\n        self.cancel()\n        # Clear the outputs\n        self.Outputs.annotated_data.send(None)\n        if Network is not None:\n            self.Outputs.graph.send(None)\n\n        # Clear internal state\n        self.clear()\n        self._invalidate_pca_projection()\n\n        # Make sure the dataset is ok\n        if self.data is not None and len(self.data.domain.attributes) < 1:\n            self.Error.empty_dataset()\n            self.data = None\n\n        if self.data is None:\n            return\n\n        # Can't have more PCA components than the number of attributes\n        n_attrs = len(data.domain.attributes)\n        self.pca_components_slider.setMaximum(min(_MAX_PCA_COMPONENTS, n_attrs))\n        # Can't have more k neighbors than there are data points\n        self.k_neighbors_spin.setMaximum(min(_MAX_K_NEIGBOURS, len(data) - 1))\n\n        self.info_label.setText(\"Clustering not yet run.\")\n\n        self.commit()\n\n    def clear(self):\n        self.__cancel_task(wait=False)\n        self.preprocessed_data = None\n        self.pca_projection = None\n        self.graph = None\n        self.partition = None\n        self.Error.clear()\n        self.Information.modified.clear()\n        self.info_label.setText(\"No data on input.\")\n\n    def onDeleteWidget(self):\n        self.__cancel_task(wait=True)\n        self.__executor.shutdown(True)\n        self.clear()\n        self.data = None\n        super().onDeleteWidget()\n\n    def send_report(self):\n        pca = report.bool_str(self.apply_pca)\n        if self.apply_pca:\n            pca += report.plural(\", {number} component{s}\", self.pca_components)\n\n        self.report_items((\n            (\"Normalize data\", report.bool_str(self.normalize)),\n            (\"PCA preprocessing\", pca),\n            (\"Metric\", METRICS[self.metric_idx][0]),\n            (\"k neighbors\", self.k_neighbors),\n            (\"Resolution\", self.resolution),\n        ))\n\n\nclass TaskState(QObject):\n\n    status_changed = Signal(str)\n    _p_status_changed = Signal(str)\n\n    progress_changed = Signal(float)\n    _p_progress_changed = Signal(float)\n\n    partial_result_ready = Signal(object)\n    _p_partial_result_ready = Signal(object)\n\n    def __init__(self, *args):\n        super().__init__(*args)\n        self.__future = None\n        self.watcher = FutureWatcher()\n        self.__interuption_requested = False\n        self.__progress = 0\n        # Helpers to route the signal emits via a this object's queue.\n        # This ensures 'atomic' disconnect from signals for targets/slots\n        # in the same thread. Requires that the event loop is running in this\n        # object's thread.\n        self._p_status_changed.connect(\n            self.status_changed, Qt.QueuedConnection)\n        self._p_progress_changed.connect(\n            self.progress_changed, Qt.QueuedConnection)\n        self._p_partial_result_ready.connect(\n            self.partial_result_ready, Qt.QueuedConnection)\n\n    @property\n    def future(self):\n        # type: () -> Future\n        return self.__future\n\n    def set_status(self, text):\n        self._p_status_changed.emit(text)\n\n    def set_progress_value(self, value):\n        if round(value, 1) > round(self.__progress, 1):\n            # Only emit progress when it has changed sufficiently\n            self._p_progress_changed.emit(value)\n            self.__progress = value\n\n    def set_partial_results(self, value):\n        self._p_partial_result_ready.emit(value)\n\n    def is_interuption_requested(self):\n        return self.__interuption_requested\n\n    def start(self, executor, func=None):\n        # type: (futures.Executor, Callable[[], Any]) -> Future\n        assert self.future is None\n        assert not self.__interuption_requested\n        self.__future = executor.submit(func)\n        self.watcher.setFuture(self.future)\n        return self.future\n\n    def cancel(self):\n        assert not self.__interuption_requested\n        self.__interuption_requested = True\n        if self.future is not None:\n            rval = self.future.cancel()\n        else:\n            # not even scheduled yet\n            rval = True\n        return rval\n\n\nclass InteruptRequested(BaseException):\n    pass\n\n\nclass Results(namespace):\n    pca_projection = None    # type: Optional[Table]\n    pca_components = None    # type: Optional[int]\n    normalize = None         # type: Optional[bool]\n    k_neighbors = None       # type: Optional[int]\n    metric = None            # type: Optional[str]\n    graph = None             # type: Optional[nx.Graph]\n    resolution = None        # type: Optional[float]\n    partition = None         # type: Optional[np.ndarray]\n\n\ndef run_on_data(data, normalize, pca_components, k_neighbors, metric, resolution, state):\n    # type: (Table, Optional[int], int, str, float, bool, TaskState) -> Results\n    \"\"\"\n    Run the louvain clustering on `data`.\n\n    state is used to report progress and partial results. Returns early if\n    `task.is_interuption_requested()` returns true.\n\n    Parameters\n    ----------\n    data : Table\n        Data table\n    normalize : bool\n        If `True`, the data is first normalized before computing PCA.\n    pca_components : Optional[int]\n        If not `None` then the data is first projected onto first\n        `pca_components` principal components.\n    k_neighbors : int\n        Passed to `table_to_knn_graph`\n    metric : str\n        Passed to `table_to_knn_graph`\n    resolution : float\n        Passed to `Louvain`\n    state : TaskState\n\n    Returns\n    -------\n    res : Results\n    \"\"\"\n    state = state  # type: TaskState\n    res = Results(\n        normalize=normalize, pca_components=pca_components,\n        k_neighbors=k_neighbors, metric=metric, resolution=resolution,\n    )\n    step = 0\n    if state.is_interuption_requested():\n        return res\n\n    if pca_components is not None:\n        steps = 3\n        state.set_status(\"Computing PCA...\")\n        pca = PCA(n_components=pca_components, random_state=0)\n\n        data = res.pca_projection = pca(data)(data)\n        assert isinstance(data, Table)\n        state.set_partial_results((\"pca_projection\", res.pca_projection))\n        step += 1\n    else:\n        steps = 2\n\n    if state.is_interuption_requested():\n        return res\n\n    state.set_progress_value(100. * step / steps)\n    state.set_status(\"Building graph...\")\n\n    # Apply Louvain preprocessing before converting the table into a graph\n    louvain = Louvain(resolution=resolution, random_state=0)\n    data = louvain.preprocess(data)\n\n    if state.is_interuption_requested():\n        return res\n\n    def pcallback(val):\n        state.set_progress_value((100. * step + 100 * val) / steps)\n        if state.is_interuption_requested():\n            raise InteruptRequested()\n\n    try:\n        res.graph = graph = matrix_to_knn_graph(\n            data.X, k_neighbors=k_neighbors, metric=metric,\n            progress_callback=pcallback)\n    except InteruptRequested:\n        return res\n\n    state.set_partial_results((\"graph\", res.graph))\n\n    step += 1\n    state.set_progress_value(100 * step / steps)\n    state.set_status(\"Detecting communities...\")\n    if state.is_interuption_requested():\n        return res\n\n    res.partition = louvain(graph)\n    state.set_partial_results((\"partition\", res.partition))\n    return res\n\n\ndef run_on_graph(graph, resolution, state):\n    # type: (nx.Graph, float, TaskState) -> Results\n    \"\"\"\n    Run the louvain clustering on `graph`.\n    \"\"\"\n    state = state  # type: TaskState\n    res = Results(resolution=resolution)\n    louvain = Louvain(resolution=resolution, random_state=0)\n    state.set_status(\"Detecting communities...\")\n    if state.is_interuption_requested():\n        return res\n    partition = louvain(graph)\n    res.partition = partition\n    state.set_partial_results((\"partition\", res.partition))\n    return res\n\n\nif __name__ == \"__main__\":  # pragma: no cover\n    WidgetPreview(OWLouvainClustering).run(Table(\"iris\"))\n"}
A user will ask you to solve a task. You should first draft your thinking process (inner monologue). Then, generate the solution.

Your response format must follow the template below:
<think>
Your thoughts or/and draft, like working through an exercise on scratch paper. Be as casual and as long as you want until you are confident to generate a correct solution.
</think>
<solution>
Final solution presented to the user.
</solution>
system
We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[ISSUE]

**Title:** Incorrect Handling of `context_settings` During Settings Migration

**Description:**
When migrating settings from older versions, the `context_settings` are not properly handled. Specifically, empty `context_settings` are not removed, and existing `context_settings` entries are not correctly migrated into individual settings. This results in leftover `context_settings` in the settings and improperly migrated configuration values.

**Example Code:**
```python
# Migrating with empty context_settings
settings = {"context_settings": []}
OWLouvainClustering.migrate_settings(settings, 1)
# Expected: settings should be empty

# Migrating with existing context_settings
settings = {
    "context_settings": [Context(values={
        "__version__": 1,
        "apply_pca": (True, -2),
        "k_neighbors": (29, -2),
        "metric_idx": (1, -2),
        "normalize": (False, -2),
        "pca_components": (10, -2),
        "resolution": (1.0, -2)
    })]
}
OWLouvainClustering.migrate_settings(settings, 1)
# Expected: settings contains individual entries like 'apply_pca': True, 'k_neighbors': 29, etc.
```

**Expected Behavior:**
- If `context_settings` is empty, it should be removed from the `settings`.
- If `context_settings` contains values, each setting should be migrated into individual entries within `settings`.

**Actual Behavior:**
- Empty `context_settings` are not removed, leaving `settings` with an empty `context_settings`.
- Existing `context_settings` are not correctly migrated, resulting in improper settings configuration.

[/ISSUE]
--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.

--- BEGIN FILE ---
```
### Orange/widgets/unsupervised/owlouvainclustering.py
from functools import partial

from concurrent import futures
from concurrent.futures import Future

from types import SimpleNamespace as namespace
from typing import Optional, Callable, Tuple, Any

import numpy as np
import scipy.sparse as sp
import networkx as nx

from AnyQt.QtCore import (
    Qt, QObject, QTimer, pyqtSignal as Signal, pyqtSlot as Slot
)
from AnyQt.QtWidgets import QSlider, QCheckBox, QWidget, QLabel

from Orange.clustering.louvain import matrix_to_knn_graph, Louvain
from Orange.data import Table, DiscreteVariable
from Orange.data.util import get_unique_names, array_equal
from Orange import preprocess
from Orange.projection import PCA
from Orange.widgets import widget, gui, report
from Orange.widgets.settings import Setting
from Orange.widgets.utils.annotated_data import add_columns, \
    ANNOTATED_DATA_SIGNAL_NAME
from Orange.widgets.utils.concurrent import FutureWatcher
from Orange.widgets.utils.signals import Input, Output
from Orange.widgets.utils.widgetpreview import WidgetPreview
from Orange.widgets.widget import Msg

try:
    from orangecontrib.network.network import Network
except ImportError:
    Network = None


_MAX_PCA_COMPONENTS = 50
_DEFAULT_PCA_COMPONENTS = 25
_MAX_K_NEIGBOURS = 200
_DEFAULT_K_NEIGHBORS = 30


METRICS = [("Euclidean", "l2"), ("Manhattan", "l1")]


class OWLouvainClustering(widget.OWWidget):
    name = "Louvain Clustering"
    description = "Detects communities in a network of nearest neighbors."
    icon = "icons/LouvainClustering.svg"
    priority = 2110

    want_main_area = False
    resizing_enabled = False

    class Inputs:
        data = Input("Data", Table, default=True)

    class Outputs:
        annotated_data = Output(ANNOTATED_DATA_SIGNAL_NAME, Table, default=True)
        if Network is not None:
            graph = Output("Network", Network)

    apply_pca = Setting(True)
    pca_components = Setting(_DEFAULT_PCA_COMPONENTS)
    normalize = Setting(True)
    metric_idx = Setting(0)
    k_neighbors = Setting(_DEFAULT_K_NEIGHBORS)
    resolution = Setting(1.)
    auto_commit = Setting(False)

    class Information(widget.OWWidget.Information):
        modified = Msg("Press commit to recompute clusters and send new data")

    class Error(widget.OWWidget.Error):
        empty_dataset = Msg("No features in data")

    def __init__(self):
        super().__init__()

        self.data = None  # type: Optional[Table]
        self.preprocessed_data = None  # type: Optional[Table]
        self.pca_projection = None  # type: Optional[Table]
        self.graph = None  # type: Optional[nx.Graph]
        self.partition = None  # type: Optional[np.array]
        # Use a executor with a single worker, to limit CPU overcommitment for
        # cancelled tasks. The method does not have a fine cancellation
        # granularity so we assure that there are not N - 1 jobs executing
        # for no reason only to be thrown away. It would be better to use the
        # global pool but implement a limit on jobs from this source.
        self.__executor = futures.ThreadPoolExecutor(max_workers=1)
        self.__task = None   # type: Optional[TaskState]
        self.__invalidated = False
        # coalescing commit timer
        self.__commit_timer = QTimer(self, singleShot=True)
        self.__commit_timer.timeout.connect(self.commit)

        # Set up UI
        info_box = gui.vBox(self.controlArea, "Info")
        self.info_label = gui.widgetLabel(info_box, "No data on input.")  # type: QLabel

        preprocessing_box = gui.vBox(self.controlArea, "Preprocessing")
        self.normalize_cbx = gui.checkBox(
            preprocessing_box, self, "normalize", label="Normalize data",
            callback=self._invalidate_preprocessed_data,
        )  # type: QCheckBox
        self.apply_pca_cbx = gui.checkBox(
            preprocessing_box, self, "apply_pca", label="Apply PCA preprocessing",
            callback=self._apply_pca_changed,
        )  # type: QCheckBox
        self.pca_components_slider = gui.hSlider(
            preprocessing_box, self, "pca_components", label="PCA Components: ", minValue=2,
            maxValue=_MAX_PCA_COMPONENTS,
            callback=self._invalidate_pca_projection, tracking=False
        )  # type: QSlider

        graph_box = gui.vBox(self.controlArea, "Graph parameters")
        self.metric_combo = gui.comboBox(
            graph_box, self, "metric_idx", label="Distance metric",
            items=[m[0] for m in METRICS], callback=self._invalidate_graph,
            orientation=Qt.Horizontal,
        )  # type: gui.OrangeComboBox
        self.k_neighbors_spin = gui.spin(
            graph_box, self, "k_neighbors", minv=1, maxv=_MAX_K_NEIGBOURS,
            label="k neighbors", controlWidth=80, alignment=Qt.AlignRight,
            callback=self._invalidate_graph,
        )  # type: gui.SpinBoxWFocusOut
        self.resolution_spin = gui.hSlider(
            graph_box, self, "resolution", minValue=0, maxValue=5., step=1e-1,
            label="Resolution", intOnly=False, labelFormat="%.1f",
            callback=self._invalidate_partition, tracking=False,
        )  # type: QSlider
        self.resolution_spin.parent().setToolTip(
            "The resolution parameter affects the number of clusters to find. "
            "Smaller values tend to produce more clusters and larger values "
            "retrieve less clusters."
        )
        self.apply_button = gui.auto_apply(
            self.controlArea, self, "auto_commit", box=None,
            commit=lambda: self.commit(), callback=lambda: self._on_auto_commit_changed()
        )  # type: QWidget

    def _preprocess_data(self):
        if self.preprocessed_data is None:
            if self.normalize:
                normalizer = preprocess.Normalize(center=False)
                self.preprocessed_data = normalizer(self.data)
            else:
                self.preprocessed_data = self.data

    def _apply_pca_changed(self):
        self.controls.pca_components.setEnabled(self.apply_pca)
        self._invalidate_graph()

    def _invalidate_preprocessed_data(self):
        self.preprocessed_data = None
        self._invalidate_pca_projection()
        # If we don't apply PCA, this still invalidates the graph, otherwise
        # this change won't be propagated further
        if not self.apply_pca:
            self._invalidate_graph()

    def _invalidate_pca_projection(self):
        self.pca_projection = None
        if not self.apply_pca:
            return

        self._invalidate_graph()
        self._set_modified(True)

    def _invalidate_graph(self):
        self.graph = None
        self._invalidate_partition()
        self._set_modified(True)

    def _invalidate_partition(self):
        self.partition = None
        self._invalidate_output()
        self.Information.modified()
        self._set_modified(True)

    def _invalidate_output(self):
        self.__invalidated = True
        if self.__task is not None:
            self.__cancel_task(wait=False)

        if self.auto_commit:
            self.__commit_timer.start()
        else:
            self.__set_state_ready()

    def _set_modified(self, state):
        """
        Mark the widget (GUI) as containing modified state.
        """
        if self.data is None:
            # does not apply when we have no data
            state = False
        elif self.auto_commit:
            # does not apply when auto commit is on
            state = False
        self.Information.modified(shown=state)

    def _on_auto_commit_changed(self):
        if self.auto_commit and self.__invalidated:
            self.commit()

    def cancel(self):
        """Cancel any running jobs."""
        self.__cancel_task(wait=False)
        self.__set_state_ready()

    def commit(self):
        self.__commit_timer.stop()
        self.__invalidated = False
        self._set_modified(False)

        # Cancel current running task
        self.__cancel_task(wait=False)

        if self.data is None:
            self.__set_state_ready()
            return

        self.Error.clear()

        if self.partition is not None:
            self.__set_state_ready()
            self._send_data()
            return

        self._preprocess_data()

        state = TaskState(self)

        # Prepare/assemble the task(s) to run; reuse partial results
        if self.apply_pca:
            if self.pca_projection is not None:
                data = self.pca_projection
                pca_components = None
            else:
                data = self.preprocessed_data
                pca_components = self.pca_components
        else:
            data = self.preprocessed_data
            pca_components = None

        if self.graph is not None:
            # run on graph only; no need to do PCA and k-nn search ...
            graph = self.graph
            k_neighbors = metric = None
        else:
            k_neighbors, metric = self.k_neighbors, METRICS[self.metric_idx][1]
            graph = None

        if graph is None:
            task = partial(
                run_on_data, data, pca_components=pca_components,
                normalize=self.normalize, k_neighbors=k_neighbors,
                metric=metric, resolution=self.resolution, state=state,
            )
        else:
            task = partial(
                run_on_graph, graph, resolution=self.resolution, state=state
            )

        self.info_label.setText("Running...")
        self.__set_state_busy()
        self.__start_task(task, state)

    @Slot(object)
    def __set_partial_results(self, result):
        # type: (Tuple[str, Any]) -> None
        which, res = result
        if which == "pca_projection":
            assert isinstance(res, Table) and len(res) == len(self.data)
            self.pca_projection = res
        elif which == "graph":
            assert isinstance(res, nx.Graph)
            self.graph = res
        elif which == "partition":
            assert isinstance(res, np.ndarray)
            self.partition = res
        else:
            assert False, which

    @Slot(object)
    def __on_done(self, future):
        # type: (Future["Results"]) -> None
        assert future.done()
        assert self.__task is not None
        assert self.__task.future is future
        assert self.__task.watcher.future() is future
        self.__task, task = None, self.__task
        task.deleteLater()

        self.__set_state_ready()

        result = future.result()
        self.__set_results(result)

    @Slot(str)
    def setStatusMessage(self, text):
        super().setStatusMessage(text)

    @Slot(float)
    def progressBarSet(self, value, *a, **kw):
        super().progressBarSet(value, *a, **kw)

    def __set_state_ready(self):
        self.progressBarFinished()
        self.setInvalidated(False)
        self.setStatusMessage("")

    def __set_state_busy(self):
        self.progressBarInit()
        self.setInvalidated(True)

    def __start_task(self, task, state):
        # type: (Callable[[], Any], TaskState) -> None
        assert self.__task is None
        state.status_changed.connect(self.setStatusMessage)
        state.progress_changed.connect(self.progressBarSet)
        state.partial_result_ready.connect(self.__set_partial_results)
        state.watcher.done.connect(self.__on_done)
        state.start(self.__executor, task)
        state.setParent(self)
        self.__task = state

    def __cancel_task(self, wait=True):
        # Cancel and dispose of the current task
        if self.__task is not None:
            state, self.__task = self.__task, None
            state.cancel()
            state.partial_result_ready.disconnect(self.__set_partial_results)
            state.status_changed.disconnect(self.setStatusMessage)
            state.progress_changed.disconnect(self.progressBarSet)
            state.watcher.done.disconnect(self.__on_done)
            if wait:
                futures.wait([state.future])
                state.deleteLater()
            else:
                w = FutureWatcher(state.future, parent=state)
                w.done.connect(state.deleteLater)

    def __set_results(self, results):
        # type: ("Results") -> None
        # NOTE: All of these have already been set by __set_partial_results,
        # we double check that they are aliases
        if results.pca_projection is not None:
            assert self.pca_components == results.pca_components
            assert self.pca_projection is results.pca_projection
            self.pca_projection = results.pca_projection
        if results.graph is not None:
            assert results.metric == METRICS[self.metric_idx][1]
            assert results.k_neighbors == self.k_neighbors
            assert self.graph is results.graph
            self.graph = results.graph
        if results.partition is not None:
            assert results.resolution == self.resolution
            assert self.partition is results.partition
            self.partition = results.partition

        # Display the number of found clusters in the UI
        num_clusters = len(np.unique(self.partition))
        self.info_label.setText("%d clusters found." % num_clusters)

        self._send_data()

    def _send_data(self):
        if self.partition is None or self.data is None:
            return
        domain = self.data.domain
        # Compute the frequency of each cluster index
        counts = np.bincount(self.partition)
        indices = np.argsort(counts)[::-1]
        index_map = {n: o for n, o in zip(indices, range(len(indices)))}
        new_partition = list(map(index_map.get, self.partition))

        cluster_var = DiscreteVariable(
            get_unique_names(domain, "Cluster"),
            values=["C%d" % (i + 1) for i, _ in enumerate(np.unique(new_partition))]
        )

        new_domain = add_columns(domain, metas=[cluster_var])
        new_table = self.data.transform(new_domain)
        new_table.get_column_view(cluster_var)[0][:] = new_partition
        self.Outputs.annotated_data.send(new_table)

        if Network is not None:
            n_edges = self.graph.number_of_edges()
            edges = sp.coo_matrix(
                (np.ones(n_edges), np.array(self.graph.edges()).T),
                shape=(n_edges, n_edges))
            graph = Network(new_table, edges)
            self.Outputs.graph.send(graph)

    @Inputs.data
    def set_data(self, data):
        self.Error.clear()

        prev_data, self.data = self.data, data
        # Make sure to properly enable/disable slider based on `apply_pca` setting
        self.controls.pca_components.setEnabled(self.apply_pca)

        if prev_data and self.data and array_equal(prev_data.X, self.data.X):
            if self.auto_commit and not self.isInvalidated():
                self._send_data()
            return

        self.cancel()
        # Clear the outputs
        self.Outputs.annotated_data.send(None)
        if Network is not None:
            self.Outputs.graph.send(None)

        # Clear internal state
        self.clear()
        self._invalidate_pca_projection()

        # Make sure the dataset is ok
        if self.data is not None and len(self.data.domain.attributes) < 1:
            self.Error.empty_dataset()
            self.data = None

        if self.data is None:
            return

        # Can't have more PCA components than the number of attributes
        n_attrs = len(data.domain.attributes)
        self.pca_components_slider.setMaximum(min(_MAX_PCA_COMPONENTS, n_attrs))
        # Can't have more k neighbors than there are data points
        self.k_neighbors_spin.setMaximum(min(_MAX_K_NEIGBOURS, len(data) - 1))

        self.info_label.setText("Clustering not yet run.")

        self.commit()

    def clear(self):
        self.__cancel_task(wait=False)
        self.preprocessed_data = None
        self.pca_projection = None
        self.graph = None
        self.partition = None
        self.Error.clear()
        self.Information.modified.clear()
        self.info_label.setText("No data on input.")

    def onDeleteWidget(self):
        self.__cancel_task(wait=True)
        self.__executor.shutdown(True)
        self.clear()
        self.data = None
        super().onDeleteWidget()

    def send_report(self):
        pca = report.bool_str(self.apply_pca)
        if self.apply_pca:
            pca += report.plural(", {number} component{s}", self.pca_components)

        self.report_items((
            ("Normalize data", report.bool_str(self.normalize)),
            ("PCA preprocessing", pca),
            ("Metric", METRICS[self.metric_idx][0]),
            ("k neighbors", self.k_neighbors),
            ("Resolution", self.resolution),
        ))


class TaskState(QObject):

    status_changed = Signal(str)
    _p_status_changed = Signal(str)

    progress_changed = Signal(float)
    _p_progress_changed = Signal(float)

    partial_result_ready = Signal(object)
    _p_partial_result_ready = Signal(object)

    def __init__(self, *args):
        super().__init__(*args)
        self.__future = None
        self.watcher = FutureWatcher()
        self.__interuption_requested = False
        self.__progress = 0
        # Helpers to route the signal emits via a this object's queue.
        # This ensures 'atomic' disconnect from signals for targets/slots
        # in the same thread. Requires that the event loop is running in this
        # object's thread.
        self._p_status_changed.connect(
            self.status_changed, Qt.QueuedConnection)
        self._p_progress_changed.connect(
            self.progress_changed, Qt.QueuedConnection)
        self._p_partial_result_ready.connect(
            self.partial_result_ready, Qt.QueuedConnection)

    @property
    def future(self):
        # type: () -> Future
        return self.__future

    def set_status(self, text):
        self._p_status_changed.emit(text)

    def set_progress_value(self, value):
        if round(value, 1) > round(self.__progress, 1):
            # Only emit progress when it has changed sufficiently
            self._p_progress_changed.emit(value)
            self.__progress = value

    def set_partial_results(self, value):
        self._p_partial_result_ready.emit(value)

    def is_interuption_requested(self):
        return self.__interuption_requested

    def start(self, executor, func=None):
        # type: (futures.Executor, Callable[[], Any]) -> Future
        assert self.future is None
        assert not self.__interuption_requested
        self.__future = executor.submit(func)
        self.watcher.setFuture(self.future)
        return self.future

    def cancel(self):
        assert not self.__interuption_requested
        self.__interuption_requested = True
        if self.future is not None:
            rval = self.future.cancel()
        else:
            # not even scheduled yet
            rval = True
        return rval


class InteruptRequested(BaseException):
    pass


class Results(namespace):
    pca_projection = None    # type: Optional[Table]
    pca_components = None    # type: Optional[int]
    normalize = None         # type: Optional[bool]
    k_neighbors = None       # type: Optional[int]
    metric = None            # type: Optional[str]
    graph = None             # type: Optional[nx.Graph]
    resolution = None        # type: Optional[float]
    partition = None         # type: Optional[np.ndarray]


def run_on_data(data, normalize, pca_components, k_neighbors, metric, resolution, state):
    # type: (Table, Optional[int], int, str, float, bool, TaskState) -> Results
    """
    Run the louvain clustering on `data`.

    state is used to report progress and partial results. Returns early if
    `task.is_interuption_requested()` returns true.

    Parameters
    ----------
    data : Table
        Data table
    normalize : bool
        If `True`, the data is first normalized before computing PCA.
    pca_components : Optional[int]
        If not `None` then the data is first projected onto first
        `pca_components` principal components.
    k_neighbors : int
        Passed to `table_to_knn_graph`
    metric : str
        Passed to `table_to_knn_graph`
    resolution : float
        Passed to `Louvain`
    state : TaskState

    Returns
    -------
    res : Results
    """
    state = state  # type: TaskState
    res = Results(
        normalize=normalize, pca_components=pca_components,
        k_neighbors=k_neighbors, metric=metric, resolution=resolution,
    )
    step = 0
    if state.is_interuption_requested():
        return res

    if pca_components is not None:
        steps = 3
        state.set_status("Computing PCA...")
        pca = PCA(n_components=pca_components, random_state=0)

        data = res.pca_projection = pca(data)(data)
        assert isinstance(data, Table)
        state.set_partial_results(("pca_projection", res.pca_projection))
        step += 1
    else:
        steps = 2

    if state.is_interuption_requested():
        return res

    state.set_progress_value(100. * step / steps)
    state.set_status("Building graph...")

    # Apply Louvain preprocessing before converting the table into a graph
    louvain = Louvain(resolution=resolution, random_state=0)
    data = louvain.preprocess(data)

    if state.is_interuption_requested():
        return res

    def pcallback(val):
        state.set_progress_value((100. * step + 100 * val) / steps)
        if state.is_interuption_requested():
            raise InteruptRequested()

    try:
        res.graph = graph = matrix_to_knn_graph(
            data.X, k_neighbors=k_neighbors, metric=metric,
            progress_callback=pcallback)
    except InteruptRequested:
        return res

    state.set_partial_results(("graph", res.graph))

    step += 1
    state.set_progress_value(100 * step / steps)
    state.set_status("Detecting communities...")
    if state.is_interuption_requested():
        return res

    res.partition = louvain(graph)
    state.set_partial_results(("partition", res.partition))
    return res


def run_on_graph(graph, resolution, state):
    # type: (nx.Graph, float, TaskState) -> Results
    """
    Run the louvain clustering on `graph`.
    """
    state = state  # type: TaskState
    res = Results(resolution=resolution)
    louvain = Louvain(resolution=resolution, random_state=0)
    state.set_status("Detecting communities...")
    if state.is_interuption_requested():
        return res
    partition = louvain(graph)
    res.partition = partition
    state.set_partial_results(("partition", res.partition))
    return res


if __name__ == "__main__":  # pragma: no cover
    WidgetPreview(OWLouvainClustering).run(Table("iris"))

```
--- END FILE ---

Please first localize the bug based on the issue statement, and then generate *SEARCH/REPLACE* edits to fix the issue.

Every *SEARCH/REPLACE* edit must use this format:
1. The file path
2. The start of search block: <<<<<<< SEARCH
3. A contiguous chunk of lines to search for in the existing source code
4. The dividing line: =======
5. The lines to replace into the source code
6. The end of the replace block: >>>>>>> REPLACE

Here is an example:

```python
### mathweb/flask/app.py
<<<<<<< SEARCH
from flask import Flask
=======
import math
from flask import Flask
>>>>>>> REPLACE
```
</solution>

Please note that the *SEARCH/REPLACE* edit REQUIRES PROPER INDENTATION. If you would like to add the line '        print(x)', you must fully write that out, with all those spaces before the code!
Wrap each *SEARCH/REPLACE* edit in a code block as shown in the example above. If you have multiple *SEARCH/REPLACE* edits, use a separate code block for each one.
user