1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 """Module for model minimisation/optimisation."""
24
25
26 from numpy import float64, identity
27 import sys
28
29
30 from lib.errors import RelaxError, RelaxIntListIntError, RelaxLenError
31 from lib.float import isNaN
32 from lib.io import write_data
33 from multi import Processor_box
34 from pipe_control.mol_res_spin import return_spin, spin_loop
35 from pipe_control import pipes
36 from pipe_control.pipes import check_pipe
37 from specific_analyses.api import return_api, return_parameter_object
38 from status import Status; status = Status()
39 from user_functions.data import Uf_tables; uf_tables = Uf_tables()
40
41
43 """Create and return the per-model scaling matrices.
44
45 @keyword scaling: If True, diagonal scaling is enabled during optimisation to allow the problem to be better conditioned.
46 @type scaling: bool
47 @return: The list of diagonal and square scaling matrices.
48 @rtype: list of numpy rank-2, float64 array or list of None
49 """
50
51
52 api = return_api()
53 param_object = return_parameter_object()
54
55
56 scaling_matrix = []
57
58
59 for model_info in api.model_loop():
60
61 if not scaling:
62 scaling_matrix.append(None)
63 continue
64
65
66 names = api.get_param_names(model_info)
67
68
69 if names == None or len(names) == 0:
70 scaling_matrix.append(None)
71 continue
72
73
74 n = len(names)
75
76
77 scaling_matrix.append(identity(n, float64))
78 i = 0
79
80
81 for i in range(n):
82 scaling_matrix[-1][i, i] = param_object.scaling(names[i], model_info=model_info)
83
84
85 return scaling_matrix
86
87
88 -def calc(verbosity=1):
143
144
145 -def grid_search(lower=None, upper=None, inc=None, verbosity=1, constraints=True, skip_preset=True):
146 """The grid search function.
147
148 @keyword lower: The lower bounds of the grid search which must be equal to the number of parameters in the model.
149 @type lower: array of numbers
150 @keyword upper: The upper bounds of the grid search which must be equal to the number of parameters in the model.
151 @type upper: array of numbers
152 @keyword inc: The increments for each dimension of the space for the grid search. The number of elements in the array must equal to the number of parameters in the model.
153 @type inc: int or list of int
154 @keyword verbosity: The amount of information to print. The higher the value, the greater the verbosity.
155 @type verbosity: int
156 @keyword constraints: If True, constraints are applied during the grid search (elinating parts of the grid). If False, no constraints are used.
157 @type constraints: bool
158 @keyword skip_preset: This argument, when True, allows any parameter which already has a value set to be skipped in the grid search.
159 @type skip_preset: bool
160 """
161
162
163 check_pipe()
164
165
166 api = return_api()
167
168
169 api.overfit_deselect()
170
171
172 model_lower, model_upper, model_inc = grid_setup(lower, upper, inc, verbosity=verbosity, skip_preset=skip_preset)
173
174
175 scaling_matrix = assemble_scaling_matrix()
176
177
178 processor_box = Processor_box()
179 processor = processor_box.processor
180
181
182 if hasattr(cdp, 'sim_state') and cdp.sim_state == 1:
183
184 for i in range(cdp.sim_number):
185
186 reset_min_stats(sim_index=i, verbosity=verbosity)
187
188
189 if status.current_analysis:
190 status.auto_analysis[status.current_analysis].mc_number = i
191 else:
192 status.mc_number = i
193
194
195 api.grid_search(lower=model_lower, upper=model_upper, inc=model_inc, scaling_matrix=scaling_matrix, constraints=constraints, verbosity=verbosity-1, sim_index=i)
196
197
198 if verbosity and not processor.is_queued():
199 print("Simulation " + repr(i+1))
200
201
202 if status.current_analysis:
203 status.auto_analysis[status.current_analysis].mc_number = None
204 else:
205 status.mc_number = None
206
207
208 else:
209
210 reset_min_stats(verbosity=verbosity)
211
212
213 api.grid_search(lower=model_lower, upper=model_upper, inc=model_inc, scaling_matrix=scaling_matrix, constraints=constraints, verbosity=verbosity)
214
215
216 processor.run_queue()
217
218
219 -def grid_setup(lower=None, upper=None, inc=None, verbosity=1, skip_preset=True):
220 """Determine the per-model grid bounds, allowing for the zooming grid search.
221
222 @keyword lower: The user supplied lower bounds of the grid search which must be equal to the number of parameters in the model.
223 @type lower: list of numbers
224 @keyword upper: The user supplied upper bounds of the grid search which must be equal to the number of parameters in the model.
225 @type upper: list of numbers
226 @keyword inc: The user supplied grid search increments.
227 @type inc: int or list of int
228 @keyword verbosity: The amount of information to print. The higher the value, the greater the verbosity.
229 @type verbosity: int
230 @keyword skip_preset: This argument, when True, allows any parameter which already has a value set to be skipped in the grid search.
231 @type skip_preset: bool
232 @return: The per-model grid upper and lower bounds. The first dimension of each structure corresponds to the model, the second the model parameters.
233 @rtype: tuple of lists of lists of float, lists of lists of float, list of lists of int
234 """
235
236
237 api = return_api()
238 param_object = return_parameter_object()
239
240
241 model_lower = []
242 model_upper = []
243 model_inc = []
244
245
246 for model_info in api.model_loop():
247
248 names = api.get_param_names(model_info)
249 values = api.get_param_values(model_info)
250
251
252 if names == None or len(names) == 0:
253 model_lower.append([])
254 model_upper.append([])
255 model_inc.append([])
256 continue
257
258
259 n = len(names)
260
261
262 if n == 0:
263 raise RelaxError("Cannot run a grid search on a model with zero parameters.")
264
265
266 if lower != None and len(lower) != n:
267 raise RelaxLenError('lower bounds', n)
268 if upper != None and len(upper) != n:
269 raise RelaxLenError('upper bounds', n)
270
271
272 if isinstance(inc, list) and len(inc) != n:
273 raise RelaxLenError('increment', n)
274 if isinstance(inc, list):
275 for i in range(n):
276 if not (isinstance(inc[i], int) or inc[i] == None):
277 raise RelaxIntListIntError('increment', inc)
278 elif not isinstance(inc, int):
279 raise RelaxIntListIntError('increment', inc)
280
281
282 if isinstance(inc, int):
283 model_inc.append([inc]*n)
284 else:
285 model_inc.append(inc)
286
287
288 api.print_model_title(prefix="Grid search setup: ", model_info=model_info)
289
290
291 zoom = 0
292 if hasattr(cdp, 'grid_zoom_level'):
293 zoom = cdp.grid_zoom_level
294 zoom_factor = 1.0 / 2.0**zoom
295 if zoom > 0:
296 print("Zooming grid level of %s, scaling the grid size by a factor of %s.\n" % (zoom, zoom_factor))
297
298
299 model_lower.append([])
300 model_upper.append([])
301
302
303 data = []
304 for i in range(n):
305
306 comment = 'Default bounds'
307 if lower != None and upper != None:
308 comment = 'User supplied lower and upper bound'
309 elif lower != None:
310 comment = 'User supplied lower bound'
311 elif upper != None:
312 comment = 'User supplied upper bound'
313
314
315 incs = model_inc[-1][i]
316
317
318 if incs == None and values[i] in [None, {}, []]:
319 raise RelaxError("The parameter '%s' has no preset value, therefore a grid increment of None is not valid." % names[i])
320
321
322 if lower != None:
323 lower_i = lower[i]
324 else:
325 lower_i = param_object.grid_lower(names[i], incs=incs, model_info=model_info)
326
327
328 if upper != None:
329 upper_i = upper[i]
330 else:
331 upper_i = param_object.grid_upper(names[i], incs=incs, model_info=model_info)
332
333
334 skip = False
335 if skip_preset:
336
337 if zoom:
338 skip = False
339
340
341 elif values[i] in [None, {}, []]:
342 skip = False
343
344
345 elif isNaN(values[i]):
346 skip = False
347
348
349 else:
350 skip = True
351
352
353 if incs == None:
354 skip = True
355
356
357 if skip:
358 lower_i = values[i]
359 upper_i = values[i]
360 model_inc[-1][i] = incs = 1
361 comment = 'Preset value'
362
363
364 elif zoom:
365
366 size = upper_i - lower_i
367 zoom_size = size * zoom_factor
368 half_size = zoom_size / 2.0
369 comment = 'Zoom grid width of %s %s' % (zoom_size, param_object.units(names[i]))
370
371
372 lower_zoom = values[i] - half_size
373 upper_zoom = values[i] + half_size
374
375
376 if zoom > 0 and lower_zoom < lower_i:
377
378 shift = lower_i - lower_zoom
379
380
381 upper_i = upper_zoom + shift
382
383
384 elif zoom > 0 and upper_zoom > upper_i:
385
386 shift = upper_i - upper_zoom
387
388
389 lower_i = lower_zoom + shift
390
391
392 else:
393 lower_i = lower_zoom
394 upper_i = upper_zoom
395
396
397 data.append([names[i], "%15s" % lower_i, "%15s" % upper_i, "%15s" % incs, comment])
398
399
400 scaling = param_object.scaling(names[i], model_info=model_info)
401 lower_i /= scaling
402 upper_i /= scaling
403
404
405 model_lower[-1].append(lower_i)
406 model_upper[-1].append(upper_i)
407
408
409 if verbosity:
410 write_data(out=sys.stdout, headings=["Parameter", "Lower bound", "Upper bound", "Increments", "Comment"], data=data)
411 sys.stdout.write('\n')
412
413
414 return model_lower, model_upper, model_inc
415
416
418 """Store the grid zoom level.
419
420 @keyword level: The zoom level.
421 @type level: int
422 """
423
424
425 check_pipe()
426
427
428 cdp.grid_zoom_level = level
429
430
431 -def minimise(min_algor=None, line_search=None, hessian_mod=None, hessian_type=None, func_tol=None, grad_tol=None, max_iter=None, constraints=True, scaling=True, verbosity=1, sim_index=None):
432 """Minimisation function.
433
434 @keyword min_algor: The minimisation algorithm to use.
435 @type min_algor: str
436 @keyword line_search: 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.
437 @type line_search: str or None
438 @keyword hessian_mod: 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.
439 @type hessian_mod: str or None
440 @keyword hessian_type: The Hessian type. This will only be used in a few trust region algorithms, and defaults to BFGS.
441 @type hessian_type: str or None
442 @keyword func_tol: The function tolerance which, when reached, terminates optimisation. Setting this to None turns of the check.
443 @type func_tol: None or float
444 @keyword grad_tol: The gradient tolerance which, when reached, terminates optimisation. Setting this to None turns of the check.
445 @type grad_tol: None or float
446 @keyword max_iter: The maximum number of iterations for the algorithm.
447 @type max_iter: int
448 @keyword constraints: If True, constraints are used during optimisation.
449 @type constraints: bool
450 @keyword scaling: If True, diagonal scaling is enabled during optimisation to allow the problem to be better conditioned.
451 @type scaling: bool
452 @keyword verbosity: The amount of information to print. The higher the value, the greater the verbosity.
453 @type verbosity: int
454 @keyword sim_index: The index of the simulation to optimise. This should be None if normal optimisation is desired.
455 @type sim_index: None or int
456 """
457
458
459 check_pipe()
460
461
462 api = return_api()
463
464
465 if constraints:
466 min_options = [min_algor]
467
468
469 min_algor = api.constraint_algorithm()
470 else:
471 min_options = []
472 if line_search != None:
473 min_options.append(line_search)
474 if hessian_mod != None:
475 min_options.append(hessian_mod)
476 if hessian_type != None:
477 min_options.append(hessian_type)
478 min_options = tuple(min_options)
479
480
481 api.overfit_deselect()
482
483
484 scaling_matrix = assemble_scaling_matrix(scaling)
485
486
487 processor_box = Processor_box()
488 processor = processor_box.processor
489
490
491 if sim_index != None:
492
493 reset_min_stats(sim_index=sim_index, verbosity=verbosity)
494
495
496 api.minimise(min_algor=min_algor, min_options=min_options, func_tol=func_tol, grad_tol=grad_tol, max_iterations=max_iter, constraints=constraints, scaling_matrix=scaling_matrix, verbosity=verbosity, sim_index=sim_index)
497
498
499 elif hasattr(cdp, 'sim_state') and cdp.sim_state == 1:
500 for i in range(cdp.sim_number):
501
502 reset_min_stats(sim_index=i, verbosity=verbosity)
503
504
505 if status.current_analysis:
506 status.auto_analysis[status.current_analysis].mc_number = i
507 else:
508 status.mc_number = i
509
510
511 api.minimise(min_algor=min_algor, min_options=min_options, func_tol=func_tol, grad_tol=grad_tol, max_iterations=max_iter, constraints=constraints, scaling_matrix=scaling_matrix, verbosity=verbosity-1, sim_index=i)
512
513
514 if verbosity and not processor.is_queued():
515 print("Simulation " + repr(i+1))
516
517
518 if status.current_analysis:
519 status.auto_analysis[status.current_analysis].mc_number = None
520 else:
521 status.mc_number = None
522
523
524 else:
525
526 reset_min_stats(verbosity=verbosity)
527
528
529 api.minimise(min_algor=min_algor, min_options=min_options, func_tol=func_tol, grad_tol=grad_tol, max_iterations=max_iter, constraints=constraints, scaling_matrix=scaling_matrix, verbosity=verbosity)
530
531
532 processor.run_queue()
533
534
536 """Function for resetting all minimisation statistics.
537
538 @keyword data_pipe: The name of the data pipe to reset the minimisation statistics of. This defaults to the current data pipe.
539 @type data_pipe: str
540 @keyword sim_index: The optional Monte Carlo simulation index.
541 @type sim_index: int
542 @keyword verbosity: The amount of information to print. The higher the value, the greater the verbosity.
543 @type verbosity: int
544 """
545
546
547 if data_pipe == None:
548 data_pipe = pipes.cdp_name()
549
550
551 dp = pipes.get_pipe(data_pipe)
552
553
554 names = [
555 'chi2',
556 'iter',
557 'f_count',
558 'g_count',
559 'h_count',
560 'warning'
561 ]
562
563
564 flag = False
565 for name in names:
566
567 sim_name = name + '_sim'
568
569
570 if sim_index == None:
571
572 if hasattr(dp, name):
573 setattr(dp, name, None)
574 flag = False
575
576
577 else:
578 if hasattr(dp, sim_name):
579
580 sim_obj = getattr(dp, sim_name)
581
582
583 if sim_index < len(sim_obj):
584 sim_obj[sim_index] = None
585
586
587 for spin in spin_loop(skip_desel=False):
588
589 if sim_index == None:
590
591 if hasattr(spin, name):
592 setattr(spin, name, None)
593 flag = True
594
595
596 else:
597 if hasattr(spin, sim_name):
598
599 sim_obj = getattr(spin, sim_name)
600
601
602 if sim_index < len(sim_obj):
603 sim_obj[sim_index] = None
604
605
606 if verbosity and flag and sim_index == None:
607 print("Resetting the minimisation statistics.")
608
609
610 -def set(val=None, error=None, param=None, scaling=None, spin_id=None):
611 """Set global or spin specific minimisation parameters.
612
613 @keyword val: The parameter values.
614 @type val: number
615 @keyword param: The parameter names.
616 @type param: str
617 @keyword scaling: Unused.
618 @type scaling: float
619 @keyword spin_id: The spin identification string.
620 @type spin_id: str
621 """
622
623
624 if spin_id == None:
625
626 if param == 'chi2':
627 cdp.chi2 = val
628
629
630 elif param == 'iter':
631 cdp.iter = val
632
633
634 elif param == 'f_count':
635 cdp.f_count = val
636
637
638 elif param == 'g_count':
639 cdp.g_count = val
640
641
642 elif param == 'h_count':
643 cdp.h_count = val
644
645
646 else:
647
648 spin = return_spin(spin_id)
649
650
651 if param == 'chi2':
652 spin.chi2 = val
653
654
655 elif param == 'iter':
656 spin.iter = val
657
658
659 elif param == 'f_count':
660 spin.f_count = val
661
662
663 elif param == 'g_count':
664 spin.g_count = val
665
666
667 elif param == 'h_count':
668 spin.h_count = val
669