Package specific_fns :: Module relax_fit
[hide private]
[frames] | no frames]

Source Code for Module specific_fns.relax_fit

  1  ############################################################################### 
  2  #                                                                             # 
  3  # Copyright (C) 2004-2012 Edward d'Auvergne                                   # 
  4  #                                                                             # 
  5  # This file is part of the program relax.                                     # 
  6  #                                                                             # 
  7  # relax is free software; you can redistribute it and/or modify               # 
  8  # it under the terms of the GNU General Public License as published by        # 
  9  # the Free Software Foundation; either version 2 of the License, or           # 
 10  # (at your option) any later version.                                         # 
 11  #                                                                             # 
 12  # relax is distributed in the hope that it will be useful,                    # 
 13  # but WITHOUT ANY WARRANTY; without even the implied warranty of              # 
 14  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               # 
 15  # GNU General Public License for more details.                                # 
 16  #                                                                             # 
 17  # You should have received a copy of the GNU General Public License           # 
 18  # along with relax; if not, write to the Free Software                        # 
 19  # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA   # 
 20  #                                                                             # 
 21  ############################################################################### 
 22   
 23  # Module docstring. 
 24  """The relaxation curve fitting specific code.""" 
 25   
 26  # Python module imports. 
 27  from minfx.generic import generic_minimise 
 28  from minfx.grid import grid 
 29  from numpy import array, average, dot, float64, identity, zeros 
 30  from numpy.linalg import inv 
 31  from re import match, search 
 32  from warnings import warn 
 33   
 34  # relax module imports. 
 35  from api_base import API_base 
 36  from api_common import API_common 
 37  from dep_check import C_module_exp_fn 
 38  from generic_fns import pipes 
 39  from generic_fns.mol_res_spin import exists_mol_res_spin_data, generate_spin_id, return_spin, spin_loop 
 40  from relax_errors import RelaxError, RelaxFuncSetupError, RelaxLenError, RelaxNoModelError, RelaxNoSequenceError 
 41  from relax_warnings import RelaxDeselectWarning 
 42  from user_functions.data import Uf_tables; uf_tables = Uf_tables() 
 43  from user_functions.objects import Desc_container 
 44   
 45  # C modules. 
 46  if C_module_exp_fn: 
 47      from maths_fns.relax_fit import setup, func, dfunc, d2func, back_calc_I 
 48   
 49   
50 -class Relax_fit(API_base, API_common):
51 """Class containing functions for relaxation curve fitting.""" 52
53 - def __init__(self):
54 """Initialise the class by placing API_common methods into the API.""" 55 56 # Execute the base class __init__ method. 57 super(Relax_fit, self).__init__() 58 59 # Place methods into the API. 60 self.base_data_loop = self._base_data_loop_spin 61 self.model_loop = self._model_loop_spin 62 self.return_conversion_factor = self._return_no_conversion_factor 63 self.return_value = self._return_value_general 64 self.set_error = self._set_error_spin 65 self.set_param_values = self._set_param_values_spin 66 self.set_selected_sim = self._set_selected_sim_spin 67 self.sim_init_values = self._sim_init_values_spin 68 self.sim_return_param = self._sim_return_param_spin 69 self.sim_return_selected = self._sim_return_selected_spin 70 71 # Set up the spin parameters. 72 self.PARAMS.add('intensities', scope='spin', py_type=list, grace_string='\\qPeak intensities\\Q') 73 self.PARAMS.add('relax_times', scope='spin', py_type=list, grace_string='\\qRelaxation time period (s)\\Q') 74 self.PARAMS.add('rx', scope='spin', default=8.0, desc='Either the R1 or R2 relaxation rate', set='params', py_type=float, grace_string='\\qR\\sx\\Q', err=True, sim=True) 75 self.PARAMS.add('i0', scope='spin', default=10000.0, desc='The initial intensity', py_type=float, set='params', grace_string='\\qI\\s0\\Q', err=True, sim=True) 76 self.PARAMS.add('iinf', scope='spin', default=0.0, desc='The intensity at infinity', py_type=float, set='params', grace_string='\\qI\\sinf\\Q', err=True, sim=True) 77 self.PARAMS.add('params', scope='spin', desc='The model parameters', py_type=list) 78 79 # Add the minimisation data. 80 self.PARAMS.add_min_data(min_stats_global=False, min_stats_spin=True)
81 82
83 - def _assemble_param_vector(self, spin=None, sim_index=None):
84 """Assemble the exponential curve parameter vector (as a numpy array). 85 86 @keyword spin: The spin data container. 87 @type spin: SpinContainer instance 88 @keyword sim_index: The optional MC simulation index. 89 @type sim_index: int 90 @return: An array of the parameter values of the exponential model. 91 @rtype: numpy array 92 """ 93 94 # Initialise. 95 param_vector = [] 96 97 # Loop over the model parameters. 98 for i in xrange(len(spin.params)): 99 # Relaxation rate. 100 if spin.params[i] == 'Rx': 101 if sim_index != None: 102 param_vector.append(spin.rx_sim[sim_index]) 103 elif spin.rx == None: 104 param_vector.append(0.0) 105 else: 106 param_vector.append(spin.rx) 107 108 # Initial intensity. 109 elif spin.params[i] == 'I0': 110 if sim_index != None: 111 param_vector.append(spin.i0_sim[sim_index]) 112 elif spin.i0 == None: 113 param_vector.append(0.0) 114 else: 115 param_vector.append(spin.i0) 116 117 # Intensity at infinity. 118 elif spin.params[i] == 'Iinf': 119 if sim_index != None: 120 param_vector.append(spin.iinf_sim[sim_index]) 121 elif spin.iinf == None: 122 param_vector.append(0.0) 123 else: 124 param_vector.append(spin.iinf) 125 126 # Return a numpy array. 127 return array(param_vector, float64)
128 129
130 - def _assemble_scaling_matrix(self, spin=None, scaling=True):
131 """Create and return the scaling matrix. 132 133 @keyword spin: The spin data container. 134 @type spin: SpinContainer instance 135 @keyword scaling: A flag which if false will cause the identity matrix to be returned. 136 @type scaling: bool 137 @return: The diagonal and square scaling matrix. 138 @rtype: numpy diagonal matrix 139 """ 140 141 # Initialise. 142 scaling_matrix = identity(len(spin.params), float64) 143 i = 0 144 145 # No diagonal scaling. 146 if not scaling: 147 return scaling_matrix 148 149 # Loop over the parameters. 150 for i in xrange(len(spin.params)): 151 # Relaxation rate. 152 if spin.params[i] == 'Rx': 153 pass 154 155 # Intensity scaling. 156 elif search('^i', spin.params[i]): 157 # Find the position of the first time point. 158 pos = cdp.relax_times.index(min(cdp.relax_times)) 159 160 # Scaling. 161 scaling_matrix[i, i] = 1.0 / average(spin.intensities[pos]) 162 163 # Increment i. 164 i = i + 1 165 166 # Return the scaling matrix. 167 return scaling_matrix
168 169
170 - def _back_calc(self, spin=None, relax_time_id=None):
171 """Back-calculation of peak intensity for the given relaxation time. 172 173 @keyword spin: The spin container. 174 @type spin: SpinContainer instance 175 @keyword relax_time_id: The ID string for the desired relaxation time. 176 @type relax_time_id: str 177 @return: The peak intensity for the desired relaxation time. 178 @rtype: float 179 """ 180 181 # Create the initial parameter vector. 182 param_vector = self._assemble_param_vector(spin=spin) 183 184 # Create a scaling matrix. 185 scaling_matrix = self._assemble_scaling_matrix(spin=spin, scaling=False) 186 187 # The keys. 188 keys = spin.intensities.keys() 189 190 # The peak intensities and times. 191 values = [] 192 errors = [] 193 times = [] 194 for key in keys: 195 values.append(spin.intensities[key]) 196 errors.append(spin.intensity_err[key]) 197 times.append(cdp.relax_times[key]) 198 199 # Initialise the relaxation fit functions. 200 setup(num_params=len(spin.params), num_times=len(cdp.relax_times), values=values, sd=errors, relax_times=times, scaling_matrix=scaling_matrix) 201 202 # Make a single function call. This will cause back calculation and the data will be stored in the C module. 203 self._func(param_vector) 204 205 # Get the data back. 206 results = back_calc_I() 207 208 # Return the correct peak height. 209 return results[keys.index(relax_time_id)]
210 211
212 - def _disassemble_param_vector(self, param_vector=None, spin=None, sim_index=None):
213 """Disassemble the parameter vector. 214 215 @keyword param_vector: The parameter vector. 216 @type param_vector: numpy array 217 @keyword spin: The spin data container. 218 @type spin: SpinContainer instance 219 @keyword sim_index: The optional MC simulation index. 220 @type sim_index: int 221 """ 222 223 # Monte Carlo simulations. 224 if sim_index != None: 225 # The relaxation rate. 226 spin.rx_sim[sim_index] = param_vector[0] 227 228 # Initial intensity. 229 spin.i0_sim[sim_index] = param_vector[1] 230 231 # Intensity at infinity. 232 if cdp.curve_type == 'inv': 233 spin.iinf_sim[sim_index] = param_vector[2] 234 235 # Parameter values. 236 else: 237 # The relaxation rate. 238 spin.rx = param_vector[0] 239 240 # Initial intensity. 241 spin.i0 = param_vector[1] 242 243 # Intensity at infinity. 244 if cdp.curve_type == 'inv': 245 spin.iinf = param_vector[2]
246 247
248 - def _func(self, params):
249 """Wrapper function for the C module, for converting numpy arrays. 250 251 @param params: The parameter array from the minimisation code. 252 @type params: numpy array 253 @return: The function value generated by the C module. 254 @rtype: float 255 """ 256 257 # Call the C code. 258 chi2 = func(params.tolist()) 259 260 # Return the chi2 value. 261 return chi2
262 263
264 - def _dfunc(self, params):
265 """Wrapper function for the C module, for converting numpy arrays. 266 267 The currently does nothing. 268 """
269 270
271 - def _d2func(self, params):
272 """Wrapper function for the C module, for converting numpy arrays. 273 274 The currently does nothing. 275 """
276 277
278 - def _grid_search_setup(self, spin=None, param_vector=None, lower=None, upper=None, inc=None, scaling_matrix=None):
279 """The grid search setup function. 280 281 @keyword spin: The spin data container. 282 @type spin: SpinContainer instance 283 @keyword param_vector: The parameter vector. 284 @type param_vector: numpy array 285 @keyword lower: The lower bounds of the grid search which must be equal to the 286 number of parameters in the model. This optional argument is 287 only used when doing a grid search. 288 @type lower: array of numbers 289 @keyword upper: The upper bounds of the grid search which must be equal to the 290 number of parameters in the model. This optional argument is 291 only used when doing a grid search. 292 @type upper: array of numbers 293 @keyword inc: The increments for each dimension of the space for the grid 294 search. The number of elements in the array must equal to the 295 number of parameters in the model. This argument is only used 296 when doing a grid search. 297 @type inc: array of int 298 @keyword scaling_matrix: The scaling matrix. 299 @type scaling_matrix: numpy diagonal matrix 300 @return: A tuple of the grid size and the minimisation options. For the 301 minimisation options, the first dimension corresponds to the 302 model parameter. The second dimension is a list of the number 303 of increments, the lower bound, and upper bound. 304 @rtype: (int, list of lists [int, float, float]) 305 """ 306 307 # The length of the parameter array. 308 n = len(param_vector) 309 310 # Make sure that the length of the parameter array is > 0. 311 if n == 0: 312 raise RelaxError("Cannot run a grid search on a model with zero parameters.") 313 314 # Lower bounds. 315 if lower != None and len(lower) != n: 316 raise RelaxLenError('lower bounds', n) 317 318 # Upper bounds. 319 if upper != None and len(upper) != n: 320 raise RelaxLenError('upper bounds', n) 321 322 # Increments. 323 if isinstance(inc, list) and len(inc) != n: 324 raise RelaxLenError('increment', n) 325 elif isinstance(inc, int): 326 inc = [inc]*n 327 328 # Set up the default bounds. 329 if not lower: 330 # Init. 331 lower = [] 332 upper = [] 333 334 # Loop over the parameters. 335 for i in range(n): 336 # Relaxation rate (from 0 to 20 s^-1). 337 if spin.params[i] == 'Rx': 338 lower.append(0.0) 339 upper.append(20.0) 340 341 # Intensity 342 elif search('^I', spin.params[i]): 343 # Find the ID of the first time point. 344 min_time = min(cdp.relax_times.values()) 345 for key in cdp.relax_times.keys(): 346 if cdp.relax_times[key] == min_time: 347 id = key 348 break 349 350 # Defaults. 351 lower.append(0.0) 352 upper.append(average(spin.intensities[id])) 353 354 # Parameter scaling. 355 for i in range(n): 356 lower[i] = lower[i] / scaling_matrix[i, i] 357 upper[i] = upper[i] / scaling_matrix[i, i] 358 359 return inc, lower, upper
360 361
362 - def _linear_constraints(self, spin=None, scaling_matrix=None):
363 """Set up the relaxation curve fitting linear constraint matrices A and b. 364 365 Standard notation 366 ================= 367 368 The relaxation rate constraints are:: 369 370 Rx >= 0 371 372 The intensity constraints are:: 373 374 I0 >= 0 375 Iinf >= 0 376 377 378 Matrix notation 379 =============== 380 381 In the notation A.x >= b, where A is an matrix of coefficients, x is an array of parameter 382 values, and b is a vector of scalars, these inequality constraints are:: 383 384 | 1 0 0 | | Rx | | 0 | 385 | | | | | | 386 | 1 0 0 | . | I0 | >= | 0 | 387 | | | | | | 388 | 1 0 0 | | Iinf | | 0 | 389 390 391 @keyword spin: The spin data container. 392 @type spin: SpinContainer instance 393 @keyword scaling_matrix: The diagonal, square scaling matrix. 394 @type scaling_matrix: numpy diagonal matrix 395 """ 396 397 # Initialisation (0..j..m). 398 A = [] 399 b = [] 400 n = len(spin.params) 401 zero_array = zeros(n, float64) 402 i = 0 403 j = 0 404 405 # Loop over the parameters. 406 for k in xrange(len(spin.params)): 407 # Relaxation rate. 408 if spin.params[k] == 'Rx': 409 # Rx >= 0. 410 A.append(zero_array * 0.0) 411 A[j][i] = 1.0 412 b.append(0.0) 413 j = j + 1 414 415 # Intensity parameter. 416 elif search('^I', spin.params[k]): 417 # I0, Iinf >= 0. 418 A.append(zero_array * 0.0) 419 A[j][i] = 1.0 420 b.append(0.0) 421 j = j + 1 422 423 # Increment i. 424 i = i + 1 425 426 # Convert to numpy data structures. 427 A = array(A, float64) 428 b = array(b, float64) 429 430 return A, b
431 432
433 - def _model_setup(self, model, params):
434 """Update various model specific data structures. 435 436 @param model: The exponential curve type. 437 @type model: str 438 @param params: A list consisting of the model parameters. 439 @type params: list of str 440 """ 441 442 # Set the model. 443 cdp.curve_type = model 444 445 # Loop over the sequence. 446 for spin in spin_loop(): 447 # Skip deselected spins. 448 if not spin.select: 449 continue 450 451 # Initialise the data structures (if needed). 452 self.data_init(spin) 453 454 # The model and parameter names. 455 spin.model = model 456 spin.params = params
457 458
459 - def _relax_time(self, time=0.0, spectrum_id=None):
460 """Set the relaxation time period associated with a given spectrum. 461 462 @keyword time: The time, in seconds, of the relaxation period. 463 @type time: float 464 @keyword spectrum_id: The spectrum identification string. 465 @type spectrum_id: str 466 """ 467 468 # Test if the spectrum id exists. 469 if spectrum_id not in cdp.spectrum_ids: 470 raise RelaxError("The peak heights corresponding to spectrum id '%s' have not been loaded." % spectrum_id) 471 472 # Initialise the global relaxation time data structure if needed. 473 if not hasattr(cdp, 'relax_times'): 474 cdp.relax_times = {} 475 476 # Add the time at the correct position. 477 cdp.relax_times[spectrum_id] = time
478 479
480 - def _select_model(self, model='exp'):
481 """Function for selecting the model of the exponential curve. 482 483 @keyword model: The exponential curve type. Can be one of 'exp' or 'inv'. 484 @type model: str 485 """ 486 487 # Test if the current pipe exists. 488 pipes.test() 489 490 # Test if the pipe type is set to 'relax_fit'. 491 function_type = cdp.pipe_type 492 if function_type != 'relax_fit': 493 raise RelaxFuncSetupError(specific_setup.get_string(function_type)) 494 495 # Test if sequence data is loaded. 496 if not exists_mol_res_spin_data(): 497 raise RelaxNoSequenceError 498 499 # Two parameter exponential fit. 500 if model == 'exp': 501 print("Two parameter exponential fit.") 502 params = ['Rx', 'I0'] 503 504 # Three parameter inversion recovery fit. 505 elif model == 'inv': 506 print("Three parameter inversion recovery fit.") 507 params = ['Rx', 'I0', 'Iinf'] 508 509 # Invalid model. 510 else: 511 raise RelaxError("The model '" + model + "' is invalid.") 512 513 # Set up the model. 514 self._model_setup(model, params)
515 516
517 - def create_mc_data(self, data_id=None):
518 """Create the Monte Carlo peak intensity data. 519 520 @keyword data_id: The spin identification string, as yielded by the base_data_loop() generator method. 521 @type data_id: str 522 @return: The Monte Carlo simulation data. 523 @rtype: list of floats 524 """ 525 526 # Initialise the MC data data structure. 527 mc_data = {} 528 529 # Get the spin container. 530 spin = return_spin(data_id) 531 532 # Skip deselected spins. 533 if not spin.select: 534 return 535 536 # Skip spins which have no data. 537 if not hasattr(spin, 'intensities'): 538 return 539 540 # Test if the model is set. 541 if not hasattr(spin, 'model') or not spin.model: 542 raise RelaxNoModelError 543 544 # Loop over the spectral time points. 545 for id in cdp.relax_times.keys(): 546 # Back calculate the value. 547 value = self._back_calc(spin=spin, relax_time_id=id) 548 549 # Append the value. 550 mc_data[id] = value 551 552 # Return the MC data. 553 return mc_data
554 555
556 - def data_init(self, data_cont, sim=False):
557 """Initialise the spin specific data structures. 558 559 @param data_cont: The spin container. 560 @type data_cont: SpinContainer instance 561 @keyword sim: The Monte Carlo simulation flag, which if true will initialise the simulation data structure. 562 @type sim: bool 563 """ 564 565 # Loop over the data structure names. 566 for name in self.data_names(set='params'): 567 # Data structures which are initially empty arrays. 568 list_data = [ 'params' ] 569 if name in list_data: 570 init_data = [] 571 572 # Otherwise initialise the data structure to None. 573 else: 574 init_data = None 575 576 # If the name is not in 'data_cont', add it. 577 if not hasattr(data_cont, name): 578 setattr(data_cont, name, init_data)
579 580 581 default_value_doc = Desc_container("Relaxation curve fitting default values") 582 default_value_doc.add_paragraph("These values are completely arbitrary as peak heights (or volumes) are extremely variable and the Rx value is a compensation for both the R1 and R2 values.") 583 _table = uf_tables.add_table(label="table: curve-fit default values", caption="Relaxation curve fitting default values.") 584 _table.add_headings(["Data type", "Object name", "Value"]) 585 _table.add_row(["Relaxation rate", "'rx'", "8.0"]) 586 _table.add_row(["Initial intensity", "'i0'", "10000.0"]) 587 _table.add_row(["Intensity at infinity", "'iinf'", "0.0"]) 588 default_value_doc.add_table(_table.label) 589 590
591 - def grid_search(self, lower=None, upper=None, inc=None, constraints=True, verbosity=1, sim_index=None):
592 """The exponential curve fitting grid search method. 593 594 @keyword lower: The lower bounds of the grid search which must be equal to the number of parameters in the model. 595 @type lower: array of numbers 596 @keyword upper: The upper bounds of the grid search which must be equal to the number of parameters in the model. 597 @type upper: array of numbers 598 @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. 599 @type inc: array of int 600 @keyword constraints: If True, constraints are applied during the grid search (eliminating parts of the grid). If False, no constraints are used. 601 @type constraints: bool 602 @keyword verbosity: A flag specifying the amount of information to print. The higher the value, the greater the verbosity. 603 @type verbosity: int 604 @keyword sim_index: The index of the simulation to apply the grid search to. If None, the normal model is optimised. 605 @type sim_index: int 606 """ 607 608 # Minimisation. 609 self.minimise(min_algor='grid', lower=lower, upper=upper, inc=inc, constraints=constraints, verbosity=verbosity, sim_index=sim_index)
610 611
612 - 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):
613 """Relaxation curve fitting minimisation method. 614 615 @keyword min_algor: The minimisation algorithm to use. 616 @type min_algor: str 617 @keyword min_options: An array of options to be used by the minimisation algorithm. 618 @type min_options: array of str 619 @keyword func_tol: The function tolerance which, when reached, terminates optimisation. Setting this to None turns of the check. 620 @type func_tol: None or float 621 @keyword grad_tol: The gradient tolerance which, when reached, terminates optimisation. Setting this to None turns of the check. 622 @type grad_tol: None or float 623 @keyword max_iterations: The maximum number of iterations for the algorithm. 624 @type max_iterations: int 625 @keyword constraints: If True, constraints are used during optimisation. 626 @type constraints: bool 627 @keyword scaling: If True, diagonal scaling is enabled during optimisation to allow the problem to be better conditioned. 628 @type scaling: bool 629 @keyword verbosity: The amount of information to print. The higher the value, the greater the verbosity. 630 @type verbosity: int 631 @keyword sim_index: The index of the simulation to optimise. This should be None if normal optimisation is desired. 632 @type sim_index: None or int 633 @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. 634 @type lower: array of numbers 635 @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. 636 @type upper: array of numbers 637 @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. 638 @type inc: array of int 639 """ 640 641 # Test if sequence data is loaded. 642 if not exists_mol_res_spin_data(): 643 raise RelaxNoSequenceError 644 645 # Loop over the sequence. 646 for spin, mol_name, res_num, res_name in spin_loop(full_info=True): 647 # Skip deselected spins. 648 if not spin.select: 649 continue 650 651 # Skip spins which have no data. 652 if not hasattr(spin, 'intensities'): 653 continue 654 655 # Create the initial parameter vector. 656 param_vector = self._assemble_param_vector(spin=spin) 657 658 # Diagonal scaling. 659 scaling_matrix = self._assemble_scaling_matrix(spin=spin, scaling=scaling) 660 if len(scaling_matrix): 661 param_vector = dot(inv(scaling_matrix), param_vector) 662 663 # Get the grid search minimisation options. 664 if match('^[Gg]rid', min_algor): 665 inc, lower, upper = self._grid_search_setup(spin=spin, param_vector=param_vector, lower=lower, upper=upper, inc=inc, scaling_matrix=scaling_matrix) 666 667 # Linear constraints. 668 if constraints: 669 A, b = self._linear_constraints(spin=spin, scaling_matrix=scaling_matrix) 670 else: 671 A, b = None, None 672 673 # Print out. 674 if verbosity >= 1: 675 # Get the spin id string. 676 spin_id = generate_spin_id(mol_name, res_num, res_name, spin.num, spin.name) 677 678 # Individual spin printout. 679 if verbosity >= 2: 680 print("\n\n") 681 682 string = "Fitting to spin " + repr(spin_id) 683 print(("\n\n" + string)) 684 print((len(string) * '~')) 685 686 687 # Initialise the function to minimise. 688 ###################################### 689 690 # The keys. 691 keys = spin.intensities.keys() 692 693 # The peak intensities and times. 694 values = [] 695 errors = [] 696 times = [] 697 for key in keys: 698 # The values. 699 if sim_index == None: 700 values.append(spin.intensities[key]) 701 else: 702 values.append(spin.sim_intensities[sim_index][key]) 703 704 # The errors. 705 errors.append(spin.intensity_err[key]) 706 707 # The relaxation times. 708 times.append(cdp.relax_times[key]) 709 710 setup(num_params=len(spin.params), num_times=len(cdp.relax_times), values=values, sd=errors, relax_times=times, scaling_matrix=scaling_matrix.tolist()) 711 712 713 # Setup the minimisation algorithm when constraints are present. 714 ################################################################ 715 716 if constraints and not match('^[Gg]rid', min_algor): 717 algor = min_options[0] 718 else: 719 algor = min_algor 720 721 722 # Levenberg-Marquardt minimisation. 723 ################################### 724 725 if match('[Ll][Mm]$', algor) or match('[Ll]evenburg-[Mm]arquardt$', algor): 726 # Reconstruct the error data structure. 727 lm_error = zeros(len(spin.relax_times), float64) 728 index = 0 729 for k in xrange(len(spin.relax_times)): 730 lm_error[index:index+len(relax_error[k])] = relax_error[k] 731 index = index + len(relax_error[k]) 732 733 min_options = min_options + (self.relax_fit.lm_dri, lm_error) 734 735 736 # Minimisation. 737 ############### 738 739 # Grid search. 740 if search('^[Gg]rid', min_algor): 741 results = grid(func=self._func, args=(), num_incs=inc, lower=lower, upper=upper, A=A, b=b, verbosity=verbosity) 742 743 # Unpack the results. 744 param_vector, chi2, iter_count, warning = results 745 f_count = iter_count 746 g_count = 0.0 747 h_count = 0.0 748 749 # Minimisation. 750 else: 751 results = generic_minimise(func=self._func, dfunc=self._dfunc, d2func=self._d2func, 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) 752 753 # Unpack the results. 754 if results == None: 755 return 756 param_vector, chi2, iter_count, f_count, g_count, h_count, warning = results 757 758 # Scaling. 759 if scaling: 760 param_vector = dot(scaling_matrix, param_vector) 761 762 # Disassemble the parameter vector. 763 self._disassemble_param_vector(param_vector=param_vector, spin=spin, sim_index=sim_index) 764 765 # Monte Carlo minimisation statistics. 766 if sim_index != None: 767 # Chi-squared statistic. 768 spin.chi2_sim[sim_index] = chi2 769 770 # Iterations. 771 spin.iter_sim[sim_index] = iter_count 772 773 # Function evaluations. 774 spin.f_count_sim[sim_index] = f_count 775 776 # Gradient evaluations. 777 spin.g_count_sim[sim_index] = g_count 778 779 # Hessian evaluations. 780 spin.h_count_sim[sim_index] = h_count 781 782 # Warning. 783 spin.warning_sim[sim_index] = warning 784 785 786 # Normal statistics. 787 else: 788 # Chi-squared statistic. 789 spin.chi2 = chi2 790 791 # Iterations. 792 spin.iter = iter_count 793 794 # Function evaluations. 795 spin.f_count = f_count 796 797 # Gradient evaluations. 798 spin.g_count = g_count 799 800 # Hessian evaluations. 801 spin.h_count = h_count 802 803 # Warning. 804 spin.warning = warning
805 806
807 - def overfit_deselect(self):
808 """Deselect spins which have insufficient data to support minimisation.""" 809 810 # Print out. 811 print("\n\nOver-fit spin deselection.\n") 812 813 # Test the sequence data exists. 814 if not exists_mol_res_spin_data(): 815 raise RelaxNoSequenceError 816 817 # Loop over spin data. 818 for spin, spin_id in spin_loop(return_id=True): 819 # Skip deselected spins. 820 if not spin.select: 821 continue 822 823 # Check if data exists. 824 if not hasattr(spin, 'intensities'): 825 warn(RelaxDeselectWarning(spin_id, 'missing intensity data')) 826 spin.select = False 827 828 # Require 3 or more data points. 829 elif len(spin.intensities) < 3: 830 warn(RelaxDeselectWarning(spin_id, 'insufficient data, 3 or more data points are required')) 831 spin.select = False
832 833
834 - def return_data(self, spin):
835 """Function for returning the peak intensity data structure. 836 837 @param spin: The spin container. 838 @type spin: SpinContainer instance 839 @return: The peak intensity data structure. 840 @rtype: list of float 841 """ 842 843 return spin.intensities
844 845 846 return_data_name_doc = Desc_container("Relaxation curve fitting data type string matching patterns") 847 _table = uf_tables.add_table(label="table: curve-fit data type patterns", caption="Relaxation curve fitting data type string matching patterns.") 848 _table.add_headings(["Data type", "Object name"]) 849 _table.add_row(["Relaxation rate", "'rx'"]) 850 _table.add_row(["Peak intensities (series)", "'intensities'"]) 851 _table.add_row(["Initial intensity", "'i0'"]) 852 _table.add_row(["Intensity at infinity", "'iinf'"]) 853 _table.add_row(["Relaxation period times (series)", "'relax_times'"]) 854 return_data_name_doc.add_table(_table.label) 855 856
857 - def return_error(self, data_id):
858 """Return the standard deviation data structure. 859 860 @param data_id: The spin identification string, as yielded by the base_data_loop() generator 861 method. 862 @type data_id: str 863 @return: The standard deviation data structure. 864 @rtype: list of float 865 """ 866 867 # Get the spin container. 868 spin = return_spin(data_id) 869 870 # Return the error list. 871 return spin.intensity_err
872 873
874 - def return_units(self, param):
875 """Dummy method which returns None as the stats have no units. 876 877 @param param: The name of the parameter to return the units string for. 878 @type param: str 879 @return: Nothing. 880 @rtype: None 881 """ 882 883 # Unitless. 884 return None
885 886 887 set_doc = Desc_container("Relaxation curve fitting set details") 888 set_doc.add_paragraph("Only three parameters can be set, the relaxation rate (Rx), the initial intensity (I0), and the intensity at infinity (Iinf). Setting the parameter Iinf has no effect if the chosen model is that of the exponential curve which decays to zero.") 889 890
891 - def sim_pack_data(self, data_id, sim_data):
892 """Pack the Monte Carlo simulation data. 893 894 @param data_id: The spin identification string, as yielded by the base_data_loop() generator method. 895 @type data_id: str 896 @param sim_data: The Monte Carlo simulation data. 897 @type sim_data: list of float 898 """ 899 900 # Get the spin container. 901 spin = return_spin(data_id) 902 903 # Create the data structure. 904 spin.sim_intensities = sim_data
905