1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 """The minimisation user function definitions."""
24
25
26 from graphics import WIZARD_IMAGE_PATH
27 from pipe_control import minimise
28 from user_functions.data import Uf_info, Uf_tables; uf_info = Uf_info(); uf_tables = Uf_tables()
29 from user_functions.objects import Desc_container
30
31
32
33 uf_class = uf_info.add_class('minimise')
34 uf_class.title = "Class for setting parameter values."
35 uf_class.menu_text = "&minimise"
36 uf_class.gui_icon = "relax.rosenbrock"
37
38
39
40 uf = uf_info.add_uf('minimise.calculate')
41 uf.title = "Calculate the model parameters or the current target function value."
42 uf.title_short = "Model parameter or target function value calculation."
43 uf.display = True
44 uf.add_keyarg(
45 name = "verbosity",
46 default = 1,
47 basic_types = ["int"],
48 desc_short = "verbosity level",
49 desc = "The amount of information to print to screen. Zero corresponds to minimal output while higher values increase the amount of output. The default value is 1."
50 )
51
52 uf.desc.append(Desc_container())
53 uf.desc[-1].add_paragraph("The operation of this user function is two-fold and depends on whether the solution for the models of the current analysis are found by direct calculation or by optimisation. The dual operations are:")
54 uf.desc[-1].add_item_list_element("Direct calculation models", "For these models, the parameters will be directly calculated from the base data. This will be the exact solution and the user function will store the parameter values. The grid search and optimisation user functions are not implemented for this analysis type.")
55 uf.desc[-1].add_item_list_element("Optimised models", "This will call the target function normally used for optimisation for each model using the current parameter values. This can be used to manually find the chi-squared value for different parameter values. The parameter values will not be affected.")
56 uf.backend = minimise.calc
57 uf.menu_text = "&calculate"
58 uf.gui_icon = "oxygen.categories.applications-education"
59 uf.wizard_size = (900, 500)
60 uf.wizard_image = WIZARD_IMAGE_PATH + 'minimise.png'
61
62
63
64 uf = uf_info.add_uf('minimise.execute')
65 uf.title = "Perform an optimisation."
66 uf.title_short = "Minimisation."
67 uf.display = True
68 uf.add_keyarg(
69 name = "min_algor",
70 default = "newton",
71 basic_types = ["str"],
72 desc_short = "minimisation algorithm",
73 desc = "The optimisation algorithm to use.",
74 wiz_element_type = 'combo',
75 wiz_combo_choices = [
76 "Back-and-forth coordinate descent",
77 "Steepest descent",
78 "Quasi-Newton BFGS",
79 "Newton",
80 "Newton-CG",
81 "Cauchy point",
82 "Dogleg",
83 "CG-Steihaug",
84 "Exact trust region",
85 "Fletcher-Reeves",
86 "Polak-Ribiere",
87 "Polak-Ribiere +",
88 "Hestenes-Stiefel",
89 "Simplex",
90 "Levenberg-Marquardt",
91 "Simulated Annealing"
92 ],
93 wiz_combo_data = [
94 "cd",
95 "sd",
96 "bfgs",
97 "newton",
98 "ncg",
99 "cauchy",
100 "dogleg",
101 "steihaug",
102 "exact",
103 "fr",
104 "pr",
105 "pr+",
106 "hs",
107 "simplex",
108 "lm",
109 "sa"
110 ],
111 wiz_read_only = True
112 )
113 uf.add_keyarg(
114 name = "line_search",
115 basic_types = ["str"],
116 desc_short = "line search algorithm",
117 desc = "The line search algorithm which will only be used in combination with the line search and conjugate gradient methods. This will default to the More and Thuente line search.",
118 wiz_element_type = 'combo',
119 wiz_combo_choices = [
120 "Backtracking",
121 "Nocedal and Wright interpolation",
122 "Nocedal and Wright for the Wolfe conditions",
123 "More and Thuente",
124 "No line search"
125 ],
126 wiz_combo_data = [
127 "back",
128 "nwi",
129 "nww",
130 "mt",
131 "no line"
132 ],
133 wiz_read_only = True,
134 can_be_none = True
135 )
136 uf.add_keyarg(
137 name = "hessian_mod",
138 basic_types = ["str"],
139 desc_short = "hessian modification",
140 desc = "The Hessian modification. This will only be used in the algorithms which use the Hessian, and defaults to Gill, Murray, and Wright modified Cholesky algorithm.",
141 wiz_element_type = 'combo',
142 wiz_combo_choices = [
143 "Unmodified Hessian",
144 "Eigenvalue modification",
145 "Cholesky with added multiple of the identity",
146 "The Gill, Murray, and Wright modified Cholesky algorithm",
147 "The Schnabel and Eskow 1999 algorithm"
148 ],
149 wiz_combo_data = [
150 "no hessian mod",
151 "eigen",
152 "chol",
153 "gmw",
154 "se99"
155 ],
156 wiz_read_only = True,
157 can_be_none = True
158 )
159 uf.add_keyarg(
160 name = "hessian_type",
161 basic_types = ["str"],
162 desc_short = "hessian type",
163 desc = "The Hessian type. This will only be used in a few trust region algorithms, and defaults to BFGS.",
164 wiz_element_type = 'combo',
165 wiz_combo_choices = [
166 "Quasi-Newton BFGS",
167 "Newton"
168 ],
169 wiz_combo_data = [
170 "bfgs",
171 "newton"
172 ],
173 wiz_read_only = True,
174 can_be_none = True
175 )
176 uf.add_keyarg(
177 name = "func_tol",
178 default = 1e-25,
179 basic_types = ["float"],
180 desc_short = "function tolerance",
181 desc = "The function tolerance. This is used to terminate minimisation once the function value between iterations is less than the tolerance. The default value is 1e-25."
182 )
183 uf.add_keyarg(
184 name = "grad_tol",
185 basic_types = ["float"],
186 desc_short = "gradient tolerance",
187 desc = "The gradient tolerance. Minimisation is terminated if the current gradient value is less than the tolerance. The default value is None.",
188 can_be_none = True
189 )
190 uf.add_keyarg(
191 name = "max_iter",
192 default = 10000000,
193 basic_types = ["int"],
194 min = 1,
195 max = 1000000000,
196 desc_short = "maximum number of iterations",
197 desc = "The maximum number of iterations. The default value is 1e7."
198 )
199 uf.add_keyarg(
200 name = "constraints",
201 default = True,
202 basic_types = ["bool"],
203 desc_short = "constraints flag",
204 desc = "A boolean flag specifying whether the parameters should be constrained. The default is to turn constraints on (constraints=True)."
205 )
206 uf.add_keyarg(
207 name = "scaling",
208 default = True,
209 basic_types = ["bool"],
210 desc_short = "diagonal scaling flag",
211 desc = "The diagonal scaling boolean flag. The default that scaling is on (scaling=True)."
212 )
213 uf.add_keyarg(
214 name = "verbosity",
215 default = 1,
216 basic_types = ["int"],
217 desc_short = "verbosity level",
218 desc = "The amount of information to print to screen. Zero corresponds to minimal output while higher values increase the amount of output. The default value is 1."
219 )
220
221 uf.desc.append(Desc_container())
222 uf.desc[-1].add_paragraph("This will perform an optimisation starting from the current parameter values. This is only suitable for data pipe types which have target functions and hence support optimisation.")
223
224 uf.desc.append(Desc_container("Diagonal scaling"))
225 uf.desc[-1].add_paragraph("Diagonal scaling is the transformation of parameter values such that each value has a similar order of magnitude. Certain minimisation techniques, for example the trust region methods, perform extremely poorly with badly scaled problems. In addition, methods which are insensitive to scaling such as Newton minimisation may still benefit due to the minimisation of round off errors.")
226 uf.desc[-1].add_paragraph("In Model-free analysis for example, if S2 = 0.5, te = 200 ps, and Rex = 15 1/s at 600 MHz, the unscaled parameter vector would be [0.5, 2.0e-10, 1.055e-18]. Rex is divided by (2 * pi * 600,000,000)**2 to make it field strength independent. The scaling vector for this model may be something like [1.0, 1e-9, 1/(2 * pi * 6e8)**2]. By dividing the unscaled parameter vector by the scaling vector the scaled parameter vector is [0.5, 0.2, 15.0]. To revert to the original unscaled parameter vector, the scaled parameter vector and scaling vector are multiplied.")
227
228 uf.desc.append(Desc_container("Minimisation algorithms"))
229 uf.desc[-1].add_paragraph("A minimisation function is selected if the minimisation algorithm matches a certain pattern. Because the python regular expression 'match' statement is used, various strings can be supplied to select the same minimisation algorithm. Below is a list of the minimisation algorithms available together with the corresponding patterns.")
230 uf.desc[-1].add_paragraph("This is a short description of python regular expression, for more information, see the regular expression syntax section of the Python Library Reference. Some of the regular expression syntax used in this function is:")
231 uf.desc[-1].add_item_list_element("'[]'", "A sequence or set of characters to match to a single character. For example, '[Nn]ewton' will match both 'Newton' and 'newton'.")
232 uf.desc[-1].add_item_list_element("'^'", "Match the start of the string.")
233 uf.desc[-1].add_item_list_element("'$'", "Match the end of the string. For example, '^[Ll][Mm]$' will match 'lm' and 'LM' but will not match if characters are placed either before or after these strings.")
234 uf.desc[-1].add_paragraph("To select a minimisation algorithm, use a string which matches one of the following patterns given in the tables.")
235 uf.desc[-1].add_paragraph("Unconstrained line search methods:")
236 table = uf_tables.add_table(label="table: min - line search", caption="Minimisation algorithms -- unconstrained line search methods.")
237 table.add_headings(["Minimisation algorithm", "Patterns"])
238 table.add_row(["Back-and-forth coordinate descent", "'^[Cc][Dd]$' or '^[Cc]oordinate[ _-][Dd]escent$'"])
239 table.add_row(["Steepest descent", "'^[Ss][Dd]$' or '^[Ss]teepest[ _-][Dd]escent$'"])
240 table.add_row(["Quasi-Newton BFGS", "'^[Bb][Ff][Gg][Ss]$'"])
241 table.add_row(["Newton", "'^[Nn]ewton$'"])
242 table.add_row(["Newton-CG", "'^[Nn]ewton[ _-][Cc][Gg]$' or '^[Nn][Cc][Gg]$'"])
243 uf.desc[-1].add_table(table.label)
244 uf.desc[-1].add_paragraph("Unconstrained trust-region methods:")
245 table = uf_tables.add_table(label="table: min - trust-region", caption="Minimisation algorithms -- unconstrained trust-region methods.")
246 table.add_headings(["Minimisation algorithm", "Patterns"])
247 table.add_row(["Cauchy point", "'^[Cc]auchy'"])
248 table.add_row(["Dogleg", "'^[Dd]ogleg'"])
249 table.add_row(["CG-Steihaug", "'^[Cc][Gg][-_ ][Ss]teihaug' or '^[Ss]teihaug'"])
250 table.add_row(["Exact trust region", "'^[Ee]xact'"])
251 uf.desc[-1].add_table(table.label)
252 uf.desc[-1].add_paragraph("Unconstrained conjugate gradient methods:")
253 table = uf_tables.add_table(label="table: min - conjugate gradient", caption="Minimisation algorithms -- unconstrained conjugate gradient methods.")
254 table.add_headings(["Minimisation algorithm", "Patterns"])
255 table.add_row(["Fletcher-Reeves", "'^[Ff][Rr]$' or '^[Ff]letcher[-_ ][Rr]eeves$'"])
256 table.add_row(["Polak-Ribiere", "'^[Pp][Rr]$' or '^[Pp]olak[-_ ][Rr]ibiere$'"])
257 table.add_row(["Polak-Ribiere +", r"'^[Pp][Rr]\+$' or '^[Pp]olak[-_ ][Rr]ibiere\+$'"])
258 table.add_row(["Hestenes-Stiefel", "'^[Hh][Ss]$' or '^[Hh]estenes[-_ ][Ss]tiefel$'"])
259 uf.desc[-1].add_table(table.label)
260 uf.desc[-1].add_paragraph("Miscellaneous unconstrained methods:")
261 table = uf_tables.add_table(label="table: min - misc", caption="Minimisation algorithms -- miscellaneous unconstrained methods.")
262 table.add_headings(["Minimisation algorithm", "Patterns"])
263 table.add_row(["Simplex", "'^[Ss]implex$'"])
264 table.add_row(["Levenberg-Marquardt", "'^[Ll][Mm]$' or '^[Ll]evenburg-[Mm]arquardt$'"])
265 uf.desc[-1].add_table(table.label)
266 uf.desc[-1].add_paragraph("Global minimisation methods:")
267 table = uf_tables.add_table(label="table: min - global", caption="Minimisation algorithms -- global minimisation methods.")
268 table.add_headings(["Minimisation algorithm", "Patterns"])
269 table.add_row(["Simulated Annealing", "'^[Ss][Aa]$' or '^[Ss]imulated [Aa]nnealing$'"])
270 uf.desc[-1].add_table(table.label)
271
272 uf.desc.append(Desc_container("Minimisation options"))
273 uf.desc[-1].add_paragraph("The minimisation options can be given in any order.")
274 uf.desc[-1].add_paragraph("Line search algorithms. These are used in the line search methods and the conjugate gradient methods. The default is the Backtracking line search. The algorithms are:")
275 table = uf_tables.add_table(label="table: min sub-algor - line search", caption="Minimisation sub-algorithms -- line search algorithms.")
276 table.add_headings(["Line search algorithm", "Patterns"])
277 table.add_row(["Backtracking line search", "'^[Bb]ack'"])
278 table.add_row(["Nocedal and Wright interpolation based line search", "'^[Nn][Ww][Ii]' or '^[Nn]ocedal[ _][Ww]right[ _][Ii]nt'"])
279 table.add_row(["Nocedal and Wright line search for the Wolfe conditions", "'^[Nn][Ww][Ww]' or '^[Nn]ocedal[ _][Ww]right[ _][Ww]olfe'"])
280 table.add_row(["More and Thuente line search", "'^[Mm][Tt]' or '^[Mm]ore[ _][Tt]huente$'"])
281 table.add_row(["No line search", "'^[Nn]o [Ll]ine [Ss]earch$'"])
282 uf.desc[-1].add_table(table.label)
283 uf.desc[-1].add_paragraph("Hessian modifications. These are used in the Newton, Dogleg, and Exact trust region algorithms:")
284 table = uf_tables.add_table(label="table: min sub-algor - Hessian mod", caption="Minimisation sub-algorithms -- Hessian modifications.")
285 table.add_headings(["Hessian modification", "Patterns"])
286 table.add_row(["Unmodified Hessian", "'^[Nn]o [Hh]essian [Mm]od'"])
287 table.add_row(["Eigenvalue modification", "'^[Ee]igen'"])
288 table.add_row(["Cholesky with added multiple of the identity", "'^[Cc]hol'"])
289 table.add_row(["The Gill, Murray, and Wright modified Cholesky algorithm", "'^[Gg][Mm][Ww]$'"])
290 table.add_row(["The Schnabel and Eskow 1999 algorithm", "'^[Ss][Ee]99'"])
291 uf.desc[-1].add_table(table.label)
292 uf.desc[-1].add_paragraph("Hessian type, these are used in a few of the trust region methods including the Dogleg and Exact trust region algorithms. In these cases, when the Hessian type is set to Newton, a Hessian modification can also be supplied as above. The default Hessian type is Newton, and the default Hessian modification when Newton is selected is the GMW algorithm:")
293 table = uf_tables.add_table(label="table: min sub-algor - Hessian type", caption="Minimisation sub-algorithms -- Hessian type.")
294 table.add_headings(["Hessian type", "Patterns"])
295 table.add_row(["Quasi-Newton BFGS", "'^[Bb][Ff][Gg][Ss]$'"])
296 table.add_row(["Newton", "'^[Nn]ewton$'"])
297 uf.desc[-1].add_table(table.label)
298 uf.desc[-1].add_paragraph("For Newton minimisation, the default line search algorithm is the More and Thuente line search, while the default Hessian modification is the GMW algorithm.")
299
300 uf.desc.append(Desc_container("Prompt examples"))
301 uf.desc[-1].add_paragraph("To apply Newton minimisation together with the GMW81 Hessian modification algorithm, the More and Thuente line search algorithm, a function tolerance of 1e-25, no gradient tolerance, a maximum of 10,000,000 iterations, constraints turned on to limit parameter values, and have normal printout, type any combination of:")
302 uf.desc[-1].add_prompt("relax> minimise.execute('newton')")
303 uf.desc[-1].add_prompt("relax> minimise.execute('Newton')")
304 uf.desc[-1].add_prompt("relax> minimise.execute('newton', 'gmw')")
305 uf.desc[-1].add_prompt("relax> minimise.execute('newton', 'mt')")
306 uf.desc[-1].add_prompt("relax> minimise.execute('newton', 'gmw', 'mt')")
307 uf.desc[-1].add_prompt("relax> minimise.execute('newton', 'mt', 'gmw')")
308 uf.desc[-1].add_prompt("relax> minimise.execute('newton', func_tol=1e-25)")
309 uf.desc[-1].add_prompt("relax> minimise.execute('newton', func_tol=1e-25, grad_tol=None)")
310 uf.desc[-1].add_prompt("relax> minimise.execute('newton', max_iter=1e7)")
311 uf.desc[-1].add_prompt("relax> minimise.execute('newton', constraints=True, max_iter=1e7)")
312 uf.desc[-1].add_prompt("relax> minimise.execute('newton', verbosity=1)")
313 uf.desc[-1].add_paragraph("To use constrained Simplex minimisation with a maximum of 5000 iterations, type:")
314 uf.desc[-1].add_prompt("relax> minimise.execute('simplex', constraints=True, max_iter=5000)")
315 uf.backend = minimise.minimise
316 uf.menu_text = "&execute"
317 uf.gui_icon = "relax.rosenbrock"
318 uf.wizard_height_desc = 300
319 uf.wizard_size = (1000, 750)
320 uf.wizard_image = WIZARD_IMAGE_PATH + 'minimise.png'
321
322
323
324 uf = uf_info.add_uf('minimise.grid_search')
325 uf.title = "Perform a grid search to find an initial non-biased parameter set for optimisation."
326 uf.title_short = "Grid search optimisation."
327 uf.display = True
328 uf.add_keyarg(
329 name = "lower",
330 basic_types = ["number"],
331 container_types = ["list"],
332 dim = (None,),
333 desc_short = "lower bounds",
334 desc = "An array of the lower bound parameter values for the grid search. The length of the array should be equal to the number of parameters in the model.",
335 can_be_none = True
336 )
337 uf.add_keyarg(
338 name = "upper",
339 basic_types = ["number"],
340 container_types = ["list"],
341 dim = (None,),
342 desc_short = "upper bounds",
343 desc = "An array of the upper bound parameter values for the grid search. The length of the array should be equal to the number of parameters in the model.",
344 can_be_none = True
345 )
346 uf.add_keyarg(
347 name = "inc",
348 default = 21,
349 basic_types = ["int"],
350 container_types = ["list"],
351 dim = [(), (None,)],
352 desc_short = "incrementation value",
353 desc = "The number of increments to search over. If a single integer is given then the number of increments will be equal in all dimensions. Different numbers of increments in each direction can be set if 'inc' is set to an array of integers of length equal to the number of parameters.",
354 none_elements = True
355 )
356 uf.add_keyarg(
357 name = "verbosity",
358 default = 1,
359 basic_types = ["int"],
360 desc_short = "verbosity level",
361 desc = "The amount of information to print to screen. Zero corresponds to minimal output while higher values increase the amount of output. The default value is 1."
362 )
363 uf.add_keyarg(
364 name = "constraints",
365 default = True,
366 basic_types = ["bool"],
367 desc_short = "constraints flag",
368 desc = "A boolean flag specifying whether the parameters should be constrained. The default is to turn constraints on (constraints=True)."
369 )
370 uf.add_keyarg(
371 name = "skip_preset",
372 default = True,
373 basic_types = ["bool"],
374 desc_short = "skip preset parameter flag",
375 desc = "This argument, when True, allows any parameter which already has a value set to be skipped in the grid search. This value will be overridden and turned off when a zooming grid search is active."
376 )
377
378 uf.desc.append(Desc_container())
379 uf.desc[-1].add_paragraph("The optimisation of a mathematical model normally consists of two parts - a coarse grid search across the parameter space to find an initial set of parameter values followed by the use of a high precision optimisation algorithm to exactly find the local or global solution. The grid search is an essential tool as it allows a non-biased initial optimisation position to be found. It avoids the statistical bias and preconditioning introduced by using a self chosen initial parameter set. The high computational cost of the grid search is almost always favourable to the statistical bias of a user defined starting position.")
380 uf.desc[-1].add_paragraph("The region of the parameter space that the grid search covers is defined by the lower and upper grid bounds. These will generally default to the entire parameter space except for when the parameter is non-bounded, for example a 3D position in the PDB space. This user function will print out the grid bounds used and, if the default bounds are deemed to be insufficient, then the lower, upper or both bounds can supplied. This only works if all active models have the same parameters. The coarseness or fineness of the grid is defined by the number of increments to search across between the bounds. For an alternative to using large numbers of increments, see the zooming grid search.")
381 uf.desc[-1].add_paragraph("It is possible to decrease the dimensionality of the grid search, and hence drop the computational cost by orders of magnitude, if certain parameter values are know a priori. For example if the values are determined via a different experiment. Such parameters can be set with the value setting user function. Then, when the skip preset flag is set, these parameters will be skipped in the grid search. This feature should not be abused and statistical bias should be avoided at all cost.")
382 uf.desc[-1].add_paragraph("The parameter skipping logic is as follows. Firstly setting the increments argument to a list with None elements causes the corresponding parameters to be skipped in the grid search, or an error to be raised if no preset parameter is present. This overrides all other settings. Secondly the preset skipping flag only allows parameters to be skipped if the zooming grid search is non-active and a value is preset.")
383 uf.backend = minimise.grid_search
384 uf.menu_text = "&grid_search"
385 uf.gui_icon = "relax.grid_search"
386 uf.wizard_height_desc = 370
387 uf.wizard_size = (1000, 700)
388 uf.wizard_image = WIZARD_IMAGE_PATH + 'minimise.png'
389
390
391
392 uf = uf_info.add_uf('minimise.grid_zoom')
393 uf.title = "Activate the zooming grid search by setting the zoom level."
394 uf.title_short = "Zooming grid search activation."
395 uf.add_keyarg(
396 name = "level",
397 default = 0,
398 basic_types = ["number"],
399 desc_short = "zoom level",
400 desc = "The zooming grid search level. This can be any number, positive or negative."
401 )
402
403 uf.desc.append(Desc_container())
404 uf.desc[-1].add_paragraph("The optimisation of a mathematical model normally consists of two parts - a coarse grid search to find an initial set of parameter values followed by the use of a high precision optimisation algorithm to exactly find the local or global solution. But in certain situations, for example where a parallelised grid search is advantageous, a finer grid may be desired. The zooming grid search provides a more efficient alternative to simply increasing the number of increments used in the initial grid search. Note that in most situations, standard optimisation algorithms will be by far computationally less expensive.")
405 uf.desc[-1].add_paragraph("The zooming grid search can be activated via this user function. After setting the desired zoom level, the original grid search user function should be called again. The zoom level is used to decrease the total area of the grid search. The grid width for each dimension of the parameter space will be divided by 2**zoom_level. So a level of 1 will halve all dimensions, a level of 2 will quarter the widths, a level of 3 will be an eighth of the widths, etc.")
406 uf.desc[-1].add_paragraph("The zooming algorithm proceeds as follows. The new zoomed grid will be centred at the current parameter values. However if the new grid is outside of the bounds of the original grid, the entire grid will be translated so that it lies entirely within the original bounds. This is to avoid grid points lying within undefined regions of the space. An exception is when the zoom factor is negative, hence the new grid will be larger than the original.")
407 uf.desc[-1].add_paragraph("An example of using the zooming grid search is to first perform a standard initial grid search, then set the zoom level to 1 and perform a second grid search. Continue for zoom levels 2, 3, etc. until the desired fineness is obtained. Note that convergence is not guaranteed - as the zoom level is increased to infinity, the parameter values do not necessarily converge to the local minimum. Therefore performing standard optimisation is recommended after completing a zooming grid search. ")
408 uf.backend = minimise.grid_zoom
409 uf.menu_text = "&grid_zoom"
410 uf.gui_icon = "oxygen.actions.zoom-in"
411 uf.wizard_height_desc = 500
412 uf.wizard_size = (1000, 700)
413 uf.wizard_image = WIZARD_IMAGE_PATH + 'minimise.png'
414