1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 """The frame order API object."""
24
25
26 from copy import deepcopy
27 from math import pi
28 from minfx.generic import generic_minimise
29 from minfx.grid import grid_point_array
30 from numpy import float64, zeros
31 from re import search
32
33
34 from lib.errors import RelaxError, RelaxNoModelError
35 from pipe_control import pipes
36 from pipe_control.interatomic import interatomic_loop, return_interatom
37 from pipe_control.mol_res_spin import return_spin, spin_loop
38 from pipe_control.rdc import check_rdcs
39 from specific_analyses.api_base import API_base
40 from specific_analyses.api_common import API_common
41 from specific_analyses.frame_order.data import domain_moving
42 from specific_analyses.frame_order.optimisation import grid_row, store_bc_data, target_fn_setup, unpack_opt_results
43 from specific_analyses.frame_order.parameter_object import Frame_order_params
44 from specific_analyses.frame_order.parameters import assemble_param_vector, assemble_scaling_matrix, linear_constraints, param_num, update_model
45
46
48 """Class containing the specific methods of the Frame Order theories."""
49
50
51 instance = None
52
67
68
70 """Generator method for looping over the base data - RDCs and PCSs.
71
72 This loop yields the following:
73
74 - The RDC identification data for the interatomic data container and alignment.
75 - The PCS identification data for the spin data container and alignment.
76
77 @return: The base data type ('rdc' or 'pcs'), the spin or interatomic data container information (either one or two spin IDs), and the alignment ID string.
78 @rtype: list of str
79 """
80
81
82 for interatom in interatomic_loop(selection1=domain_moving()):
83
84 if not check_rdcs(interatom):
85 continue
86
87
88 for align_id in cdp.rdc_ids:
89
90 yield ['rdc', interatom.spin_id1, interatom.spin_id2, align_id]
91
92
93 for spin, spin_id in spin_loop(selection=domain_moving(), return_id=True):
94
95 if not spin.select:
96 continue
97
98
99 if not hasattr(spin, 'pcs'):
100 continue
101
102
103 for align_id in cdp.pcs_ids:
104
105 yield ['pcs', spin_id, align_id]
106
107
108 - def calculate(self, spin_id=None, verbosity=1, sim_index=None):
109 """Calculate the chi-squared value for the current parameter values.
110
111 @keyword spin_id: The spin identification string (unused).
112 @type spin_id: None
113 @keyword verbosity: The amount of information to print. The higher the value, the greater the verbosity.
114 @type verbosity: int
115 @keyword sim_index: The optional MC simulation index (unused).
116 @type sim_index: None or int
117 """
118
119
120 model, param_vector, scaling_matrix = target_fn_setup(sim_index=sim_index, verbosity=verbosity)
121
122
123 chi2 = model.func(param_vector)
124
125
126 cdp.chi2 = chi2
127
128
129 store_bc_data(model)
130
131
132 print("Chi2: %s" % chi2)
133
134
136 """Return the 'Log barrier' optimisation constraint algorithm.
137
138 @return: The 'Log barrier' constraint algorithm.
139 @rtype: str
140 """
141
142
143 return 'Log barrier'
144
145
147 """Create the Monte Carlo data by back calculating the RDCs or PCSs.
148
149 @keyword data_id: The data set as yielded by the base_data_loop() generator method.
150 @type data_id: list of str
151 @return: The Monte Carlo simulation data.
152 @rtype: list of floats
153 """
154
155
156 mc_data = []
157
158
159 if data_id[0] == 'rdc':
160
161 data_type, spin_id1, spin_id2, align_id = data_id
162
163
164 interatom = return_interatom(spin_id1, spin_id2)
165
166
167 if not hasattr(interatom, 'rdc_bc'):
168 self.calculate()
169
170
171 if not hasattr(interatom, 'rdc_bc') or align_id not in interatom.rdc_bc:
172 data = None
173 else:
174 data = interatom.rdc_bc[align_id]
175
176
177 mc_data.append(data)
178
179
180 elif data_id[0] == 'pcs':
181
182 data_type, spin_id, align_id = data_id
183
184
185 spin = return_spin(spin_id)
186
187
188 if not hasattr(spin, 'pcs_bc'):
189 self.calculate()
190
191
192 if not hasattr(spin, 'pcs_bc') or align_id not in spin.pcs_bc:
193 data = None
194 else:
195 data = spin.pcs_bc[align_id]
196
197
198 mc_data.append(data)
199
200
201 return mc_data
202
203
204 - def duplicate_data(self, pipe_from=None, pipe_to=None, model_info=None, global_stats=False, verbose=True):
205 """Duplicate the data specific to a single frame order data pipe.
206
207 @keyword pipe_from: The data pipe to copy the data from.
208 @type pipe_from: str
209 @keyword pipe_to: The data pipe to copy the data to.
210 @type pipe_to: str
211 @param model_info: The model index from model_loop().
212 @type model_info: int
213 @keyword global_stats: The global statistics flag.
214 @type global_stats: bool
215 @keyword verbose: Unused.
216 @type verbose: bool
217 """
218
219
220 if pipes.has_pipe(pipe_to):
221 raise RelaxError("The data pipe '%s' already exists." % pipe_to)
222
223
224 pipes.copy(pipe_from=pipe_from, pipe_to=pipe_to)
225
226
227 - def eliminate(self, name, value, model_info, args, sim=None):
228 """Model elimination method.
229
230 @param name: The parameter name.
231 @type name: str
232 @param value: The parameter value.
233 @type value: float
234 @param model_info: The model index from model_info().
235 @type model_info: int
236 @param args: The elimination constant overrides.
237 @type args: None or tuple of float
238 @keyword sim: The Monte Carlo simulation index.
239 @type sim: int
240 @return: True if the model is to be eliminated, False otherwise.
241 @rtype: bool
242 """
243
244
245 text = "The %s parameter of %.5g is %s than %.5g, eliminating "
246 if sim == None:
247 text += "the model."
248 else:
249 text += "simulation %i." % sim
250
251
252 if name == 'cone_s1' and hasattr(cdp, 'cone_s1'):
253 if cdp.cone_s1 > 1.0:
254 print(text % ("cone S1 order", cdp.cone_s1, "greater", 1.0))
255 return True
256 if cdp.cone_s1 < -0.125:
257 print(text % ("cone S1 order", cdp.cone_s1, "less", -0.125))
258 return True
259
260
261 if name == 'cone_theta' and hasattr(cdp, 'cone_theta'):
262 if cdp.cone_theta >= pi:
263 print(text % ("cone opening angle theta", cdp.cone_theta, "greater", pi))
264 return True
265 if cdp.cone_theta < 0.0:
266 print(text % ("cone opening angle theta", cdp.cone_theta, "less", 0))
267 return True
268
269
270 if name == 'cone_theta_x' and hasattr(cdp, 'cone_theta_x'):
271 if cdp.cone_theta_x >= pi:
272 print(text % ("cone opening angle theta x", cdp.cone_theta_x, "greater", pi))
273 return True
274 if cdp.cone_theta_x < 0.001:
275 print(text % ("cone opening angle theta x", cdp.cone_theta_x, "less", 0.001))
276 return True
277 if name == 'cone_theta_y' and hasattr(cdp, 'cone_theta_y'):
278 if cdp.cone_theta_y >= pi:
279 print(text % ("cone opening angle theta y", cdp.cone_theta_y, "greater", pi))
280 return True
281 if cdp.cone_theta_y < 0.001:
282 print(text % ("cone opening angle theta y", cdp.cone_theta_y, "less", 0.001))
283 return True
284
285
286 if name == 'cone_sigma_max' and hasattr(cdp, 'cone_sigma_max'):
287 if cdp.cone_sigma_max >= pi:
288 print(text % ("torsion angle sigma_max", cdp.cone_sigma_max, "greater", pi))
289 return True
290 if cdp.cone_sigma_max < 0.0:
291 print(text % ("torsion angle sigma_max", cdp.cone_sigma_max, "less", 0.0))
292 return True
293
294
295 return False
296
297
299 """Return a vector of parameter names.
300
301 @keyword model_info: The model index from model_info().
302 @type model_info: int
303 @return: The vector of parameter names.
304 @rtype: list of str
305 """
306
307
308 update_model()
309
310
311 return cdp.params
312
313
315 """Return a vector of parameter values.
316
317 @keyword model_info: The model index from model_info(). This is zero for the global models or equal to the global spin index (which covers the molecule, residue, and spin indices).
318 @type model_info: int
319 @keyword sim_index: The Monte Carlo simulation index.
320 @type sim_index: int
321 @return: The vector of parameter values.
322 @rtype: list of str
323 """
324
325
326 return assemble_param_vector(sim_index=sim_index)
327
328
329 - def grid_search(self, lower=None, upper=None, inc=None, constraints=False, verbosity=0, sim_index=None):
330 """Perform a grid search.
331
332 @keyword lower: The lower bounds of the grid search which must be equal to the number of parameters in the model.
333 @type lower: list of float
334 @keyword upper: The upper bounds of the grid search which must be equal to the number of parameters in the model.
335 @type upper: list of float
336 @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.
337 @type inc: int or list of int
338 @keyword constraints: If True, constraints are applied during the grid search (eliminating parts of the grid). If False, no constraints are used.
339 @type constraints: bool
340 @keyword verbosity: A flag specifying the amount of information to print. The higher the value, the greater the verbosity.
341 @type verbosity: int
342 @keyword sim_index: The Monte Carlo simulation index.
343 @type sim_index: None or int
344 """
345
346
347 if not hasattr(cdp, 'model'):
348 raise RelaxNoModelError('Frame Order')
349
350
351 scaling_matrix = assemble_scaling_matrix(scaling=True)
352
353
354 n = param_num()
355
356
357 if isinstance(inc, int):
358 incs = [inc]*n
359 else:
360 incs = inc
361
362
363 if len(incs) != n:
364 raise RelaxError("The size of the increment list %s does not match the number of parameters in %s." % (incs, cdp.params))
365
366
367 lower_list = lower
368 upper_list = upper
369 grid = []
370 """This structure is a list of lists. The first dimension corresponds to the model
371 parameter. The second dimension are the grid node positions."""
372
373
374 for i in range(n):
375
376 if incs[i] == None:
377 grid.append(None)
378 continue
379
380
381 dist_type = None
382 end_point = True
383
384
385 if cdp.params[i] in ['pivot_x', 'pivot_y', 'pivot_z']:
386 val = getattr(cdp, cdp.params[i])
387 lower = val - 10.0
388 upper = val + 10.0
389
390
391 if cdp.params[i] in ['ave_pos_x', 'ave_pos_y', 'ave_pos_z']:
392 lower = -5
393 upper = 5
394
395
396 if cdp.params[i] in ['ave_pos_alpha', 'ave_pos_gamma', 'eigen_alpha', 'eigen_gamma', 'axis_phi']:
397 lower = 0.0
398 upper = 2*pi * (1.0 - 1.0/incs[i])
399
400
401 if cdp.params[i] in ['axis_alpha']:
402 lower = -pi
403 upper = pi * (1.0 - 1.0/incs[i])
404
405
406 if cdp.params[i] in ['ave_pos_beta', 'eigen_beta', 'axis_theta']:
407
408 if not isinstance(inc, list):
409 incs[i] = int(incs[i] / 2) + 1
410
411
412 dist_type = 'acos'
413 end_point = False
414
415
416 lower = 0.0
417 upper = pi
418
419
420 if cdp.params[i] == 'cone_s1':
421 lower = -0.125
422 upper = 1.0
423
424
425 if cdp.params[i] in ['cone_theta', 'cone_theta_x', 'cone_theta_y', 'cone_sigma_max']:
426 lower = pi * (1.0/incs[i])
427 upper = pi * (1.0 - 1.0/incs[i])
428
429
430 if lower_list:
431 lower = lower_list[i]
432 if upper_list:
433 upper = upper_list[i]
434
435
436 row = grid_row(incs[i], lower, upper, dist_type=dist_type, end_point=end_point)
437 grid.append(row)
438
439
440 if not end_point:
441 incs[i] -= 1
442
443
444 total_pts = 1
445 for i in range(n):
446
447 if grid[i] == None:
448 continue
449
450 total_pts = total_pts * len(grid[i])
451
452
453 max_pts = 50e6
454 if total_pts > max_pts:
455 raise RelaxError("The total number of grid points '%s' exceeds the maximum of '%s'." % (total_pts, int(max_pts)))
456
457
458 pts = zeros((total_pts, n), float64)
459 indices = zeros(n, int)
460 for i in range(total_pts):
461
462 for j in range(n):
463
464 if grid[j] == None:
465
466 pts[i, j] = getattr(cdp, cdp.params[j]) / scaling_matrix[j, j]
467
468
469 else:
470 pts[i, j] = grid[j][indices[j]] / scaling_matrix[j, j]
471
472
473 for j in range(n):
474 if incs[j] != None and indices[j] < incs[j]-1:
475 indices[j] += 1
476 break
477 else:
478 indices[j] = 0
479
480
481 self.minimise(min_algor='grid', min_options=pts, constraints=constraints, verbosity=verbosity, sim_index=sim_index)
482
483
485 """Create bounds for the OpenDX mapping function.
486
487 @param param: The name of the parameter to return the lower and upper bounds of.
488 @type param: str
489 @param spin_id: The spin identification string (unused).
490 @type spin_id: None
491 @return: The upper and lower bounds of the parameter.
492 @rtype: list of float
493 """
494
495
496 if param in ['ave_pos_x', 'ave_pos_y', 'ave_pos_z']:
497 return [-100.0, 100]
498 if param in ['ave_pos_alpha', 'ave_pos_beta', 'ave_pos_gamma']:
499 return [0.0, 2*pi]
500
501
502 if param == 'axis_theta':
503 return [0.0, pi]
504
505
506 if param == 'axis_phi':
507 return [0.0, 2*pi]
508
509
510 if param == 'axis_alpha':
511 return [0.0, 2*pi]
512
513
514 if param == 'cone_theta':
515 return [0.0, pi]
516
517
518 - def minimise(self, min_algor=None, min_options=None, func_tol=None, grad_tol=None, max_iterations=None, constraints=False, scaling=True, verbosity=0, sim_index=None, lower=None, upper=None, inc=None):
519 """Minimisation function.
520
521 @param min_algor: The minimisation algorithm to use.
522 @type min_algor: str
523 @param min_options: An array of options to be used by the minimisation algorithm.
524 @type min_options: array of str
525 @param func_tol: The function tolerance which, when reached, terminates optimisation. Setting this to None turns of the check.
526 @type func_tol: None or float
527 @param grad_tol: The gradient tolerance which, when reached, terminates optimisation. Setting this to None turns of the check.
528 @type grad_tol: None or float
529 @param max_iterations: The maximum number of iterations for the algorithm.
530 @type max_iterations: int
531 @param constraints: If True, constraints are used during optimisation.
532 @type constraints: bool
533 @param scaling: If True, diagonal scaling is enabled during optimisation to allow the problem to be better conditioned.
534 @type scaling: bool
535 @param verbosity: A flag specifying the amount of information to print. The higher the value, the greater the verbosity.
536 @type verbosity: int
537 @param sim_index: The index of the simulation to optimise. This should be None if normal optimisation is desired.
538 @type sim_index: None or int
539 @keyword lower: The lower bounds of the grid search which must be equal to the number of parameters in the model. This optional argument is only used when doing a grid search.
540 @type lower: array of numbers
541 @keyword upper: The upper bounds of the grid search which must be equal to the number of parameters in the model. This optional argument is only used when doing a grid search.
542 @type upper: array of numbers
543 @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. This argument is only used when doing a grid search.
544 @type inc: array of int
545 """
546
547
548 model, param_vector, scaling_matrix = target_fn_setup(sim_index=sim_index, verbosity=verbosity, scaling=scaling)
549
550
551 A, b = None, None
552 if constraints:
553 A, b = linear_constraints(scaling_matrix=scaling_matrix)
554
555
556 if search('^[Gg]rid', min_algor):
557 results = grid_point_array(func=model.func, args=(), points=min_options, verbosity=verbosity)
558
559
560 else:
561 results = generic_minimise(func=model.func, args=(), x0=param_vector, min_algor=min_algor, min_options=min_options, func_tol=func_tol, grad_tol=grad_tol, maxiter=max_iterations, A=A, b=b, full_output=True, print_flag=verbosity)
562
563
564 unpack_opt_results(results, scaling, scaling_matrix, sim_index)
565
566
567 store_bc_data(model)
568
569
571 """Return a description of the model.
572
573 @param model_info: The model index from model_loop().
574 @type model_info: int
575 @return: The model description.
576 @rtype: str
577 """
578
579 return ""
580
581
583 """Return the k, n, and chi2 model statistics.
584
585 k - number of parameters.
586 n - number of data points.
587 chi2 - the chi-squared value.
588
589
590 @keyword model_info: Unused.
591 @type model_info: None
592 @keyword spin_id: Unused.
593 @type spin_id: None
594 @keyword global_stats: Unused.
595 @type global_stats: None
596 @return: The optimisation statistics, in tuple format, of the number of parameters (k), the number of data points (n), and the chi-squared value (chi2).
597 @rtype: tuple of (int, int, float)
598 """
599
600
601 k = len(cdp.params)
602
603
604 n = 0
605 for data in self.base_data_loop():
606 n += 1
607
608
609 if not hasattr(cdp, 'chi2'):
610 raise RelaxError("Statistics are not available, most likely because the model has not been optimised.")
611
612
613 return k, n, cdp.chi2
614
615
617 """Return the RDC or PCS error structure.
618
619 @param data_id: The data set as yielded by the base_data_loop() generator method.
620 @type data_id: list of str
621 @return: The array of RDC or PCS error values.
622 @rtype: list of float
623 """
624
625
626 mc_errors = []
627
628
629 if data_id[0] == 'rdc':
630
631 data_type, spin_id1, spin_id2, align_id = data_id
632
633
634 interatom = return_interatom(spin_id1, spin_id2)
635
636
637 if not hasattr(interatom, 'rdc_err'):
638 raise RelaxError("The RDC errors are missing for interatomic data container between spins '%s' and '%s'." % (spin_id1, spin_id2))
639
640
641 if align_id not in interatom.rdc_err:
642 mc_errors.append(None)
643
644
645 else:
646 mc_errors.append(interatom.rdc_err[align_id])
647
648
649 elif data_id[0] == 'pcs':
650
651 data_type, spin_id, align_id = data_id
652
653
654 spin = return_spin(spin_id)
655
656
657 if not hasattr(spin, 'pcs_err'):
658 raise RelaxError("The PCS errors are missing for spin '%s'." % spin_id)
659
660
661 if align_id not in spin.pcs_err:
662 mc_errors.append(None)
663
664
665 else:
666 mc_errors.append(spin.pcs_err[align_id])
667
668
669 return mc_errors
670
671
672 - def set_error(self, model_info, index, error):
673 """Set the parameter errors.
674
675 @param model_info: The model information originating from model_loop() (unused).
676 @type model_info: None
677 @param index: The index of the parameter to set the errors for.
678 @type index: int
679 @param error: The error value.
680 @type error: float
681 """
682
683
684 inc = 0
685
686
687 for param in self.data_names(set='params'):
688
689 if param not in cdp.params:
690 continue
691
692
693 if index == inc:
694 setattr(cdp, param + "_err", error)
695
696
697 inc = inc + 1
698
699
700 if cdp.model == 'iso cone, free rotor' and inc == index:
701 setattr(cdp, 'cone_theta_err', error)
702
703
704
706 """Set the simulation selection flag for the spin.
707
708 @param model_info: The model information originating from model_loop() (unused).
709 @type model_info: None
710 @param select_sim: The selection flag for the simulations.
711 @type select_sim: bool
712 """
713
714
715 cdp.select_sim = deepcopy(select_sim)
716
717
719 """Initialise the Monte Carlo parameter values."""
720
721
722 param_names = self.data_names(set='params')
723
724
725 model_params = deepcopy(cdp.params)
726
727
728 if cdp.model == 'iso cone, free rotor':
729 param_names.append('cone_theta')
730 model_params.append('cone_theta')
731
732
733 min_names = self.data_names(set='min')
734
735
736
737
738
739
740 for object_name in param_names:
741
742 if object_name not in model_params:
743 continue
744
745
746 sim_object_name = object_name + '_sim'
747
748
749 if hasattr(cdp, sim_object_name):
750 raise RelaxError("Monte Carlo parameter values have already been set.")
751
752
753
754
755
756
757 for object_name in param_names:
758
759 if object_name not in model_params:
760 continue
761
762
763 sim_object_name = object_name + '_sim'
764
765
766 setattr(cdp, sim_object_name, [])
767
768
769 sim_object = getattr(cdp, sim_object_name)
770
771
772 for j in range(cdp.sim_number):
773
774 sim_object.append(deepcopy(getattr(cdp, object_name)))
775
776
777 for object_name in min_names:
778
779 sim_object_name = object_name + '_sim'
780
781
782 setattr(cdp, sim_object_name, [])
783
784
785 sim_object = getattr(cdp, sim_object_name)
786
787
788 for j in range(cdp.sim_number):
789
790 sim_object.append(deepcopy(getattr(cdp, object_name)))
791
792
794 """Pack the Monte Carlo simulation data.
795
796 @param data_id: The data set as yielded by the base_data_loop() generator method.
797 @type data_id: list of str
798 @param sim_data: The Monte Carlo simulation data.
799 @type sim_data: list of float
800 """
801
802
803 if data_id[0] == 'rdc':
804
805 data_type, spin_id1, spin_id2, align_id = data_id
806
807
808 interatom = return_interatom(spin_id1, spin_id2)
809
810
811 if not hasattr(interatom, 'rdc_sim'):
812 interatom.rdc_sim = {}
813
814
815 interatom.rdc_sim[align_id] = []
816 for i in range(cdp.sim_number):
817 interatom.rdc_sim[align_id].append(sim_data[i][0])
818
819
820 elif data_id[0] == 'pcs':
821
822 data_type, spin_id, align_id = data_id
823
824
825 spin = return_spin(spin_id)
826
827
828 if not hasattr(spin, 'pcs_sim'):
829 spin.pcs_sim = {}
830
831
832 spin.pcs_sim[data_id[2]] = []
833 for i in range(cdp.sim_number):
834 spin.pcs_sim[data_id[2]].append(sim_data[i][0])
835
836
838 """Return the array of simulation parameter values.
839
840 @param model_info: The model information originating from model_loop().
841 @type model_info: unknown
842 @param index: The index of the parameter to return the array of values for.
843 @type index: int
844 """
845
846
847 inc = 0
848
849
850 param_names = self.data_names(set='params')
851
852
853 for param in param_names:
854
855 if param not in cdp.params:
856 continue
857
858
859 if index == inc:
860 return getattr(cdp, param + "_sim")
861
862
863 inc = inc + 1
864
865
866 if cdp.model == 'iso cone, free rotor' and inc == index:
867 return getattr(cdp, 'cone_theta_sim')
868
869
871 """Return the array of selected simulation flags for the spin.
872
873 @param model_info: The model information originating from model_loop() (unused).
874 @type model_info: None
875 @return: The array of selected simulation flags.
876 @rtype: list of int
877 """
878
879
880 return cdp.select_sim
881