We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
FR: Single-Layer Neural Network possible?
I could be misreading NN widget help file [1], but is it possible to specify a single-layer NN - i.e. no hidden layers? If I enter 0 for the number of hidden layers neurons, it gives an error (screenshot no 1), while if I leave it blank, it defaults to 10. As an example, for a NN with 4 inputs, one output with logistic, what would I need to input in the neurons count box to get it (screenshot no 2)?

![image](https://user-images.githubusercontent.com/32625394/152094575-c5fffdb2-00d1-4d7f-930a-6de602b94b66.png) ![image](https://user-images.githubusercontent.com/32625394/152094061-2da1ebca-4ec8-4981-ab70-98c2e32656cf.png)

[1] Orange Data Mining - Neural Network Widget
https://orangedatamining.com/widget-catalog/model/neuralnetwork/

Env: Windows + Orange 3.31

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `Orange/widgets/model/owneuralnetwork.py`
Content:
```
1 from functools import partial
2 import copy
3 import logging
4 import re
5 import concurrent.futures
6 from itertools import chain
7 
8 import numpy as np
9 
10 from AnyQt.QtWidgets import QFormLayout, QLabel
11 from AnyQt.QtCore import Qt, QThread, QObject
12 from AnyQt.QtCore import pyqtSlot as Slot, pyqtSignal as Signal
13 
14 from Orange.data import Table
15 from Orange.modelling import NNLearner
16 from Orange.widgets import gui
17 from Orange.widgets.settings import Setting
18 from Orange.widgets.utils.owlearnerwidget import OWBaseLearner
19 
20 from Orange.widgets.utils.concurrent import ThreadExecutor, FutureWatcher
21 from Orange.widgets.utils.widgetpreview import WidgetPreview
22 
23 
24 class Task(QObject):
25     """
26     A class that will hold the state for an learner evaluation.
27     """
28     done = Signal(object)
29     progressChanged = Signal(float)
30 
31     future = None      # type: concurrent.futures.Future
32     watcher = None     # type: FutureWatcher
33     cancelled = False  # type: bool
34 
35     def setFuture(self, future):
36         if self.future is not None:
37             raise RuntimeError("future is already set")
38         self.future = future
39         self.watcher = FutureWatcher(future, parent=self)
40         self.watcher.done.connect(self.done)
41 
42     def cancel(self):
43         """
44         Cancel the task.
45 
46         Set the `cancelled` field to True and block until the future is done.
47         """
48         # set cancelled state
49         self.cancelled = True
50         self.future.cancel()
51         concurrent.futures.wait([self.future])
52 
53     def emitProgressUpdate(self, value):
54         self.progressChanged.emit(value)
55 
56     def isInterruptionRequested(self):
57         return self.cancelled
58 
59 
60 class CancelTaskException(BaseException):
61     pass
62 
63 
64 class OWNNLearner(OWBaseLearner):
65     name = "Neural Network"
66     description = "A multi-layer perceptron (MLP) algorithm with " \
67                   "backpropagation."
68     icon = "icons/NN.svg"
69     priority = 90
70     keywords = ["mlp"]
71 
72     LEARNER = NNLearner
73 
74     activation = ["identity", "logistic", "tanh", "relu"]
75     act_lbl = ["Identity", "Logistic", "tanh", "ReLu"]
76     solver = ["lbfgs", "sgd", "adam"]
77     solv_lbl = ["L-BFGS-B", "SGD", "Adam"]
78 
79     learner_name = Setting("Neural Network")
80     hidden_layers_input = Setting("100,")
81     activation_index = Setting(3)
82     solver_index = Setting(2)
83     max_iterations = Setting(200)
84     alpha_index = Setting(0)
85     replicable = Setting(True)
86     settings_version = 1
87 
88     alphas = list(chain([x / 10000 for x in range(1, 10)],
89                         [x / 1000 for x in range(1, 10)],
90                         [x / 100 for x in range(1, 10)],
91                         [x / 10 for x in range(1, 10)],
92                         range(1, 10),
93                         range(10, 100, 5),
94                         range(100, 200, 10),
95                         range(100, 1001, 50)))
96 
97     def add_main_layout(self):
98         # this is part of init, pylint: disable=attribute-defined-outside-init
99         form = QFormLayout()
100         form.setFieldGrowthPolicy(form.AllNonFixedFieldsGrow)
101         form.setLabelAlignment(Qt.AlignLeft)
102         gui.widgetBox(self.controlArea, True, orientation=form)
103         form.addRow(
104             "Neurons in hidden layers:",
105             gui.lineEdit(
106                 None, self, "hidden_layers_input",
107                 orientation=Qt.Horizontal, callback=self.settings_changed,
108                 tooltip="A list of integers defining neurons. Length of list "
109                         "defines the number of layers. E.g. 4, 2, 2, 3.",
110                 placeholderText="e.g. 10,"))
111         form.addRow(
112             "Activation:",
113             gui.comboBox(
114                 None, self, "activation_index", orientation=Qt.Horizontal,
115                 label="Activation:", items=[i for i in self.act_lbl],
116                 callback=self.settings_changed))
117 
118         form.addRow(
119             "Solver:",
120             gui.comboBox(
121                 None, self, "solver_index", orientation=Qt.Horizontal,
122                 label="Solver:", items=[i for i in self.solv_lbl],
123                 callback=self.settings_changed))
124         self.reg_label = QLabel()
125         slider = gui.hSlider(
126             None, self, "alpha_index",
127             minValue=0, maxValue=len(self.alphas) - 1,
128             callback=lambda: (self.set_alpha(), self.settings_changed()),
129             createLabel=False)
130         form.addRow(self.reg_label, slider)
131         self.set_alpha()
132 
133         form.addRow(
134             "Maximal number of iterations:",
135             gui.spin(
136                 None, self, "max_iterations", 10, 1000000, step=10,
137                 label="Max iterations:", orientation=Qt.Horizontal,
138                 alignment=Qt.AlignRight, callback=self.settings_changed))
139 
140         form.addRow(
141             gui.checkBox(
142                 None, self, "replicable", label="Replicable training",
143                 callback=self.settings_changed, attribute=Qt.WA_LayoutUsesWidgetRect)
144         )
145 
146     def set_alpha(self):
147         # called from init, pylint: disable=attribute-defined-outside-init
148         self.strength_C = self.alphas[self.alpha_index]
149         self.reg_label.setText("Regularization, α={}:".format(self.strength_C))
150 
151     @property
152     def alpha(self):
153         return self.alphas[self.alpha_index]
154 
155     def setup_layout(self):
156         # this is part of init, pylint: disable=attribute-defined-outside-init
157         super().setup_layout()
158 
159         self._task = None  # type: Optional[Task]
160         self._executor = ThreadExecutor()
161 
162         # just a test cancel button
163         b = gui.button(self.apply_button, self, "Cancel",
164                        callback=self.cancel, addToLayout=False)
165         self.apply_button.layout().insertStretch(0, 100)
166         self.apply_button.layout().insertWidget(0, b)
167 
168     def create_learner(self):
169         return self.LEARNER(
170             hidden_layer_sizes=self.get_hidden_layers(),
171             activation=self.activation[self.activation_index],
172             solver=self.solver[self.solver_index],
173             alpha=self.alpha,
174             random_state=1 if self.replicable else None,
175             max_iter=self.max_iterations,
176             preprocessors=self.preprocessors)
177 
178     def get_learner_parameters(self):
179         return (("Hidden layers", ', '.join(map(str, self.get_hidden_layers()))),
180                 ("Activation", self.act_lbl[self.activation_index]),
181                 ("Solver", self.solv_lbl[self.solver_index]),
182                 ("Alpha", self.alpha),
183                 ("Max iterations", self.max_iterations),
184                 ("Replicable training", self.replicable))
185 
186     def get_hidden_layers(self):
187         layers = tuple(map(int, re.findall(r'\d+', self.hidden_layers_input)))
188         if not layers:
189             layers = (10,)
190             self.hidden_layers_input = "10,"
191         return layers
192 
193     def update_model(self):
194         self.show_fitting_failed(None)
195         self.model = None
196         if self.check_data():
197             self.__update()
198         else:
199             self.Outputs.model.send(self.model)
200 
201     @Slot(float)
202     def setProgressValue(self, value):
203         assert self.thread() is QThread.currentThread()
204         self.progressBarSet(value)
205 
206     def __update(self):
207         if self._task is not None:
208             # First make sure any pending tasks are cancelled.
209             self.cancel()
210         assert self._task is None
211 
212         max_iter = self.learner.kwargs["max_iter"]
213 
214         # Setup the task state
215         task = Task()
216         lastemitted = 0.
217 
218         def callback(iteration):
219             nonlocal task
220             nonlocal lastemitted
221             if task.isInterruptionRequested():
222                 raise CancelTaskException()
223             progress = round(iteration / max_iter * 100)
224             if progress != lastemitted:
225                 task.emitProgressUpdate(progress)
226                 lastemitted = progress
227 
228         # copy to set the callback so that the learner output is not modified
229         # (currently we can not pass callbacks to learners __call__)
230         learner = copy.copy(self.learner)
231         learner.callback = callback
232 
233         def build_model(data, learner):
234             try:
235                 return learner(data)
236             except CancelTaskException:
237                 return None
238 
239         build_model_func = partial(build_model, self.data, learner)
240 
241         task.setFuture(self._executor.submit(build_model_func))
242         task.done.connect(self._task_finished)
243         task.progressChanged.connect(self.setProgressValue)
244 
245         # set in setup_layout; pylint: disable=attribute-defined-outside-init
246         self._task = task
247 
248         self.progressBarInit()
249         self.setBlocking(True)
250 
251     @Slot(concurrent.futures.Future)
252     def _task_finished(self, f):
253         """
254         Parameters
255         ----------
256         f : Future
257             The future instance holding the built model
258         """
259         assert self.thread() is QThread.currentThread()
260         assert self._task is not None
261         assert self._task.future is f
262         assert f.done()
263         self._task.deleteLater()
264         self._task = None  # pylint: disable=attribute-defined-outside-init
265         self.setBlocking(False)
266         self.progressBarFinished()
267 
268         try:
269             self.model = f.result()
270         except Exception as ex:  # pylint: disable=broad-except
271             # Log the exception with a traceback
272             log = logging.getLogger()
273             log.exception(__name__, exc_info=True)
274             self.model = None
275             self.show_fitting_failed(ex)
276         else:
277             self.model.name = self.learner_name
278             self.model.instances = self.data
279             self.model.skl_model.orange_callback = None  # remove unpicklable callback
280             self.Outputs.model.send(self.model)
281 
282     def cancel(self):
283         """
284         Cancel the current task (if any).
285         """
286         if self._task is not None:
287             self._task.cancel()
288             assert self._task.future.done()
289             # disconnect from the task
290             self._task.done.disconnect(self._task_finished)
291             self._task.progressChanged.disconnect(self.setProgressValue)
292             self._task.deleteLater()
293             self._task = None  # pylint: disable=attribute-defined-outside-init
294 
295         self.progressBarFinished()
296         self.setBlocking(False)
297 
298     def onDeleteWidget(self):
299         self.cancel()
300         super().onDeleteWidget()
301 
302     @classmethod
303     def migrate_settings(cls, settings, version):
304         if not version:
305             alpha = settings.pop("alpha", None)
306             if alpha is not None:
307                 settings["alpha_index"] = \
308                     np.argmin(np.abs(np.array(cls.alphas) - alpha))
309 
310 
311 if __name__ == "__main__":  # pragma: no cover
312     WidgetPreview(OWNNLearner).run(Table("iris"))
313
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/Orange/widgets/model/owneuralnetwork.py b/Orange/widgets/model/owneuralnetwork.py
--- a/Orange/widgets/model/owneuralnetwork.py
+++ b/Orange/widgets/model/owneuralnetwork.py
@@ -14,6 +14,7 @@
 from Orange.data import Table
 from Orange.modelling import NNLearner
 from Orange.widgets import gui
+from Orange.widgets.widget import Msg
 from Orange.widgets.settings import Setting
 from Orange.widgets.utils.owlearnerwidget import OWBaseLearner
 
@@ -81,11 +82,11 @@
     activation_index = Setting(3)
     solver_index = Setting(2)
     max_iterations = Setting(200)
-    alpha_index = Setting(0)
+    alpha_index = Setting(1)
     replicable = Setting(True)
-    settings_version = 1
+    settings_version = 2
 
-    alphas = list(chain([x / 10000 for x in range(1, 10)],
+    alphas = list(chain([0], [x / 10000 for x in range(1, 10)],
                         [x / 1000 for x in range(1, 10)],
                         [x / 100 for x in range(1, 10)],
                         [x / 10 for x in range(1, 10)],
@@ -94,6 +95,11 @@
                         range(100, 200, 10),
                         range(100, 1001, 50)))
 
+    class Warning(OWBaseLearner.Warning):
+        no_layers = Msg("ANN without hidden layers is equivalent to logistic "
+                        "regression with worse fitting.\nWe recommend using "
+                        "logistic regression.")
+
     def add_main_layout(self):
         # this is part of init, pylint: disable=attribute-defined-outside-init
         form = QFormLayout()
@@ -184,10 +190,10 @@
                 ("Replicable training", self.replicable))
 
     def get_hidden_layers(self):
+        self.Warning.no_layers.clear()
         layers = tuple(map(int, re.findall(r'\d+', self.hidden_layers_input)))
         if not layers:
-            layers = (10,)
-            self.hidden_layers_input = "10,"
+            self.Warning.no_layers()
         return layers
 
     def update_model(self):
@@ -306,6 +312,8 @@
             if alpha is not None:
                 settings["alpha_index"] = \
                     np.argmin(np.abs(np.array(cls.alphas) - alpha))
+        elif version < 2:
+            settings["alpha_index"] = settings.get("alpha_index", 0) + 1
 
 
 if __name__ == "__main__":  # pragma: no cover

{"golden_diff": "diff --git a/Orange/widgets/model/owneuralnetwork.py b/Orange/widgets/model/owneuralnetwork.py\n--- a/Orange/widgets/model/owneuralnetwork.py\n+++ b/Orange/widgets/model/owneuralnetwork.py\n@@ -14,6 +14,7 @@\n from Orange.data import Table\n from Orange.modelling import NNLearner\n from Orange.widgets import gui\n+from Orange.widgets.widget import Msg\n from Orange.widgets.settings import Setting\n from Orange.widgets.utils.owlearnerwidget import OWBaseLearner\n \n@@ -81,11 +82,11 @@\n     activation_index = Setting(3)\n     solver_index = Setting(2)\n     max_iterations = Setting(200)\n-    alpha_index = Setting(0)\n+    alpha_index = Setting(1)\n     replicable = Setting(True)\n-    settings_version = 1\n+    settings_version = 2\n \n-    alphas = list(chain([x / 10000 for x in range(1, 10)],\n+    alphas = list(chain([0], [x / 10000 for x in range(1, 10)],\n                         [x / 1000 for x in range(1, 10)],\n                         [x / 100 for x in range(1, 10)],\n                         [x / 10 for x in range(1, 10)],\n@@ -94,6 +95,11 @@\n                         range(100, 200, 10),\n                         range(100, 1001, 50)))\n \n+    class Warning(OWBaseLearner.Warning):\n+        no_layers = Msg(\"ANN without hidden layers is equivalent to logistic \"\n+                        \"regression with worse fitting.\\nWe recommend using \"\n+                        \"logistic regression.\")\n+\n     def add_main_layout(self):\n         # this is part of init, pylint: disable=attribute-defined-outside-init\n         form = QFormLayout()\n@@ -184,10 +190,10 @@\n                 (\"Replicable training\", self.replicable))\n \n     def get_hidden_layers(self):\n+        self.Warning.no_layers.clear()\n         layers = tuple(map(int, re.findall(r'\\d+', self.hidden_layers_input)))\n         if not layers:\n-            layers = (10,)\n-            self.hidden_layers_input = \"10,\"\n+            self.Warning.no_layers()\n         return layers\n \n     def update_model(self):\n@@ -306,6 +312,8 @@\n             if alpha is not None:\n                 settings[\"alpha_index\"] = \\\n                     np.argmin(np.abs(np.array(cls.alphas) - alpha))\n+        elif version < 2:\n+            settings[\"alpha_index\"] = settings.get(\"alpha_index\", 0) + 1\n \n \n if __name__ == \"__main__\":  # pragma: no cover\n", "issue": "FR: Single-Layer Neural Network possible?\nI could be misreading NN widget help file [1], but is it possible to specify a single-layer NN - i.e. no hidden layers? If I enter 0 for the number of hidden layers neurons, it gives an error (screenshot no 1), while if I leave it blank, it defaults to 10. As an example, for a NN with 4 inputs, one output with logistic, what would I need to input in the neurons count box to get it (screenshot no 2)?\r\n\r\n![image](https://user-images.githubusercontent.com/32625394/152094575-c5fffdb2-00d1-4d7f-930a-6de602b94b66.png) ![image](https://user-images.githubusercontent.com/32625394/152094061-2da1ebca-4ec8-4981-ab70-98c2e32656cf.png)\r\n\r\n[1] Orange Data Mining - Neural Network Widget\r\nhttps://orangedatamining.com/widget-catalog/model/neuralnetwork/\r\n\r\nEnv: Windows + Orange 3.31\n", "before_files": [{"content": "from functools import partial\nimport copy\nimport logging\nimport re\nimport concurrent.futures\nfrom itertools import chain\n\nimport numpy as np\n\nfrom AnyQt.QtWidgets import QFormLayout, QLabel\nfrom AnyQt.QtCore import Qt, QThread, QObject\nfrom AnyQt.QtCore import pyqtSlot as Slot, pyqtSignal as Signal\n\nfrom Orange.data import Table\nfrom Orange.modelling import NNLearner\nfrom Orange.widgets import gui\nfrom Orange.widgets.settings import Setting\nfrom Orange.widgets.utils.owlearnerwidget import OWBaseLearner\n\nfrom Orange.widgets.utils.concurrent import ThreadExecutor, FutureWatcher\nfrom Orange.widgets.utils.widgetpreview import WidgetPreview\n\n\nclass Task(QObject):\n    \"\"\"\n    A class that will hold the state for an learner evaluation.\n    \"\"\"\n    done = Signal(object)\n    progressChanged = Signal(float)\n\n    future = None      # type: concurrent.futures.Future\n    watcher = None     # type: FutureWatcher\n    cancelled = False  # type: bool\n\n    def setFuture(self, future):\n        if self.future is not None:\n            raise RuntimeError(\"future is already set\")\n        self.future = future\n        self.watcher = FutureWatcher(future, parent=self)\n        self.watcher.done.connect(self.done)\n\n    def cancel(self):\n        \"\"\"\n        Cancel the task.\n\n        Set the `cancelled` field to True and block until the future is done.\n        \"\"\"\n        # set cancelled state\n        self.cancelled = True\n        self.future.cancel()\n        concurrent.futures.wait([self.future])\n\n    def emitProgressUpdate(self, value):\n        self.progressChanged.emit(value)\n\n    def isInterruptionRequested(self):\n        return self.cancelled\n\n\nclass CancelTaskException(BaseException):\n    pass\n\n\nclass OWNNLearner(OWBaseLearner):\n    name = \"Neural Network\"\n    description = \"A multi-layer perceptron (MLP) algorithm with \" \\\n                  \"backpropagation.\"\n    icon = \"icons/NN.svg\"\n    priority = 90\n    keywords = [\"mlp\"]\n\n    LEARNER = NNLearner\n\n    activation = [\"identity\", \"logistic\", \"tanh\", \"relu\"]\n    act_lbl = [\"Identity\", \"Logistic\", \"tanh\", \"ReLu\"]\n    solver = [\"lbfgs\", \"sgd\", \"adam\"]\n    solv_lbl = [\"L-BFGS-B\", \"SGD\", \"Adam\"]\n\n    learner_name = Setting(\"Neural Network\")\n    hidden_layers_input = Setting(\"100,\")\n    activation_index = Setting(3)\n    solver_index = Setting(2)\n    max_iterations = Setting(200)\n    alpha_index = Setting(0)\n    replicable = Setting(True)\n    settings_version = 1\n\n    alphas = list(chain([x / 10000 for x in range(1, 10)],\n                        [x / 1000 for x in range(1, 10)],\n                        [x / 100 for x in range(1, 10)],\n                        [x / 10 for x in range(1, 10)],\n                        range(1, 10),\n                        range(10, 100, 5),\n                        range(100, 200, 10),\n                        range(100, 1001, 50)))\n\n    def add_main_layout(self):\n        # this is part of init, pylint: disable=attribute-defined-outside-init\n        form = QFormLayout()\n        form.setFieldGrowthPolicy(form.AllNonFixedFieldsGrow)\n        form.setLabelAlignment(Qt.AlignLeft)\n        gui.widgetBox(self.controlArea, True, orientation=form)\n        form.addRow(\n            \"Neurons in hidden layers:\",\n            gui.lineEdit(\n                None, self, \"hidden_layers_input\",\n                orientation=Qt.Horizontal, callback=self.settings_changed,\n                tooltip=\"A list of integers defining neurons. Length of list \"\n                        \"defines the number of layers. E.g. 4, 2, 2, 3.\",\n                placeholderText=\"e.g. 10,\"))\n        form.addRow(\n            \"Activation:\",\n            gui.comboBox(\n                None, self, \"activation_index\", orientation=Qt.Horizontal,\n                label=\"Activation:\", items=[i for i in self.act_lbl],\n                callback=self.settings_changed))\n\n        form.addRow(\n            \"Solver:\",\n            gui.comboBox(\n                None, self, \"solver_index\", orientation=Qt.Horizontal,\n                label=\"Solver:\", items=[i for i in self.solv_lbl],\n                callback=self.settings_changed))\n        self.reg_label = QLabel()\n        slider = gui.hSlider(\n            None, self, \"alpha_index\",\n            minValue=0, maxValue=len(self.alphas) - 1,\n            callback=lambda: (self.set_alpha(), self.settings_changed()),\n            createLabel=False)\n        form.addRow(self.reg_label, slider)\n        self.set_alpha()\n\n        form.addRow(\n            \"Maximal number of iterations:\",\n            gui.spin(\n                None, self, \"max_iterations\", 10, 1000000, step=10,\n                label=\"Max iterations:\", orientation=Qt.Horizontal,\n                alignment=Qt.AlignRight, callback=self.settings_changed))\n\n        form.addRow(\n            gui.checkBox(\n                None, self, \"replicable\", label=\"Replicable training\",\n                callback=self.settings_changed, attribute=Qt.WA_LayoutUsesWidgetRect)\n        )\n\n    def set_alpha(self):\n        # called from init, pylint: disable=attribute-defined-outside-init\n        self.strength_C = self.alphas[self.alpha_index]\n        self.reg_label.setText(\"Regularization, \u03b1={}:\".format(self.strength_C))\n\n    @property\n    def alpha(self):\n        return self.alphas[self.alpha_index]\n\n    def setup_layout(self):\n        # this is part of init, pylint: disable=attribute-defined-outside-init\n        super().setup_layout()\n\n        self._task = None  # type: Optional[Task]\n        self._executor = ThreadExecutor()\n\n        # just a test cancel button\n        b = gui.button(self.apply_button, self, \"Cancel\",\n                       callback=self.cancel, addToLayout=False)\n        self.apply_button.layout().insertStretch(0, 100)\n        self.apply_button.layout().insertWidget(0, b)\n\n    def create_learner(self):\n        return self.LEARNER(\n            hidden_layer_sizes=self.get_hidden_layers(),\n            activation=self.activation[self.activation_index],\n            solver=self.solver[self.solver_index],\n            alpha=self.alpha,\n            random_state=1 if self.replicable else None,\n            max_iter=self.max_iterations,\n            preprocessors=self.preprocessors)\n\n    def get_learner_parameters(self):\n        return ((\"Hidden layers\", ', '.join(map(str, self.get_hidden_layers()))),\n                (\"Activation\", self.act_lbl[self.activation_index]),\n                (\"Solver\", self.solv_lbl[self.solver_index]),\n                (\"Alpha\", self.alpha),\n                (\"Max iterations\", self.max_iterations),\n                (\"Replicable training\", self.replicable))\n\n    def get_hidden_layers(self):\n        layers = tuple(map(int, re.findall(r'\\d+', self.hidden_layers_input)))\n        if not layers:\n            layers = (10,)\n            self.hidden_layers_input = \"10,\"\n        return layers\n\n    def update_model(self):\n        self.show_fitting_failed(None)\n        self.model = None\n        if self.check_data():\n            self.__update()\n        else:\n            self.Outputs.model.send(self.model)\n\n    @Slot(float)\n    def setProgressValue(self, value):\n        assert self.thread() is QThread.currentThread()\n        self.progressBarSet(value)\n\n    def __update(self):\n        if self._task is not None:\n            # First make sure any pending tasks are cancelled.\n            self.cancel()\n        assert self._task is None\n\n        max_iter = self.learner.kwargs[\"max_iter\"]\n\n        # Setup the task state\n        task = Task()\n        lastemitted = 0.\n\n        def callback(iteration):\n            nonlocal task\n            nonlocal lastemitted\n            if task.isInterruptionRequested():\n                raise CancelTaskException()\n            progress = round(iteration / max_iter * 100)\n            if progress != lastemitted:\n                task.emitProgressUpdate(progress)\n                lastemitted = progress\n\n        # copy to set the callback so that the learner output is not modified\n        # (currently we can not pass callbacks to learners __call__)\n        learner = copy.copy(self.learner)\n        learner.callback = callback\n\n        def build_model(data, learner):\n            try:\n                return learner(data)\n            except CancelTaskException:\n                return None\n\n        build_model_func = partial(build_model, self.data, learner)\n\n        task.setFuture(self._executor.submit(build_model_func))\n        task.done.connect(self._task_finished)\n        task.progressChanged.connect(self.setProgressValue)\n\n        # set in setup_layout; pylint: disable=attribute-defined-outside-init\n        self._task = task\n\n        self.progressBarInit()\n        self.setBlocking(True)\n\n    @Slot(concurrent.futures.Future)\n    def _task_finished(self, f):\n        \"\"\"\n        Parameters\n        ----------\n        f : Future\n            The future instance holding the built model\n        \"\"\"\n        assert self.thread() is QThread.currentThread()\n        assert self._task is not None\n        assert self._task.future is f\n        assert f.done()\n        self._task.deleteLater()\n        self._task = None  # pylint: disable=attribute-defined-outside-init\n        self.setBlocking(False)\n        self.progressBarFinished()\n\n        try:\n            self.model = f.result()\n        except Exception as ex:  # pylint: disable=broad-except\n            # Log the exception with a traceback\n            log = logging.getLogger()\n            log.exception(__name__, exc_info=True)\n            self.model = None\n            self.show_fitting_failed(ex)\n        else:\n            self.model.name = self.learner_name\n            self.model.instances = self.data\n            self.model.skl_model.orange_callback = None  # remove unpicklable callback\n            self.Outputs.model.send(self.model)\n\n    def cancel(self):\n        \"\"\"\n        Cancel the current task (if any).\n        \"\"\"\n        if self._task is not None:\n            self._task.cancel()\n            assert self._task.future.done()\n            # disconnect from the task\n            self._task.done.disconnect(self._task_finished)\n            self._task.progressChanged.disconnect(self.setProgressValue)\n            self._task.deleteLater()\n            self._task = None  # pylint: disable=attribute-defined-outside-init\n\n        self.progressBarFinished()\n        self.setBlocking(False)\n\n    def onDeleteWidget(self):\n        self.cancel()\n        super().onDeleteWidget()\n\n    @classmethod\n    def migrate_settings(cls, settings, version):\n        if not version:\n            alpha = settings.pop(\"alpha\", None)\n            if alpha is not None:\n                settings[\"alpha_index\"] = \\\n                    np.argmin(np.abs(np.array(cls.alphas) - alpha))\n\n\nif __name__ == \"__main__\":  # pragma: no cover\n    WidgetPreview(OWNNLearner).run(Table(\"iris\"))\n", "path": "Orange/widgets/model/owneuralnetwork.py"}], "after_files": [{"content": "from functools import partial\nimport copy\nimport logging\nimport re\nimport concurrent.futures\nfrom itertools import chain\n\nimport numpy as np\n\nfrom AnyQt.QtWidgets import QFormLayout, QLabel\nfrom AnyQt.QtCore import Qt, QThread, QObject\nfrom AnyQt.QtCore import pyqtSlot as Slot, pyqtSignal as Signal\n\nfrom Orange.data import Table\nfrom Orange.modelling import NNLearner\nfrom Orange.widgets import gui\nfrom Orange.widgets.widget import Msg\nfrom Orange.widgets.settings import Setting\nfrom Orange.widgets.utils.owlearnerwidget import OWBaseLearner\n\nfrom Orange.widgets.utils.concurrent import ThreadExecutor, FutureWatcher\nfrom Orange.widgets.utils.widgetpreview import WidgetPreview\n\n\nclass Task(QObject):\n    \"\"\"\n    A class that will hold the state for an learner evaluation.\n    \"\"\"\n    done = Signal(object)\n    progressChanged = Signal(float)\n\n    future = None      # type: concurrent.futures.Future\n    watcher = None     # type: FutureWatcher\n    cancelled = False  # type: bool\n\n    def setFuture(self, future):\n        if self.future is not None:\n            raise RuntimeError(\"future is already set\")\n        self.future = future\n        self.watcher = FutureWatcher(future, parent=self)\n        self.watcher.done.connect(self.done)\n\n    def cancel(self):\n        \"\"\"\n        Cancel the task.\n\n        Set the `cancelled` field to True and block until the future is done.\n        \"\"\"\n        # set cancelled state\n        self.cancelled = True\n        self.future.cancel()\n        concurrent.futures.wait([self.future])\n\n    def emitProgressUpdate(self, value):\n        self.progressChanged.emit(value)\n\n    def isInterruptionRequested(self):\n        return self.cancelled\n\n\nclass CancelTaskException(BaseException):\n    pass\n\n\nclass OWNNLearner(OWBaseLearner):\n    name = \"Neural Network\"\n    description = \"A multi-layer perceptron (MLP) algorithm with \" \\\n                  \"backpropagation.\"\n    icon = \"icons/NN.svg\"\n    priority = 90\n    keywords = [\"mlp\"]\n\n    LEARNER = NNLearner\n\n    activation = [\"identity\", \"logistic\", \"tanh\", \"relu\"]\n    act_lbl = [\"Identity\", \"Logistic\", \"tanh\", \"ReLu\"]\n    solver = [\"lbfgs\", \"sgd\", \"adam\"]\n    solv_lbl = [\"L-BFGS-B\", \"SGD\", \"Adam\"]\n\n    learner_name = Setting(\"Neural Network\")\n    hidden_layers_input = Setting(\"100,\")\n    activation_index = Setting(3)\n    solver_index = Setting(2)\n    max_iterations = Setting(200)\n    alpha_index = Setting(1)\n    replicable = Setting(True)\n    settings_version = 2\n\n    alphas = list(chain([0], [x / 10000 for x in range(1, 10)],\n                        [x / 1000 for x in range(1, 10)],\n                        [x / 100 for x in range(1, 10)],\n                        [x / 10 for x in range(1, 10)],\n                        range(1, 10),\n                        range(10, 100, 5),\n                        range(100, 200, 10),\n                        range(100, 1001, 50)))\n\n    class Warning(OWBaseLearner.Warning):\n        no_layers = Msg(\"ANN without hidden layers is equivalent to logistic \"\n                        \"regression with worse fitting.\\nWe recommend using \"\n                        \"logistic regression.\")\n\n    def add_main_layout(self):\n        # this is part of init, pylint: disable=attribute-defined-outside-init\n        form = QFormLayout()\n        form.setFieldGrowthPolicy(form.AllNonFixedFieldsGrow)\n        form.setLabelAlignment(Qt.AlignLeft)\n        gui.widgetBox(self.controlArea, True, orientation=form)\n        form.addRow(\n            \"Neurons in hidden layers:\",\n            gui.lineEdit(\n                None, self, \"hidden_layers_input\",\n                orientation=Qt.Horizontal, callback=self.settings_changed,\n                tooltip=\"A list of integers defining neurons. Length of list \"\n                        \"defines the number of layers. E.g. 4, 2, 2, 3.\",\n                placeholderText=\"e.g. 10,\"))\n        form.addRow(\n            \"Activation:\",\n            gui.comboBox(\n                None, self, \"activation_index\", orientation=Qt.Horizontal,\n                label=\"Activation:\", items=[i for i in self.act_lbl],\n                callback=self.settings_changed))\n\n        form.addRow(\n            \"Solver:\",\n            gui.comboBox(\n                None, self, \"solver_index\", orientation=Qt.Horizontal,\n                label=\"Solver:\", items=[i for i in self.solv_lbl],\n                callback=self.settings_changed))\n        self.reg_label = QLabel()\n        slider = gui.hSlider(\n            None, self, \"alpha_index\",\n            minValue=0, maxValue=len(self.alphas) - 1,\n            callback=lambda: (self.set_alpha(), self.settings_changed()),\n            createLabel=False)\n        form.addRow(self.reg_label, slider)\n        self.set_alpha()\n\n        form.addRow(\n            \"Maximal number of iterations:\",\n            gui.spin(\n                None, self, \"max_iterations\", 10, 1000000, step=10,\n                label=\"Max iterations:\", orientation=Qt.Horizontal,\n                alignment=Qt.AlignRight, callback=self.settings_changed))\n\n        form.addRow(\n            gui.checkBox(\n                None, self, \"replicable\", label=\"Replicable training\",\n                callback=self.settings_changed, attribute=Qt.WA_LayoutUsesWidgetRect)\n        )\n\n    def set_alpha(self):\n        # called from init, pylint: disable=attribute-defined-outside-init\n        self.strength_C = self.alphas[self.alpha_index]\n        self.reg_label.setText(\"Regularization, \u03b1={}:\".format(self.strength_C))\n\n    @property\n    def alpha(self):\n        return self.alphas[self.alpha_index]\n\n    def setup_layout(self):\n        # this is part of init, pylint: disable=attribute-defined-outside-init\n        super().setup_layout()\n\n        self._task = None  # type: Optional[Task]\n        self._executor = ThreadExecutor()\n\n        # just a test cancel button\n        b = gui.button(self.apply_button, self, \"Cancel\",\n                       callback=self.cancel, addToLayout=False)\n        self.apply_button.layout().insertStretch(0, 100)\n        self.apply_button.layout().insertWidget(0, b)\n\n    def create_learner(self):\n        return self.LEARNER(\n            hidden_layer_sizes=self.get_hidden_layers(),\n            activation=self.activation[self.activation_index],\n            solver=self.solver[self.solver_index],\n            alpha=self.alpha,\n            random_state=1 if self.replicable else None,\n            max_iter=self.max_iterations,\n            preprocessors=self.preprocessors)\n\n    def get_learner_parameters(self):\n        return ((\"Hidden layers\", ', '.join(map(str, self.get_hidden_layers()))),\n                (\"Activation\", self.act_lbl[self.activation_index]),\n                (\"Solver\", self.solv_lbl[self.solver_index]),\n                (\"Alpha\", self.alpha),\n                (\"Max iterations\", self.max_iterations),\n                (\"Replicable training\", self.replicable))\n\n    def get_hidden_layers(self):\n        self.Warning.no_layers.clear()\n        layers = tuple(map(int, re.findall(r'\\d+', self.hidden_layers_input)))\n        if not layers:\n            self.Warning.no_layers()\n        return layers\n\n    def update_model(self):\n        self.show_fitting_failed(None)\n        self.model = None\n        if self.check_data():\n            self.__update()\n        else:\n            self.Outputs.model.send(self.model)\n\n    @Slot(float)\n    def setProgressValue(self, value):\n        assert self.thread() is QThread.currentThread()\n        self.progressBarSet(value)\n\n    def __update(self):\n        if self._task is not None:\n            # First make sure any pending tasks are cancelled.\n            self.cancel()\n        assert self._task is None\n\n        max_iter = self.learner.kwargs[\"max_iter\"]\n\n        # Setup the task state\n        task = Task()\n        lastemitted = 0.\n\n        def callback(iteration):\n            nonlocal task\n            nonlocal lastemitted\n            if task.isInterruptionRequested():\n                raise CancelTaskException()\n            progress = round(iteration / max_iter * 100)\n            if progress != lastemitted:\n                task.emitProgressUpdate(progress)\n                lastemitted = progress\n\n        # copy to set the callback so that the learner output is not modified\n        # (currently we can not pass callbacks to learners __call__)\n        learner = copy.copy(self.learner)\n        learner.callback = callback\n\n        def build_model(data, learner):\n            try:\n                return learner(data)\n            except CancelTaskException:\n                return None\n\n        build_model_func = partial(build_model, self.data, learner)\n\n        task.setFuture(self._executor.submit(build_model_func))\n        task.done.connect(self._task_finished)\n        task.progressChanged.connect(self.setProgressValue)\n\n        # set in setup_layout; pylint: disable=attribute-defined-outside-init\n        self._task = task\n\n        self.progressBarInit()\n        self.setBlocking(True)\n\n    @Slot(concurrent.futures.Future)\n    def _task_finished(self, f):\n        \"\"\"\n        Parameters\n        ----------\n        f : Future\n            The future instance holding the built model\n        \"\"\"\n        assert self.thread() is QThread.currentThread()\n        assert self._task is not None\n        assert self._task.future is f\n        assert f.done()\n        self._task.deleteLater()\n        self._task = None  # pylint: disable=attribute-defined-outside-init\n        self.setBlocking(False)\n        self.progressBarFinished()\n\n        try:\n            self.model = f.result()\n        except Exception as ex:  # pylint: disable=broad-except\n            # Log the exception with a traceback\n            log = logging.getLogger()\n            log.exception(__name__, exc_info=True)\n            self.model = None\n            self.show_fitting_failed(ex)\n        else:\n            self.model.name = self.learner_name\n            self.model.instances = self.data\n            self.model.skl_model.orange_callback = None  # remove unpicklable callback\n            self.Outputs.model.send(self.model)\n\n    def cancel(self):\n        \"\"\"\n        Cancel the current task (if any).\n        \"\"\"\n        if self._task is not None:\n            self._task.cancel()\n            assert self._task.future.done()\n            # disconnect from the task\n            self._task.done.disconnect(self._task_finished)\n            self._task.progressChanged.disconnect(self.setProgressValue)\n            self._task.deleteLater()\n            self._task = None  # pylint: disable=attribute-defined-outside-init\n\n        self.progressBarFinished()\n        self.setBlocking(False)\n\n    def onDeleteWidget(self):\n        self.cancel()\n        super().onDeleteWidget()\n\n    @classmethod\n    def migrate_settings(cls, settings, version):\n        if not version:\n            alpha = settings.pop(\"alpha\", None)\n            if alpha is not None:\n                settings[\"alpha_index\"] = \\\n                    np.argmin(np.abs(np.array(cls.alphas) - alpha))\n        elif version < 2:\n            settings[\"alpha_index\"] = settings.get(\"alpha_index\", 0) + 1\n\n\nif __name__ == \"__main__\":  # pragma: no cover\n    WidgetPreview(OWNNLearner).run(Table(\"iris\"))\n", "path": "Orange/widgets/model/owneuralnetwork.py"}]}