Skip to content

Commit 7bd27f7

Browse files
committed
OWtSNE: Add resume option
1 parent a781a08 commit 7bd27f7

File tree

1 file changed

+36
-13
lines changed

1 file changed

+36
-13
lines changed

Orange/widgets/unsupervised/owtsne.py

Lines changed: 36 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ class OWtSNE(OWDataProjectionWidget):
8080
embedding_variables_names = ("t-SNE-x", "t-SNE-y")
8181

8282
#: Runtime state
83-
Running, Finished, Waiting = 1, 2, 3
83+
Running, Finished, Waiting, Pending = 1, 2, 3, 4
8484

8585
class Outputs(OWDataProjectionWidget.Outputs):
8686
preprocessor = Output("Preprocessor", Preprocess)
@@ -98,6 +98,7 @@ def __init__(self):
9898
self.pca_data = None
9999
self.projection = None
100100
self.tsne_runner = None
101+
self.tsne_iterator = None
101102
self.__update_loop = None
102103
# timer for scheduling updates
103104
self.__timer = QTimer(self, singleShot=True, interval=1,
@@ -120,31 +121,42 @@ def _add_controls_start_box(self):
120121
)
121122

122123
self.perplexity_spin = gui.spin(
123-
box, self, "perplexity", 1, 500, step=1, alignment=Qt.AlignRight)
124+
box, self, "perplexity", 1, 500, step=1, alignment=Qt.AlignRight,
125+
callback=self._params_changed
126+
)
124127
form.addRow("Perplexity:", self.perplexity_spin)
128+
self.perplexity_spin.setEnabled(not self.multiscale)
125129
form.addRow(gui.checkBox(
126130
box, self, "multiscale", label="Preserve global structure",
127131
callback=self._multiscale_changed
128132
))
129-
self._multiscale_changed()
130133

131134
sbe = gui.hBox(self.controlArea, False, addToLayout=False)
132135
gui.hSlider(
133-
sbe, self, "exaggeration", minValue=1, maxValue=4, step=1)
136+
sbe, self, "exaggeration", minValue=1, maxValue=4, step=1,
137+
callback=self._params_changed
138+
)
134139
form.addRow("Exaggeration:", sbe)
135140

136141
sbp = gui.hBox(self.controlArea, False, addToLayout=False)
137142
gui.hSlider(
138-
sbp, self, "pca_components", minValue=2, maxValue=50, step=1)
143+
sbp, self, "pca_components", minValue=2, maxValue=50, step=1,
144+
callback=self._params_changed
145+
)
139146
form.addRow("PCA components:", sbp)
140147

141148
box.layout().addLayout(form)
142149

143150
gui.separator(box, 10)
144151
self.runbutton = gui.button(box, self, "Run", callback=self._toggle_run)
145152

153+
def _params_changed(self):
154+
self.__state = OWtSNE.Finished
155+
self.__set_update_loop(None)
156+
146157
def _multiscale_changed(self):
147158
self.perplexity_spin.setEnabled(not self.multiscale)
159+
self._params_changed()
148160

149161
def check_data(self):
150162
def error(err):
@@ -179,6 +191,8 @@ def _toggle_run(self):
179191
if self.__state == OWtSNE.Running:
180192
self.stop()
181193
self.commit()
194+
elif self.__state == OWtSNE.Pending:
195+
self.resume()
182196
else:
183197
self.start()
184198

@@ -189,8 +203,11 @@ def start(self):
189203
self.__start()
190204

191205
def stop(self):
192-
if self.__state == OWtSNE.Running:
193-
self.__set_update_loop(None)
206+
self.__state = OWtSNE.Pending
207+
self.__set_update_loop(None)
208+
209+
def resume(self):
210+
self.__set_update_loop(self.tsne_iterator)
194211

195212
def pca_preprocessing(self):
196213
if self.pca_data is not None and \
@@ -231,13 +248,14 @@ def __start(self):
231248
)(self.pca_data)
232249

233250
self.tsne_runner = TSNERunner(self.projection, step_size=50)
234-
235-
self.__set_update_loop(self.tsne_runner.run_optimization())
251+
self.tsne_iterator = self.tsne_runner.run_optimization()
252+
self.__set_update_loop(self.tsne_iterator)
236253
self.progressBarInit(processEvents=None)
237254

238255
def __set_update_loop(self, loop):
239256
if self.__update_loop is not None:
240-
self.__update_loop.close()
257+
if self.__state in (OWtSNE.Finished, OWtSNE.Waiting):
258+
self.__update_loop.close()
241259
self.__update_loop = None
242260
self.progressBarFinished(processEvents=None)
243261

@@ -253,8 +271,10 @@ def __set_update_loop(self, loop):
253271
else:
254272
self.setBlocking(False)
255273
self.setStatusMessage("")
256-
self.runbutton.setText("Start")
257-
self.__state = OWtSNE.Finished
274+
if self.__state in (OWtSNE.Finished, OWtSNE.Waiting):
275+
self.runbutton.setText("Start")
276+
if self.__state == OWtSNE.Pending:
277+
self.runbutton.setText("Resume")
258278
self.__timer.stop()
259279

260280
def __next_step(self):
@@ -271,13 +291,16 @@ def __next_step(self):
271291
projection, progress = next(self.__update_loop)
272292
assert self.__update_loop is loop
273293
except StopIteration:
294+
self.__state = OWtSNE.Finished
274295
self.__set_update_loop(None)
275296
self.unconditional_commit()
276297
except MemoryError:
277298
self.Error.out_of_memory()
299+
self.__state = OWtSNE.Finished
278300
self.__set_update_loop(None)
279301
except Exception as exc:
280302
self.Error.optimization_error(str(exc))
303+
self.__state = OWtSNE.Finished
281304
self.__set_update_loop(None)
282305
else:
283306
self.progressBarSet(100.0 * progress, processEvents=None)
@@ -319,8 +342,8 @@ def send_preprocessor(self):
319342

320343
def clear(self):
321344
super().clear()
322-
self.__set_update_loop(None)
323345
self.__state = OWtSNE.Waiting
346+
self.__set_update_loop(None)
324347
self.pca_data = None
325348
self.projection = None
326349

0 commit comments

Comments
 (0)