1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 from math import sqrt
24 from numpy import array, dot, float64, ones, rank, transpose, zeros
25
26
27 from float import isNaN
28 from maths_fns.alignment_tensor import dAi_dAxx, dAi_dAyy, dAi_dAxy, dAi_dAxz, dAi_dAyz, to_tensor
29 from maths_fns.chi2 import chi2, dchi2_element, d2chi2_element
30 from maths_fns.paramag_centre import vectors_single_centre, vectors_centre_per_state
31 from maths_fns.pcs import ave_pcs_tensor, ave_pcs_tensor_ddeltaij_dAmn, pcs_tensor
32 from maths_fns.rdc import ave_rdc_tensor, ave_rdc_tensor_dDij_dAmn, rdc_tensor
33 from maths_fns.rotation_matrix import euler_to_R_zyz
34 from physical_constants import pcs_constant
35 from relax_errors import RelaxError, RelaxImplementError
36
37
39 """Class containing the target function of the optimisation of the N-state model."""
40
41 - def __init__(self, model=None, N=None, init_params=None, probs=None, full_tensors=None, red_data=None, red_errors=None, full_in_ref_frame=None, fixed_tensors=None, pcs=None, pcs_errors=None, pcs_weights=None, rdcs=None, rdc_errors=None, rdc_weights=None, rdc_vect=None, temp=None, frq=None, dip_const=None, absolute_rdc=None, atomic_pos=None, paramag_centre=None, scaling_matrix=None, centre_fixed=True):
42 """Set up the class instance for optimisation.
43
44 The N-state models
45 ==================
46
47 All constant data required for the N-state model are initialised here. Depending on the base data used for optimisation, different data structures can be supplied. However a number of structures must be provided for the N-state model. These are:
48
49 - model, the type of N-state model. This can be '2-domain', 'population', or 'fixed'.
50 - N, the number of states (or structures).
51 - init_params, the initial parameter values.
52 - scaling_matrix, the matrix used for parameter scaling during optimisation.
53
54
55 2-domain N-state model
56 ----------------------
57
58 If the model type is set to '2-domain', then the following data structures should be supplied:
59
60 - full_tensors, the alignment tensors in matrix form.
61 - red_data, the alignment tensors in 5D form in a rank-1 array.
62 - red_errors, the alignment tensor errors in 5D form in a rank-1 array. This data is not obligatory.
63 - full_in_ref_frame, an array of flags specifying if the tensor in the reference frame is the full or reduced tensor.
64
65
66 The population N-state model
67 ============================
68
69 In this model, populations are optimised for each state. Additionally the alignment tensors for anisotropic data can also be optimised if they have not been supplied (through the full_tensors arg).
70
71
72 PCS base data
73 -------------
74
75 If pseudocontact shift data is to be used for optimisation, then the following should be supplied:
76
77 - pcs, the pseudocontact shifts.
78 - pcs_errors, the optional pseudocontact shift error values.
79 - temp, the temperatures of the experiments.
80 - frq, the frequencies of the experiments.
81
82
83 PCS and PRE base data
84 ---------------------
85
86 If either pseudocontact shift or PRE data is to be used for optimisation, then the following should be supplied:
87
88 - atomic_pos, the positions of all atoms.
89 - paramag_centre, the paramagnetic centre position.
90
91
92 RDC base data
93 -------------
94
95 If residual dipolar coupling data is to be used for optimisation, then the following should be supplied:
96
97 - rdcs, the residual dipolar couplings.
98 - rdc_errors, the optional residual dipolar coupling errors.
99 - rdc_vect, the interatomic vectors.
100 - dip_const, the dipolar constants.
101 - absolute_rdc, the flags specifying whether each RDC is signless.
102
103
104 @keyword model: The N-state model type. This can be one of '2-domain', 'population' or 'fixed'.
105 @type model: str
106 @keyword N: The number of states.
107 @type N: int
108 @keyword init_params: The initial parameter values. Optimisation must start at some point!
109 @type init_params: numpy float64 array
110 @keyword probs: The probabilities for each state c. The length of this list should be equal to N.
111 @type probs: list of float
112 @keyword full_tensors: An array of the {Sxx, Syy, Sxy, Sxz, Syz} values for all full tensors. The format is [Sxx1, Syy1, Sxy1, Sxz1, Syz1, Sxx2, Syy2, Sxy2, Sxz2, Syz2, ..., Sxxn, Syyn, Sxyn, Sxzn, Syzn]
113 @type full_tensors: list of rank-2, 3D numpy arrays
114 @keyword red_data: An array of the {Sxx, Syy, Sxy, Sxz, Syz} values for all reduced tensors. The format is the same as for full_tensors.
115 @type red_data: numpy float64 array
116 @keyword red_errors: An array of the {Sxx, Syy, Sxy, Sxz, Syz} errors for all reduced tensors. The array format is the same as for full_tensors.
117 @type red_errors: numpy float64 array
118 @keyword full_in_ref_frame: An array of flags specifying if the tensor in the reference frame is the full or reduced tensor.
119 @type full_in_ref_frame: numpy rank-1 array
120 @keyword fixed_tensors: An array of flags specifying if the tensor is fixed or will be optimised.
121 @type fixed_tensors: list of bool
122 @keyword pcs: The PCS lists. The first index must correspond to the different alignment media i and the second index to the spin systems j.
123 @type pcs: numpy rank-2 array
124 @keyword pcs_errors: The PCS error lists. The dimensions of this argument are the same as for 'pcs'.
125 @type pcs_errors: numpy rank-2 array
126 @keyword pcs_weights: The PCS weight lists. The dimensions of this argument are the same as for 'pcs'.
127 @type pcs_weights: numpy rank-2 array
128 @keyword rdcs: The RDC lists. The first index must correspond to the different alignment media i and the second index to the spin pairs k.
129 @type rdcs: numpy rank-2 array
130 @keyword rdc_errors: The RDC error lists. The dimensions of this argument are the same as for 'rdcs'.
131 @type rdc_errors: numpy rank-2 array
132 @keyword rdc_weights: The RDC weight lists. The dimensions of this argument are the same as for 'rdcs'.
133 @type rdc_weights: numpy rank-2 array
134 @keyword rdc_vect: The unit vector lists for the RDC. The first index must correspond to the spin pair and the second index to each structure (its size being equal to the number of states).
135 @type rdc_vect: numpy rank-2 array
136 @keyword temp: The temperature of each experiment, used for the PCS.
137 @type temp: numpy rank-1 array
138 @keyword frq: The frequency of each alignment, used for the PCS.
139 @type frq: numpy rank-1 array
140 @keyword dip_const: The dipolar constants for each spin pair. The indices correspond to the spin pairs k.
141 @type dip_const: numpy rank-1 array
142 @keyword absolute_rdc: The signless or absolute value flags for the RDCs.
143 @type absolute_rdc: numpy rank-2 array
144 @keyword atomic_pos: The atomic positions of all spins for the PCS and PRE data. The first index is the spin systems j and the second is the structure or state c.
145 @type atomic_pos: numpy rank-3 array
146 @keyword paramag_centre: The paramagnetic centre position (or positions).
147 @type paramag_centre: numpy rank-1, 3D array or rank-2, Nx3 array
148 @keyword scaling_matrix: The square and diagonal scaling matrix.
149 @type scaling_matrix: numpy rank-2 array
150 @keyword centre_fixed: A flag which if False will cause the paramagnetic centre to be optimised.
151 @type centre_fixed: bool
152 """
153
154
155 self.N = N
156 self.params = 1.0 * init_params
157 self.fixed_tensors = fixed_tensors
158 self.deltaij = pcs
159 self.Dij = rdcs
160 self.dip_vect = rdc_vect
161 self.dip_const = dip_const
162 self.absolute_rdc = absolute_rdc
163 self.temp = temp
164 self.frq = frq
165 self.atomic_pos = atomic_pos
166 self.paramag_centre = paramag_centre
167 self.centre_fixed = centre_fixed
168 self.total_num_params = len(init_params)
169
170
171 self.chi2 = 0.0
172 self.dchi2 = zeros((self.total_num_params), float64)
173 self.d2chi2 = zeros((self.total_num_params, self.total_num_params), float64)
174
175
176 self.scaling_matrix = scaling_matrix
177 if self.scaling_matrix != None:
178 self.scaling_flag = True
179 else:
180 self.scaling_flag = False
181
182
183 if model == '2-domain':
184
185 if red_data == None or not len(red_data):
186 raise RelaxError("The red_data argument " + repr(red_data) + " must be supplied.")
187 if red_errors == None or not len(red_errors):
188 raise RelaxError("The red_errors argument " + repr(red_errors) + " must be supplied.")
189 if full_in_ref_frame == None or not len(full_in_ref_frame):
190 raise RelaxError("The full_in_ref_frame argument " + repr(full_in_ref_frame) + " must be supplied.")
191
192
193 self.full_tensors = array(full_tensors, float64)
194 self.num_tensors = int(len(self.full_tensors) / 5)
195 self.red_data = red_data
196 self.red_errors = red_errors
197 self.full_in_ref_frame = full_in_ref_frame
198
199
200 self.A = zeros((self.num_tensors, 3, 3), float64)
201 for align_index in range(self.num_tensors):
202 to_tensor(self.A[align_index], self.full_tensors[5*align_index:5*align_index+5])
203
204
205
206
207
208
209 self.R = zeros((self.N, 3, 3), float64)
210 self.RT = zeros((self.N, 3, 3), float64)
211 self.red_bc = zeros((self.num_tensors, 3, 3), float64)
212 self.red_bc_vector = zeros(self.num_tensors*5, float64)
213
214
215 self.func = self.func_2domain
216 self.dfunc = None
217 self.d2func = None
218
219
220 elif model in ['population', 'fixed']:
221
222 self.num_align = 0
223 if rdcs != None:
224 self.num_align = len(rdcs)
225 elif pcs != None:
226 self.num_align = len(pcs)
227
228
229 self.rdc_flag = [True] * self.num_align
230 self.pcs_flag = [True] * self.num_align
231 for align_index in range(self.num_align):
232 if rdcs == None or len(rdcs[align_index]) == 0:
233 self.rdc_flag[align_index] = False
234 if pcs == None or len(pcs[align_index]) == 0:
235 self.pcs_flag[align_index] = False
236 self.rdc_flag_sum = sum(self.rdc_flag)
237 self.pcs_flag_sum = sum(self.pcs_flag)
238
239
240 if self.rdc_flag_sum and (rdc_vect == None or not len(rdc_vect)):
241 raise RelaxError("The rdc_vect argument " + repr(rdc_vect) + " must be supplied.")
242 if self.pcs_flag_sum and (atomic_pos == None or not len(atomic_pos)):
243 raise RelaxError("The atomic_pos argument " + repr(atomic_pos) + " must be supplied.")
244
245
246 self.num_spins = 0
247 if self.pcs_flag_sum:
248 self.num_spins = len(pcs[0])
249
250
251 self.num_interatom = 0
252 if self.rdc_flag_sum:
253 self.num_interatom = len(rdcs[0])
254
255
256 self.A = zeros((self.num_align, 3, 3), float64)
257 self.dA = zeros((5, 3, 3), float64)
258
259
260 if full_tensors != None:
261
262 self.full_tensors = array(full_tensors, float64)
263
264
265 self.num_align_params = 0
266 index = 0
267 for align_index in range(self.num_align):
268
269 if fixed_tensors[align_index]:
270 to_tensor(self.A[align_index], self.full_tensors[5*index:5*index+5])
271 index += 1
272
273
274 if not fixed_tensors[align_index]:
275 self.num_align_params += 5
276
277
278 else:
279
280 dAi_dAxx(self.dA[0])
281 dAi_dAyy(self.dA[1])
282 dAi_dAxy(self.dA[2])
283 dAi_dAxz(self.dA[3])
284 dAi_dAyz(self.dA[4])
285
286
287 if self.pcs_flag_sum:
288 err = False
289 for i in range(len(pcs_errors)):
290 for j in range(len(pcs_errors[i])):
291 if not isNaN(pcs_errors[i, j]):
292 err = True
293 if err:
294 self.pcs_sigma_ij = pcs_errors
295 else:
296
297 self.pcs_sigma_ij = 0.03 * 1e-6 * ones((self.num_align, self.num_spins), float64)
298
299
300 if self.rdc_flag_sum:
301 err = False
302 for i in range(len(rdc_errors)):
303 for j in range(len(rdc_errors[i])):
304 if not isNaN(rdc_errors[i, j]):
305 err = True
306 if err:
307 self.rdc_sigma_ij = rdc_errors
308 else:
309
310 self.rdc_sigma_ij = ones((self.num_align, self.num_interatom), float64)
311
312
313 if self.rdc_flag_sum:
314 self.missing_Dij = zeros((self.num_align, self.num_interatom), float64)
315
316
317 if self.pcs_flag_sum:
318 self.missing_deltaij = zeros((self.num_align, self.num_spins), float64)
319
320
321 if self.rdc_flag_sum or self.pcs_flag_sum:
322 for align_index in range(self.num_align):
323 if self.rdc_flag_sum:
324 for j in range(self.num_interatom):
325 if isNaN(self.Dij[align_index, j]):
326
327 self.missing_Dij[align_index, j] = 1
328
329
330 self.Dij[align_index, j] = 0.0
331
332
333 self.rdc_sigma_ij[align_index, j] = 1.0
334
335
336 rdc_weights[align_index, j] = 1.0
337
338
339 self.rdc_sigma_ij[align_index, j] = self.rdc_sigma_ij[align_index, j] / sqrt(rdc_weights[align_index, j])
340
341
342 if self.pcs_flag_sum:
343 for j in range(self.num_spins):
344 if isNaN(self.deltaij[align_index, j]):
345
346 self.missing_deltaij[align_index, j] = 1
347
348
349 self.deltaij[align_index, j] = 0.0
350
351
352 self.pcs_sigma_ij[align_index, j] = 1.0
353
354
355 pcs_weights[align_index, j] = 1.0
356
357
358 self.pcs_sigma_ij[align_index, j] = self.pcs_sigma_ij[align_index, j] / sqrt(pcs_weights[align_index, j])
359
360
361 if self.pcs_flag_sum:
362
363 self.paramag_unit_vect = zeros(atomic_pos.shape, float64)
364 self.paramag_dist = zeros((self.num_spins, self.N), float64)
365 self.pcs_const = zeros((self.num_align, self.num_spins, self.N), float64)
366 if self.paramag_centre == None:
367 self.paramag_centre = zeros(3, float64)
368
369
370 self.paramag_info()
371
372
373 self.deltaij_theta = zeros((self.num_align, self.num_spins), float64)
374 self.ddeltaij_theta = zeros((self.total_num_params, self.num_align, self.num_spins), float64)
375 self.d2deltaij_theta = zeros((self.total_num_params, self.total_num_params, self.num_align, self.num_spins), float64)
376
377
378 self.Dij_theta = zeros((self.num_align, self.num_interatom), float64)
379 self.dDij_theta = zeros((self.total_num_params, self.num_align, self.num_interatom), float64)
380 self.d2Dij_theta = zeros((self.total_num_params, self.total_num_params, self.num_align, self.num_interatom), float64)
381
382
383 if not self.centre_fixed:
384 self.func = self.func_population
385 self.dfunc = None
386 self.d2func = None
387
388
389 else:
390 self.func = self.func_population
391 self.dfunc = self.dfunc_population
392 self.d2func = self.d2func_population
393
394
395 if model == 'fixed':
396
397 self.func = self.func_tensor_opt
398 self.dfunc = self.dfunc_tensor_opt
399 self.d2func = self.d2func_tensor_opt
400
401
402 self.zero_hessian_rdc = zeros(self.num_interatom, float64)
403 self.zero_hessian_pcs = zeros(self.num_spins, float64)
404
405
406 if probs:
407 self.probs = probs
408
409
410 else:
411 self.probs = ones(self.N, float64) / self.N
412
413
414 - def func_2domain(self, params):
415 """The target function for optimisation of the 2-domain N-state model.
416
417 This function should be passed to the optimisation algorithm. It accepts, as an array, a vector of parameter values and, using these, returns the single chi-squared value corresponding to that coordinate in the parameter space. If no tensor errors are supplied, then the SSE (the sum of squares error) value is returned instead. The chi-squared is simply the SSE normalised to unit variance (the SSE divided by the error squared).
418
419 @param params: The vector of parameter values.
420 @type params: list of float
421 @return: The chi-squared or SSE value.
422 @rtype: float
423 """
424
425
426 if self.scaling_flag:
427 params = dot(params, self.scaling_matrix)
428
429
430 self.red_bc = self.red_bc * 0.0
431
432
433 for c in range(self.N):
434
435 euler_to_R_zyz(params[self.N-1+3*c], params[self.N-1+3*c+1], params[self.N-1+3*c+2], self.R[c])
436
437
438 self.RT[c] = transpose(self.R[c])
439
440
441 if c < self.N-1:
442 pc = params[c]
443
444
445 else:
446 pc = 1.0
447 for c2 in range(self.N-1):
448 pc = pc - params[c2]
449
450
451 for align_index in range(self.num_tensors):
452
453 if self.full_in_ref_frame[align_index]:
454 self.red_bc[align_index] = self.red_bc[align_index] + pc * dot(self.RT[c], dot(self.A[align_index], self.R[c]))
455
456
457 else:
458 self.red_bc[align_index] = self.red_bc[align_index] + pc * dot(self.R[c], dot(self.A[align_index], self.RT[c]))
459
460
461 for align_index in range(self.num_tensors):
462 self.red_bc_vector[5*align_index] = self.red_bc[align_index, 0, 0]
463 self.red_bc_vector[5*align_index+1] = self.red_bc[align_index, 1, 1]
464 self.red_bc_vector[5*align_index+2] = self.red_bc[align_index, 0, 1]
465 self.red_bc_vector[5*align_index+3] = self.red_bc[align_index, 0, 2]
466 self.red_bc_vector[5*align_index+4] = self.red_bc[align_index, 1, 2]
467
468
469 return chi2(self.red_data, self.red_bc_vector, self.red_errors)
470
471
473 """The target function for optimisation of the flexible population N-state model.
474
475 Description
476 ===========
477
478 This function should be passed to the optimisation algorithm. It accepts, as an array, a vector of parameter values and, using these, returns the single chi-squared value corresponding to that coordinate in the parameter space. If no RDC errors are supplied, then the SSE (the sum of squares error) value is returned instead. The chi-squared is simply the SSE normalised to unit variance (the SSE divided by the error squared).
479
480
481 Indices
482 =======
483
484 For this calculation, five indices are looped over and used in the various data structures. These include:
485 - i, the index over alignments,
486 - j, the index over spin systems,
487 - c, the index over the N-states (or over the structures),
488 - n, the index over the first dimension of the alignment tensor n = {x, y, z},
489 - m, the index over the second dimension of the alignment tensor m = {x, y, z}.
490
491
492 Equations
493 =========
494
495 To calculate the function value, a chain of equations are used. This includes the chi-squared equation and the RDC equation.
496
497
498 The chi-squared equation
499 ------------------------
500
501 The equations are::
502
503 ___
504 \ (Dij - Dij(theta)) ** 2
505 chi^2(theta) = > ----------------------- ,
506 /__ sigma_ij ** 2
507 ij
508
509 ___
510 \ (delta_ij - delta_ij(theta)) ** 2
511 chi^2(theta) = > --------------------------------- ,
512 /__ sigma_ij ** 2
513 ij
514
515 where:
516 - theta is the parameter vector,
517 - Dij are the measured RDCs for alignment i, spin j,
518 - Dij(theta) are the back calculated RDCs for alignment i, spin j,
519 - delta_ij are the measured PCSs for alignment i, spin j,
520 - delta_ij(theta) are the back calculated PCSs for alignment i, spin j,
521 - sigma_ij are the RDC or PCS errors.
522
523 Both chi-squared values sum.
524
525
526 The RDC equation
527 ----------------
528
529 The RDC equation is::
530
531 _N_
532 \ T
533 Dij(theta) = dj > pc . mu_jc . Ai . mu_jc,
534 /__
535 c=1
536
537 where:
538 - dj is the dipolar constant for spin j,
539 - N is the total number of states or structures,
540 - pc is the weight or probability associated with state c,
541 - mu_jc is the unit vector corresponding to spin j and state c,
542 - Ai is the alignment tensor.
543
544 The dipolar constant is henceforth defined as::
545
546 dj = 3 / (2pi) d',
547
548 where the factor of 2pi is to convert from units of rad.s^-1 to Hertz, the factor of 3 is associated with the alignment tensor and the pure dipolar constant in SI units is::
549
550 mu0 gI.gS.h_bar
551 d' = - --- ----------- ,
552 4pi r**3
553
554 where:
555 - mu0 is the permeability of free space,
556 - gI and gS are the gyromagnetic ratios of the I and S spins,
557 - h_bar is Dirac's constant which is equal to Planck's constant divided by 2pi,
558 - r is the distance between the two spins.
559
560
561 The PCS equation
562 ----------------
563
564 The PCS equation is::
565
566 _N_
567 \ T
568 delta_ij(theta) = > pc . dijc . mu_jc . Ai . mu_jc,
569 /__
570 c=1
571
572 where:
573 - djci is the PCS constant for spin j, state c and experiment or alignment i,
574 - N is the total number of states or structures,
575 - pc is the weight or probability associated with state c,
576 - mu_jc is the unit vector corresponding to spin j and state c,
577 - Ai is the alignment tensor.
578
579 The PCS constant is defined as::
580
581 mu0 15kT 1
582 dijc = --- ----- ---- ,
583 4pi Bo**2 r**3
584
585 where:
586 - mu0 is the permeability of free space,
587 - k is Boltzmann's constant,
588 - T is the absolute temperature (different for each experiment),
589 - Bo is the magnetic field strength (different for each experiment),
590 - r is the distance between the paramagnetic centre (electron spin) and the nuclear spin (different for each spin and state).
591
592
593 Stored data structures
594 ======================
595
596 There are a number of data structures calculated by this function and stored for subsequent use in the gradient and Hessian functions. This include the back calculated RDCs and the alignment tensors.
597
598 Dij(theta)
599 ----------
600
601 The back calculated RDCs. This is a rank-2 tensor with indices {i, j}.
602
603 delta_ij(theta)
604 ---------------
605
606 The back calculated PCS. This is a rank-2 tensor with indices {i, j}.
607
608 Ai
609 --
610
611 The alignment tensors. This is a rank-3 tensor with indices {i, n, m}.
612
613
614 @param params: The vector of parameter values.
615 @type params: numpy rank-1 array
616 @return: The chi-squared or SSE value.
617 @rtype: float
618 """
619
620
621 if self.scaling_flag:
622 params = dot(params, self.scaling_matrix)
623
624
625 chi2_sum = 0.0
626
627
628 if self.N > 1:
629 self.probs = params[-(self.N-1):]
630
631
632 if not self.centre_fixed:
633
634 self.paramag_centre = params[-3:]
635
636
637 self.paramag_info()
638
639
640 index = 0
641 for align_index in range(self.num_align):
642
643 if not self.fixed_tensors[align_index]:
644 to_tensor(self.A[align_index], params[5*index:5*index + 5])
645 index += 1
646
647
648 if self.rdc_flag[align_index]:
649
650 for j in range(self.num_interatom):
651
652 if not self.missing_Dij[align_index, j]:
653 self.Dij_theta[align_index, j] = ave_rdc_tensor(self.dip_const[j], self.dip_vect[j], self.N, self.A[align_index], weights=self.probs, absolute=self.absolute_rdc[align_index, j])
654
655
656 if self.pcs_flag[align_index]:
657
658 for j in range(self.num_spins):
659
660 if not self.missing_deltaij[align_index, j]:
661 self.deltaij_theta[align_index, j] = ave_pcs_tensor(self.pcs_const[align_index, j], self.paramag_unit_vect[j], self.N, self.A[align_index], weights=self.probs)
662
663
664 if self.rdc_flag[align_index]:
665 chi2_sum = chi2_sum + chi2(self.Dij[align_index], self.Dij_theta[align_index], self.rdc_sigma_ij[align_index])
666
667
668 if self.pcs_flag[align_index]:
669 chi2_sum = chi2_sum + chi2(self.deltaij[align_index], self.deltaij_theta[align_index], self.pcs_sigma_ij[align_index])
670
671
672 return chi2_sum
673
674
676 """The target function for optimisation of the alignment tensor from RDC and/or PCS data.
677
678 Description
679 ===========
680
681 This function should be passed to the optimisation algorithm. It accepts, as an array, a vector of parameter values and, using these, returns the single chi-squared value corresponding to that coordinate in the parameter space. If no RDC or PCS errors are supplied, then the SSE (the sum of squares error) value is returned instead. The chi-squared is simply the SSE normalised to unit variance (the SSE divided by the error squared).
682
683
684 Indices
685 =======
686
687 For this calculation, five indices are looped over and used in the various data structures. These include:
688 - i, the index over alignments,
689 - j, the index over spin systems,
690 - c, the index over the N-states (or over the structures),
691 - n, the index over the first dimension of the alignment tensor n = {x, y, z},
692 - m, the index over the second dimension of the alignment tensor m = {x, y, z}.
693
694
695 Equations
696 =========
697
698 To calculate the function value, a chain of equations are used. This includes the chi-squared equation and the RDC and PCS equations.
699
700
701 The chi-squared equation
702 ------------------------
703
704 The equations are::
705
706 ___
707 \ (Dij - Dij(theta)) ** 2
708 chi^2(theta) = > ----------------------- ,
709 /__ sigma_ij ** 2
710 ij
711
712 ___
713 \ (delta_ij - delta_ij(theta)) ** 2
714 chi^2(theta) = > --------------------------------- ,
715 /__ sigma_ij ** 2
716 ij
717
718 where:
719 - theta is the parameter vector,
720 - Dij are the measured RDCs for alignment i, spin j,
721 - Dij(theta) are the back calculated RDCs for alignment i, spin j,
722 - delta_ij are the measured PCSs for alignment i, spin j,
723 - delta_ij(theta) are the back calculated PCSs for alignment i, spin j,
724 - sigma_ij are the RDC or PCS errors.
725
726 Both chi-squared values sum.
727
728
729 The RDC equation
730 ----------------
731
732 The RDC equation is::
733
734 _N_
735 dj \ T
736 Dij(theta) = -- > mu_jc . Ai . mu_jc,
737 N /__
738 c=1
739
740 where:
741 - dj is the dipolar constant for spin j,
742 - N is the total number of states or structures,
743 - mu_jc is the unit vector corresponding to spin j and state c,
744 - Ai is the alignment tensor.
745
746 The dipolar constant is henceforth defined as::
747
748 dj = 3 / (2pi) d',
749
750 where the factor of 2pi is to convert from units of rad.s^-1 to Hertz, the factor of 3 is associated with the alignment tensor and the pure dipolar constant in SI units is::
751
752 mu0 gI.gS.h_bar
753 d' = - --- ----------- ,
754 4pi r**3
755
756 where:
757 - mu0 is the permeability of free space,
758 - gI and gS are the gyromagnetic ratios of the I and S spins,
759 - h_bar is Dirac's constant which is equal to Planck's constant divided by 2pi,
760 - r is the distance between the two spins.
761
762
763 The PCS equation
764 ----------------
765
766 The PCS equation is::
767
768 _N_
769 1 \ T
770 delta_ij(theta) = - > dijc . mu_jc . Ai . mu_jc,
771 N /__
772 c=1
773
774 where:
775 - djci is the PCS constant for spin j, state c and experiment or alignment i,
776 - N is the total number of states or structures,
777 - mu_jc is the unit vector corresponding to spin j and state c,
778 - Ai is the alignment tensor.
779
780 The PCS constant is defined as::
781
782 mu0 15kT 1
783 dijc = --- ----- ---- ,
784 4pi Bo**2 r**3
785
786 where:
787 - mu0 is the permeability of free space,
788 - k is Boltzmann's constant,
789 - T is the absolute temperature (different for each experiment),
790 - Bo is the magnetic field strength (different for each experiment),
791 - r is the distance between the paramagnetic centre (electron spin) and the nuclear spin (different for each spin and state).
792
793
794 Stored data structures
795 ======================
796
797 There are a number of data structures calculated by this function and stored for subsequent use in the gradient and Hessian functions. This include the back calculated RDCs and the alignment tensors.
798
799 Dij(theta)
800 ----------
801
802 The back calculated RDCs. This is a rank-2 tensor with indices {i, j}.
803
804 delta_ij(theta)
805 ---------------
806
807 The back calculated PCS. This is a rank-2 tensor with indices {i, j}.
808
809 Ai
810 --
811
812 The alignment tensors. This is a rank-3 tensor with indices {i, n, m}.
813
814
815 @param params: The vector of parameter values.
816 @type params: numpy rank-1 array
817 @return: The chi-squared or SSE value.
818 @rtype: float
819 """
820
821
822 if self.scaling_flag:
823 params = dot(params, self.scaling_matrix)
824
825
826 chi2_sum = 0.0
827
828
829 if not self.centre_fixed:
830
831 self.paramag_centre = params[-3:]
832
833
834 self.paramag_info()
835
836
837 index = 0
838 for align_index in range(self.num_align):
839
840 if not self.fixed_tensors[align_index]:
841 to_tensor(self.A[align_index], params[5*index:5*index + 5])
842 index += 1
843
844
845 if self.rdc_flag[align_index]:
846
847 for j in range(self.num_interatom):
848
849 if not self.missing_Dij[align_index, j]:
850 self.Dij_theta[align_index, j] = ave_rdc_tensor(self.dip_const[j], self.dip_vect[j], self.N, self.A[align_index], weights=self.probs, absolute=self.absolute_rdc[align_index, j])
851
852
853 if self.pcs_flag[align_index]:
854
855 for j in range(self.num_spins):
856
857 if not self.missing_deltaij[align_index, j]:
858 self.deltaij_theta[align_index, j] = ave_pcs_tensor(self.pcs_const[align_index, j], self.paramag_unit_vect[j], self.N, self.A[align_index], weights=self.probs)
859
860
861 if self.rdc_flag[align_index]:
862 chi2_sum = chi2_sum + chi2(self.Dij[align_index], self.Dij_theta[align_index], self.rdc_sigma_ij[align_index])
863
864
865 if self.pcs_flag[align_index]:
866 chi2_sum = chi2_sum + chi2(self.deltaij[align_index], self.deltaij_theta[align_index], self.pcs_sigma_ij[align_index])
867
868
869 return chi2_sum
870
871
873 """The gradient function for optimisation of the flexible population N-state model.
874
875 Description
876 ===========
877
878 This function should be passed to the optimisation algorithm. It accepts, as an array, a vector of parameter values and, using these, returns the chi-squared gradient corresponding to that coordinate in the parameter space. If no RDC errors are supplied, then the SSE (the sum of squares error) gradient is returned instead. The chi-squared gradient is simply the SSE gradient normalised to unit variance (the SSE divided by the error squared).
879
880
881 Indices
882 =======
883
884 For this calculation, six indices are looped over and used in the various data structures. These include:
885 - k, the index over all parameters,
886 - i, the index over alignments,
887 - j, the index over spin systems,
888 - c, the index over the N-states (or over the structures),
889 - m, the index over the first dimension of the alignment tensor m = {x, y, z}.
890 - n, the index over the second dimension of the alignment tensor n = {x, y, z},
891
892
893 Equations
894 =========
895
896 To calculate the chi-squared gradient, a chain of equations are used. This includes the chi-squared gradient, the RDC gradient and the alignment tensor gradient.
897
898
899 The chi-squared gradient
900 ------------------------
901
902 The equation is::
903 ___
904 dchi^2(theta) \ / Dij - Dij(theta) dDij(theta) \
905 ------------- = -2 > | ---------------- . ----------- |
906 dthetak /__ \ sigma_ij**2 dthetak /
907 ij
908
909 where:
910 - theta is the parameter vector,
911 - Dij are the measured RDCs or PCSs,
912 - Dij(theta) are the back calculated RDCs or PCSs,
913 - sigma_ij are the RDC or PCS errors,
914 - dDij(theta)/dthetak is the RDC or PCS gradient for parameter k.
915
916
917 The RDC gradient
918 ----------------
919
920 This gradient is different for the various parameter types.
921
922 pc partial derivative
923 ~~~~~~~~~~~~~~~~~~~~~
924
925 The population parameter partial derivative is::
926
927 dDij(theta) T
928 ----------- = dj . mu_jc . Ai . mu_jc,
929 dpc
930
931 where:
932 - dj is the dipolar constant for spin j,
933 - mu_jc is the unit vector corresponding to spin j and state c,
934 - Ai is the alignment tensor.
935
936 Amn partial derivative
937 ~~~~~~~~~~~~~~~~~~~~~~
938
939 The alignment tensor element partial derivative is::
940
941 _N_
942 dDij(theta) \ T dAi
943 ----------- = dj > pc . mu_jc . ---- . mu_jc,
944 dAmn /__ dAmn
945 c=1
946
947 where:
948 - dj is the dipolar constant for spin j,
949 - pc is the weight or probability associated with state c,
950 - mu_jc is the unit vector corresponding to spin j and state c,
951 - dAi/dAmn is the partial derivative of the alignment tensor with respect to element Amn.
952
953
954 The PCS gradient
955 ----------------
956
957 This gradient is also different for the various parameter types.
958
959 pc partial derivative
960 ~~~~~~~~~~~~~~~~~~~~~
961
962 The population parameter partial derivative is::
963
964 ddeltaij(theta) T
965 --------------- = dijc . mu_jc . Ai . mu_jc,
966 dpc
967
968 where:
969 - djc is the pseudocontact shift constant for spin j and state c,
970 - mu_jc is the unit vector corresponding to spin j and state c,
971 - Ai is the alignment tensor.
972
973 Amn partial derivative
974 ~~~~~~~~~~~~~~~~~~~~~~
975
976 The alignment tensor element partial derivative is::
977
978 _N_
979 ddelta_ij(theta) \ T dAi
980 ---------------- = > pc . djc . mu_jc . ---- . mu_jc,
981 dAmn /__ dAmn
982 c=1
983
984 where:
985 - djc is the pseudocontact shift constant for spin j and state c,
986 - pc is the weight or probability associated with state c,
987 - mu_jc is the unit vector corresponding to spin j and state c,
988 - dAi/dAmn is the partial derivative of the alignment tensor with respect to element Amn.
989
990
991 The alignment tensor gradient
992 -----------------------------
993
994 The five unique elements of the tensor {Axx, Ayy, Axy, Axz, Ayz} give five different partial derivatives. These are::
995
996 dAi | 1 0 0 |
997 ---- = | 0 0 0 |,
998 dAxx | 0 0 -1 |
999
1000 dAi | 0 0 0 |
1001 ---- = | 0 1 0 |,
1002 dAyy | 0 0 -1 |
1003
1004 dAi | 0 1 0 |
1005 ---- = | 1 0 0 |,
1006 dAxy | 0 0 0 |
1007
1008 dAi | 0 0 1 |
1009 ---- = | 0 0 0 |,
1010 dAxz | 1 0 0 |
1011
1012 dAi | 0 0 0 |
1013 ---- = | 0 0 1 |.
1014 dAyz | 0 1 0 |
1015
1016 As these are invariant, they can be pre-calculated.
1017
1018
1019 Stored data structures
1020 ======================
1021
1022 There are a number of data structures calculated by this function and stored for subsequent use in the Hessian function. This include the back calculated RDC and PCS gradients and the alignment tensor gradients.
1023
1024 dDij(theta)/dthetak
1025 -------------------
1026
1027 The back calculated RDC gradient. This is a rank-3 tensor with indices {k, i, j}.
1028
1029 ddeltaij(theta)/dthetak
1030 -----------------------
1031
1032 The back calculated PCS gradient. This is a rank-3 tensor with indices {k, i, j}.
1033
1034 dAi/dAmn
1035 --------
1036
1037 The alignment tensor gradients. This is a rank-3 tensor with indices {5, n, m}.
1038
1039
1040 @param params: The vector of parameter values. This is unused as it is assumed that
1041 func_population() was called first.
1042 @type params: numpy rank-1 array
1043 @return: The chi-squared or SSE gradient.
1044 @rtype: numpy rank-1 array
1045 """
1046
1047
1048 if self.scaling_flag:
1049 params = dot(params, self.scaling_matrix)
1050
1051
1052 self.dchi2 = self.dchi2 * 0.0
1053
1054
1055 index = 0
1056 for align_index in range(self.num_align):
1057
1058 if self.fixed_tensors[align_index]:
1059 continue
1060
1061
1062 if self.fixed_tensors[align_index] and self.rdc_flag[align_index] and not self.missing_Dij[align_index, j]:
1063 for j in range(self.num_interatom):
1064 self.dDij_theta[align_index*5, align_index, j] = ave_rdc_tensor_dDij_dAmn(self.dip_const[j], self.dip_vect[j], self.N, self.dA[0], weights=self.probs, absolute=self.absolute_rdc[align_index, j])
1065 self.dDij_theta[align_index*5+1, align_index, j] = ave_rdc_tensor_dDij_dAmn(self.dip_const[j], self.dip_vect[j], self.N, self.dA[1], weights=self.probs, absolute=self.absolute_rdc[align_index, j])
1066 self.dDij_theta[align_index*5+2, align_index, j] = ave_rdc_tensor_dDij_dAmn(self.dip_const[j], self.dip_vect[j], self.N, self.dA[2], weights=self.probs, absolute=self.absolute_rdc[align_index, j])
1067 self.dDij_theta[align_index*5+3, align_index, j] = ave_rdc_tensor_dDij_dAmn(self.dip_const[j], self.dip_vect[j], self.N, self.dA[3], weights=self.probs, absolute=self.absolute_rdc[align_index, j])
1068 self.dDij_theta[align_index*5+4, align_index, j] = ave_rdc_tensor_dDij_dAmn(self.dip_const[j], self.dip_vect[j], self.N, self.dA[4], weights=self.probs, absolute=self.absolute_rdc[align_index, j])
1069
1070
1071 if self.fixed_tensors[align_index] and self.pcs_flag[align_index] and not self.missing_deltaij[align_index, j]:
1072 for j in range(self.num_spins):
1073 self.ddeltaij_theta[align_index*5, align_index, j] = ave_pcs_tensor_ddeltaij_dAmn(self.pcs_const[align_index, j], self.paramag_unit_vect[j], self.N, self.dA[0], weights=self.probs)
1074 self.ddeltaij_theta[align_index*5+1, align_index, j] = ave_pcs_tensor_ddeltaij_dAmn(self.pcs_const[align_index, j], self.paramag_unit_vect[j], self.N, self.dA[1], weights=self.probs)
1075 self.ddeltaij_theta[align_index*5+2, align_index, j] = ave_pcs_tensor_ddeltaij_dAmn(self.pcs_const[align_index, j], self.paramag_unit_vect[j], self.N, self.dA[2], weights=self.probs)
1076 self.ddeltaij_theta[align_index*5+3, align_index, j] = ave_pcs_tensor_ddeltaij_dAmn(self.pcs_const[align_index, j], self.paramag_unit_vect[j], self.N, self.dA[3], weights=self.probs)
1077 self.ddeltaij_theta[align_index*5+4, align_index, j] = ave_pcs_tensor_ddeltaij_dAmn(self.pcs_const[align_index, j], self.paramag_unit_vect[j], self.N, self.dA[4], weights=self.probs)
1078
1079
1080 for c in range(self.N - 1):
1081
1082 param_index = self.num_align_params + c
1083
1084
1085 for j in range(self.num_interatom):
1086 if self.rdc_flag[align_index] and not self.missing_Dij[align_index, j]:
1087 self.dDij_theta[param_index, align_index, j] = rdc_tensor(self.dip_const[j], self.dip_vect[j, c], self.A[align_index], absolute=self.absolute_rdc[align_index, j])
1088
1089
1090 for j in range(self.num_spins):
1091 if self.pcs_flag[align_index] and not self.missing_deltaij[align_index, j]:
1092 self.ddeltaij_theta[param_index, align_index, j] = pcs_tensor(self.pcs_const[align_index, j, c], self.paramag_unit_vect[j, c], self.A[align_index])
1093
1094
1095 for k in range(self.total_num_params):
1096
1097 if self.rdc_flag[align_index]:
1098 self.dchi2[k] = self.dchi2[k] + dchi2_element(self.Dij[align_index], self.Dij_theta[align_index], self.dDij_theta[k, align_index], self.rdc_sigma_ij[align_index])
1099
1100
1101 if self.pcs_flag[align_index]:
1102 self.dchi2[k] = self.dchi2[k] + dchi2_element(self.deltaij[align_index], self.deltaij_theta[align_index], self.ddeltaij_theta[k, align_index], self.pcs_sigma_ij[align_index])
1103
1104
1105 index += 1
1106
1107
1108 if self.scaling_flag:
1109 self.dchi2 = dot(self.dchi2, self.scaling_matrix)
1110
1111
1112 return self.dchi2
1113
1114
1116 """The gradient function for optimisation of the alignment tensor from RDC and/or PCS data.
1117
1118 Description
1119 ===========
1120
1121 This function should be passed to the optimisation algorithm. It accepts, as an array, a vector of parameter values and, using these, returns the chi-squared gradient corresponding to that coordinate in the parameter space. If no RDC errors are supplied, then the SSE (the sum of squares error) gradient is returned instead. The chi-squared gradient is simply the SSE gradient normalised to unit variance (the SSE divided by the error squared).
1122
1123 Indices
1124 =======
1125
1126 For this calculation, six indices are looped over and used in the various data structures. These include:
1127 - k, the index over all parameters,
1128 - i, the index over alignments,
1129 - j, the index over spin systems,
1130 - c, the index over the N-states (or over the structures),
1131 - m, the index over the first dimension of the alignment tensor m = {x, y, z}.
1132 - n, the index over the second dimension of the alignment tensor n = {x, y, z},
1133
1134
1135 Equations
1136 =========
1137
1138 To calculate the chi-squared gradient, a chain of equations are used. This includes the chi-squared gradient, the RDC gradient and the alignment tensor gradient.
1139
1140
1141 The chi-squared gradient
1142 ------------------------
1143
1144 The equation is::
1145 ___
1146 dchi^2(theta) \ / Dij - Dij(theta) dDij(theta) \
1147 ------------- = -2 > | ---------------- . ----------- |
1148 dthetak /__ \ sigma_ij**2 dthetak /
1149 ij
1150
1151 where:
1152 - theta is the parameter vector,
1153 - Dij are the measured RDCs or PCSs,
1154 - Dij(theta) are the back calculated RDCs or PCSs,
1155 - sigma_ij are the RDC or PCS errors,
1156 - dDij(theta)/dthetak is the RDC or PCS gradient for parameter k.
1157
1158
1159 The RDC gradient
1160 ----------------
1161
1162 The only parameters are the tensor components.
1163
1164 Amn partial derivative
1165 ~~~~~~~~~~~~~~~~~~~~~~
1166
1167 The alignment tensor element partial derivative is::
1168
1169 _N_
1170 dDij(theta) dj \ T dAi
1171 ----------- = -- > mu_jc . ---- . mu_jc,
1172 dAmn N /__ dAmn
1173 c=1
1174
1175 where:
1176 - dj is the dipolar constant for spin j,
1177 - N is the total number of states or structures,
1178 - mu_jc is the unit vector corresponding to spin j and state c,
1179 - dAi/dAmn is the partial derivative of the alignment tensor with respect to element Amn.
1180
1181
1182 The PCS gradient
1183 ----------------
1184
1185 Amn partial derivative
1186 ~~~~~~~~~~~~~~~~~~~~~~
1187
1188 The alignment tensor element partial derivative is::
1189
1190 _N_
1191 ddelta_ij(theta) 1 \ T dAi
1192 ---------------- = - > djc . mu_jc . ---- . mu_jc,
1193 dAmn N /__ dAmn
1194 c=1
1195
1196 where:
1197 - djc is the pseudocontact shift constant for spin j and state c,
1198 - N is the total number of states or structures,
1199 - mu_jc is the unit vector corresponding to spin j and state c,
1200 - dAi/dAmn is the partial derivative of the alignment tensor with respect to element Amn.
1201
1202
1203 The alignment tensor gradient
1204 -----------------------------
1205
1206 The five unique elements of the tensor {Axx, Ayy, Axy, Axz, Ayz} give five different partial derivatives. These are::
1207
1208 dAi | 1 0 0 |
1209 ---- = | 0 0 0 |,
1210 dAxx | 0 0 -1 |
1211
1212 dAi | 0 0 0 |
1213 ---- = | 0 1 0 |,
1214 dAyy | 0 0 -1 |
1215
1216 dAi | 0 1 0 |
1217 ---- = | 1 0 0 |,
1218 dAxy | 0 0 0 |
1219
1220 dAi | 0 0 1 |
1221 ---- = | 0 0 0 |,
1222 dAxz | 1 0 0 |
1223
1224 dAi | 0 0 0 |
1225 ---- = | 0 0 1 |.
1226 dAyz | 0 1 0 |
1227
1228 As these are invariant, they can be pre-calculated.
1229
1230
1231 Stored data structures
1232 ======================
1233
1234 There are a number of data structures calculated by this function and stored for subsequent use in the Hessian function. This include the back calculated RDC and PCS gradients and the alignment tensor gradients.
1235
1236 dDij(theta)/dthetak
1237 -------------------
1238
1239 The back calculated RDC gradient. This is a rank-3 tensor with indices {k, i, j}.
1240
1241 ddeltaij(theta)/dthetak
1242 -----------------------
1243
1244 The back calculated PCS gradient. This is a rank-3 tensor with indices {k, i, j}.
1245
1246 dAi/dAmn
1247 --------
1248
1249 The alignment tensor gradients. This is a rank-3 tensor with indices {5, n, m}.
1250
1251
1252 @param params: The vector of parameter values. This is unused as it is assumed that func_population() was called first.
1253 @type params: numpy rank-1 array
1254 @return: The chi-squared or SSE gradient.
1255 @rtype: numpy rank-1 array
1256 """
1257
1258
1259 if self.scaling_flag:
1260 params = dot(params, self.scaling_matrix)
1261
1262
1263 self.dchi2 = self.dchi2 * 0.0
1264
1265
1266 index = 0
1267 for align_index in range(self.num_align):
1268
1269 if self.fixed_tensors[align_index]:
1270 continue
1271
1272
1273 for j in range(self.num_interatom):
1274 if self.rdc_flag[align_index] and not self.missing_Dij[align_index, j]:
1275 self.dDij_theta[index*5, index, j] = ave_rdc_tensor_dDij_dAmn(self.dip_const[j], self.dip_vect[j], self.N, self.dA[0], weights=self.probs, absolute=self.absolute_rdc[align_index, j])
1276 self.dDij_theta[index*5+1, index, j] = ave_rdc_tensor_dDij_dAmn(self.dip_const[j], self.dip_vect[j], self.N, self.dA[1], weights=self.probs, absolute=self.absolute_rdc[align_index, j])
1277 self.dDij_theta[index*5+2, index, j] = ave_rdc_tensor_dDij_dAmn(self.dip_const[j], self.dip_vect[j], self.N, self.dA[2], weights=self.probs, absolute=self.absolute_rdc[align_index, j])
1278 self.dDij_theta[index*5+3, index, j] = ave_rdc_tensor_dDij_dAmn(self.dip_const[j], self.dip_vect[j], self.N, self.dA[3], weights=self.probs, absolute=self.absolute_rdc[align_index, j])
1279 self.dDij_theta[index*5+4, index, j] = ave_rdc_tensor_dDij_dAmn(self.dip_const[j], self.dip_vect[j], self.N, self.dA[4], weights=self.probs, absolute=self.absolute_rdc[align_index, j])
1280
1281
1282 for j in range(self.num_spins):
1283 if self.pcs_flag[align_index] and not self.missing_deltaij[align_index, j]:
1284 self.ddeltaij_theta[index*5, index, j] = ave_pcs_tensor_ddeltaij_dAmn(self.pcs_const[index, j], self.paramag_unit_vect[j], self.N, self.dA[0], weights=self.probs)
1285 self.ddeltaij_theta[index*5+1, index, j] = ave_pcs_tensor_ddeltaij_dAmn(self.pcs_const[index, j], self.paramag_unit_vect[j], self.N, self.dA[1], weights=self.probs)
1286 self.ddeltaij_theta[index*5+2, index, j] = ave_pcs_tensor_ddeltaij_dAmn(self.pcs_const[index, j], self.paramag_unit_vect[j], self.N, self.dA[2], weights=self.probs)
1287 self.ddeltaij_theta[index*5+3, index, j] = ave_pcs_tensor_ddeltaij_dAmn(self.pcs_const[index, j], self.paramag_unit_vect[j], self.N, self.dA[3], weights=self.probs)
1288 self.ddeltaij_theta[index*5+4, index, j] = ave_pcs_tensor_ddeltaij_dAmn(self.pcs_const[index, j], self.paramag_unit_vect[j], self.N, self.dA[4], weights=self.probs)
1289
1290
1291 for k in range(self.total_num_params):
1292
1293 if self.rdc_flag[align_index]:
1294 self.dchi2[k] = self.dchi2[k] + dchi2_element(self.Dij[index], self.Dij_theta[index], self.dDij_theta[k, index], self.rdc_sigma_ij[index])
1295
1296
1297 if self.pcs_flag[align_index]:
1298 self.dchi2[k] = self.dchi2[k] + dchi2_element(self.deltaij[index], self.deltaij_theta[index], self.ddeltaij_theta[k, index], self.pcs_sigma_ij[index])
1299
1300
1301 index += 1
1302
1303
1304 if self.scaling_flag:
1305 self.dchi2 = dot(self.dchi2, self.scaling_matrix)
1306
1307
1308 return self.dchi2
1309
1310
1312 """The Hessian function for optimisation of the flexible population N-state model.
1313
1314 Description
1315 ===========
1316
1317 This function should be passed to the optimisation algorithm. It accepts, as an array, a vector of parameter values and, using these, returns the chi-squared Hessian corresponding to that coordinate in the parameter space. If no RDC/PCS errors are supplied, then the SSE (the sum of squares error) Hessian is returned instead. The chi-squared Hessian is simply the SSE Hessian normalised to unit variance (the SSE divided by the error squared).
1318
1319
1320 Indices
1321 =======
1322
1323 For this calculation, six indices are looped over and used in the various data structures. These include:
1324 - k, the index over all parameters,
1325 - i, the index over alignments,
1326 - j, the index over spin systems,
1327 - c, the index over the N-states (or over the structures),
1328 - m, the index over the first dimension of the alignment tensor m = {x, y, z}.
1329 - n, the index over the second dimension of the alignment tensor n = {x, y, z},
1330
1331
1332 Equations
1333 =========
1334
1335 To calculate the chi-squared gradient, a chain of equations are used. This includes the chi-squared gradient, the RDC gradient and the alignment tensor gradient.
1336
1337
1338 The chi-squared Hessian
1339 -----------------------
1340
1341 The equation is::
1342 ___
1343 d2chi^2(theta) \ 1 / dDij(theta) dDij(theta) d2Dij(theta) \
1344 --------------- = 2 > ---------- | ----------- . ----------- - (Dij-Dij(theta)) . --------------- |.
1345 dthetaj.dthetak /__ sigma_i**2 \ dthetaj dthetak dthetaj.dthetak /
1346 ij
1347
1348 where:
1349 - theta is the parameter vector,
1350 - Dij are the measured RDCs or PCSs,
1351 - Dij(theta) are the back calculated RDCs or PCSs,
1352 - sigma_ij are the RDC or PCS errors,
1353 - dDij(theta)/dthetak is the RDC or PCS gradient for parameter k.
1354 - d2Dij(theta)/dthetaj.dthetak is the RDC or PCS Hessian for parameters j and k.
1355
1356
1357 The RDC Hessian
1358 ---------------
1359
1360 pc-pd second partial derivatives
1361 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1362
1363 The probability parameter second partial derivative is::
1364
1365 d2Dij(theta)
1366 ------------ = 0.
1367 dpc.dpd
1368
1369
1370 pc-Anm second partial derivatives
1371 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1372
1373 The probability parameter-tensor element second partial derivative is::
1374
1375 d2Dij(theta) T dAi
1376 ------------ = dj . mu_jc . ---- . mu_jc.
1377 dpc.dAmn dAmn
1378
1379
1380 Amn-Aop second partial derivatives
1381 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1382
1383 The alignment tensor element second partial derivative is::
1384
1385 d2Dij(theta)
1386 ------------ = 0.
1387 dAmn.dAop
1388
1389
1390 The PCS Hessian
1391 ---------------
1392
1393 pc-pd second partial derivatives
1394 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1395
1396 The probability parameter second partial derivative is::
1397
1398 d2delta_ij(theta)
1399 ----------------- = 0.
1400 dpc.dpd
1401
1402
1403 pc-Anm second partial derivatives
1404 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1405
1406 The probability parameter-tensor element second partial derivative is::
1407
1408 d2delta_ij(theta) T dAi
1409 ----------------- = djc . mu_jc . ---- . mu_jc.
1410 dpc.dAmn dAmn
1411
1412
1413 Amn-Aop second partial derivatives
1414 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1415
1416 The alignment tensor element second partial derivative is::
1417
1418 d2delta_ij(theta)
1419 ----------------- = 0
1420 dAmn.dAop
1421
1422
1423 The alignment tensor Hessian
1424 ----------------------------
1425
1426 The five unique elements of the tensor {Axx, Ayy, Axy, Axz, Ayz} all have the same second partial derivative of::
1427
1428 d2Ai | 0 0 0 |
1429 --------- = | 0 0 0 |.
1430 dAmn.dAop | 0 0 0 |
1431
1432
1433 @param params: The vector of parameter values. This is unused as it is assumed that func_population() was called first.
1434 @type params: numpy rank-1 array
1435 @return: The chi-squared or SSE Hessian.
1436 @rtype: numpy rank-2 array
1437 """
1438
1439
1440 if self.scaling_flag:
1441 params = dot(params, self.scaling_matrix)
1442
1443
1444 self.d2chi2 = self.d2chi2 * 0.0
1445
1446
1447 index = 0
1448 for align_index in range(self.num_align):
1449
1450 if self.fixed_tensors[align_index]:
1451 continue
1452
1453
1454 for c in range(self.N - 1):
1455
1456 pc_index = self.num_align_params + c
1457
1458
1459 for j in range(self.num_interatom):
1460 if self.fixed_tensors[align_index] and self.rdc_flag[align_index] and not self.missing_Dij[align_index, j]:
1461 self.d2Dij_theta[pc_index, i*5+0, align_index, j] = self.d2Dij_theta[align_index*5+0, pc_index, align_index, j] = rdc_tensor(self.dip_const[j], self.dip_vect[j, c], self.dA[0], absolute=self.absolute_rdc[align_index, j])
1462 self.d2Dij_theta[pc_index, i*5+1, align_index, j] = self.d2Dij_theta[align_index*5+1, pc_index, align_index, j] = rdc_tensor(self.dip_const[j], self.dip_vect[j, c], self.dA[1], absolute=self.absolute_rdc[align_index, j])
1463 self.d2Dij_theta[pc_index, i*5+2, align_index, j] = self.d2Dij_theta[align_index*5+2, pc_index, align_index, j] = rdc_tensor(self.dip_const[j], self.dip_vect[j, c], self.dA[2], absolute=self.absolute_rdc[align_index, j])
1464 self.d2Dij_theta[pc_index, i*5+3, align_index, j] = self.d2Dij_theta[align_index*5+3, pc_index, align_index, j] = rdc_tensor(self.dip_const[j], self.dip_vect[j, c], self.dA[3], absolute=self.absolute_rdc[align_index, j])
1465 self.d2Dij_theta[pc_index, i*5+4, align_index, j] = self.d2Dij_theta[align_index*5+4, pc_index, align_index, j] = rdc_tensor(self.dip_const[j], self.dip_vect[j, c], self.dA[4], absolute=self.absolute_rdc[align_index, j])
1466
1467
1468 for j in range(self.num_spins):
1469 if self.fixed_tensors[align_index] and self.pcs_flag[align_index] and not self.missing_deltaij[align_index, j]:
1470 self.d2deltaij_theta[pc_index, i*5+0, align_index, j] = self.d2deltaij_theta[align_index*5+0, pc_index, align_index, j] = pcs_tensor(self.pcs_const[align_index, j, c], self.paramag_unit_vect[j, c], self.dA[0])
1471 self.d2deltaij_theta[pc_index, i*5+1, align_index, j] = self.d2deltaij_theta[align_index*5+1, pc_index, align_index, j] = pcs_tensor(self.pcs_const[align_index, j, c], self.paramag_unit_vect[j, c], self.dA[1])
1472 self.d2deltaij_theta[pc_index, i*5+2, align_index, j] = self.d2deltaij_theta[align_index*5+2, pc_index, align_index, j] = pcs_tensor(self.pcs_const[align_index, j, c], self.paramag_unit_vect[j, c], self.dA[2])
1473 self.d2deltaij_theta[pc_index, i*5+3, align_index, j] = self.d2deltaij_theta[align_index*5+3, pc_index, align_index, j] = pcs_tensor(self.pcs_const[align_index, j, c], self.paramag_unit_vect[j, c], self.dA[3])
1474 self.d2deltaij_theta[pc_index, i*5+4, align_index, j] = self.d2deltaij_theta[align_index*5+4, pc_index, align_index, j] = pcs_tensor(self.pcs_const[align_index, j, c], self.paramag_unit_vect[j, c], self.dA[4])
1475
1476
1477 index += 1
1478
1479
1480 for align_index in range(self.num_align):
1481
1482 for j in range(self.total_num_params):
1483 for k in range(self.total_num_params):
1484
1485 if self.fixed_tensors[align_index] and self.rdc_flag[align_index]:
1486 self.d2chi2[j, k] = self.d2chi2[j, k] + d2chi2_element(self.Dij[align_index], self.Dij_theta[align_index], self.dDij_theta[j, align_index], self.dDij_theta[k, align_index], self.d2Dij_theta[j, k, align_index], self.rdc_sigma_ij[align_index])
1487
1488
1489 if self.fixed_tensors[align_index] and self.pcs_flag[align_index]:
1490 self.d2chi2[j, k] = self.d2chi2[j, k] + d2chi2_element(self.deltaij[align_index], self.deltaij_theta[align_index], self.ddeltaij_theta[j, align_index], self.ddeltaij_theta[k, align_index], self.d2deltaij_theta[j, k, align_index], self.pcs_sigma_ij[align_index])
1491
1492
1493 if self.scaling_flag:
1494 self.d2chi2 = dot(self.d2chi2, self.scaling_matrix)
1495
1496
1497 return self.d2chi2
1498
1499
1501 """The Hessian function for optimisation of the alignment tensor from RDC and/or PCS data.
1502
1503 Description
1504 ===========
1505
1506 This function should be passed to the optimisation algorithm. It accepts, as an array, a vector of parameter values and, using these, returns the chi-squared Hessian corresponding to that coordinate in the parameter space. If no RDC/PCS errors are supplied, then the SSE (the sum of squares error) Hessian is returned instead. The chi-squared Hessian is simply the SSE Hessian normalised to unit variance (the SSE divided by the error squared).
1507
1508 Indices
1509 =======
1510
1511 For this calculation, six indices are looped over and used in the various data structures. These include:
1512 - k, the index over all parameters,
1513 - i, the index over alignments,
1514 - j, the index over spin systems,
1515 - c, the index over the N-states (or over the structures),
1516 - m, the index over the first dimension of the alignment tensor m = {x, y, z}.
1517 - n, the index over the second dimension of the alignment tensor n = {x, y, z},
1518
1519
1520 Equations
1521 =========
1522
1523 To calculate the chi-squared gradient, a chain of equations are used. This includes the chi-squared gradient, the RDC gradient and the alignment tensor gradient.
1524
1525
1526 The chi-squared Hessian
1527 -----------------------
1528
1529 The equation is::
1530 ___
1531 d2chi^2(theta) \ 1 / dDij(theta) dDij(theta) d2Dij(theta) \
1532 --------------- = 2 > ---------- | ----------- . ----------- - (Dij-Dij(theta)) . --------------- |.
1533 dthetaj.dthetak /__ sigma_i**2 \ dthetaj dthetak dthetaj.dthetak /
1534 ij
1535
1536 where:
1537 - theta is the parameter vector,
1538 - Dij are the measured RDCs or PCSs,
1539 - Dij(theta) are the back calculated RDCs or PCSs,
1540 - sigma_ij are the RDC or PCS errors,
1541 - dDij(theta)/dthetak is the RDC or PCS gradient for parameter k.
1542 - d2Dij(theta)/dthetaj.dthetak is the RDC or PCS Hessian for parameters j and k.
1543
1544
1545 The RDC Hessian
1546 ---------------
1547
1548 The only parameters are the tensor components.
1549
1550
1551 Amn-Aop second partial derivatives
1552 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1553
1554 The alignment tensor element second partial derivative is::
1555
1556 d2Dij(theta)
1557 ------------ = 0.
1558 dAmn.dAop
1559
1560
1561 The PCS Hessian
1562 ---------------
1563
1564 Amn-Aop second partial derivatives
1565 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1566
1567 The alignment tensor element second partial derivative is::
1568
1569 d2delta_ij(theta)
1570 ----------------- = 0
1571 dAmn.dAop
1572
1573
1574 The alignment tensor Hessian
1575 ----------------------------
1576
1577 The five unique elements of the tensor {Axx, Ayy, Axy, Axz, Ayz} all have the same second partial derivative of::
1578
1579 d2Ai | 0 0 0 |
1580 --------- = | 0 0 0 |.
1581 dAmn.dAop | 0 0 0 |
1582
1583
1584 @param params: The vector of parameter values. This is unused as it is assumed that func_population() was called first.
1585 @type params: numpy rank-1 array
1586 @return: The chi-squared or SSE Hessian.
1587 @rtype: numpy rank-2 array
1588 """
1589
1590
1591 if self.scaling_flag:
1592 params = dot(params, self.scaling_matrix)
1593
1594
1595 self.d2chi2 = self.d2chi2 * 0.0
1596
1597
1598 index = 0
1599 for align_index in range(self.num_align):
1600
1601 if self.fixed_tensors[align_index]:
1602 continue
1603
1604
1605 for j in range(self.total_num_params):
1606 for k in range(self.total_num_params):
1607
1608 if self.rdc_flag[align_index]:
1609 self.d2chi2[j, k] = self.d2chi2[j, k] + d2chi2_element(self.Dij[align_index], self.Dij_theta[align_index], self.dDij_theta[j, align_index], self.dDij_theta[k, align_index], self.zero_hessian_rdc, self.rdc_sigma_ij[align_index])
1610
1611
1612 if self.pcs_flag[align_index]:
1613 self.d2chi2[j, k] = self.d2chi2[j, k] + d2chi2_element(self.deltaij[align_index], self.deltaij_theta[align_index], self.ddeltaij_theta[j, align_index], self.ddeltaij_theta[k, align_index], self.zero_hessian_pcs, self.pcs_sigma_ij[align_index])
1614
1615
1616 index += 1
1617
1618
1619 if self.scaling_flag:
1620 self.d2chi2 = dot(self.d2chi2, self.scaling_matrix)
1621
1622
1623 return self.d2chi2
1624
1625
1627 """Calculate the paramagnetic centre to spin vectors, distances and constants."""
1628
1629
1630 if rank(self.paramag_centre) == 1:
1631 vectors_single_centre(self.atomic_pos, self.paramag_centre, self.paramag_unit_vect, self.paramag_dist)
1632 else:
1633 vectors_centre_per_state(self.atomic_pos, self.paramag_centre, self.paramag_unit_vect, self.paramag_dist)
1634
1635
1636 for align_index in range(self.num_align):
1637 for j in range(self.num_spins):
1638 for c in range(self.N):
1639 self.pcs_const[align_index, j, c] = pcs_constant(self.temp[align_index], self.frq[align_index], self.paramag_dist[j, c])
1640