1 """A trialset is a L{qubx.table.SimpleTable} with one stored model per row.
2
3 Copyright 2012 Research Foundation State University of New York
4 This file is part of QUB Express.
5
6 QUB Express is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 QUB Express is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License,
17 named LICENSE.txt, in the QUB Express program directory. If not, see
18 <http://www.gnu.org/licenses/>.
19
20
21 """
22
23 import time
24 import traceback
25
26 import qubx.date
27 import qubx.global_namespace
28 import qubx.table
29 import qubx.tree
30 from qubx.accept import *
31 from qubx.tree import node_data_or_def
32 from qubx.util_types import WeakEvent, Reffer
33
35 """A table with one model per row (in qubx.tree QMF format); each row represents one result such as an optimizer run.
36 Fields include LL, AIC, BIC, Date (as a string), Model (name), and Data (name), and additional ones will be added as needed when adding rows.
37
38 @ivar trialset_name: onscreen label, e.g. name of the algorithm that created it
39 @ivar models: list of L{qubx.tree.Node}s, one QMF model per row
40 """
41 - def __init__(self, trialset_name, in_qsf=False):
42 qubx.table.SimpleTable.__init__(self, 'Trials', auto_add_fields=True, global_name='QubX.Trials.trialset', sortable=True)
43 self.trialset_name = trialset_name
44 self.__in_qsf = in_qsf
45 self.models = []
46 self.__ref = Reffer()
47 self.add_field('Label', '', str, str, '')
48 self.add_field('LL', 0.0, acceptNothing, '%.6g', '')
49 self.add_field('AIC', 0.0, acceptNothing, '%.6g', '')
50 self.add_field('BIC', 0.0, acceptNothing, '%.6g', '')
51 self.add_field('Date', 0, acceptNothing, qubx.date.formatUnixDate, '')
52 self.add_field('Model', '', acceptNothing, str, '')
53 self.add_field('Data', '', acceptNothing, str, '')
54 self.OnInsert += self.__ref(self.__onInsert)
55 self.OnRemoved += self.__ref(self.__onRemoved)
56 self.OnSetInQSF = WeakEvent()
61 if self.__in_qsf == x: return
62 self.__in_qsf = x
63 self.OnSetInQSF(self, x)
64 in_qsf = property(lambda self: self.__in_qsf, lambda self, x: self.set_in_qsf(x), doc="False if not saved in any session (qsf) file.")
66 """Appends a row.
67
68 @param model_tree: a model in qubx.tree QMF format (e.g. QubX.Models.file.as_tree())
69 @param row_dict: {column_name : value} -- if column_name doesn't exist, it will be created.
70 """
71 self.append(row_dict)
72 self.models[-1] = model_tree
77
78
80 """Collection of L{TrialSet} by name."""
87 """Request a named L{TrialSet}; it will be created if necessary.
88
89 @param name:
90 @param in_qsf: False if it does not belong to any session (qsf) file
91 """
92 try:
93 return self.trialsets[self.trialset_names.index(name)]
94 except ValueError:
95 ts = TrialSet(name, in_qsf)
96 self.trialset_names.append(name)
97 self.trialsets.append(ts)
98 self.OnInsert(len(self.trialsets)-1, ts)
99 return ts
101 """Removes a named TrialSet; ok if there is none by that name."""
102 try:
103 ix = self.trialset_names.index(name)
104 self.OnRemoving(ix, self.trialsets[ix])
105 del self.trialset_names[ix]
106 self.trialsets[ix].dispose()
107 del self.trialsets[ix]
108 except ValueError:
109 pass
110
111
113 """Builds a dict representing one row in a L{TrialSet}; fills in AIC and BIC from LL and nParam; fills in Date from time.localtime().
114 Copies any keyword args into the output.
115
116 @param model_path: copied into 'Model' field
117 @param data_path: copied into 'Data' field
118 @param LL: log likelihood
119 @param nParam: number of free parameters
120 @param nData: number of data points
121 """
122 row = kw.copy()
123 row['Date'] = time.mktime(time.localtime())
124 row['Model'] = model_path
125 row['Data'] = data_path
126 row['LL'] = LL
127 if nParam and LL:
128 row['nParam'] = nParam
129 row['AIC'] = 2*nParam - 2*LL
130 if nData:
131 row['BIC'] = nParam*log(nData) - 2*LL
132 return row
133
135 """Adds entries to a dict (representing one row in a L{TrialSet}) for all nonzero k0,k1,k2 rate constants.
136
137 @param model_tree: model in qubx.tree QMF format, e.g. QubX.Models.file.as_tree()
138 @param prefix: string to prepend to each rate constant, e.g. 'Initial ', or 'Final '
139 """
140 for rate in qubx.tree.children(model_tree.find('Rates'), 'Rate'):
141 states, k0, k1, k2, p, q, pressure, pnames, qnames, pressureNames, fmts = [rate.find(nm) for nm in ('States', 'k0', 'k1', 'k2', 'P', 'Q', 'Pressure', 'PNames', 'QNames', 'PressureNames', 'RateFormats')]
142 pname = pnames.find('PName')
143 qname = qnames.find('QName')
144 pressname = pressureNames.find("PressureName")
145 fmt = fmts.find('RateFormat')
146 for i in (0,1):
147 j = (i+1)%2
148 lig = node_data_or_def(p, 0, i) and str(pname.data) or ""
149 volt = node_data_or_def(q, 0, i) and str(qname.data) or ""
150 press = node_data_or_def(pressure, 0, i) and str(pressname.data) or ""
151 From = node_data_or_def(states, i, i)
152 To = node_data_or_def(states, j, j)
153 row['%sk0 %i_%i' % (prefix, From, To)] = float(node_data_or_def(k0, 10.0, i))
154 if volt:
155 row['%sk1 %i_%i' % (prefix, From, To)] = float(node_data_or_def(k1, 0.0, i))
156 if press:
157 row['%sk2 %i_%i' % (prefix, From, To)] = float(node_data_or_def(k2, 0.0, i))
158 pname = pname.nextSameName()
159 qname = qname.nextSameName()
160 pressname = pressname.nextSameName()
161 fmt = fmt.nextSameName()
162
164 """Adds entries to a dict (representing one row in a L{TrialSet}) for relevant quantities from the Classes table.
165
166 @param model_tree: model in qubx.tree QMF format, e.g. QubX.Models.file.as_tree()
167 @param prefix: string to prepend to each entry, e.g. 'Initial ', or 'Final '
168 """
169 classes_present = set(state['Class'].data[0] for state in qubx.tree.children(model_tree.find('States'), 'State'))
170 if node_data_or_def(model_tree.find('Properties').find('IeqFv'), 0):
171 amp, std = 'Cond', 'CondStd'
172 amps, stds = model_tree['Conds'].data, model_tree['CondStds'].data
173 row['%sAmp 0' % prefix] = model_tree['Amps'].data[0]
174 row['%sStd 0' % prefix] = model_tree['Stds'].data[0]
175 else:
176 amp, std = 'Amp', 'Std'
177 amps, stds = model_tree['Amps'].data, model_tree['Stds'].data
178 for c in sorted(list(classes_present)):
179 row['%s%s %i' % (prefix, amp, c)] = amps[c]
180 row['%s%s %i' % (prefix, std, c)] = stds[c]
181
183 """Adds 'ChannelCount' from a model to a dict (representing one row in a L{TrialSet})."""
184 row['%snChannel' % prefix] = model_tree['ChannelCount'].data[0]
185