def minDist(X1, X2): """ Minimize the distance between two clusters. The following symmetries will be accounted for. Translational symmetry Global rotational symmetry """ #alignCoM(X1, X2) X1 = CoMToOrigin(X1) X2 = CoMToOrigin(X2) #align rotation degrees of freedom dist, X2 = alignRotation(X1, X2) return dist, X1, X2
def minPermDistStochastic(X1, X2, niter=100, permlist=None, verbose=False, accuracy=0.01, check_inversion=True, use_quench=False): """ Minimize the distance between two clusters. Parameters ---------- X1, X2 : the structures to align. X2 will be aligned with X1, both the center of masses will be shifted to the origin niter : int the number of basinhopping iterations to perform permlist : a list of lists of atoms A list of lists of atoms which are interchangable. e.g. if all the atoms are interchangable permlist = [range(natoms)] For a 50/50 binary mixture, permlist = [range(1,natoms/2), range(natoms/2,natoms)] verbose : whether to print status information accuracy : accuracy for determining if the structures are identical check_inversion : if true, account for point inversion symmetry use_quench : for each step of the iteration, minimize a permutationally invariant distance metric. This slows the algorithm, but can potentially make it more accurate. Notes ----- The following symmetries will be accounted for:: 1. Translational symmetry #. Global rotational symmetry #. Permutational symmetry #. Point inversion symmetry The algorithm here to find the best distance is for i in range(niter): random_rotation(coords) findBestPermutation(coords) alignRotation(coords) """ natoms = len(X1) / 3 if permlist is None: permlist = [range(natoms)] X1init = X1 X2init = X2 X1 = np.copy(X1) X2 = np.copy(X2) #first check for exact match exactmatch = ExactMatchCluster(accuracy=accuracy, permlist=permlist) if exactmatch(X1, X2): #this is kind of cheating, I would prefer to return #X2 in best alignment and the actual (small) distance return 0.0, X1, X1.copy() #bring center of mass of x1 and x2 to the origin #save the center of mass of X1 for later X1com = X1.reshape([-1,3]).sum(0) / natoms X1 = CoMToOrigin(X1) X2 = CoMToOrigin(X2) #print "X2.shape", X2.shape #find the best rotation stochastically X20 = X2.copy() distbest, mxbest = _optimizePermRot(X1, X2, niter, permlist, verbose=verbose, use_quench=use_quench) use_inversion = False if check_inversion: X20i = -X20.copy() X2 = X20i.copy() distbest1, mxbest1 = _optimizePermRot(X1, X2, niter, permlist, verbose=verbose, use_quench=use_quench) if distbest1 < distbest: if verbose: print "using inversion in minpermdist" use_inversion = True distbest = distbest1 mxbest = mxbest1 #now we know the best rotation if use_inversion: X20 = X20i X2 = applyRotation(mxbest, X20) dist, X1, X2 = findBestPermutation(X1, X2, permlist) dist, X2 = alignRotation(X1, X2) if dist > distbest+0.001: print "ERROR: minPermDistRanRot: dist is different from distbest %f %f" % (dist, distbest) if verbose: print "finaldist", dist, "distmin", distbest #add back in the center of mass of X1 X1 = X1.reshape([-1,3]) X2 = X2.reshape([-1,3]) X1 += X1com X2 += X1com X1 = X1.reshape(-1) X2 = X2.reshape(-1) return dist, X1, X2