Skip to content

resources

OptimalTx

OptimalTx(dof_ext: ndarray = None, zshift1: ndarray = None, zshift2: ndarray = None, max_z_value: float = 1.0, number_of_coordinates: int = 3)

Identifies the translation(s) that optimally satisfies two overlapping translational spaces

Parameters:

  • dof_ext (ndarray, default: None ) –

    The degrees of freedom that are external to the two translational spaces

  • zshift1 (ndarray, default: None ) –

    The translational shift of the first space

  • zshift2 (ndarray, default: None ) –

    The translational shift of the second space

  • max_z_value (float, default: 1.0 ) –

    The largest z-value considered acceptable

  • number_of_coordinates (int, default: 3 ) –

    The dimension of the search

Source code in symdesign/resources/__init__.py
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
def __init__(self, dof_ext: np.ndarray = None, zshift1: np.ndarray = None, zshift2: np.ndarray = None,
             max_z_value: float = 1., number_of_coordinates: int = 3):
    """Construct the instance

    Args:
        dof_ext: The degrees of freedom that are external to the two translational spaces
        zshift1: The translational shift of the first space
        zshift2: The translational shift of the second space
        max_z_value: The largest z-value considered acceptable
        number_of_coordinates: The dimension of the search
    """
    self.max_z_value = max_z_value
    self.number_of_coordinates = number_of_coordinates
    if dof_ext is None:  # Todo include np.zeros((1, 3)) like in SymEntry
        raise ValueError(f"Can't initialize {type(self.__name__)} without passing dof_ext")
    else:
        self.dof_ext = dof_ext  # External translational DOF with shape (number DOF external, 3)
    self.dof = self.dof_ext.copy()
    # logger.debug('self.dof', self.dof)
    self.zshift1 = zshift1  # internal translational DOF1
    self.zshift2 = zshift2  # internal translational DOF2
    self.dof9 = None
    self.dof9_t = None
    self.dof9t_dof9 = None

    # Add internal z-shift degrees of freedom to 9-dim arrays if they exist
    self.n_dof_internal = 0
    if self.zshift1 is not None:
        # logger.debug('self.zshift1', self.zshift1)
        self.dof = np.append(self.dof, -self.zshift1, axis=0)
        self.n_dof_internal += 1
    if self.zshift2 is not None:
        self.dof = np.append(self.dof, self.zshift2, axis=0)
        self.n_dof_internal += 1
    # logger.debug('self.dof', self.dof)

    # Get the length of the array as n_dof_external
    self.n_dof_external = len(self.dof_ext)
    self.n_dof = len(self.dof)
    if self.n_dof > 0:
        self.dof_convert9()
    else:
        raise ValueError(f"n_dof isn't set! Can't get the {self.__class__.__name__}"
                         " without passing dof_ext, zshift1, or zshift2")

dof_convert9

dof_convert9()

Convert input degrees of freedom to 9-dim arrays. Repeat DOF ext for each set of 3 coordinates (3 sets)

Source code in symdesign/resources/__init__.py
71
72
73
74
75
76
77
78
79
80
81
def dof_convert9(self):
    """Convert input degrees of freedom to 9-dim arrays. Repeat DOF ext for each set of 3 coordinates (3 sets)"""
    self.dof9_t = np.zeros((self.n_dof, 9))
    for dof_idx in range(self.n_dof):
        # self.dof9_t[dof_idx] = np.array(self.number_of_coordinates * [self.dof[dof_idx]]).flatten()
        self.dof9_t[dof_idx] = np.tile(self.dof[dof_idx], self.number_of_coordinates)
        # dof[dof_idx] = (np.array(3 * [self.dof_ext[dof_idx]])).flatten()
    # logger.debug('self.dof9_t', self.dof9_t)
    self.dof9 = np.transpose(self.dof9_t)
    # logger.debug('self.dof9', self.dof9)
    self.dof9t_dof9 = np.matmul(self.dof9_t, self.dof9)

solve_optimal_shift

solve_optimal_shift(coords1: ndarray, coords2: ndarray, coords_rmsd_reference: float) -> ndarray | None

This routine solves the optimal shift problem for overlapping a pair of coordinates and comparing to a reference RMSD to compute an error

Parameters:

  • coords1 (ndarray) –

    A 3 x 3 array with cartesian coordinates

  • coords2 (ndarray) –

    A 3 x 3 array with cartesian coordinates

  • coords_rmsd_reference (float) –

    The reference deviation to compare to the coords1 and coords2 error

Returns: Returns the optimal translation or None if error is too large. Optimal translation has external dof first, followed by internal tx dof

Source code in symdesign/resources/__init__.py
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
def solve_optimal_shift(self, coords1: np.ndarray, coords2: np.ndarray, coords_rmsd_reference: float) -> \
        np.ndarray | None:
    """This routine solves the optimal shift problem for overlapping a pair of coordinates and comparing to a
    reference RMSD to compute an error

    Args:
        coords1: A 3 x 3 array with cartesian coordinates
        coords2: A 3 x 3 array with cartesian coordinates
        coords_rmsd_reference: The reference deviation to compare to the coords1 and coords2 error
    Returns:
        Returns the optimal translation or None if error is too large.
            Optimal translation has external dof first, followed by internal tx dof
    """
    # form the guide coords into a matrix (column vectors)
    # guide_target_10 = np.transpose(coords1)
    # guide_query_10 = np.transpose(coords2)

    # calculate the initial difference between query and target (9 dim vector)
    # With the transpose and the flatten, it could be accomplished by normal flatten!
    guide_delta = np.transpose([coords1.flatten() - coords2.flatten()])
    # flatten column vector matrix above [[x, y, z], [x, y, z], [x, y, z]] -> [x, y, z, x, y, z, x, y, z], then T

    # # isotropic case based on simple rmsd
    # | var_tot_inv = np.zeros([9, 9])
    # | for i in range(9):
    # |     # fill in var_tot_inv with 1/ 3x the mean squared deviation (deviation sum)
    # |     var_tot_inv[i, i] = 1. / (float(self.number_of_coordinates) * coords_rmsd_reference ** 2)
    # can be simplified to just use the scalar
    var_tot = float(self.number_of_coordinates) * coords_rmsd_reference ** 2

    # solve the problem using 9-dim degrees of freedom arrays
    # self.dof9 is column major (9 x n_dof_ext) degree of freedom matrix
    # self.dof9_t transpose (row major: n_dof_ext x 9)
    # below is degrees_of_freedom / variance
    # | dinvv = np.matmul(var_tot_inv, self.dof9)  # 1/variance (9 x 9) x degree of freedom (9 x n_dof) = (9 x n_dof)
    # dinvv = self.dof9 / var_tot
    # below, each i, i is the (individual_dof^2) * 3 / variance. i, j is the (covariance of i and jdof * 3) / variance
    # | vtdinvv = np.matmul(self.dof9_t, dinvv)  # transpose of degrees of freedom (n_dof x 9) x (9 x n_dof) = (n_dof x n_dof)
    # above could be simplifed to vtdinvv = np.matmul(self.dof9_t, self.dof9) / var_tot_inv  # first part same for each guide coord
    # now done below
    # vtdinvv = np.matmul(self.dof9_t, self.dof9) / var_tot  # transpose of degrees of freedom (n_dof x 9) x (9 x n_dof) = (n_dof x n_dof)
    vtdinvv = self.dof9t_dof9 / var_tot  # transpose of degrees of freedom (n_dof x 9) x (9 x n_dof) = (n_dof x n_dof)
    vtdinvvinv = np.linalg.inv(vtdinvv)  # Inverse of above - (n_dof x n_dof)
    # below is guide atom difference / variance
    # | dinvdelta = np.matmul(var_tot_inv, guide_delta)  # 1/variance (9 x 9) x guide atom diff (9 x 1) = (9 x 1)
    # dinvdelta = guide_delta / var_tot
    # below is essentially (SUM(dof basis * guide atom basis difference) for each guide atom) /variance by each DOF
    # | vtdinvdelta = np.matmul(self.dof9_t, dinvdelta)  # transpose of degrees of freedom (n_dof x 9) x (9 x 1) = (n_dof x 1)
    vtdinvdelta = np.matmul(self.dof9_t, guide_delta) / var_tot  # transpose of degrees of freedom (n_dof x 9) x (9 x 1) = (n_dof x 1)

    # below is inverse dof covariance matrix/variance * dof guide_atom_delta sum / variance
    # | shift = np.matmul(vtdinvvinv, vtdinvdelta)  # (n_dof x n_dof) x (n_dof x 1) = (n_dof x 1)
    shift = np.matmul(vtdinvvinv, vtdinvdelta)  # (n_dof x n_dof) x (n_dof x 1) = (n_dof x 1)

    # get error value from the ideal translation and the delta
    resid = np.matmul(self.dof9, shift) - guide_delta  # (9 x n_dof) x (n_dof x 1) - (9 x 1) = (9 x 1)
    error = \
        np.sqrt(np.matmul(np.transpose(resid), resid) / float(self.number_of_coordinates)) / coords_rmsd_reference
    # NEW. Is float(3.0) a scale?
    # OLD. sqrt(variance / 3) / cluster_rmsd

    if error <= self.max_z_value:
        return shift[:, 0]  # .tolist()  # , error
    else:
        return None

solve_optimal_shifts

solve_optimal_shifts(coords1: ndarray, coords2: ndarray, coords_rmsd_reference: ndarray) -> ndarray

This routine solves the optimal shift problem for overlapping a pair of coordinates and comparing to a reference RMSD to compute an error

Parameters:

  • coords1 (ndarray) –

    A N x 3 x 3 array with cartesian coordinates

  • coords2 (ndarray) –

    A N x 3 x 3 array with cartesian coordinates

  • coords_rmsd_reference (ndarray) –

    Array with length N with reference deviation to compare to the coords1 and coords2 error

Returns: Returns the optimal translations with shape (N, number_degrees_of_freedom) if the translation is less than the calculated error. Axis 1 has degrees of freedom with external first, then internal dof

Source code in symdesign/resources/__init__.py
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
def solve_optimal_shifts(self, coords1: np.ndarray, coords2: np.ndarray, coords_rmsd_reference: np.ndarray) -> \
        np.ndarray:
    """This routine solves the optimal shift problem for overlapping a pair of coordinates and comparing to a
    reference RMSD to compute an error

    Args:
        coords1: A N x 3 x 3 array with cartesian coordinates
        coords2: A N x 3 x 3 array with cartesian coordinates
        coords_rmsd_reference: Array with length N with reference deviation to compare to the coords1 and coords2
            error
    Returns:
        Returns the optimal translations with shape (N, number_degrees_of_freedom) if the translation is less than
            the calculated error. Axis 1 has degrees of freedom with external first, then internal dof
    """
    # calculate the initial difference between each query and target (9 dim vector by coords.shape[0])
    guide_delta = (coords1 - coords2).reshape(-1, 1, 9).swapaxes(-2, -1)
    # flatten column vector matrix above [[x, y, z], [x, y, z], [x, y, z]] -> [x, y, z, x, y, z, x, y, z], then T
    # # isotropic case based on simple rmsd
    # | var_tot_inv = np.zeros([9, 9])
    # | for i in range(9):
    # |     # fill in var_tot_inv with 1/ 3x the mean squared deviation (deviation sum)
    # |     var_tot_inv[i, i] = 1. / (float(self.number_of_coordinates) * coords_rmsd_reference ** 2)
    # can be simplified to just use the scalar
    var_tot = (float(self.number_of_coordinates) * coords_rmsd_reference ** 2).reshape(-1, 1, 1)

    # solve the problem using 9-dim degrees of freedom arrays
    # self.dof9 is column major (9 x n_dof_ext) degree of freedom matrix
    # self.dof9_t transpose (row major: n_dof_ext x 9)
    # below is degrees_of_freedom / variance
    # | dinvv = np.matmul(var_tot_inv, self.dof9)  # 1/variance (9 x 9) x degree of freedom (9 x n_dof) = (9 x n_dof)
    # dinvv = self.dof9 / var_tot
    # below, each i, i is the (individual_dof^2) * 3 / variance. i, j is the (covariance of i and jdof * 3) / variance
    # | vtdinvv = np.matmul(self.dof9_t, dinvv)  # transpose of degrees of freedom (n_dof x 9) x (9 x n_dof) = (n_dof x n_dof)
    # above could be simplifed to vtdinvv = np.matmul(self.dof9_t, self.dof9) / var_tot_inv  # first part same for each guide coord
    # now done below
    # vtdinvv = np.matmul(self.dof9_t, self.dof9) / var_tot  # transpose of degrees of freedom (n_dof x 9) x (9 x n_dof) = (n_dof x n_dof)
    # vtdinvv = np.tile(self.dof9t_dof9, (coords1.shape[0], 1, 1)) / var_tot  # transpose of degrees of freedom (n_dof x 9) x (9 x n_dof) = (n_dof x n_dof)

    # vtdinvvinv = np.linalg.inv(vtdinvv)  # Inverse of above - (n_dof x n_dof)
    # below is guide atom difference / variance
    # | dinvdelta = np.matmul(var_tot_inv, guide_delta)  # 1/variance (9 x 9) x guide atom diff (9 x 1) = (9 x 1)
    # dinvdelta = guide_delta / var_tot
    # below is essentially (SUM(dof basis * guide atom basis difference) for each guide atom) /variance by each DOF
    # | vtdinvdelta = np.matmul(self.dof9_t, dinvdelta)  # transpose of degrees of freedom (n_dof x 9) x (9 x 1) = (n_dof x 1)
    # vtdinvdelta = np.matmul(np.tile(self.dof9_t, (coords1.shape[0], 1, 1)), guide_delta) / var_tot  # transpose of degrees of freedom (n_dof x 9) x (9 x 1) = (n_dof x 1)

    # below is inverse dof covariance matrix/variance * dof guide_atom_delta sum / variance
    # shift = np.matmul(vtdinvvinv, vtdinvdelta)  # (n_dof x n_dof) x (n_dof x 1) = (n_dof x 1)
    # print('self.dof9t_dof9', self.dof9t_dof9)
    # print('tiled_array', np.tile(self.dof9t_dof9, (coords1.shape[0], 1, 1)))
    coords1_len = len(coords1)
    shift = np.matmul(np.linalg.inv(np.tile(self.dof9t_dof9, (coords1_len, 1, 1)) / var_tot),
                      np.matmul(np.tile(self.dof9_t, (coords1_len, 1, 1)), guide_delta) / var_tot)  # (n_dof x n_dof) x (n_dof x 1) = (n_dof x 1)

    # get error value from the ideal translation and the delta
    resid = np.matmul(np.tile(self.dof9, (coords1_len, 1, 1)), shift) - guide_delta  # (9 x n_dof) x (n_dof x 1) - (9 x 1) = (9 x 1)
    error = np.sqrt(np.matmul(resid.swapaxes(-2, -1), resid) / float(self.number_of_coordinates)).flatten() \
        / coords_rmsd_reference

    return shift[np.nonzero(error <= self.max_z_value)].reshape(-1, self.n_dof)