def BuildFilteredMatrix(A, neighborhoods, tol): tab=Tab() print('{}in BuildFilteredMatrix()'.format(tab)) timer = Timer('BuildFilteredMatrix') timer.start() # Expects A as csr Af = A.copy() for i in range(A.shape[0]): N = neighborhoods[i] start = Af.indptr[i] end = Af.indptr[i+1] for k in range(start, end): j = Af.indices[k] if i==j: iPtr = k break for k in range(start, end): j = Af.indices[k] if j not in N: Af.data[iPtr] -= Af.data[k] Af.data[k]=0 timer.stop() return Af
def makeProlongator(self, lev): '''Make the prologator to go from level k to k+1.''' tab = Tab() print('{}making prolongator from level {} to {}'.format(tab,lev,lev+1)) (I_up, aggregates) = SA_coarsen( self.matrix(lev+1), tol=self.tol, lvl=lev+1 ) return I_up
def reportFailure(self, iter, normR, normB): '''If control.showFinal==True, print a message upon failure to converge.''' tab = Tab() if self._control.showFinal: normRel = normR if normR != 0: normRel = normR / normB print('%s%s solve FAILED: iters=%7d, ||r||/r0=%12.5g' % (tab, self.name(), iter, normR / normB))
def reportSuccess(self, iter, normR, normB): '''If control.showFinal==True, print a message upon convergence.''' tab = Tab() if self._control.showFinal: normRel = normR if normR != 0: normRel = normR / normB print('%s%s solve succeeded: iters=%7d, ||r||/r0=%12.5g' % (tab, self.name(), iter, normRel))
def reportIter(self, iter, normR, normR0): ''' If control.showIters==True, print information about the current iteration at intervals specified by the control.interval parameter. Otherwise, do nothing. ''' tab = Tab() if self._control.showIters and (iter % self._control.interval) == 0: print('%s%s iter=%7d ||r||=%12.5g ||r||/r0=%12.5g' % (tab, self.name(), iter, normR, normR / normR0))
def BuildTentativeProlongator(A, aggregates): tab=Tab() timer = Timer('BuildTentativeProlongator') timer.start() print('{}in BuildTentativeProlongator()'.format(tab)) P = sp.dok_matrix((A.shape[0], len(aggregates))) for i in range(len(aggregates)): for j in aggregates[i]: P[j,i] = 1 timer.stop() return P
def getNeighborhood(A, i, tol, a_diag): tab = Tab() #print('{}in getNeighborhood()'.format(tab)) N = {i} a_ii = a_diag[i] start = A.indptr[i] end = A.indptr[i+1] for k in range(start, end): j = A.indices[k] a_ij = A.data[k] a_jj = a_diag[j] if abs(a_ij) >= tol*np.sqrt(a_ii*a_jj): N.add(j) return N
def solve(self, A, b): tab = Tab() # Get size of matrix n, nc = A.shape # Make sure matrix is square assert (n == nc) # Make sure A and b are compatible assert (n == len(b)) # Check for the trivial case b=0, x=0 normB = self.norm(b) if normB == 0.0: return self.handleConvergence(0, np.zeros_like(b), 0, 0) # Create vectors for residual r and solution x r = np.copy(b) x = np.copy(b) if self._cycleMgr == None or not self.matrixFrozen(): mlh = SmoothedAggregationMLHierarchy(A, numLevels=self.numLevels) self._cycleMgr = VCycleManager(mlh, nuPre=self.nuPre, nuPost=self.nuPost, smoother=self.smoother) # Main loop for k in range(self.maxiter()): # Run a V-cycle x = self._cycleMgr.runCycle(b, x) # Compute residual r = b - A * x # Check for convergence normR = self.norm(r) self.reportIter(k, normR, normB) if normR < self.tau() * normB: return self.handleConvergence(k, x, normR, normB) # If we're here, maxiter has been reached. This is normally a failure, # but may be acceptable if failOnMaxiter is set to false. return self.handleMaxiter(k, x, normR, normB)
def search(self, x0, normF0, newtStep, func): tab = Tab() t = 1.0 for k in range(self.maxsteps()): x_k = x0 + t * newtStep F_k = func.evalF(x_k) normF_k = self.norm(F_k) ratio = normF_k / normF0 self.report(k, t, ratio) # Test for convergence of line search if normF_k <= (1.0 - self.alpha() * t) * normF0: return (True, x_k, F_k, normF_k) # Shrink step factor = 0.5 / ratio if factor < self.low(): factor = self.low() t = t * factor # If here, the line step hasn't produced sufficient decrease. return (False, x_k, F_k, normF_k)
def SmoothProlongator(Phat, A, Af, omega=(2/3)): tab=Tab() print('{}in SmoothProlongator()'.format(tab)) timer = Timer('SmoothProlongator') timer.start() smoothmat = omega*Af d_A = A.diagonal() for i in range(A.shape[0]): start = smoothmat.indptr[i] end = smoothmat.indptr[i+1] for k in range(start, end): j = smoothmat.indices[k] smoothmat.data[k] /= d_A[i] if i==j: smoothmat.data[k] = 1 - smoothmat.data[k] else: smoothmat.data[k] = -smoothmat.data[k] smoothed = smoothmat.dot(Phat) timer.stop() return smoothed
def SA_coarsen(A, tol=None, lvl=1): tab=Tab() print('{}in SA_coarsen()'.format(tab)) # lvl will only be used if tol is None # If tol is None, use Vanek's suggestion if tol is None: tol = 0.08*(0.5)**(lvl-1) # Build the aggregates (aggregates, neighborhoods) = BuildAggregates(A, lvl=lvl) # Build the tentative prolongator from the aggregates, A needed for it's dimensions Phat = BuildTentativeProlongator(A, aggregates) # Build the filtered matrix for the smoother Af = BuildFilteredMatrix(A, neighborhoods, tol) # Smooth the Prolongation Operator with weighted Jacobi using the filtered matrix P = SmoothProlongator(Phat, A, Af) return (P.tocsr(), aggregates)
def solve(self, A, b): ''' Solve the system A*x=b for x. * Input: * A -- System matrix, can be a numpy 2D array or a scipy sparse matrix. Must be SPD; this is not checked (doing so is too expensive). * b -- RHS vector, must be a numpy 1D array compatible with A. * Return: * A SolveStatus object containing the solution estimate, a success/failure flag, and convergence information. ''' tab = Tab() # Get size of matrix n, nc = A.shape # Make sure matrix is square assert (n == nc) # Make sure A and b are compatible assert (n == len(b)) # Check for the trivial case b=0, x=0 normB = self.norm(b) if normB == 0.0: return self.handleConvergence(0, np.zeros_like(b), 0, 0) # Form the preconditioner print('prec frozen = ', self.precFrozen()) if self.precond == None or not self.precFrozen(): print('building prec') self.precond = self.precondType().form(A) # Initialize the step, residual, and solution vectors r = np.copy(b) p = self.precond.applyRight(r) u = np.copy(p) x = np.zeros_like(b) uDotR = np.dot(u, r) # Should never be zero, since we've caught b=0 # Check anyway if uDotR == 0.0: return self.handleBreakdown(0, 'breakdown dot(u,r)==0') # Preconditioned CG loop for k in range(self.maxiter()): # Compute A*p Ap = mvmult(A, p) pTAp = np.dot(p, Ap) if pTAp == 0.0: return self.handleBreakdown(k, 'breakdown dot(p, Ap)==0') # calculate step length alpha = uDotR / pTAp # Make step and compute updated residual x = x + alpha * p r = r - alpha * Ap u = self.precond.applyRight(r) normR = self.norm(r) self.reportIter(k, normR, normB) # Check for convergence if ((normR <= self.tau() * normB) or ((not self.failOnMaxiter()) and k == self.maxiter() - 1)): return self.handleConvergence(k, x, normR, normB) # Find next step direction newUDotR = np.dot(u, r) beta = newUDotR / uDotR uDotR = newUDotR p = u + beta * p # If we're here, maxiter has been reached. This is normally a failure, # but may be acceptable if failOnMaxiter is set to false. return self.handleMaxiter(k, x, normR, normB)
def reportBreakdown(self, msg=''): '''If control.showFinal==True, print a message upon breakdown.''' tab = Tab() if self._control.showFinal: print('%s%s solve broke down: %s' % (tab, self.name(), msg))
def BuildAggregates(A, lvl=1, tol=None, phase=3): tab=Tab() tab1 = Tab() print('{}in BuildAggregates()'.format(tab)) # If tol isn't specified, then use the default by Vanek if tol is None: tol = 0.08*(0.5)**(lvl-1) timer0 = Timer('BuildAggregates init step') timer0.start() # Initialization R = {i for i in range(A.shape[0])} a_diag = A.diagonal() neighborhoods = [getNeighborhood(A,i,tol,a_diag) for i in range(A.shape[0])] aggregates = [] # isolated nodes aren't aggregated for n in neighborhoods: if len(n) == 1: aggregates.append(n) [elem] = n R.remove(elem) timer0.stop() timer1 = Timer('BuildAggregates phase 1') timer1.start() # Phase 1 if phase > 0: for i in range(A.shape[0]): # If the neighborhood of i is completely in R, create an aggregate # from the neighborhood if i in R and neighborhoods[i].issubset(R): aggregates.append(neighborhoods[i]) R -= neighborhoods[i] timer1.stop() timer2 = Timer('BuildAggregates phase 2') timer2.start() # Phase 2 if phase > 1: # Copy the aggregates since we need to modify and check the # original aggregates timer_copy = Timer('agg copy') timer_copy.start() aggcopy = copy.deepcopy(aggregates) timer_copy.stop() # Loop through elements still in R for i in range(A.shape[0]): if i in R: # We need the strongest connection max_conn_strength = 0.0 agg_idx_of_max = -1 # Loop through all aggregates, looking to see if the current # neighborhood intersections for (j, agg) in enumerate(aggcopy): #timer_disj = Timer('agg disjoint check') #timer_disj.start() isDisjoint_i_j = agg.isdisjoint(neighborhoods[i]) #timer_disj.stop() if not isDisjoint_i_j: #timer_loop = Timer('loop to find max strength') #timer_loop.start() for k in agg: if abs(A[i,k]) > max_conn_strength: max_conn_strength = abs(A[i,k]) agg_idx_of_max = j #timer_loop.stop() timer_insert = Timer('agg insertion') timer_insert.start() aggregates[agg_idx_of_max].add(i) timer_insert.stop() timer2.stop() timer3 = Timer('BuildAggregates phase 2') timer3.start() # Phase 3 if phase > 2 and not R: # Loop through elements still in R for i in range(A.shape[0]): if i in R: aggregates.append(R.intersection(neighborhoods[i])) R -= neighborhoods[i] timer3.stop() return (aggregates, neighborhoods)
def solve(self, func, xInit): ''' ''' tab = Tab() xCur = xInit.copy() FCur = func.evalF(xCur) newtStep = np.ones_like(xCur) print('freeze prec for solver=', self.freezePrec) freeze = PreconditionerFreeze(self.solver, self.freezePrec) self.linesearch.setNorm(self.norm) # Initial residual; to be used for relative residual tests r0 = self.norm(FCur) normFCur = r0 # Newton loop for i in range(self.maxiter()): # Report progress self.reportIter(i, normFCur, r0) # Check for convergence if normFCur <= r0 * self.tau() + self.tau(): return self.handleConvergence(i, xCur, normFCur, r0) # Evaluate Jacobian J = func.evalJ(xCur) # Set tolerance to be used in linear solve if isinstance(self.solver, IterativeLinearSolver): if self.fixLinTol: # Use fixed tolerance if desired (for testing) tau_lin = self.minLinTol else: # Adjust linear tol according to nonlinear residual. # new linear tolerance is the larger of: # (*) "fudge factor" times relative residual of nonlinear solve # (*) a minimum linear tolerance. # The minimum tolerance avoids pointlessly small tolerances # for the linear solver tau_lin = max(self.tolFudge * normFCur / r0, self.minLinTol) self.solver.setTolerance(tau_lin) # Solve for the Newton step tab.indent() status = self.solver.solve(J, -FCur) tab.unindent() if not status.success(): return self.handleBreakdown( i, 'solve for Newton step failed with msg={}'.format( status.msg())) p = status.soln() # Do line search to find a step length giving sufficient decrease tab.indent() (success, xCur, FCur, normFCur) = self.linesearch.search(xCur, normFCur, p, func) tab.unindent() if not success: return self.handleBreakdown(i, msg='Line search failed') # End of Newton loop. At this point, xCur, FCur, and normFCur have been # updated to the new step. # If here, we've reached the maximum number of iterations without # convergence. Report failure to converge. return self.handleMaxiter(self.maxiter(), xCur, normFCur, r0)
def report(self, k, t, ratio): tab = Tab() if self._report: print('%sk=%4d t=%12.5g ||F_k||/||F_0||=%12.5g' % (tab, k, t, ratio))