From 7bd27f70dec5780a25fc026fed055add7661ecf2 Mon Sep 17 00:00:00 2001 From: Vesna Tanko Date: Fri, 14 Dec 2018 12:03:21 +0100 Subject: [PATCH] OWtSNE: Add resume option --- Orange/widgets/unsupervised/owtsne.py | 49 ++++++++++++++++++++------- 1 file changed, 36 insertions(+), 13 deletions(-) diff --git a/Orange/widgets/unsupervised/owtsne.py b/Orange/widgets/unsupervised/owtsne.py index 0f8b3958778..2aa178eda46 100644 --- a/Orange/widgets/unsupervised/owtsne.py +++ b/Orange/widgets/unsupervised/owtsne.py @@ -80,7 +80,7 @@ class OWtSNE(OWDataProjectionWidget): embedding_variables_names = ("t-SNE-x", "t-SNE-y") #: Runtime state - Running, Finished, Waiting = 1, 2, 3 + Running, Finished, Waiting, Pending = 1, 2, 3, 4 class Outputs(OWDataProjectionWidget.Outputs): preprocessor = Output("Preprocessor", Preprocess) @@ -98,6 +98,7 @@ def __init__(self): self.pca_data = None self.projection = None self.tsne_runner = None + self.tsne_iterator = None self.__update_loop = None # timer for scheduling updates self.__timer = QTimer(self, singleShot=True, interval=1, @@ -120,22 +121,28 @@ def _add_controls_start_box(self): ) self.perplexity_spin = gui.spin( - box, self, "perplexity", 1, 500, step=1, alignment=Qt.AlignRight) + box, self, "perplexity", 1, 500, step=1, alignment=Qt.AlignRight, + callback=self._params_changed + ) form.addRow("Perplexity:", self.perplexity_spin) + self.perplexity_spin.setEnabled(not self.multiscale) form.addRow(gui.checkBox( box, self, "multiscale", label="Preserve global structure", callback=self._multiscale_changed )) - self._multiscale_changed() sbe = gui.hBox(self.controlArea, False, addToLayout=False) gui.hSlider( - sbe, self, "exaggeration", minValue=1, maxValue=4, step=1) + sbe, self, "exaggeration", minValue=1, maxValue=4, step=1, + callback=self._params_changed + ) form.addRow("Exaggeration:", sbe) sbp = gui.hBox(self.controlArea, False, addToLayout=False) gui.hSlider( - sbp, self, "pca_components", minValue=2, maxValue=50, step=1) + sbp, self, "pca_components", minValue=2, maxValue=50, step=1, + callback=self._params_changed + ) form.addRow("PCA components:", sbp) box.layout().addLayout(form) @@ -143,8 +150,13 @@ def _add_controls_start_box(self): gui.separator(box, 10) self.runbutton = gui.button(box, self, "Run", callback=self._toggle_run) + def _params_changed(self): + self.__state = OWtSNE.Finished + self.__set_update_loop(None) + def _multiscale_changed(self): self.perplexity_spin.setEnabled(not self.multiscale) + self._params_changed() def check_data(self): def error(err): @@ -179,6 +191,8 @@ def _toggle_run(self): if self.__state == OWtSNE.Running: self.stop() self.commit() + elif self.__state == OWtSNE.Pending: + self.resume() else: self.start() @@ -189,8 +203,11 @@ def start(self): self.__start() def stop(self): - if self.__state == OWtSNE.Running: - self.__set_update_loop(None) + self.__state = OWtSNE.Pending + self.__set_update_loop(None) + + def resume(self): + self.__set_update_loop(self.tsne_iterator) def pca_preprocessing(self): if self.pca_data is not None and \ @@ -231,13 +248,14 @@ def __start(self): )(self.pca_data) self.tsne_runner = TSNERunner(self.projection, step_size=50) - - self.__set_update_loop(self.tsne_runner.run_optimization()) + self.tsne_iterator = self.tsne_runner.run_optimization() + self.__set_update_loop(self.tsne_iterator) self.progressBarInit(processEvents=None) def __set_update_loop(self, loop): if self.__update_loop is not None: - self.__update_loop.close() + if self.__state in (OWtSNE.Finished, OWtSNE.Waiting): + self.__update_loop.close() self.__update_loop = None self.progressBarFinished(processEvents=None) @@ -253,8 +271,10 @@ def __set_update_loop(self, loop): else: self.setBlocking(False) self.setStatusMessage("") - self.runbutton.setText("Start") - self.__state = OWtSNE.Finished + if self.__state in (OWtSNE.Finished, OWtSNE.Waiting): + self.runbutton.setText("Start") + if self.__state == OWtSNE.Pending: + self.runbutton.setText("Resume") self.__timer.stop() def __next_step(self): @@ -271,13 +291,16 @@ def __next_step(self): projection, progress = next(self.__update_loop) assert self.__update_loop is loop except StopIteration: + self.__state = OWtSNE.Finished self.__set_update_loop(None) self.unconditional_commit() except MemoryError: self.Error.out_of_memory() + self.__state = OWtSNE.Finished self.__set_update_loop(None) except Exception as exc: self.Error.optimization_error(str(exc)) + self.__state = OWtSNE.Finished self.__set_update_loop(None) else: self.progressBarSet(100.0 * progress, processEvents=None) @@ -319,8 +342,8 @@ def send_preprocessor(self): def clear(self): super().clear() - self.__set_update_loop(None) self.__state = OWtSNE.Waiting + self.__set_update_loop(None) self.pca_data = None self.projection = None