2
* This program is for 3D superposition of sets of coordinates.
3
* It is based on the quaternions, as proposed by:
4
* Zuker & Somorjai, Bulletin of Mathematical Biology,
5
* vol. 51, No 1, p 55-78, 1989.
7
* The search for the largest eigen value is based on a QR, and not
8
* on the Jacobian based solvation approach used by most programmers.
10
* In our experience, the routine is very robust.
12
* If you use this program standalone, the call should be on the form:
13
* QBestFit <-bfxyz, -bfpdb> < crdfile.xyz > fitcrd.xyz
16
* first line: N, the number of coordinates
17
* following 2 x N lines: N coordinates (x y z, e.g. "0.134 2.345 12.3245", i.e. no commas)
18
* the first N xyz lines will be the template, the last N xyz lines will
19
* undergo the best fit transformation.
22
* first line: original rmsd, before fit.
23
* second line : bestfit rmsd.
24
* nextt 4 lines: the 4x4 transformation matrix
25
* N following lines: the N coordinates transformed.
27
* If you want it directly, the main function is:
29
* zuker_superpose(DtPoint3 *c1, DtPoint3 *c2, int len, DtMatrix4x4 M)
31
* This program can be freely used, modified and distributed, given that this
32
* header is maintained.
34
* If using this routine for publication, please cite F. Guyon and P. Tuffery.
44
#define DtFloat double /* Le type flottant. */
45
typedef DtFloat DtPoint3[3];
46
typedef DtFloat DtPoint4[4];
47
typedef DtFloat DtMatrix3x3[3][3];
48
typedef DtFloat DtMatrix4x4[4][4];
51
#define boucle(i,min,max) for((i)=(min);(i)<(max);(i)++)
52
#define max(a,b) (a<b?b:a)
53
#define min(a,b) (a<b?a:b)
54
#define sign(a,b) ((b)>=0.0 ? fabs(a) : -fabs(a))
58
double **alloc_mat(int n, int m) {
60
double **mat = (double **)calloc(m,sizeof(double *));
62
mat[i]=(double *)calloc(n, sizeof(double));
66
void free_mat(double **mat, int n){
73
void print_matrice(double **a, int n, int m, char *name){
78
printf ("%s[%d,%d]=%lf ", name, i, j, a[i][j]);
83
void print_vector(double *a, int n, char *name){
87
printf ("%s[%d]=%lf\n ", name, i, a[i]);
91
double *random_vect(int n) {
93
double *v=(double *) calloc(n, sizeof(double));
95
v[i]= (double)rand()/(RAND_MAX+1.0);
99
static double inner(double *x, double *y, int n) {
103
for (sum=0, i=0; i<n; sum+=x[i]*y[i],i++);
108
static double *product(double **A, double *x, int n) {
111
double *y=(double *)calloc(n, sizeof(double));
113
for (i=0; i<n; i++) {
122
static int lu_c (double a[4][4], int n)
129
while (err==1 && k<n) {
131
if(fabs(pivot)>=EPS) {
135
a[i][j] -= coef*a[k][j];
142
if(a[n-1][n-1]==0) err=0;
147
static void resol_lu(double a[4][4], double *b, int n)
159
b[n-1]=y[n-1]/a[n-1][n-1];
160
for(i=n-1;i>=0;i--) {
168
int power(double *a[], int n, int maxiter, double eps, double *v, double *w) {
171
double sum, l, normy, d;
175
normy=sqrt(inner(y,y,n));
176
for (i=0; i<n; i++) w[i]=y[i]/normy;
180
for (sum=0,i=0; i<n; i++) {
185
} while (d>eps*fabs(l) && niter<maxiter);
191
double best_shift(double *a[], int n) {
196
for (i=1; i<n; i++) t=max(t, a[i][i]);
199
for (i=0; i<n; i++) {
200
for (sum=0,j=0; j<n; j++)
201
if (j!=i) sum+=fabs(a[i][j]);
202
t=min(t, a[i][i]-sum);
211
double lmax_estim (double a[4][4], int n) {
215
for (i=0; i<n; i++) {
216
for (sum=0,j=0; j<n; j++)
217
if (j!=i) sum+=fabs(a[i][j]);
218
t=max(t, a[i][i]+sum);
224
int shift_power(double *a, int n, int maxiter, double eps, double *v, double *w) {
230
/* fprintf(stderr,"ESSAI avec shift_power.\n"); */
233
/* copyMat(a, tmp, n, n); */
234
for (i=0; i<n; i++) {
235
for (j=0; j<n; j++) {
236
tmp[i][j] = a[i*n+j];
239
sh=best_shift(tmp, n);
241
niter=power(tmp, n, maxiter, eps, v, w);
247
int inverse_power(double a[4][4], int n, int maxiter, double eps, double *v, double *w) {
250
double r, sum, l, normy, d;
255
for (i=0; i<n; i++) a[i][i]=a[i][i]-r;
257
/* fprintf(stderr,"ATTENTION ! cas singulier de inverse_power.\n"); */
264
normy=sqrt(inner(y,y,n));
265
for (i=0; i<n; i++) {
272
for (sum=0,i=0; i<n; i++) {
277
} while (d>eps*fabs(l) && niter<maxiter);
283
int largestEV4(double R[4][4], double v[4], double *vp)
291
memcpy(M2,R,sizeof(double)*16);
293
rs = inverse_power(R, 4, 10000, 1.e-8, vp, v);
296
return shift_power(&M2[0][0], 4, 10000, 1.e-8, vp, v);
303
/* ==================================================================
304
* Calcule la matrice d'inertie des points
306
* id : indices des atomes dans At
307
* from,tto: indices extremes
309
* ==================================================================
311
DtMatrix3x3 *XYCov(DtMatrix3x3 *pM,
323
/* fprintf(stdout,"inertia, len %d\n",aSze); */
326
Xmean[0] = Xmean[1] = Xmean[2] = 0.;
327
for (i=0;i<aSze;i++) {
333
daSze = (double) aSze;
340
Ymean[0] = Ymean[1] = Ymean[2] = 0.;
341
for (i=0;i<aSze;i++) {
347
daSze = (double) aSze;
353
/* Covariance matrix */
355
pM = (DtMatrix3x3 *) calloc(1,sizeof(DtMatrix3x3));
357
memset((void *) pM, 0, sizeof(DtMatrix3x3));
360
for (i=0;i<aSze;i++) {
361
Xx = (double) X[i][0] - Xmean[0];
362
Xy = (double) X[i][1] - Xmean[1];
363
Xz = (double) X[i][2] - Xmean[2];
364
Yx = (double) Y[i][0] - Ymean[0];
365
Yy = (double) Y[i][1] - Ymean[1];
366
Yz = (double) Y[i][2] - Ymean[2];
368
(*pM)[0][0] += Xx*Yx;
369
(*pM)[0][1] += Xx*Yy;
370
(*pM)[0][2] += Xx*Yz;
372
(*pM)[1][0] += Xy*Yx;
373
(*pM)[1][1] += Xy*Yy;
374
(*pM)[1][2] += Xy*Yz;
376
(*pM)[2][0] += Xz*Yx;
377
(*pM)[2][1] += Xz*Yy;
378
(*pM)[2][2] += Xz*Yz;
384
/* ------------------------------------------------------------------
385
Matrice 4x4 Translation
386
---------- PREMULTIPLIE -> Y = XM (X vecteur ligne) -----------
387
------------------------------------------------------------------ */
388
void MkTrnsIIMat4x4(DtMatrix4x4 m, DtPoint3 tr)
390
m[0][0] = 1.; m[0][1] = 0.; m[0][2] = 0.; m[0][3] = 0.;
391
m[1][0] = 0.; m[1][1] = 1.; m[1][2] = 0.; m[1][3] = 0.;
392
m[2][0] = 0.; m[2][1] = 0.; m[2][2] = 1.; m[2][3] = 0.;
393
m[3][0] = tr[0]; m[3][1] = tr[1]; m[3][2] = tr[2]; m[3][3] = 1.;
396
/* Matrices 4 x 4 (a x b dans c) ------------------------------------ */
397
void mulMat4x4(DtMatrix4x4 a,DtMatrix4x4 b,DtMatrix4x4 c)
403
boucle(k,0,4) c[i][j] += a[i][k]*b[k][j];
409
/* ===============================================================
410
* ---------- Transformation d'une coordonnee par rmat. ----------
411
* =============================================================== */
413
void crdRotate(DtPoint3 p,DtMatrix4x4 rmat)
417
x = p[0] * rmat[0][0] + p[1] * rmat[1][0] + p[2] * rmat[2][0] + rmat[3][0];
418
y = p[0] * rmat[0][1] + p[1] * rmat[1][1] + p[2] * rmat[2][1] + rmat[3][1];
419
z = p[0] * rmat[0][2] + p[1] * rmat[1][2] + p[2] * rmat[2][2] + rmat[3][2];
425
#define SQUARED_DISTANCE(K,R) ((K)[0] - (R)[0]) * ((K)[0] - (R)[0]) + ((K)[1] - (R)[1]) * ((K)[1] - (R)[1]) + ((K)[2] - (R)[2]) * ((K)[2] - (R)[2])
427
DtFloat squared_distance(DtPoint3 R,DtPoint3 K)
429
return (DtFloat) ((K[0] - R[0]) * (K[0] - R[0]) +
430
(K[1] - R[1]) * (K[1] - R[1]) +
431
(K[2] - R[2]) * (K[2] - R[2]));
436
* Best fit matrix as proposed by:
437
* Zuker & Somorjai, Bulletin of Mathematical Biology,
438
* vol. 51, No 1, p 55-78, 1989.
440
* c1, c2 two sets of coordinates to superpose.
441
* len : the number of coordiantes of c1, c2.
443
* M, the 4x4 transforamtion matrix to pass from c2 to c2 superposed onto c1.
446
* c1, c2, and len must be given.
447
* M should be passed, but is meaningless.
450
* c2 is superposed on c1.
451
* M, a transformation matrix ready for use.
454
double zuker_superpose(DtPoint3 *c1, DtPoint3 *c2, int len, DtMatrix4x4 M)
471
double squared_rms = 0.;
477
/* Compute transformation matrix as proposed by zuker */
479
pC = XYCov(pC, (DtPoint3 *) c1, (DtPoint3 *) c2, bc1, bc2, len);
481
P[0][0] = -C[0][0]+C[1][1]-C[2][2];
482
P[0][1] = P[1][0] = -C[0][1]-C[1][0];
483
P[0][2] = P[2][0] = -C[1][2]-C[2][1];
484
P[0][3] = P[3][0] = C[0][2]-C[2][0];
486
P[1][1] = C[0][0]-C[1][1]-C[2][2];
487
P[1][2] = P[2][1] = C[0][2]+C[2][0];
488
P[1][3] = P[3][1] = C[1][2]-C[2][1];
490
P[2][2] = -C[0][0]-C[1][1]+C[2][2];
491
P[2][3] = P[3][2] = C[0][1]-C[1][0];
493
P[3][3] = C[0][0]+C[1][1]+C[2][2];
496
printMat4x4("zuker P", P);
499
nCycles = largestEV4(P, V, &eval);
501
RM[0][0] = -V[0]*V[0]+V[1]*V[1]-V[2]*V[2]+V[3]*V[3];
502
RM[1][0] = 2*(V[2]*V[3]-V[0]*V[1]);
503
RM[2][0] = 2*(V[1]*V[2]+V[0]*V[3]);
506
RM[0][1] = -2*(V[0]*V[1]+V[2]*V[3]);
507
RM[1][1] = V[0]*V[0]-V[1]*V[1]-V[2]*V[2]+V[3]*V[3];
508
RM[2][1] = 2*(V[1]*V[3]-V[0]*V[2]);
511
RM[0][2] = 2*(V[1]*V[2]-V[0]*V[3]);
512
RM[1][2] = -2*(V[0]*V[2]+V[1]*V[3]);
513
RM[2][2] = -V[0]*V[0]-V[1]*V[1]+V[2]*V[2]+V[3]*V[3];
521
/* printMat4x4("zuker RM", RM); */
523
/* Solution eprouvee ! */
524
try[0] = - bc2[0]; try[1] = - bc2[1]; try[2] = - bc2[2];
525
MkTrnsIIMat4x4(TY, try);
526
MkTrnsIIMat4x4(TX, bc1);
527
mulMat4x4(TY,RM,TMP);
528
mulMat4x4(TMP, TX, M);
530
/* Now superpose the coordinates */
531
for (aDot=0;aDot<len;aDot++) {
532
crdRotate(c2[aDot],M);
535
/* Compute squared RMSd */
536
for (aDot=0;aDot<len;aDot++) {
537
squared_rms += squared_distance(c1[aDot],c2[aDot]);
540
return squared_rms / (double) len;