1 """Provides a visual panel (L{qubx.faces.Face}) to optimize single-molecule rate constants.
2
3 Uses math from U{qubx_single_molecule<http://www.qub.buffalo.edu/src/qub-express/api/qubx_single_molecule>}
4
5 /* Copyright 2008-2014 Research Foundation State University of New York */
6
7 /* This file is part of QUB Express. */
8
9 /* QUB Express is free software; you can redistribute it and/or modify */
10 /* it under the terms of the GNU General Public License as published by */
11 /* the Free Software Foundation, either version 3 of the License, or */
12 /* (at your option) any later version. */
13
14 /* QUB Express is distributed in the hope that it will be useful, */
15 /* but WITHOUT ANY WARRANTY; without even the implied warranty of */
16 /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
17 /* GNU General Public License for more details. */
18
19 /* You should have received a copy of the GNU General Public License, */
20 /* named LICENSE.txt, in the QUB Express program directory. If not, see */
21 /* <http://www.gnu.org/licenses/>. */
22
23 """
24
25 import itertools
26 import gc
27 import os
28 import sys
29 import time
30 import traceback
31
32 from qubx.data_types import *
33 from qubx.util_types import *
34 from qubx.faces import *
35 from qubx.GTK import *
36 import qubx.notebook
37 import qubx.optimize
38 import qubx.optimizeGTK
39 import qubx.pyenv
40 from qubx.settings import Propertied, Property
41 import qubx.tree
42 from qubx.trialset import *
43 from qubx.model import *
44 import qubx.fast.model
45 from qubx.fast.fast_utils import buffer_as_array_of_int, buffer_as_array_of_double, buffer_as_matrix_of_int, buffer_as_matrix_of_double
46 from qubx.task import *
47 from max_subi_ll import *
48 from max_inter_ll import inter_ll_or_0
49
50 import scipy.optimize
51 from numpy import *
52
53
54 MAXITER = 300
55 LBL_TDEAD = 'Tdead (ms)'
56
57 OPT_SIMPLEX = 0
58 OPT_DFP = 1
65
67 """Represents a model ready for computation."""
68 - def __init__(self, model=None, modeltree=None):
69 Anon.__init__(self, source=model, modeltree=modeltree or model.as_tree(), model=QubModel())
70 self.model.from_tree(self.modeltree)
71 self.K0, self.K1, self.K2, L, V, P = ModelToRateMatricesP(self.model)
75
77 """Represents data ready for computation."""
80
82 """Holds data, model, and results for one file."""
83 pass
84
85 -def get_data_prep(segs, model_prep, wait=True, receiver=lambda dp: None):
86 if wait:
87 return qubx.pyenv.call_async_wait(lambda rcv: get_data_prep(segs, model_prep, wait=False, receiver=rcv))
88 else:
89 QubX = qubx.global_namespace.QubX
90 view_of_file = dict([(view.file, view) for view in QubX.Data.views])
91 view_segm = []
92 vs_by_file = {}
93 for seg in segs:
94 if not (seg.file in vs_by_file):
95 vs = (view_of_file[seg.file], [])
96 view_segm.append(vs)
97 vs_by_file[seg.file] = vs
98 vs_by_file[seg.file][1].append(seg)
99 if not view_segm:
100 gobject.idle_add(receiver, DataPrep(segs, []))
101 return
102 workspaces = []
103 data_prep = DataPrep(segs, workspaces)
104
105
106
107 def build_workspace():
108 i = len(workspaces)
109 view, segm = view_segm[i]
110 ws = Workspace(view=view)
111 workspaces.append(ws)
112 if view:
113 for i in xrange(view.file.constants.size):
114 if LBL_TDEAD == view.file.constants.get(i, 'Name'):
115 ws.td_ms = view.file.constants.get(i, 'Value')
116 break
117 else:
118 ws.td_ms = view.file.sampling * 1e3
119 view.file.constants.append({'Name':LBL_TDEAD, 'Value':ws.td_ms})
120 view.file.constants.select(view.file.constants.size-1, 'Value')
121
122 ws.sampling_ms = view.file.sampling * 1e3
123 ws.sig_names = [view.file.signals.get(segm[0].signal, 'Name')]
124 ws.signals = [ [] ]
125 for seg in segm:
126 ff, ll, cc, dd = QubX.DataSource.get_idealization(seg, mark_excluded=True, get_fragments=True, get_durations=True)
127 aa = view.file.ideal[segm[0].signal].seg[seg.index].amp
128 ws.signals[0].append( (cc, dd, aa) )
129 QubX.Modeling.Stimulus.call_with_stimulus_idl(receive_stim, [(seg.f, seg.l) for seg in segm], datafile=view.file, model=model_prep.source or model_prep.model)
130 else:
131 receive_stim([], [])
132 def receive_stim(stim_names, stimidl):
133 i = len(workspaces) - 1
134 ws = workspaces[i]
135 ws.sig_names.extend(stim_names)
136 for segs_flcda in stimidl:
137 ws.signals.append( [(cc, dd, aa) for ff,ll,cc,dd,aa in segs_flcda] )
138 try:
139 ws.segs, ws.mcls, ws.scls = ProcessSignals(ws.td_ms, ws.signals)
140 except:
141 traceback.print_exc()
142 ws.segs, ws.mcls, ws.scls = [], [], []
143 del ws.signals
144 if len(workspaces) == len(view_segm):
145 gobject.idle_add(receiver, data_prep)
146 else:
147 gobject.idle_add(build_workspace)
148 build_workspace()
149
151 - def __init__(self, datasrc, Data, stimulus):
171 tdead = property(lambda self: self.get_tdead(), lambda self, x: self.set_tdead(x))
173 if self.__datafile:
174 for i in xrange(self.__datafile.constants.size):
175 if LBL_TDEAD == self.__datafile.constants.get(i, 'Name'):
176 return self.__datafile.constants.get(i, 'Value')
177 td_ms = self.__datafile.sampling * 1e3
178 self.__datafile.constants.append({'Name':LBL_TDEAD, 'Value':td_ms})
179 return td_ms
181 if self.__datafile:
182 qubx.pyenv.env.OnScriptable('QubX.Data.file.constants[QubX.Data.file.constants.index(%s), "Value"] = %s' % (repr(LBL_TDEAD), repr(x)))
183 cc = self.__datafile.constants
184 try:
185 cc[cc.index(LBL_TDEAD), 'Value'] = x
186 except KeyError:
187 print 'surprise!'
188 cc.append({'Name':LBL_TDEAD, 'Value':x})
190 if self.__use_group != x:
191 self.__use_group = x
192 self.invalidate()
193 use_group = property(lambda self: self.__use_group, lambda self, x: self.set_use_group(x))
197 if self.__use_group != None:
198 self.invalidate()
200 if (self.__use_group != None) and (field_name == 'Group'):
201 self.invalidate()
213 if self.__datafile.constants[r,'Name'] == LBL_TDEAD:
214 self.invalidate()
215 self.OnChangeTdead()
216 - def get(self, receiver):
225
226
227 QUBX_SINGLE_RATES_METHODS = ["MIL", "MSL"]
228
229 @Propertied(Property('use_Peq', True, "True to start each segment at equilibrium; False to use States:Pr"),
230 Property('use_group', False, "True to fit a group of data files", in_tree=False),
231 Property('use_group_index', 1, 'Desired Group in Data table'),
232 Property('useGPU', True, "True to use OpenCL when available"),
233 Property("method", QUBX_SINGLE_RATES_METHODS[0], "choice of algorithm in %s" % ', '.join(QUBX_SINGLE_RATES_METHODS)),
234 Property("tdead_in_samples", True, "False to display Tdead in milliseconds")
235 )
238 Face.__init__(self, 'MILRates', 'QubX.Modeling.MILRates')
239 self.__ref = Reffer()
240 self.propertied_connect_settings('Rates')
241
242 self.set_size_request(200, 50)
243 self.QubX = qubx.pyenv.env.globals['QubX']
244 self.QubX.OnQuit += self.__ref(self.qubx_finalize)
245 self.models = self.QubX.Models
246 self.models.OnSwitch += self.__ref(self.__onSwitchModel), 'Rates.onSwitchModel'
247 self.robot = Robot('MILRates')
248 self.robot.OnException += self.__ref(self.__onException)
249 self.robot.do(self.__robot_setup_cl_context)
250 self.plexig = MultiplexedSignals(self.QubX.DataSource, self.QubX.Data, self.QubX.Modeling.Stimulus)
251 self.plexig.use_group = self.use_group_index if self.use_group else None
252 self.plexig.OnInvalidate += self.__ref(self.__onInvalidateData)
253 self.plexig.OnChangeTdead += self.__ref(self.__onChangeTdead)
254 self.model_prep = Product(self.__get_model_prep)
255 self.model_prep.OnInvalidate += self.__ref(self.__onInvalidateModel)
256 self.rates = Product(self.__get_rates)
257 self.rates.OnInvalidate += self.__ref(self.__onInvalidateModel)
258 self.LL = 0.0
259 self.iterations = 0
260 self.grads = []
261 self.__on_trial = None
262
263 qubx.optimize.setup_opt("qubx_single_rates_optimize",
264 {"max step" : 0.1})
265
266 self.OnLL = WeakEvent()
267 self.OnIteration = WeakEvent()
268 self.OnOptimized = WeakEvent()
269
270 qubx.notebook.Notebook.register_auto('Rates_eval.Messages', 'Messages, on MILRates: Get LL', False)
271 qubx.notebook.Notebook.register_auto('Rates_eval.Model.Picture', 'Model picture, on MILRates: Get LL', False)
272 qubx.notebook.Notebook.register_auto('Rates_eval.Model.PictureNO', 'Model picture, no overlays, on MILRates: Get LL', False)
273 qubx.notebook.Notebook.register_auto('Rates_eval.Data.Picture', 'Data picture, on MILRates: Get LL', False)
274 qubx.notebook.Notebook.register_auto('Rates_eval.Data.PictureNO', 'Data picture, no overlays, on MILRates: Get LL', False)
275 qubx.notebook.Notebook.register_auto('Rates_eval.DurHist.Charts', 'DurHist charts, on MILRates: Get LL', False)
276 qubx.notebook.Notebook.register_auto('Rates_eval.DurHist.TimeConst', 'DurHist time constants, on MILRates: Get LL', False)
277 qubx.notebook.Notebook.register_auto('Rates_eval.Model.Rates', 'Rates table, on MILRates: Optimize', False)
278
279 qubx.notebook.Notebook.register_auto('Rates_opt.Messages', 'Messages, on MILRates: Optimize', True)
280 qubx.notebook.Notebook.register_auto('Rates_opt.Model.Picture', 'Model picture, on MILRates: Optimize', True)
281 qubx.notebook.Notebook.register_auto('Rates_opt.Model.PictureNO', 'Model picture, no overlays, on MILRates: Optimize', False)
282 qubx.notebook.Notebook.register_auto('Rates_opt.Data.Picture', 'Data picture, on MILRates: Optimize', True)
283 qubx.notebook.Notebook.register_auto('Rates_opt.Data.PictureNO', 'Data picture, no overlays, on MILRates: Optimize', False)
284 qubx.notebook.Notebook.register_auto('Rates_opt.DurHist.Charts', 'DurHist charts, on MILRates: Optimize', True)
285 qubx.notebook.Notebook.register_auto('Rates_opt.DurHist.TimeConst', 'DurHist time constants, on MILRates: Optimize', True)
286 qubx.notebook.Notebook.register_auto('Rates_opt.Model.Rates', 'Rates table, on MILRates: Optimize', False)
287 qubx.notebook.Notebook.register_auto('Rates_opt.Model.Constraints', 'Kinetic Constraints table, on MILRates: Optimize', True)
288
289 cols = pack_item(gtk.HBox(), self, expand=True)
290 ctrls = pack_item(gtk.VBox(), cols, expand=True)
291 row = pack_item(gtk.HBox(), ctrls)
292 self.chkPeq = pack_check('start at equilibrium prob. ', row)
293 self.propertied_connect_check('use_Peq', self.chkPeq)
294 pack_space(row, expand=True)
295 self.chkUseSingle = pack_radio('Single file', row)
296 self.chkUseGroup = pack_radio('Multi-file: Group', row, group=self.chkUseSingle)
297 self.chkUseGroup.set_tooltip_text('Assign Groups in the Data table')
298 self.propertied_connect_radios('use_group', [(False, self.chkUseSingle),
299 (True, self.chkUseGroup)])
300 self.txtUseGroup = pack_item(qubx.GTK.NumEntry(self.use_group_index, acceptIntGreaterThan(-1), width_chars=3), row)
301 self.propertied_connect_NumEntry('use_group_index', self.txtUseGroup)
302 pack_space(row, expand=True)
303
304 pack_label('', row).set_markup('t<sub>dead</sub>:')
305 td = (1e-3*self.plexig.tdead/self.QubX.Data.file.sampling) if self.tdead_in_samples else self.plexig.tdead
306 self.txtTdead = pack_item(qubx.GTK.NumEntry(td, acceptFloatGreaterThanOrEqualTo(0.0), '%.2g', width_chars=5), row)
307 self.txtTdead.set_tooltip_text("""Note for multi-file: this is Tdead for the onscreen Data file only.
308 To edit another file's Tdead, first select it in the Data panel.""")
309 self.txtTdead.OnChange += self.__ref(self.__onEditTdead)
310 self.chkTdeadMS = pack_radio('[ms]', row)
311 self.chkTdeadSamples = pack_radio('[samples]', row, group=self.chkTdeadMS)
312 self.propertied_connect_radios('tdead_in_samples', [(False, self.chkTdeadMS), (True, self.chkTdeadSamples)])
313 pack_space(row, expand=True)
314
315 self.txtStatus = gtk.TextView()
316 self.outStatus = TextViewAppender(self.txtStatus)
317 self.txtStatus.set_editable(False)
318 SetFixedWidth(self.txtStatus)
319 pack_scrolled(self.txtStatus, ctrls, expand=True)
320 self.outStatus.write("""Based on "Estimating single-channel kinetic parameters from idealized patch-clamp data containing missed events," Qin F, Auerbach A, Sachs F, Biophys J. 1996 Jan; 70(1):264-80
321 and "Maximum likelihood estimation of aggregated Markov processes," Qin F, Auerbach A, Sachs F, Proc Biol Sci. 1997 Mar 22; 264(1380):375-83.
322
323 """)
324
325 ctrls = pack_item(gtk.VBox(), cols)
326 row = pack_item(gtk.HBox(), ctrls)
327 pack_label('LL:', row)
328 self.txtLL = pack_item(gtk.Entry(), row, expand=True)
329 self.txtLL.set_editable(False)
330 self.txtLL.set_width_chars(8)
331 self.chkUseGPU = pack_check("use GPU", ctrls)
332 self.chkUseGPU.set_tooltip_text("If an OpenCL-capable graphics processor is available")
333 self.propertied_connect_check('useGPU', self.chkUseGPU)
334 self.mnuMethod = pack_item(gtk.combo_box_new_text(), ctrls)
335 for i, method_name in enumerate(QUBX_SINGLE_RATES_METHODS):
336 self.mnuMethod.append_text(method_name)
337 if method_name == self.method:
338 self.mnuMethod.set_active(i)
339 self.mnuMethod.connect('changed', self.__onChangeMethod)
340 self.btnGetLL = pack_button('Get LL', ctrls, on_click=self.__onClickGetLL)
341 self.btnAddToTrials = pack_button('Add to Trials', ctrls, on_click=self.__onClickAddToTrials)
342 self.btnOpt = pack_button('Optimize Rates', ctrls, on_click=self.__onClickOpt, at_end=True)
343 self.optOptions = qubx.optimizeGTK.OptOptionsFace('MILRates', 'qubx_single_rates_optimize', 'QubX.About.MILRates')
344
345 self.__data = self.__model = None
346 self.__optimizing = False
347 self.__onSwitchModel(self.models, self.models.file)
348 self.cl_context = None
349
350 self.QubX.Modeling.Utils.add_optimizer('MIL', ModelPrep, get_data_prep, self.get_ll_mil, self.optimize_mil, self.OnLL, self.OnIteration, self.OnOptimized)
351 self.QubX.Modeling.Utils.add_optimizer('MSL', ModelPrep, get_data_prep, self.get_ll_msl, self.optimize_msl, self.OnLL, self.OnIteration, self.OnOptimized)
352 optimizing = property(lambda self: self.__optimizing)
373
374
376 traceback.print_exception(typ, val, trace, file=self.outStatus)
394 if model == self.__model: return
395 if self.__model:
396 for event in (self.__model.states.OnSet,
397 self.__model.states.OnInsert,
398 self.__model.states.OnRemoved,
399 self.__model.rates.OnInsert,
400 self.__model.rates.OnRemoved,
401 self.__model.constraints_kin.OnEdit,
402 self.__model.OnChangeBalanceLoops,
403 self.__model.OnSetChannelCount):
404 event -= self.__ref(self.__onSetModel)
405 self.__model.rates.OnSet -= self.__ref(self.__onSetRate)
406 self.__model = model
407 if self.__model:
408 for event in (self.__model.states.OnSet,
409 self.__model.states.OnInsert,
410 self.__model.states.OnRemoved,
411 self.__model.rates.OnInsert,
412 self.__model.rates.OnRemoved,
413 self.__model.constraints_kin.OnEdit,
414 self.__model.OnChangeBalanceLoops,
415 self.__model.OnSetChannelCount):
416 event += self.__ref(self.__onSetModel)
417 self.__model.rates.OnSet += self.__ref(self.__onSetRate)
418 self.__onSetModel()
419 model = property(lambda self: self.__model, lambda self, x: self.set_model(x))
425 if not self.__optimizing:
426 self.robot.do(gobject.idle_add, self.__gray_ll)
435 color = gdk.color_parse(gray and "#999999" or "#000000")
436 self.txtLL.modify_text(gtk.STATE_NORMAL, color)
438 self.__receiver = receiver
439 self.txtStatus.get_buffer().set_text('')
440 Tasks.add_task(self.robot)
441 for ctrl in [self.btnGetLL, self.btnAddToTrials, self.btnOpt, self.chkPeq]:
442 ctrl.set_sensitive(False)
444 try:
445 self.LL = result[0]
446 except:
447 self.LL = result
448 Tasks.remove_task(self.robot)
449 for ctrl in [self.btnGetLL, self.btnAddToTrials, self.btnOpt, self.chkPeq]:
450 ctrl.set_sensitive(True)
451 if self.method == 'MIL':
452 self.request_show()
453 self.__optimizing = False
454 if self.__receiver:
455 self.__receiver(result)
456 del self.__receiver
458 self.cl_context = msl_accel_context(MSL_ACCEL_OPENCL)
460 if self.__model == None:
461 return None
462 return ModelPrep(self.__model)
470 for k, v in kw.iteritems():
471 try:
472 setattr(self, k, v)
473 except:
474 print 'MIL/MSL optimizer has no option named %s' % k
476 qubx.pyenv.env.OnScriptable('QubX.Modeling.MILRates.get_ll()')
477 self.get_ll(wait=False)
484 - def get_ll(self, model_prep=None, data_prep=None, wait=True, receiver=lambda LL: None, on_trial=None, **kw):
485 if wait:
486 return qubx.pyenv.call_async_wait(self.__start_get_ll, model_prep, data_prep, on_trial=on_trial, **kw)[0]
487 else:
488 self.__start_get_ll(receiver, model_prep, data_prep, on_trial=on_trial, **kw)
489 - def __start_get_ll(self, receiver=lambda LL: None, model_prep=None, data_prep=None, on_trial=None, **kw):
527 - def make_trial(self, fields={}, initial_tree=None, model_prep=None, data_prep=None):
528 if model_prep:
529 model = model_prep.source or model_prep.model
530 else:
531 model = self.QubX.Models.file
532 if data_prep and data_prep.segs:
533 data = data_prep.segs[0].file
534 else:
535 data = self.QubX.Data.file
536 mtree = model.as_tree()
537 nParam = fields['nParam'] if ('nParam' in fields) else 0
538 row = make_trial_row(model.path, data.path, self.LL, nParam, nData=self.robot.Nevent,
539 nEvent=self.robot.Nevent)
540 if initial_tree:
541 add_trial_rates(row, initial_tree, 'Initial ')
542 add_trial_rates(row, mtree)
543 for k,v in fields.iteritems():
544 row[k] = v
545 return mtree, row
547 qubx.pyenv.env.OnScriptable('QubX.Modeling.MILRates.optimize(on_trial=QubX.Modeling.MILRates.add_to_trials)')
548 self.optimize(wait=False, on_trial=self.add_to_trials)
555 - def optimize(self, model_prep=None, data_prep=None, wait=True, receiver=lambda LL: None, on_trial=None, **kw):
556 if wait:
557 return qubx.pyenv.call_async_wait(self.__start_opt, model_prep, data_prep, on_trial=on_trial, **kw)[0]
558 else:
559 self.__start_opt(receiver, model_prep, data_prep, on_trial=on_trial, **kw)
560 - def __start_opt(self, receiver=lambda LL: None, model_prep=None, data_prep=None, ext_return=False, on_trial=None, **kw):
561 self.iterations = 0
562 self.iter_trials = []
563 self.grads = []
564 self.__on_trial = on_trial
565 self.__optimizing = True
566 self.__begin_task(receiver)
567 self.__set_keys(**kw)
568 mp = model_prep
569 if mp is None:
570 mp = self.model_prep.val
571 if mp.source:
572 mp.source.undoStack.enable(False)
573 mp.set_rates2(*self.rates.val)
574 self.__mtree_pre_opt = mp.model.as_tree()
575 Trials = qubx.global_namespace.QubX.Trials
576 Trials.get_trialset("%s Iterations"%self.method, in_qsf=True).clear()
577 self.robot.do(self.robot_opt, self.use_Peq,
578 qubx.optimize.get_opts("qubx_single_rates_optimize",
579 on_start=lambda rec, opts: gobject.idle_add(self.optOptions.optimizers.onStartOpt, rec, opts),
580 on_end=lambda rec, opts: gobject.idle_add(self.optOptions.optimizers.onEndOpt, rec, opts)),
581 mp, data_prep, ext_return)
582 if self.method == 'MIL':
583 self.QubX.Figures.DurHist.request_show()
584 self.QubX.Models.view.optimizing = True
586 self.iterations += 1
587 self.LL = LL
588 if model_prep.source:
589 K012ToModel(K0, K1, K2, model_prep.source)
590 self.txtLL.set_text('%.6g' % LL)
591 self.__gray_ll(False)
592 self.outStatus.write('Iter: %i\tLL: %8.5g\tgrads: %s\n' % (self.iterations, LL, ', '.join("%8.5g"%g for g in grads)))
593 self.OnIteration(self.iterations, LL, self.grads)
594 - def __onOptDone(self, LL, workspaces, model_prep, ext_return):
595 self.QubX.Models.view.optimizing = False
596 self.outStatus.write('Finished optimizing (%s). LL = %.6g\n' % (self.robot.method, LL))
597 try:
598 if model_prep.source:
599 model_prep.source.undoStack.enable()
600 model_prep.source.undoStack.push_undo(bind(model_prep.source.from_tree, self.__mtree_pre_opt),
601 bind(model_prep.source.from_tree, model_prep.source.as_tree()))
602 self.__set_model_confidence(model_prep, self.robot.K, self.robot.dK)
603 if model_prep.source:
604 model_prep.source.undoStack.seal_undo('set rates')
605 self.txtLL.set_text('%.6g' % LL)
606 self.__gray_ll(False)
607 finally:
608 if ext_return:
609 self.__end_task((LL, self.iterations, self.grads, self.hessian))
610 else:
611 self.__end_task(LL)
612 if self.__on_trial:
613 mtree, trial_row = self.make_trial({'nParam' : len(self.grads), 'Iterations' : self.iterations},
614 initial_tree=self.__mtree_pre_opt, model_prep=model_prep, data_prep=self.robot.data_prep)
615 for i, g in enumerate(self.grads):
616 trial_row['Gradient %i' % i] = g
617 self.__on_trial(mtree, trial_row)
618 self.__on_trial = None
619 qubx.notebook.Notebook.send(qubx.notebook.NbText("""MILRates: Finished optimizing %d events after %d iterations (%s).
620 Data: %s
621 Model: %s
622 P0: %s
623 LL: %.6g""" % (self.robot.Nevent, self.robot.iter, self.robot.method,
624 '\n '.join(os.path.split(ws.view.file.path)[1] for ws in workspaces),
625 model_prep.source and os.path.split(model_prep.source.path)[1] or "(from script)",
626 self.robot.Peq and "equilibrium" or "fixed (States table)",
627 LL)), auto_id='Rates_opt.Messages')
628 qubx.notebook.Notebook.send(self.QubX.Models.view.nbPicture, auto_id='Rates_opt.Model.Picture')
629 qubx.notebook.Notebook.send(self.QubX.Models.view.nbPictureNO, auto_id='Rates_opt.Model.PictureNO')
630 qubx.notebook.Notebook.send(self.QubX.Data.view.hires.nbPicture, auto_id='Rates_opt.Data.Picture')
631 qubx.notebook.Notebook.send(self.QubX.Data.view.hires.nbPictureNO, auto_id='Rates_opt.Data.PictureNO')
632 qubx.notebook.Notebook.send(self.QubX.Tables.find_view('Rates').nbTable, auto_id='Rates_opt.Model.Rates')
633 qubx.notebook.Notebook.send(self.QubX.Tables.find_view('Kinetic Constraints').nbTable, auto_id='Rates_opt.Model.Constraints')
634
635 self.QubX.Figures.DurHist.update(force=True)
636 self.QubX.Figures.DurHist.robot.do(self.__dhrobot_send_durhists, 'Rates_opt.DurHist.Charts', 'Rates_opt.DurHist.TimeConst')
637
638 ts = qubx.global_namespace.QubX.Trials.get_trialset("%s Iterations"%self.method, in_qsf=True)
639 for mtree, row in self.iter_trials:
640 ts.append_trial(mtree, row)
641
642 self.OnOptimized(self.iterations, LL, self.grads, self.hessian)
644 have_dK = any(dK)
645 self.outStatus.write('%10s%7s' % ("Rate From", "To"), scroll=False)
646 self.outStatus.write_bold('%14s' % "k0", scroll=False)
647 if have_dK:
648 self.outStatus.write('%11s' % "+/-", scroll=False)
649 self.outStatus.write_bold('%14s' % 'k1', scroll=False)
650 if have_dK:
651 self.outStatus.write('%11s' % "+/-", scroll=False)
652 self.outStatus.write_bold('%14s' % 'k2', scroll=False)
653 if have_dK:
654 self.outStatus.write('%11s' % "+/-", scroll=False)
655 self.outStatus.write('\n')
656 model = model_prep.source
657 if not model:
658 return
659 k_ix = K_index(model.states.size)
660 rr = model.rates
661 for r in xrange(rr.size):
662 a, b = [rr.get(r, f) for f in ('From', 'To')]
663 i = k_ix.index((a,b))
664 self.outStatus.write('%10d%7d' % (a,b), scroll=False)
665 self.outStatus.write_bold('%14.5g' % rr.get(r, 'k0'), scroll=False)
666 k0_lo = k0_hi = k0_std = 0.0
667 if dK[i]:
668 k0_lo = float(exp(K[i] - dK[i]))
669 k0_hi = float(exp(K[i] + dK[i]))
670 k0_std = float(exp(K[i]) * dK[i])
671 rr.set(r, 'k0 lo', k0_lo)
672 rr.set(r, 'k0 hi', k0_hi)
673 rr.set(r, 'k0 std', k0_std)
674 if have_dK:
675 self.outStatus.write('%11.3g' % k0_std, scroll=False)
676 i += len(k_ix)
677 self.outStatus.write_bold('%14.5g' % K[i], scroll=False)
678 k1_lo = k1_hi = k1_std = 0.0
679 if dK[i]:
680 k1_lo = float(K[i] - dK[i])
681 k1_hi = float(K[i] + dK[i])
682 k1_std = float(dK[i])
683 rr.set(r, 'k1 lo', k1_lo)
684 rr.set(r, 'k1 hi', k1_hi)
685 rr.set(r, 'k1 std', k1_std)
686 if have_dK:
687 self.outStatus.write('%11.3g' % k1_std, scroll=False)
688 i += len(k_ix)
689 self.outStatus.write_bold('%14.5g' % K[i], scroll=False)
690 k2_lo = k2_hi = k2_std = 0.0
691 if dK[i]:
692 k2_lo = float(K[i] - dK[i])
693 k2_hi = float(K[i] + dK[i])
694 k2_std = float(dK[i])
695 rr.set(r, 'k2 lo', k2_lo)
696 rr.set(r, 'k2 hi', k2_hi)
697 rr.set(r, 'k2 std', k2_std)
698 if have_dK:
699 self.outStatus.write('%11.3g' % k2_std, scroll=False)
700 self.outStatus.write('\n')
702 gobject.idle_add(self.outStatus.write, msg+"\n")
704 self.robot.Peq = Peq
705 self.robot.model_prep = model_prep
706 self.robot.model = self.robot.model_prep.model
707 self.robot.K = K012toK(self.robot.model_prep.K0, self.robot.model_prep.K1, self.robot.model_prep.K2)
708 self.robot.clazz = ModelToClazz(self.robot.model)
709 self.robot.method = self.method
710 if self.robot.Peq:
711 self.robot.P0 = None
712 else:
713 self.robot.P0 = ModelToP0(self.robot.model)
714 if self.robot.clazz.shape[0] == 0:
715 self.robot.data_prep = Anon(ws=[])
716 elif data_prep:
717 self.robot.data_prep = data_prep
718 else:
719 self.plexig.model_prep = model_prep
720 self.robot.data_prep = self.robot.gui_call_recv(self.plexig.request, self.robot)[0]
721 self.robot.workspaces = self.robot.data_prep.ws
722 self.robot.Nevent = 0
723 for ws in self.robot.workspaces:
724 try:
725 ws.single_model.dispose()
726 except:
727 pass
728 ws.single_model = ModelToFast(self.robot.model, ws.sig_names, self.robot_on_status)
729 ws.multi_model = qubx.fast.model.MultiModel(ws.single_model)
730 ws.multi_model.update()
731 if self.robot.Peq:
732 ws.P0 = None
733 else:
734 ws.P0 = buffer_as_array_of_double(ws.multi_model.multi_model[0].P0, ws.multi_model.multi_model[0].Nstate)
735 if self.robot.method == 'MIL':
736 if len(ws.scls) > 1:
737 gobject.idle_add(self.outStatus.write, "MIL can't analyze data with a changing stimulus. Please use MSL instead.\n")
738 return False
739 elif __builtins__['any'](mc[0] < 0 for mc in ws.mcls):
740 gobject.idle_add(self.outStatus.write, "MIL can't analyze data with blank or excluded regions. Please extract, or use MSL instead.\n")
741 return False
742 self.robot.Nevent += sum(len(durs) for clss, durs in ws.segs)
743 if not self.cl_context:
744 self.__robot_setup_cl_context()
745 if not ws.cl_data:
746 ws.cl_data = msl_accel_data(self.cl_context, ws.segs, ws.mcls, ws.scls)
747 ws.cl_model = msl_accel_models(self.cl_context, cl_dimension(ws.multi_model.multi_model[0].Nstate), 1, len(ws.scls))
748 return True
750 self.robot.Nevent += sum(len(durs) for clss, durs in segs)
751 gobject.idle_add(self.outStatus.write, '%s: %i events\n' % (lbl, self.robot.Nevent))
752 return [([seg], mcls, scls) for seg in segs if len(seg[0])]
754 if not self.robot.workspaces:
755 gobject.idle_add(self.outStatus.write, "The model is empty.\n")
756 return 0.0
757 elif not self.robot.data_prep.ws:
758 gobject.idle_add(self.outStatus.write, "There are no data files in this group (see the Data table).\n")
759 return 0.0
760 if self.robot.Nevent == 0:
761 gobject.idle_add(self.outStatus.write, "To use this panel, first Idealize your single-molecule data.\nFor ensemble data, try MacRates.\n")
762 return 0.0
763 LL = 0.0
764 K0, K1, K2 = KtoK012(K, self.robot.workspaces[0].single_model.K0)
765 argses = []
766 iarr, imat, dmat = buffer_as_array_of_int, buffer_as_matrix_of_int, buffer_as_matrix_of_double
767 for ws in self.robot.workspaces:
768 ws.single_model.K0[:,:] = K0[:,:]
769 ws.single_model.K1[:,:] = K1[:,:]
770 ws.single_model.K2[:,:] = K2[:,:]
771 ws.multi_model.update()
772 mm = ws.multi_model.multi_model
773 Ns = mm[0].Nstate
774 method = self.cl_context.run_mil if (self.robot.method == 'MIL') else self.cl_context.run_msl
775 ws.cl_model.reset()
776 if ws.cl_model.setup_model(ws.cl_data, iarr(mm[0].clazz, Ns), ws.P0, dmat(mm[0].K0[0], Ns, Ns), dmat(mm[0].K1[0], Ns, Ns), dmat(mm[0].K2[0], Ns, Ns),
777 imat(mm[0].L[0], Ns, Ns), imat(mm[0].V[0], Ns, Ns), imat(mm[0].P[0], Ns, Ns),
778 ws.td_ms*1e-3, lambda msg, obj: gobject.idle_add(self.outStatus.write, msg+"\n")):
779 return -1e21
780 if method(ws.cl_data, ws.cl_model, self.useGPU and MSL_ACCEL_OPENCL or MSL_ACCEL_OPENMP):
781 gobject.idle_add(self.disable_gpu)
782 if method(ws.cl_data, ws.cl_model, MSL_ACCEL_OPENMP):
783 return -1e20
784 LL += ws.cl_model.ll[0]
785 return LL
787 self.useGPU = False
788 self.chkGPU.set_sensitive(False)
789 self.chkGPU.set_tooltip_text("OpenCL is not available; needs a better graphics card or new drivers.")
790 - def robot_get_ll(self, Peq, model_prep, data_prep, on_trial):
791 if not self.robot_setup(Peq, model_prep, data_prep):
792 gobject.idle_add(self.__onGotLL, 0.0, on_trial)
793 else:
794 try:
795 LL = self.robot_eval(self.robot.K)
796 gobject.idle_add(self.__onGotLL, LL, on_trial)
797 except:
798 gobject.idle_add(self.__end_task, -1.0)
799 raise
800 del self.robot.workspaces
801 - def robot_opt(self, Peq, optimize, model_prep, data_prep, ext_return):
802 self.robot.optimize = optimize
803 self.robot.progress = 0.0
804 self.robot.iter = 0
805 self.robot.LL = 0.0
806 if not self.robot_setup(Peq, model_prep, data_prep):
807 if ext_return:
808 gobject.idle_add(self.__end_task, (-1.0, 0, [], None))
809 else:
810 gobject.idle_add(self.__end_task, -1.0)
811 return
812 try:
813 K0, K1, K2 = KtoK012(self.robot.K, self.robot.workspaces[0].single_model.K0)
814 A, B = self.robot.model.constraints_kin.get_matrices_p(K0, K1, K2,
815 self.robot.workspaces[0].single_model.L, self.robot.workspaces[0].single_model.V,
816 self.robot.workspaces[0].single_model.P)
817
818
819 self.robot.Kr, AinR, BinR, self.robot.Kr_indices = qubx.fast.model.EliminateFixedParams(self.robot.K, A, B)
820 A, B = qubx.fast.model.ReduceConstraints(AinR, BinR)
821 self.robot.A, self.robot.B, self.robot.Ainv, pars = qubx.fast.model.SetupLinearConstraints(A, B, self.robot.Kr)
822 self.robot.Asc, self.robot.Bsc, self.robot.Asci, pars = start_at_ones(pars)
823 self.robot.pars = array([x for x in pars.A[:,0]])
824 self.robot.npar = len(self.robot.pars)
825 if self.robot.npar > 0:
826 for ws in self.robot.workspaces:
827 ws.cl_models = msl_accel_models(self.cl_context, cl_dimension(ws.multi_model.multi_model[0].Nstate), 1+2*self.robot.npar, len(ws.scls))
828 score, pars, iterations, grads, Hessian = self.robot.optimize(array(self.robot.pars), self.robot_func, self.robot_iter, self.robot_grad_func)
829
830 self.robot_iter(self.robot.pars, grads, iterations, - self.robot.LL)
831 else:
832 score, pars, iterations, grads, Hessian = self.robot_eval(self.robot.K), [], 0, [], numpy.zeros(shape=(0,0))
833 self.robot.LL = - score
834 dKr = CalcStdOfK(self.robot.A, self.robot.Asc, Hessian)
835 self.robot.dK = zeros(shape=self.robot.K.shape, dtype='float64')
836 qubx.fast.model.Kr_to_K(dKr, self.robot.dK, self.robot.Kr_indices)
837 self.iterations = iterations
838 self.grads = grads
839 self.hessian = Hessian
840 except:
841 traceback.print_exc()
842 score = -1.0
843 if ext_return:
844 gobject.idle_add(self.__end_task, -1.0, self.robot.iter, [], None)
845 else:
846 gobject.idle_add(self.__end_task, -1.0)
847 raise
848 self.LL = score
849 gobject.idle_add(self.__onOptDone, self.robot.LL, self.robot.workspaces, self.robot.model_prep, ext_return)
850 del self.robot.workspaces
851 del self.robot.model_prep
859 iarr, imat, dmat = buffer_as_array_of_int, buffer_as_matrix_of_int, buffer_as_matrix_of_double
860 LL = 0.0
861 for ix in xrange(self.robot.npar):
862 grads[ix] = 0.0
863 valids = []
864 xpars = numpy.array(pars[:self.robot.npar], dtype='float64', copy=True)
865 for ws in self.robot.workspaces:
866 ws.cl_models.reset()
867 for ix in xrange(0, self.robot.npar+1):
868 deltas = [0] if (ix == self.robot.npar) else [-dx, dx]
869 for d in deltas:
870 xpars[:self.robot.npar] = pars[:self.robot.npar]
871 if d:
872 xpars[ix] += d
873
874 self.robot.Kr = ParsToK(parsToK(xpars, self.robot.Asc, self.robot.Asci, self.robot.Bsc), self.robot.A, self.robot.Ainv, self.robot.B)
875 qubx.fast.model.Kr_to_K(self.robot.Kr, self.robot.K, self.robot.Kr_indices)
876
877 K0, K1, K2 = KtoK012(self.robot.K, self.robot.workspaces[0].single_model.K0)
878 ws.single_model.K0[:,:] = K0[:,:]
879 ws.single_model.K1[:,:] = K1[:,:]
880 ws.single_model.K2[:,:] = K2[:,:]
881 ws.multi_model.update()
882 mm = ws.multi_model.multi_model
883 Ns = mm[0].Nstate
884
885 valid = True
886 if ws.cl_models.setup_model(ws.cl_data, iarr(mm[0].clazz, Ns), ws.P0, dmat(mm[0].K0[0], Ns, Ns), dmat(mm[0].K1[0], Ns, Ns), dmat(mm[0].K2[0], Ns, Ns),
887 imat(mm[0].L[0], Ns, Ns), imat(mm[0].V[0], Ns, Ns), imat(mm[0].P[0], Ns, Ns),
888 ws.td_ms*1e-3, lambda msg, obj: gobject.idle_add(self.outStatus.write, msg+"\n")):
889 valid = False
890 if not d:
891 return 1e20
892 valids.append(valid)
893 method = self.cl_context.run_mil if (self.robot.method == 'MIL') else self.cl_context.run_msl
894 ws.cl_err = method(ws.cl_data, ws.cl_models, self.useGPU and MSL_ACCEL_OPENCL or MSL_ACCEL_OPENMP)
895
896
897
898 if ws.cl_err:
899 gobject.idle_add(self.disable_gpu)
900
901 ws.cl_err = method(ws.cl_data, ws.cl_models, MSL_ACCEL_OPENMP)
902 if ws.cl_err:
903 return 1e20
904 LL += ws.cl_models.ll[ws.cl_models.Nmodel-1]
905 for ix in xrange(self.robot.npar):
906 if valids[2*ix+1] and valids[2*ix]:
907 grads[ix] -= (ws.cl_models.ll[2*ix+1] - ws.cl_models.ll[2*ix]) / (2*dx)
908 elif valids[2*ix+1]:
909 grads[ix] -= (ws.cl_models.ll[2*ix+1] - ws.cl_models.ll[ws.cl_models.Nmodel-1]) / dx;
910 elif valids[2*ix]:
911 grads[ix] -= (ws.cl_models.ll[ws.cl_models.Nmodel-1] - ws.cl_models.ll[2*ix]) / dx;
912 return -LL
913 - def robot_iter(self, pars, grads, iterations, score):
914 self.robot.iter = iterations
915 self.robot.LL = - score
916 self.robot.pars[:] = pars[:self.robot.npar]
917 self.robot.progress = (self.robot.iter * 100.0 / MAXITER)
918 self.robot.Kr = ParsToK(parsToK(self.robot.pars, self.robot.Asc, self.robot.Asci, self.robot.Bsc), self.robot.A, self.robot.Ainv, self.robot.B)
919 qubx.fast.model.Kr_to_K(self.robot.Kr, self.robot.K, self.robot.Kr_indices)
920 K0, K1, K2 = KtoK012(self.robot.K, self.robot.workspaces[0].single_model.K0)
921 gobject.idle_add(self.__onIteration, - score, K0, K1, K2, self.robot.model_prep, grads)
922 mtree, row = self.make_trial({"Iteration": iterations}, model_prep=self.robot.model_prep, data_prep=self.robot.data_prep)
923 self.iter_trials.append((mtree, row))
924
927 use_lib, clazz, P0, K0, K1, K2, L, V, P, tdead_sec, segs, mcls, scls = args
928 constants = scls[0]
929 renumber = array([mc[0] for mc in mcls], dtype='int32')
930 mil_segs = [(array(renumber[classes], copy=True, dtype='int32'), durations) for classes, durations in segs]
931 mil_args = (clazz, P0, K0, K1, K2, L, V, P, constants, tdead_sec, mil_segs)
932 return inter_ll_or_0(mil_args)
933