def makeVectors(): '''return two unit vectors that are randomly distributed on a sphere with the condition that they are mutually perpendicular''' p1, p2 = [[gauss(0, 1) for i in range(3)] for j in range(2)] p3 = cross(p1, p2) norm1 = norm(p1) norm3 = norm(p3) p1 = [x / norm1 for x in p1] p3 = [x / norm3 for x in p3] return p1, p3
def offset(self, o): b = deepcopy(self) pt0 = perp2(b.t0) pt1 = perp2(b.t1) c1 = proj(b.bp[0]) c2 = proj(b.bp[1]) c3 = proj(b.bp[2]) t2 = unit(c3 - c1) pt2 = perp2(t2) cc1 = unit(c2 - b.p0) cc2 = unit(b.p1 - c2) dp1 = dot(b.t0, cc1) dp2 = dot(b.t1, cc2) #t2 = unit(pt0 + refl(pt0,cc1)) #t3 = unit(pt1 + refl(pt1,cc2)) t2 = perp2(cc1) t3 = perp2(cc2) b.p0 = b.p0 + o * pt0 b.p1 = b.p1 + o * pt1 c2 = c2 + o * pt2 c1 = c1 + o / dp1 * t2 c3 = c3 + o / dp2 * t3 w1 = b.bp[0][2] #dot( b.t0,unit(c2 - b.p0)) w2 = b.bp[2][2] #dot( b.t1,unit(b.p1 - c2)) w1 = dot(b.t0, cc1) w2 = dot(b.t1, cc2) alpha = norm(c1 - c2) beta = norm(c3 - c2) b.r = alpha / beta b.bp = (hom(c1, w1), hom(c2, 1), hom(c3, w2), alpha, beta) b.h1 = [hom(b.p0, 1), b.bp[0], b.bp[1]] b.h2 = [b.bp[1], b.bp[2], hom(b.p1, 1)] return b
def __enter__(self): self.f = self.func(self.X) f = array([self.f]).T self.J = self.Dfun(self.X) self.A = self.J.T.dot(self.J) self.g = -self.J.T.dot(f) I = identity(len(self.A)) self.mu = self.tau*diag(self.A)*I self.F0 = 0.5*f.T.dot(f)[0][0] self.append((self.iter, self.X, norm(self.f))) return self
def normalize_spfs(self): """Normalizes the spf vectors """ for i in range(self.nel): ind = self.psistart[1,i] for j in range(self.nmodes): nspf = self.nspfs[i,j] npbf = self.npbfs[j] for k in range(nspf): nrm = LA.norm(self.psi[ind:ind+npbf]) if abs(nrm) > 1.e-30: self.psi[ind:ind+npbf] /= nrm ind += npbf
def __enter__(self): self.f = self.func(self.X) f = array([self.f]).T self.J = self.Dfun(self.X) self.A = self.J.T.dot(self.J) self.g = -self.J.T.dot(f) self.mu = self.tau self.F0 = 0.5*f.T.dot(f)[0][0] X = self.X if len(self.bounds)<>0: X = type(X)([f(x) for x, f in zip(X, self.box_contraints_transformation)]) if len(self.scaling_of_variables)<>0: X = [f.inverse(x) for x, f in zip(X, self.scaling_transformation)] self.append((self.iter, X, norm(self.f))) return self
def biarc_r_from_arcs(a1, a2): alpha = norm(a1[0] - proj(a1[1])) beta = norm(a1[3] - proj(a1[4])) return alpha / beta
def check_qmr(self): bx0 = self.x0.copy() x, info = qmr(self.A, self.b, self.x0, callback=callback) assert_array_equal(bx0, self.x0) assert norm(dot(self.A, x) - self.b) < 5*self.tol
(1.0 / self[k][k] * (A[i][k] - s)) except ValueError: raise LinAlgError('Matrix is not positive definite - Cholesky decomposition cannot be computed') def solve(self, b): n = len(b) c = [0.0 for i in xrange(n)] for i in xrange(n): c[i] = (b[i] - sum( self[i][k] * c[k] for k in xrange(i) )) / self[i][i] x = [0.0 for i in xrange(n)] for i in xrange(n-1, -1, -1): x[i] = (c[i] - sum( self[k][i] * x[k] for k in xrange(i+1, n) )) / self[i][i] return x if __name__ == '__main__': A = array([[2, 1, 1, 3, 2], [1, 2, 2, 1, 1], [1, 2, 9, 1, 5], [3, 1, 1, 7, 1], [2, 1, 5, 1, 8]], dtype=float) b = array([1, 1, 1, 1, 1], dtype=float) L = cholesky( A ) X = L.solve(b) from linalg import norm from libarray import * print 'Must be near 0: ', norm(A.dot(array([X]).T)- b)
def fmin(func, x0, Dfun=None, gtol=1.0e-06, xtol=1.0e-04, maxfev=1000, maxiter=50, epsfcn=1e-6, damping=(10e-3, 2.0), verbose=True): with levmar(func, x0, Dfun=Dfun, gtol=gtol, xtol=xtol, maxfev=maxfev, maxiter=maxiter, epsfcn=epsfcn, damping=damping) as opt: if verbose: print 'Start Levenberg Marquart Optimizer...' print '{step:>7}{x}{residual:>13}'.format(step='step', x='{:>13}'*len(x0), residual='residual').format(*('X[%d]'%i for i in xrange(len(x0)))) print '{step:>7}{x}{residual:>13.3e}'.format(step=0, x='{:>13.3e}'*len(x0), residual=norm(opt.f)).format(*x0) while 1: iter=0 try: iter, X, residual = opt.next() if verbose: print '{step:>7}{x}{residual:>13.3e}'.format(step=iter, x='{:>13.3e}'*len(X), residual=residual).format(*X) except StopIteration, exit_message: if verbose: print '---------' print 'Optimization terminated successfully.' print ' Exit Message: %s'%(exit_message) print ' Iterations: %d'%(iter) print ' Function evaluations: %d'%(opt.ifev) break
def next(self): isJnull = self.checkJacobian() for i, f in enumerate(self.f): if f == float('nan'): raise LevMarError('The point %d is a Nan. The optimizer has been stopped.'%i) # stopping criteria if norm(self.g.T[0])<self.gtol: raise StopIteration('Magnitude of gradient smaller than the \'gtol\' tolerance.') # increment iter self.iter += 1 if self.iter>self.maxiter: raise StopIteration('Number of iterations exceeded \'maxiter\' or number of function evaluations exceeded \'maxfev\'.') # save previous state self.X0, self.A0, self.g0, self.f0 = self.X.copy(), self.A.copy(), self.g.copy(), self.f.copy(), # compute dX I = identity(len(self.A)) self.dX = array(solve(self.A + self.mu, self.g.T[0], verbose=False)) # stopping criteria #if norm(self.dX)<self.xtol*(norm(self.X)+self.xtol): # raise StopIteration() if not(False in [a<b for a, b in zip(abs(self.dX), self.xtol*(abs(self.X)+self.xtol))]): raise StopIteration('Change in x smaller than the \'xtol\' tolerance.') self.X = self.X + self.dX # compute f using the new X self.f = self.func(self.X) f = array([self.f]).T self.Fn = 0.5*f.T.dot(f)[0][0] dX = array([self.dX]).T self.dL = (0.5*(dX.T.dot(self.mu.dot(dX) + self.g)))[0][0] self.dF = self.F0-self.Fn if len(self.bounds): isStepAcceptable = not(False in [min(bound)<=x<=max(bound) for x, bound in zip(self.X, self.bounds)]) else: isStepAcceptable = True # if step acceptable if ((self.dF>0 and self.dL>0) or (self.dF<0 and self.dL<0)) and isStepAcceptable: # compute jacobian, A and g self.J = self.Dfun(self.X) self.A = self.J.T.dot(self.J) self.g = -self.J.T.dot(f) # damp mu and update F0 parameter self.mu = self.mu*max(0.333333333, (1.0-(2.0*(self.dF/self.dL)-1.0)**3)/2.0) self.nu = self.damping[1] self.F0 = self.Fn else: # restore self.X, self.A, self.g, self.f = self.X0.copy(), self.A0.copy(), self.g0.copy(), self.f0.copy(), # damp mu self.mu = self.mu*self.nu self.nu = 2.0*self.nu # save and return X and residual residual = norm(self.f) self.append((self.iter, self.X, residual)) return self.iter, self.X, residual
except StopIteration, exit_message: if verbose: print '---------' print 'Optimization terminated successfully.' print ' Exit Message: %s'%(exit_message) print ' Iterations: %d'%(iter) print ' Function evaluations: %d'%(opt.ifev) break if verbose: print ' Best step:' if len(opt): iter, X, residual = min(list(opt[i] for i in xrange(len(opt))), key=lambda step: step[2]) print '{step:>7}{x}{residual:>13.3e}'.format(step=iter, x='{:>13.3e}'*len(X), residual=residual).format(*X) else: iter = 0; residual = 0.0; X=x0 print '{step:>7}{x}{residual:>13}'.format(step=0, x='{:>13.3e}'*len(x0), residual=norm(opt.f)).format(*x0) if opt.traceback: print 'LevMarError:', opt.traceback[1] if verbose: print return iter, X, residual def fit(func, x0, xmeas, ymeas, gtol=1.0e-08, xtol=1.0e-06, maxfev=100, epsfcn=1e-8, damping=(10e-3, 2.0), verbose=True): def f(X, xmeas, ymeas): return array([(ymeas-func(x, *X))/ymeas for x in xmeas]) return fmin(f, x0, Dfun=None, gtol=gtol, xtol=xtol, maxfev=maxfev, epsfcn=epsfcn, damping=damping, verbose=verbose)
def makeRand(): '''return a single unit vector randomly distributed on a sphere''' v1 = [gauss(0, 1) for i in range(3)] return [x / norm(v1) for x in v1]
def makePerp(v1): '''return a random unit vector on a circle perpendicular to v1''' v2 = [gauss(0, 1) for i in range(3)] v3 = cross(v1, v2) return [x / norm(v3) for x in v3]
def main(argv): # MPI init comm = MPI.COMM_WORLD rank = comm.Get_rank() size = comm.Get_size() # discretization object data.discretization = data.Discretization() discretization = data.discretization # arguments processing readcmdline(argv) # remove old files generated by write_binary function clean_directory(rank) comm.Barrier() # domain object (object per SubDomain) data.domain = data.SubDomain(rank, size, discretization, comm) domain = data.domain # redefine variables for shortening the code nx = domain.nx ny = domain.ny nt = discretization.nt # iteration parameters max_cg_iters = 200 max_newton_iters = 50 tolerance = 1.e-6 # message of beginning if domain.rank == 0: print( "========================================================================" ) print("Welcome to mini-stencil!") print("Version :: Python 3 with MPI4Py: " + str(domain.size) + " MPI ranks") print("Mesh :: " + str(discretization.nx) + " * " + str(discretization.ny) + " dx = " + str(discretization.dx)) print("Iteration :: " + "CG " + str(max_cg_iters) + ", Newton " + str(max_newton_iters) + ", tolerance " + str(tolerance)) print( "========================================================================" ) # details of the cartesian division of the processors if domain.rank == 0: print("Cartesian division details for each processor (subdomain)\n") domain.print() if domain.rank == 0 and data.verbose_output: print( "========================================================================" ) print("Verbose output\n") # solution matrices # ny = ROW and nx = COLUMN (!! VERY IMPORTANT !!) data.x_new = np.zeros((ny, nx), dtype=np.float64) data.x_old = np.zeros((ny, nx), dtype=np.float64) # boundary vectors # initialized on 0 (dirichlet boundary condition) data.bndN = np.zeros((1, nx), dtype=np.float64) data.bndS = np.zeros((1, nx), dtype=np.float64) data.bndE = np.zeros((1, ny), dtype=np.float64) data.bndW = np.zeros((1, ny), dtype=np.float64) # buffer vectors for exchanging boundary information # they act as temporary dataholders for exchange data.buffN = np.zeros((1, nx), dtype=np.float64) data.buffS = np.zeros((1, nx), dtype=np.float64) data.buffE = np.zeros((1, ny), dtype=np.float64) data.buffW = np.zeros((1, ny), dtype=np.float64) # Ax = b with b being 0 in the initial condition b = np.zeros((ny, nx), dtype=np.float64) # solution for conjugate gradient function deltax = np.zeros((ny, nx), dtype=np.float64) # convergence if domain.rank == 0: con = np.zeros((0), dtype=np.float64) # convergence of the last iteration res = np.empty((nt), dtype=np.float64) # residual of each timestep if (data.custom_init): for i in data.discretization.points: x = i[0] y = i[1] s = i[2] if (x >= (domain.startx) and x <= (domain.endx) and y >= (domain.starty) and y <= (domain.endy)): x -= (domain.startx) y -= (domain.starty) data.x_new[y, x] = s else: # Default initial condition (!! PROPORTIONAL TO THE GRID SIZE !!) # A circle of concentration 0.1 centred at (xdim/4, ydim/4) # with radius no larger than 1/8 of both xdim and ydim # x lenght is always one while y lenght is variable depending on the dimensions xc = 1.0 / 4.0 yc = (discretization.ny - 1) * discretization.dx / 4.0 # min ensure that radius will be not larger than 1/8 of xdim or ydim radius = min(xc, yc) / 2.0 # Startx and starty begins artificially to count from 1 but it is actually 0 for j in range(domain.starty, domain.endy + 1): # (j - 1) displace the circle slightly on the upper right of the domain # instead of being in the corner y = (j) * discretization.dx for i in range(domain.startx, domain.endx + 1): # (i - 1) displace the circle slightly on the upper right of the domain # instead of being in the corner x = (i) * discretization.dx # Test if grid point is inside circle # Actually the circle only lies in the (0, 0) domain if ((x - xc) * (x - xc) + (y - yc) * (y - yc)) < (radius * radius): data.x_new[j - domain.starty][i - domain.startx] = 0.1 # Start time before solving the system start_time = time.time() # Main timeloop for timestep in range(1, nt + 1): # x_old = x_new # get back the solution of previous timestep # to compute the solution of the actual timestep data.x_old[:] = data.x_new # new residual at each time step residual = 0.0 converged = False # newton iterations used to solve a system of non linear equations it = 0 for it in range(0, max_newton_iters): # we get b in Ax = b by using the diffusion function # b = f(x_n) operators.diffusion(data.x_new, b, timestep, it) # residual of newton methods residual = linalg.norm(b) # for plotting convergence of the newton methods if domain.rank == 0 and timestep == nt: con = np.append(con, residual) if residual < tolerance: converged = True break # solve linear system to get deltax # Ax = b where A is J(x) and x deltax (we already have b) # we have to calculate the Jacobian and then solve to get deltax # J(x) * deltax = f(x) cg_converged = False cg_converged = linalg.cg_solver(deltax, b, max_cg_iters, tolerance, cg_converged) # Check that the CG solver converged if not cg_converged: break # x_new = x_new - deltax # deltax = f(x_n)/f'(x_n) = f(x_n) * J^-1(x) # deltax got from cg_converged function data.x_new -= deltax # stats for printing results data.iters_newton += it + 1 # Verbose mode (output stats at each Newton iteration) if converged and data.verbose_output: print("Step " + str(timestep) + " required " + str(it) + " iterations for residual " + str(residual)) if not converged: print("Step " + str(timestep) + " ERROR : nonlinear iterations failed to converge") break # final residual for a series of newton iteration for each timestep if domain.rank == 0: res[timestep - 1] = residual # total computational time for getting the solution timespent = time.time() - start_time # Avoid polluating printed output time.sleep(0.2) # final results printed if (domain.rank == 0): print( "--------------------------------------------------------------------------------" ) print("Simulation took " + str(timespent) + " seconds") print( str(data.iters_cg) + " conjugate gradient iterations, at rate of " + str(data.iters_cg / timespent) + " iters/second") print(str(data.iters_newton) + " newton iterations") print(str(data.flops_count) + " floating point operations") print( "--------------------------------------------------------------------------------" ) print("Goodbye !") # write final solution to BIN file for visualization # wld way used in the old c version # wept and modified for showing how to write in one file with MPI if data.printed_output: filename = write_binary("output", data.x_old, domain, discretization) read_binary(filename, domain, discretization) # interactive visualization of the final solution # all the subdomains solutions are merged into one matrix using MPI if data.interactive_output or data.printed_matrix: # merged results from all subdomains result = merge_solution(discretization, domain) # print final matrix result in txt file if data.printed_matrix and domain.rank == 0: print("output.txt generated in current directory !") np.savetxt("./output.txt", result) # generate interactive visualization if data.interactive_output and domain.rank == 0: print( "(Close the python interactive windows to stop the program.)") plot_solution(result) plot_convergence(con)
def next(self): isJnull = self.checkJacobian() if 0 in isJnull: print LevMarWarning('The Jacobian is rank deficient.') if nan in self.f: raise LevMarError('One point of \'f\' is a Nan. The optimizer has been stopped.') # stopping criteria if norm(self.g)<self.gtol: raise StopIteration('Magnitude of gradient smaller than the \'gtol\' tolerance.') # increment iter self.iter += 1 if self.iter>self.maxiter: raise StopIteration('Number of iterations exceeded \'maxiter\' or number of function evaluations exceeded \'maxfev\'.') # save previous state self.X0 = self.X.copy() self.A0 = self.A.copy() self.g0 = self.g.copy() self.f0 = self.f.copy() # compute dX I = identity(len(self.A)) A = self.A mu = self.mu g = self.g def geth(A, g, mu): mA = max(abs(A.flatten())) for i in xrange(5): try: I = identity(len(A)) L = chol(A + mu*diag(A)*I) h = L.solve(g) return array(h), mu except LinAlgError: mu = max(10.0*mu, 1e-15*mA) raise LevMarError('The matrix A + mu*diag(A) is not positive. The optimizer has been stopped.') #self.dX = array(solve(A + mu*diag(A)*I, g.T[0])) self.dX, self.mu = geth(A, g.T[0], mu) # stopping criteria if norm(self.dX)<self.xtol*(norm(self.X)+self.xtol): raise StopIteration('Change in x smaller than the \'xtol\' tolerance.') self.X = self.X + self.dX # compute f using the new X self.f = self.func(self.X) f = array([self.f]).T dX = array([self.dX]).T self.Fn = 0.5*f.T.dot(f)[0][0] # Using the state A, mu, g, h and considering a Taylor expansion model L, the trust region dL = L(h)-L(0) is calculated. # then dF/dL is the gain of the trust region. dL is positive self.dL = 0.5*dX.T.dot((self.mu*diag(self.A)*I).dot(dX) + self.g)[0][0] self.dF = self.F0-self.Fn # if step acceptable, dF is sometimes null due to machine accuracy if self.dF>0 and self.dL>0 : # compute jacobian, A and g self.J = self.Dfun(self.X) self.A = self.J.T.dot(self.J) self.g = -self.J.T.dot(f) # damp mu and update F0 parameter self.mu = self.mu*max(1.0/3.0, (1.0-(2.0*(self.dF/self.dL)-1.0)**3)/2.0) self.nu = self.damping[1] self.F0 = self.Fn if norm(self.g0-self.g) < self.gtol: raise StopIteration('Change in g smaller than the \'gtol\' tolerance.') else: # restore self.X = self.X0.copy() self.A = self.A0.copy() self.g = self.g0.copy() self.f = self.f0.copy() # damp mu self.mu = self.mu*self.nu self.nu = 2.0*self.nu # save and return X and residual residual = norm(self.f) X = self.X if len(self.bounds)<>0: X = type(X)([f(x) for x, f in zip(X, self.box_contraints_transformation)]) if len(self.scaling_of_variables)<>0: X = [f.inverse(x) for x, f in zip(X, self.scaling_transformation)] self.append((self.iter, X, residual)) return self.iter, X, residual
def proc_orient(line, inFile, fragDict): ''' This is called when a line containing '!!orient' is read from inFile. It continues reading inFile until reaching the line '!!oend'. It parses the arguments on the !!orient line, adds lines for the dummy atoms used to orient the fragment, and then stores the rest of the lines in the !!orient block. If the centroid coordinates were not given, it calculates them from the atoms it reads. It returns a template with placeholders for the dummy atoms and a Namespace containing the !!orient arguments. ''' # Process the !!orient line. Find the correct FRAG block from fragDict and # add dummy atoms to it. Store this modified FRAG block. orientArgs = parse_instructions(line) fragLines = make_frag(fragDict[orientArgs.frag]) dummyAtoms = [ 'afix 99{}\n'.format(orientArgs.afix), 'REM !! To remove the dummy atoms while preserving the rest of\n' 'REM !! the model, simply refine this model in ShelXL and delete\n' 'REM !! the lines from here through "PERP 1 ..." from the \n' 'REM !! resulting .res file.\n' 'part -1 10.0 !!ornt\n', 'cent 1 cent_xyz 10 10.02 !!ornt\n', 'pivt 1 pivt_xyz 10 10.02 !!ornt\n', 'perp 1 perp_xyz 10 10.02 !!ornt\n' ] # Read and process lines until !!oend. Atoms are saved to atomMatchList and # their coordinates are changed to 0 0 0. All other lines are unchanged. # All lines are written to the list orientLines. atomMatchList, orientLines = [], [] while True: atomMatch = atomRE.match(line) if atomMatch and not line[0].upper() in shelxWords: atomMatchList.append(atomMatch) line = atomMatch.group(1) + ' 0 0 0 11 {}\n'.format( orientArgs.uiso) if line.lower().startswith('!!oend'): orientLines.append('afix 0\n') break orientLines.append(line) line = inFile.readline() # If the centroid was given on the !!orient line, then we name it here; # otherwise calculate it from the atoms within the orient...oend block. # If the centroid is on 0,0,0 then we nudge it slightly so that ShelXL # treats it as a real position. if orientArgs.x is not None: cent = [orientArgs.x, orientArgs.y, orientArgs.z] else: xyzArray = [[0, 0, 0] for i in range(len(atomMatchList))] for i in range(len(atomMatchList)): xyzArray[i] = [ float(atomMatchList[i].group(j)) for j in range(2, 5) ] cent = centroid(xyzArray) if norm(cent) < 0.001: cent = vec_sum(cent, [.001, .001, .001]) orientArgs.x, orientArgs.y, orientArgs.z = cent[0], cent[1], cent[2] orientArgs.cent = cent outLines = fragLines + dummyAtoms + orientLines return outLines, orientArgs