/* * BioJava development code * * This code may be freely distributed and modified under the * terms of the GNU Lesser General Public Licence. This should * be distributed with the code. If you do not have a copy, * see: * * http://www.gnu.org/copyleft/lesser.html * * Copyright for this code is held jointly by the individual * authors. These should be listed in @author doc comments. * * For more information on the BioJava project and its aims, * or to join the biojava-l mailing list, visit the home page * at: * * http://www.biojava.org/ * * Created on Dec 4, 2005 * */ package org.biojava.nbio.structure.geometry; import javax.vecmath.Matrix4d; import javax.vecmath.Point3d; import javax.vecmath.Vector3d; import org.biojava.nbio.structure.jama.Matrix; import org.biojava.nbio.structure.jama.SingularValueDecomposition; /** * A class that calculates the superposition between two sets of points using an * SVD Matrix Decomposition. It was introduced by Wolfgang Kabsch, hence the * alternative name Kabsh algorithm. Inspired by the biopython SVDSuperimposer * class. * * @author Andreas Prlic * @author Aleix Lafita * @since 1.5 * @version %I% %G% * */ public class SuperPositionSVD extends SuperPositionAbstract { /** * Constructor for the SVD superposition algorithm. * * @param centered * true if the point arrays are centered at the origin (faster), * false otherwise */ public SuperPositionSVD(boolean centered) { super(centered); } @Override public Matrix4d superpose(Point3d[] fixed, Point3d[] moved) { checkInput(fixed, moved); Point3d cena = CalcPoint.centroid(fixed); Point3d cenb = CalcPoint.centroid(moved); double[][] centAcoords = new double[][] { { cena.x, cena.y, cena.z } }; Matrix centroidA = new Matrix(centAcoords); double[][] centBcoords = new double[][] { { cenb.x, cenb.y, cenb.z } }; Matrix centroidB = new Matrix(centBcoords); // center at centroid cena.negate(); cenb.negate(); Point3d[] ats1 = CalcPoint.clonePoint3dArray(fixed); CalcPoint.translate(new Vector3d(cena), ats1); Point3d[] ats2 = CalcPoint.clonePoint3dArray(moved); CalcPoint.translate(new Vector3d(cenb), ats2); double[][] coordSet1 = new double[ats1.length][3]; double[][] coordSet2 = new double[ats2.length][3]; // copy the atoms into the internal coords; for (int i = 0; i < ats1.length; i++) { coordSet1[i] = new double[3]; ats1[i].get(coordSet1[i]); coordSet2[i] = new double[3]; ats2[i].get(coordSet2[i]); } // now this is the bridge to the Jama package: Matrix a = new Matrix(coordSet1); Matrix b = new Matrix(coordSet2); // # correlation matrix Matrix b_trans = b.transpose(); Matrix corr = b_trans.times(a); SingularValueDecomposition svd = corr.svd(); Matrix u = svd.getU(); // v is alreaady transposed ! difference to numermic python ... Matrix vt = svd.getV(); Matrix vt_orig = (Matrix) vt.clone(); Matrix u_transp = u.transpose(); Matrix rot_nottrans = vt.times(u_transp); Matrix rot = rot_nottrans.transpose(); // check if we have found a reflection double det = rot.det(); if (det < 0) { vt = vt_orig.transpose(); vt.set(2, 0, (0 - vt.get(2, 0))); vt.set(2, 1, (0 - vt.get(2, 1))); vt.set(2, 2, (0 - vt.get(2, 2))); Matrix nv_transp = vt.transpose(); rot_nottrans = nv_transp.times(u_transp); rot = rot_nottrans.transpose(); } Matrix cb_tmp = centroidB.times(rot); Matrix tran = centroidA.minus(cb_tmp); return Matrices.getTransformation(rot, tran); } @Override public double getRmsd(Point3d[] x, Point3d[] y) { Point3d[] yclone = CalcPoint.clonePoint3dArray(y); superposeAndTransform(x, yclone); return CalcPoint.rmsd(x, yclone); } }