1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26 """The model-free analysis optimisation functions."""
27
28
29 from minfx.generic import generic_minimise
30 from minfx.grid import grid, grid_point_array
31 from numpy import array, dot, float64
32 import sys
33
34
35 import lib.arg_check
36 from lib.errors import RelaxError, RelaxInfError, RelaxMultiVectorError, RelaxNaNError
37 from lib.float import isNaN, isInf
38 from lib.periodic_table import periodic_table
39 from lib.text.sectioning import subsection
40 from multi import Memo, Result_command, Slave_command
41 from pipe_control import pipes
42 from pipe_control.interatomic import return_interatom_list
43 from pipe_control.mol_res_spin import return_spin, return_spin_from_index
44 from specific_analyses.model_free.parameters import assemble_param_vector, disassemble_param_vector
45 from target_functions.mf import Mf
46
47
48 -def disassemble_result(param_vector=None, func=None, iter=None, fc=None, gc=None, hc=None, warning=None, spin=None, sim_index=None, model_type=None, scaling_matrix=None):
49 """Disassemble the optimisation results.
50
51 @keyword param_vector: The model-free parameter vector.
52 @type param_vector: numpy array
53 @keyword func: The optimised chi-squared value.
54 @type func: float
55 @keyword iter: The number of optimisation steps required to find the minimum.
56 @type iter: int
57 @keyword fc: The function count.
58 @type fc: int
59 @keyword gc: The gradient count.
60 @type gc: int
61 @keyword hc: The Hessian count.
62 @type hc: int
63 @keyword warning: Any optimisation warnings.
64 @type warning: str or None
65 @keyword spin: The spin container.
66 @type spin: SpinContainer instance or None
67 @keyword sim_index: The Monte Carlo simulation index.
68 @type sim_index: int or None
69 @keyword model_type: The model-free model type, one of 'mf', 'local_tm', 'diff', or
70 'all'.
71 @type model_type: str
72 @keyword scaling_matrix: The diagonal, square scaling matrix.
73 @type scaling_matrix: numpy diagonal matrix
74 """
75
76
77 if param_vector is None:
78 return
79
80
81 cdp = pipes.get_pipe()
82
83
84 if isInf(func):
85 raise RelaxInfError('chi-squared')
86
87
88 if isNaN(func):
89 raise RelaxNaNError('chi-squared')
90
91
92 if scaling_matrix is not None:
93 param_vector = dot(scaling_matrix, param_vector)
94
95
96 if sim_index == None:
97
98 chi2 = None
99 if (model_type == 'mf' or model_type == 'local_tm') and hasattr(cdp, 'chi2'):
100 chi2 = spin.chi2
101 if (model_type == 'diff' or model_type == 'all') and hasattr(cdp, 'chi2'):
102 chi2 = cdp.chi2
103
104
105 spin_text = ''
106 if spin != None and hasattr(spin, '_spin_ids') and len(spin._spin_ids):
107 spin_text = " for the spin '%s'" % spin._spin_ids[0]
108
109
110 if chi2 != None and func >= chi2:
111 print("Discarding the optimisation results%s, the optimised chi-squared value is higher than the current value (%s >= %s)." % (spin_text, func, chi2))
112
113
114 return
115
116
117 else:
118 print("Storing the optimisation results%s, the optimised chi-squared value is lower than the current value (%s < %s)." % (spin_text, func, chi2))
119
120
121 disassemble_param_vector(model_type, param_vector=param_vector, spin=spin, sim_index=sim_index)
122
123
124 if sim_index != None:
125
126 if model_type == 'mf' or model_type == 'local_tm':
127
128
129 spin.chi2_sim[sim_index] = func
130
131
132 spin.iter_sim[sim_index] = iter
133
134
135 spin.f_count_sim[sim_index] = fc
136
137
138 spin.g_count_sim[sim_index] = gc
139
140
141 spin.h_count_sim[sim_index] = hc
142
143
144 spin.warning_sim[sim_index] = warning
145
146
147 elif model_type == 'diff' or model_type == 'all':
148
149 cdp.chi2_sim[sim_index] = func
150
151
152 cdp.iter_sim[sim_index] = iter
153
154
155 cdp.f_count_sim[sim_index] = fc
156
157
158 cdp.g_count_sim[sim_index] = gc
159
160
161 cdp.h_count_sim[sim_index] = hc
162
163
164 cdp.warning_sim[sim_index] = warning
165
166
167 else:
168
169 if model_type == 'mf' or model_type == 'local_tm':
170
171 spin.chi2 = func
172
173
174 spin.iter = iter
175
176
177 spin.f_count = fc
178
179
180 spin.g_count = gc
181
182
183 spin.h_count = hc
184
185
186 spin.warning = warning
187
188
189 elif model_type == 'diff' or model_type == 'all':
190
191 cdp.chi2 = func
192
193
194 cdp.iter = iter
195
196
197 cdp.f_count = fc
198
199
200 cdp.g_count = gc
201
202
203 cdp.h_count = hc
204
205
206 cdp.warning = warning
207
208
209 -def minimise_data_setup(data_store, min_algor, num_data_sets, min_options, spin=None, sim_index=None):
210 """Set up all the data required for minimisation.
211
212 @param data_store: A data storage container.
213 @type data_store: class instance
214 @param min_algor: The minimisation algorithm to use.
215 @type min_algor: str
216 @param num_data_sets: The number of data sets.
217 @type num_data_sets: int
218 @param min_options: The minimisation options array.
219 @type min_options: list
220 @keyword spin: The spin data container.
221 @type spin: SpinContainer instance
222 @keyword sim_index: The optional MC simulation index.
223 @type sim_index: int
224 @return: An insane tuple. The full tuple is (ri_data, ri_data_err, equations, param_types, param_values, r, csa, num_frq, frq, num_ri, remap_table, noe_r1_table, ri_types, num_params, xh_unit_vectors, diff_type, diff_params)
225 @rtype: tuple
226 """
227
228
229 data_store.ri_data = []
230 data_store.ri_data_err = []
231 data_store.equations = []
232 data_store.param_types = []
233 data_store.param_values = None
234 data_store.r = []
235 data_store.csa = []
236 data_store.num_frq = []
237 data_store.frq = []
238 data_store.num_ri = []
239 data_store.remap_table = []
240 data_store.noe_r1_table = []
241 data_store.ri_types = []
242 data_store.gx = []
243 data_store.gh = []
244 data_store.num_params = []
245 data_store.xh_unit_vectors = []
246 if data_store.model_type == 'local_tm':
247 data_store.mf_params = []
248 elif data_store.model_type == 'diff':
249 data_store.param_values = []
250
251
252 if min_algor == 'back_calc':
253
254 data_store.ri_data = [0.0]
255 data_store.ri_data_err = [0.000001]
256 data_store.equations = [spin.equation]
257 data_store.param_types = [spin.params]
258 data_store.csa = [spin.csa]
259 data_store.num_frq = [1]
260 data_store.frq = [[min_options[3]]]
261 data_store.num_ri = [1]
262 data_store.remap_table = [[0]]
263 data_store.noe_r1_table = [[None]]
264 data_store.ri_types = [[min_options[2]]]
265 data_store.gx = [periodic_table.gyromagnetic_ratio(spin.isotope)]
266
267
268 interatoms = return_interatom_list(spin_hash=spin._hash)
269 for i in range(len(interatoms)):
270
271 if not interatoms[i].dipole_pair:
272 continue
273
274
275 if data_store.spin_id != interatoms[i].spin_id1:
276 spin_id2 = interatoms[i].spin_id1
277 else:
278 spin_id2 = interatoms[i].spin_id2
279 spin2 = return_spin(spin_id=spin_id2)
280
281
282 data_store.r = [interatoms[i].r]
283 data_store.gh = [periodic_table.gyromagnetic_ratio(spin2.isotope)]
284 if data_store.model_type != 'local_tm' and cdp.diff_tensor.type != 'sphere':
285 data_store.xh_unit_vectors = [interatoms[i].vector]
286 else:
287 data_store.xh_unit_vectors = [None]
288
289
290 data_store.num_params = [len(spin.params)]
291
292
293 for j in range(num_data_sets):
294
295 if data_store.model_type == 'diff' or data_store.model_type == 'all':
296 spin_index = j
297 spin, data_store.spin_id = return_spin_from_index(global_index=spin_index, return_spin_id=True)
298
299
300 if not spin.select:
301 continue
302
303
304 if not hasattr(spin, 'ri_data') or not hasattr(spin, 'ri_data_err'):
305 continue
306
307
308 for ri_id in cdp.ri_ids:
309
310 if not ri_id in spin.ri_data_err:
311 continue
312
313
314 err = spin.ri_data_err[ri_id]
315
316
317 if err != None and err == 0.0:
318 raise RelaxError("Zero error for spin '%s' for the relaxation data ID '%s', minimisation not possible." % (data_store.spin_id, ri_id))
319 elif err != None and err < 0.0:
320 raise RelaxError("Negative error of %s for spin '%s' for the relaxation data ID '%s', minimisation not possible." % (err, data_store.spin_id, ri_id))
321
322
323 data = relax_data_opt_structs(spin, sim_index=sim_index)
324
325
326 data_store.ri_data.append(data[0])
327 data_store.ri_data_err.append(data[1])
328 data_store.num_frq.append(data[2])
329 data_store.num_ri.append(data[3])
330 data_store.ri_types.append(data[4])
331 data_store.frq.append(data[5])
332 data_store.remap_table.append(data[6])
333 data_store.noe_r1_table.append(data[7])
334 if sim_index == None or data_store.model_type == 'diff':
335 data_store.csa.append(spin.csa)
336 else:
337 data_store.csa.append(spin.csa_sim[sim_index])
338
339
340 data_store.equations.append(spin.equation)
341 data_store.param_types.append(spin.params)
342 data_store.gx.append(periodic_table.gyromagnetic_ratio(spin.isotope))
343
344
345 interatoms = return_interatom_list(spin_hash=spin._hash)
346 for i in range(len(interatoms)):
347
348 if not interatoms[i].dipole_pair:
349 continue
350
351
352 if data_store.spin_id != interatoms[i].spin_id1:
353 spin_id2 = interatoms[i].spin_id1
354 else:
355 spin_id2 = interatoms[i].spin_id2
356 spin2 = return_spin(spin_id=spin_id2)
357
358
359 data_store.gh.append(periodic_table.gyromagnetic_ratio(spin2.isotope))
360 if sim_index == None or data_store.model_type == 'diff' or not hasattr(interatoms[i], 'r_sim'):
361 data_store.r.append(interatoms[i].r)
362 else:
363 data_store.r.append(interatoms[i].r_sim[sim_index])
364
365
366 if data_store.model_type != 'local_tm' and cdp.diff_tensor.type != 'sphere':
367
368 if lib.arg_check.is_num_list(interatoms[i].vector[0], raise_error=False):
369 raise RelaxMultiVectorError(data_store.spin_id)
370
371
372 data_store.xh_unit_vectors.append(interatoms[i].vector)
373
374
375 else:
376 data_store.xh_unit_vectors.append(None)
377
378
379 break
380
381
382 if data_store.model_type == 'local_tm':
383 pass
384
385
386 data_store.num_params.append(len(spin.params))
387
388
389 if data_store.model_type == 'diff':
390 data_store.param_values.append(assemble_param_vector(model_type='mf'))
391
392
393 for k in range(len(data_store.ri_data)):
394 data_store.ri_data[k] = array(data_store.ri_data[k], float64)
395 data_store.ri_data_err[k] = array(data_store.ri_data_err[k], float64)
396
397
398 if data_store.model_type == 'local_tm':
399 data_store.diff_type = 'sphere'
400 else:
401 data_store.diff_type = cdp.diff_tensor.type
402
403
404 data_store.diff_params = None
405 if data_store.model_type == 'mf':
406
407 if data_store.diff_type == 'sphere':
408 data_store.diff_params = [cdp.diff_tensor.tm]
409
410
411 elif data_store.diff_type == 'spheroid':
412 data_store.diff_params = [cdp.diff_tensor.tm, cdp.diff_tensor.Da, cdp.diff_tensor.theta, cdp.diff_tensor.phi]
413
414
415 elif data_store.diff_type == 'ellipsoid':
416 data_store.diff_params = [cdp.diff_tensor.tm, cdp.diff_tensor.Da, cdp.diff_tensor.Dr, cdp.diff_tensor.alpha, cdp.diff_tensor.beta, cdp.diff_tensor.gamma]
417 elif min_algor == 'back_calc' and data_store.model_type == 'local_tm':
418
419 data_store.diff_params = [spin.local_tm]
420
421
423 """Package the relaxation data into the data structures used for optimisation.
424
425 @param spin: The spin container to extract the data from.
426 @type spin: SpinContainer instance
427 @keyword sim_index: The optional MC simulation index.
428 @type sim_index: int
429 @return: The structures ri_data, ri_data_err, num_frq, num_ri, ri_ids, frq, remap_table, noe_r1_table.
430 @rtype: tuple
431 """
432
433
434 ri_data = []
435 ri_data_err = []
436 ri_labels = []
437 frq = []
438 remap_table = []
439 noe_r1_table = []
440
441
442 for ri_id in cdp.ri_ids:
443
444 if ri_id not in spin.ri_data:
445 continue
446
447
448 if sim_index == None:
449 data = spin.ri_data[ri_id]
450 else:
451 data = spin.ri_data_sim[ri_id][sim_index]
452
453
454 err = spin.ri_data_err[ri_id]
455
456
457 if data == None or err == None:
458 continue
459
460
461 ri_data.append(data)
462 ri_data_err.append(err)
463
464
465 ri_labels.append(cdp.ri_type[ri_id])
466
467
468 if cdp.spectrometer_frq[ri_id] not in frq:
469 frq.append(cdp.spectrometer_frq[ri_id])
470
471
472 remap_table.append(frq.index(cdp.spectrometer_frq[ri_id]))
473
474
475 noe_r1_table.append(None)
476
477
478 num_ri = len(ri_data)
479
480
481 for i in range(num_ri):
482
483 if cdp.ri_type[cdp.ri_ids[i]] == 'NOE':
484 for j in range(num_ri):
485 if cdp.ri_type[cdp.ri_ids[j]] == 'R1' and cdp.spectrometer_frq[cdp.ri_ids[i]] == cdp.spectrometer_frq[cdp.ri_ids[j]]:
486 noe_r1_table[i] = j
487
488
489 return ri_data, ri_data_err, len(frq), num_ri, ri_labels, frq, remap_table, noe_r1_table
490
491
492
494 """The model-free memo class.
495
496 Not quite a momento so a memo.
497 """
498
499 - def __init__(self, model_free=None, model_type=None, spin=None, sim_index=None, scaling_matrix=None):
500 """Initialise the model-free memo class.
501
502 This memo stores the model-free class instance so that the disassemble_result() method can be called to store the optimisation results. The other args are those required by this method but not generated through optimisation.
503
504 @keyword model_free: The model-free class instance.
505 @type model_free: specific_analyses.model_free.Model_free instance
506 @keyword spin: The spin data container. If this argument is supplied, then the spin_id argument will be ignored.
507 @type spin: SpinContainer instance
508 @keyword sim_index: The optional MC simulation index.
509 @type sim_index: int
510 @keyword scaling_matrix: The diagonal, square scaling matrix.
511 @type scaling_matrix: numpy diagonal matrix
512 """
513
514
515 super(MF_memo, self).__init__()
516
517
518 self.model_free = model_free
519 self.model_type = model_type
520 self.spin = spin
521 self.sim_index = sim_index
522 self.scaling_matrix = scaling_matrix
523
524
525
527 """Command class for standard model-free minimisation."""
528
534
535
537 """Model-free optimisation.
538
539 @return: The optimisation results consisting of the parameter vector, function value, iteration count, function count, gradient count, Hessian count, and warnings.
540 @rtype: tuple of numpy array, float, int, int, int, int, str
541 """
542
543
544 results = generic_minimise(func=self.mf.func, dfunc=self.mf.dfunc, d2func=self.mf.d2func, args=(), x0=self.opt_params.param_vector, min_algor=self.opt_params.min_algor, min_options=self.opt_params.min_options, func_tol=self.opt_params.func_tol, grad_tol=self.opt_params.grad_tol, maxiter=self.opt_params.max_iterations, A=self.opt_params.A, b=self.opt_params.b, full_output=True, print_flag=self.opt_params.verbosity)
545
546
547 return results
548
549
550 - def run(self, processor, completed):
551 """Setup and perform the model-free optimisation."""
552
553
554 self.mf = Mf(init_params=self.opt_params.param_vector, model_type=self.data.model_type, diff_type=self.data.diff_type, diff_params=self.data.diff_params, scaling_matrix=self.data.scaling_matrix, num_spins=self.data.num_spins, equations=self.data.equations, param_types=self.data.param_types, param_values=self.data.param_values, relax_data=self.data.ri_data, errors=self.data.ri_data_err, bond_length=self.data.r, csa=self.data.csa, num_frq=self.data.num_frq, frq=self.data.frq, num_ri=self.data.num_ri, remap_table=self.data.remap_table, noe_r1_table=self.data.noe_r1_table, ri_labels=self.data.ri_types, gx=self.data.gx, gh=self.data.gh, h_bar=self.data.h_bar, mu0=self.data.mu0, num_params=self.data.num_params, vectors=self.data.xh_unit_vectors)
555
556
557 if self.opt_params.verbosity >= 1 and (self.data.model_type == 'mf' or self.data.model_type == 'local_tm'):
558 subsection(file=sys.stdout, text="Optimisation: Spin '%s'" % self.data.spin_id, prespace=2, postspace=0)
559
560
561 results = self.optimise()
562
563
564 param_vector, func, iter, fc, gc, hc, warning = results
565
566 processor.return_object(MF_result_command(processor, self.memo_id, param_vector, func, iter, fc, gc, hc, warning, completed=False))
567
568
570 """Store all the data required for model-free optimisation.
571
572 @param data: The data used to initialise the model-free target function class.
573 @type data: class instance
574 @param opt_params: The parameters and data required for optimisation using minfx.
575 @type opt_params: class instance
576 """
577
578
579 self.data = data
580 self.opt_params = opt_params
581
582
583
585 """Command class for the model-free grid search."""
586
592
593
595 """Model-free grid search.
596
597 @return: The optimisation results consisting of the parameter vector, function value, iteration count, function count, gradient count, Hessian count, and warnings.
598 @rtype: tuple of numpy array, float, int, int, int, int, str
599 """
600
601
602 if not hasattr(self.opt_params, 'subdivision'):
603 results = grid(func=self.mf.func, args=(), num_incs=self.opt_params.inc, lower=self.opt_params.lower, upper=self.opt_params.upper, A=self.opt_params.A, b=self.opt_params.b, verbosity=self.opt_params.verbosity)
604
605
606 else:
607 results = grid_point_array(func=self.mf.func, args=(), points=self.opt_params.subdivision, verbosity=self.opt_params.verbosity)
608
609
610 param_vector, func, iter, warning = results
611 fc = iter
612 gc = 0.0
613 hc = 0.0
614
615
616 return param_vector, func, iter, fc, gc, hc, warning
617
618
619
621 """Class for processing the model-free results."""
622
623 - def __init__(self, processor, memo_id, param_vector, func, iter, fc, gc, hc, warning, completed):
638
639
640 - def run(self, processor, memo):
641 """Disassemble the model-free optimisation results.
642
643 @param processor: Unused!
644 @type processor: None
645 @param memo: The model-free memo.
646 @type memo: memo
647 """
648
649
650 disassemble_result(param_vector=self.param_vector, func=self.func, iter=self.iter, fc=self.fc, gc=self.gc, hc=self.hc, warning=self.warning, spin=memo.spin, sim_index=memo.sim_index, model_type=memo.model_type, scaling_matrix=memo.scaling_matrix)
651