def _optimizePermRot(X1, X2, niter, permlist, verbose=False, use_quench=True): if use_quench: pot = MinPermDistPotential(X1, X2.copy(), permlist=permlist) distbest = getDistxyz(X1, X2) mxbest = np.identity(3) X20 = X2.copy() for i in range(niter): #get and apply a random rotation aa = random_aa() if not use_quench: mx = aa2mx(aa) mxtot = mx #print "X2.shape", X2.shape else: #optimize the rotation using a permutationally invariand distance metric ret = defaults.quenchRoutine(aa, pot.getEnergyGradient, tol=0.01) aa1 = ret[0] mx1 = aa2mx(aa1) mxtot = mx1 X2 = applyRotation(mxtot, X20) #optimize the permutations dist, X1, X2 = findBestPermutation(X1, X2, permlist) if verbose: print "dist", dist, "distbest", distbest #print "X2.shape", X2.shape #optimize the rotation dist, Q2 = getAlignRotation(X1, X2) # print "dist", dist, "Q2", Q2 mx2 = q2mx(Q2) mxtot = np.dot(mx2, mxtot) if dist < distbest: distbest = dist mxbest = mxtot return distbest, mxbest
def minPermDistLong(X1, X2, max_permutations = 10000): """ Minimize the distance between two clusters. The following symmetries will be accounted for Translational symmetry Global rotational symmetry Permutational symmetry This routine is deterministic, but ludicrously slow. Use the minPermDistStochastic instead """ print "This routine is deterministic, but ludicrously slow. Use the minPermDistStochastic instead" X2in = np.copy(X2) X2min = np.copy(X2) dmin = np.linalg.norm( X2-X1 ) nsites = len(X1) / 3 #this is a really dumb way to do this nperm = math.factorial(nsites) print nperm, "permutations in total. Stopping at ", max_permutations count = 0 for perm in itertools.permutations(range(nsites)): #print perm X2 = permuteArray( X2in, perm) dist, X1, X2 = minDist(X1, X2) if dist < dmin: dmin = dist X2min = np.copy(X2) #print dist, np.linalg.norm(X1-X2) if dmin < 1e-6: print "found exact match (to accuracy 1e-6)" break count += 1 if count % 1000 == 0: print "finished", count, "permutations out of", min(nperm, max_permutations), " mindist is", dmin, "last try was", dist if count == max_permutations: print "reached maximum number of perterbations (", max_permutations, ")." use_hungarian = True if use_hungarian: print "will now fix alignment and calculate the best permutation using the Hungarian algorithm" #print "dist before hungarian algorithm", dmin ret = findBestPermutation( X1, X2min ) if ret != None: dmin, X1, X2min = findBestPermutation( X1, X2min ) #print "dist after hungarian algorithm", dmin break return dmin, X1, X2min
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