def choose_random_points(e, f, nneg, npos): # Subroutine selects nneg+npos points randomly # temporary it does not select negative points # It is a very bad idea to pick all points randomly # Use for testing purpose if (nneg + npos) % 2 != 0: print('Number of chosen points should be even!', nneg, npos, nneg + npos) npos += 1 q = nneg + npos r = len(e) points = random.sample(range(0, r - 1), q - 1) # points = np.random.randint(low=0, high=r - 1, size=q - 1) points.sort() for i in range(0, q - 2): if points[i] == points[i + 1]: points[i + 1] += 1 ee = mp.zeros(q, 1) ff = mp.zeros(q, f.cols) for i in range(0, q - 1): ee[i] = e[points[i]] ff[i] = f[points[i]] return ee, ff
def pade_coeficients(f, e): # Subroutine pade_coeficients() finds coefficient of Pade approximant # f - values of complex function for approximation # e - complex points in which function f is determined r = len(e) / 2 # Preparation of arrays to calc Pade coefficiens s = mp.zeros(2 * r, 1) x = mp.zeros(2 * r) for i in range(0, 2 * r): s[i] = f[i] * e[i]**r for j in range(0, r): x[i, j] = e[i]**j for j in range(r, 2 * r): x[i, j] = -f[i] * e[i]**(j - r) # Solving the equation: |p| # | |=X^{-1}*s # |q| # Here we should catch exception in linalg!! try: x = mp.inverse(x) pq = x * s # x **= -1 # pq = np.dot(x, s) success = True except ZeroDivisionError as err: if 'matrix is numerically singular' in err.message: pq = 123456.7 success = False else: raise return pq, success
def check_site_type(site_type, theta0): if site_type == 'single': func = NH3mkm.get_odes if not len(theta0) == 4: theta0 = mp.zeros(1, 4) elif site_type == 'two-site': func = NH3mkm.get_odes_two_site if not len(theta0) == 6: theta0 = mp.zeros(1, 6) return func, theta0
def pade_ls_coefficients(f, e, n): # Subroutine pade_ls_coeficients() finds coefficient of Pade approximant by Least Squares method # f - values of complex function for approximation # e - complex points in which function z is determined # n - number of coefficients, should be less than number of points in e (n<m) m = len(e) r = n / 2 # Preparation of arrays to calc Pade coefficiens s = mp.zeros(m, 1) x = mp.zeros(m, n) for i in range(0, m): s[i] = f[i] * e[i]**r for j in range(0, r): x[i, j] = e[i]**j for j in range(r, 2 * r): x[i, j] = -f[i] * e[i]**(j - r) # Solving the equation: aX=b, where # a=x, b=s, # |p| # X = | | # |q| # pq = linalg.lstsq(x, s)[0] success = True solver = 'LU solver' try: pq = mp.lu_solve(x, s) # success = True # except ZeroDivisionError as err: except ZeroDivisionError: # if 'matrix is numerically singular' in err.message: try: pq = mp.qr_solve(x, s) pq = pq[0] solver = 'QR solver' # success = True except ValueError: # if 'matrix is numerically singular' in err.message: success = False pq = 123456.7 # else: # raise if success is True: pq.rows += 1 pq[n, 0] = mp.mpc(1, 0) return pq, success, solver
def pade(coef, e): # Calculation of analitical function on a arbitrary mesh for a given Pade coefficient # e - energy mesh (can be complex or real) # coef - Pade coefficients nlines = len(e) r = len(coef) // 2 f = mp.zeros(nlines, 1) # pq = mp.ones(r * 2 + 1, 1) # for i in range(0, r): # pq[i] = coef[i] # pq[i + r] = coef[i + r] for iw in range(0, nlines): p = mp.mpc(0.0) q = mp.mpc(0.0) for i in range(0, r): p += coef[i] * e[iw]**i for i in range(0, r): # print(i, r, i+r) q += coef[i + r] * e[iw]**i # f[iw] = np.divide(p, q) f[iw] = fdiv(p, q) f = fp.matrix(f) return f
def _scheme_from_rc_mpmath(alpha, beta): # Create vector cut of the first value of beta n = len(alpha) b = mp.zeros(n, 1) for i in range(n - 1): b[i] = mp.sqrt(beta[i + 1]) z = mp.zeros(1, n) z[0, 0] = 1 d = mp.matrix(alpha) tridiag_eigen(mp, d, b, z) # nx1 matrix -> list of mpf x = numpy.array([mp.mpf(sympy.N(xx, mp.dps)) for xx in d]) w = numpy.array([mp.mpf(sympy.N(beta[0], mp.dps)) * mp.power(ww, 2) for ww in z]) return x, w
def integrate_odes( self, site_type='single', theta0=mp.zeros(1, 4), timespan=[0, 1e8], h0=1e-20, mxstep=200000, # maximum number of steps rtol=1E-12, # relative tolerance atol=1E-15, # Absolute tolerance full_output=1): func, theta0 = NH3mkm.check_site_type(site_type, theta0) self.theta0 = theta0 args = self, # Integrate the ODEs for 1E10 sec (enough to reach steady-state) theta, out = odeint( func, # system of ODEs theta0, # initial guess timespan, # time span args=args, h0=h0, # initial time step mxstep=mxstep, # maximum number of steps rtol=rtol, # relative tolerance atol=atol, # Absolute tolerance full_output=full_output) self.integrated_thetas = theta self.ode_output = out # Return the value of theta at the last timestep return theta[-1, :]
def pade_m(coef, e, m): # Calculation of analitical function on a arbitrary mesh for a given Pade coefficient # and first known momenta of function # e - energy mesh (can be complex or real) # coef - Pade coefficients # m - first three momenta of function nlines = len(e) r = len(coef) / 2 f = mp.zeros(nlines, 1) pq = mp.ones(r * 2 + 1, 1) # for i in range(0, r): # pq[i] = coef[i] # pq[i + r] = coef[i + r] for iw in range(0, nlines): p = mp.mpc(0.0) q = mp.mpc(0.0) for i in range(0, r): p += coef[i] * e[iw]**i for i in range(0, r + 1): q += coef[i + r] * e[iw]**i f[iw] = fdiv(p, q) # f[iw] = np.divide(p, q) f[iw] /= e[iw]**3 f[iw] += m[0] / e[iw] + m[1] / (e[iw]**2) + m[2] / (e[iw]**3) f = fp.matrix(f) return f
def mpf_matrix_fadd(A, B): """ Given a m x n matrix A and m x n matrix B either in numpy.matrix or mpmath.matrix format, this function computes the sum C = A + B exactly. The output matrix C is always given in the MPMATH format. Parameters ---------- A - m x n matrix B - m x n matrix Returns ------- C - m x n matrix """ if isinstance(A, numpy.matrix): try: A = python2mpf_matrix(A) except ValueError as e: raise ValueError('Cannot compute exact sum of two matrices. %s' % e) else: if not isinstance(A, mpmath.matrix): raise ValueError('Cannot compute exact sum of two matrices: unexpected input type, excpected numpy.matrix or mpmath.matrix but got %s') %type(A) if isinstance(B, numpy.matrix): try: B = python2mpf_matrix(B) except ValueError as e: raise ValueError('Cannot compute exact sum of two matrices. %s' % e) else: if not isinstance(B, mpmath.matrix): raise ValueError('Cannot compute exact sum of two matrices: unexpected input type, excpected numpy.matrix or mpmath.matrix but got %s') % type(B) #here both A and B are mpmath matrices #we consider that A and B are MPF matrices if their first elements are of type mpf, let's test it if not isinstance(A[0,0], mpmath.mpf) or not isinstance(B[0,0], mpmath.mpf): raise ValueError('Cannot compute exact product of two matrices: cannot sum complex matrices.') #test sizes if A.rows != B.rows or A.cols != B.cols: raise ValueError('Cannot compute exact sum of two matrices: incorrect sizes.') m = A.rows n = A.cols C = mp.zeros(m, n) for i in range(0, m): for j in range(0, n): C[i,j] = mpmath.fadd(A[i,j], B[i,j], exact=True) if mpmath.isnan(C[i,j]) or mpmath.isinf(C[i,j]): print('WARNING: in matrix sum an abnormal number (NaN/Inf) occured: %f' % C[i,j]) return C
def evaluate_all_at(self, x, n=0): num = self.num if n == 0: row = mp.zeros(1, num) if self._use_mp else np.zeros(num) row = cheby.evaluate_Tn(x, row, use_mp=self._use_mp) return row dnT = self.Tderiv(n) s = self._scl**n fl = mp.mpf if self._use_mp else float return [s * fl(dnT[i](x)) for i in range(num)]
def mpf_matrix_lt_inverse(L): """ For a lower-triangular n x n real matrix L with 1s on the main diagonal this function computes its inverse X exactly such that L * X = I Matrix L must be in the numpy.matrix of mpmath.matrix formats. The function returns X as n x n real a mpmath.matrix Parameters ---------- L - n x n lower-triangular matrix Returns ------- X - n x n matrix """ if isinstance(L, numpy.matrix): try: L = python2mpf_matrix(L) except ValueError as e: raise ValueError('Cannot compute exact difference of two matrices. %s') % e else: if not isinstance(L, mpmath.matrix): raise ValueError('Cannot compute exact difference of two matrices: unexpected input type, excpected numpy.matrix or mpmath.matrix but got %s') % type(L) #checking the size if L.rows != L.cols: raise ValueError('Cannot compute inverse: matrix must be square but istead is %s x %s' % L.rows, L.cols) else: n = L.rows #checking if the matrix is indeed lower-triangular with 1s on the main diagonal for i in range(0,n): if L[i,i] != mpmath.mp.one: raise ValueError('Cannot compute inverse: matrix must have 1s on the main diagonal.') for j in range(i+1, n): if L[i,j] != mpmath.mp.zero: raise ValueError('Cannot compute inverse: matrix must be lower triangular.') X = mp.zeros(n, n) for i in range(0,n): X[:, i] = my_forward_subst(L, i) return X
def _gauss_from_coefficients_mpmath(alpha, beta, decimal_places): mp.dps = decimal_places # Create vector cut of the first value of beta n = len(alpha) b = mp.zeros(n, 1) for i in range(n - 1): # work around <https://github.com/fredrik-johansson/mpmath/issues/382> x = beta[i + 1] if isinstance(x, numpy.int64): x = int(x) b[i] = mp.sqrt(x) z = mp.zeros(1, n) z[0, 0] = 1 d = mp.matrix(alpha) tridiag_eigen(mp, d, b, z) # nx1 matrix -> list of mpf x = numpy.array([mp.mpf(sympy.N(xx, decimal_places)) for xx in d]) w = numpy.array([ mp.mpf(sympy.N(beta[0], decimal_places)) * mp.power(ww, 2) for ww in z ]) return x, w
def run_svd_r(A, full_matrices=False, verbose=True): m, n = A.rows, A.cols eps = mp.exp(0.8 * mp.log(mp.eps)) if verbose: print("original matrix:\n", str(A)) print("full", full_matrices) U, S0, V = mp.svd_r(A, full_matrices=full_matrices) S = mp.zeros(U.cols, V.rows) for j in xrange(min(m, n)): S[j, j] = S0[j] if verbose: print("U:\n", str(U)) print("S:\n", str(S0)) print("V:\n", str(V)) C = U * S * V - A err = mp.mnorm(C) if verbose: print("C\n", str(C), "\n", err) assert err < eps D = V * V.transpose() - mp.eye(V.rows) err = mp.mnorm(D) if verbose: print("D:\n", str(D), "\n", err) assert err < eps E = U.transpose() * U - mp.eye(U.cols) err = mp.mnorm(E) if verbose: print("E:\n", str(E), "\n", err) assert err < eps
def run_svd_r(A, full_matrices = False, verbose = True): m, n = A.rows, A.cols eps = mp.exp(0.8 * mp.log(mp.eps)) if verbose: print("original matrix:\n", str(A)) print("full", full_matrices) U, S0, V = mp.svd_r(A, full_matrices = full_matrices) S = mp.zeros(U.cols, V.rows) for j in xrange(min(m, n)): S[j,j] = S0[j] if verbose: print("U:\n", str(U)) print("S:\n", str(S0)) print("V:\n", str(V)) C = U * S * V - A err = mp.mnorm(C) if verbose: print("C\n", str(C), "\n", err) assert err < eps D = V * V.transpose() - mp.eye(V.rows) err = mp.mnorm(D) if verbose: print("D:\n", str(D), "\n", err) assert err < eps E = U.transpose() * U - mp.eye(U.cols) err = mp.mnorm(E) if verbose: print("E:\n", str(E), "\n", err) assert err < eps
def shell_Green_grid_Arnoldi_RgandImMmn_Uconverge_mp(n,k,R1,R2, invchi, gridpts=1000, Unormtol=1e-10, veclim=3, delveclim=2, maxveclim=40, plotVectors=False): np.seterr(over='raise',under='raise',invalid='raise') #for high angular momentum number could have floating point issues; in this case, raise error. Outer method will catch the error and use the mpmath version instead rgrid = np.linspace(R1,R2,gridpts) rsqrgrid = rgrid**2 rdiffgrid = np.diff(rgrid) """ RgMgrid = sp.spherical_jn(n, k*rgrid) #the argument for radial part of spherical waves is kr ImMgrid = sp.spherical_yn(n, k*rgrid) RgMgrid = RgMgrid.astype(np.complex) ImMgrid = ImMgrid.astype(np.complex) RgMgrid = complex_to_mp(RgMgrid) ImMgrid = complex_to_mp(ImMgrid) """ RgMgrid = mp_vec_spherical_jn(n, k*rgrid) ImMgrid = mp_vec_spherical_yn(n, k*rgrid) vecRgMgrid = RgMgrid / mp.sqrt(rgrid_Mmn_normsqr(RgMgrid, rsqrgrid,rdiffgrid)) vecImMgrid = ImMgrid - rgrid_Mmn_vdot(vecRgMgrid, ImMgrid, rsqrgrid,rdiffgrid)*vecRgMgrid vecImMgrid /= mp.sqrt(rgrid_Mmn_normsqr(vecImMgrid,rsqrgrid,rdiffgrid)) if plotVectors: rgrid_Mmn_plot(vecRgMgrid,rgrid) rgrid_Mmn_plot(vecImMgrid,rgrid) unitMvecs = [vecRgMgrid,vecImMgrid] GvecRgMgrid = shell_Green_grid_Mmn_vec_mp(n,k, rsqrgrid,rdiffgrid, RgMgrid,ImMgrid, vecRgMgrid) GvecImMgrid = shell_Green_grid_Mmn_vec_mp(n,k, rsqrgrid,rdiffgrid, RgMgrid,ImMgrid, vecImMgrid) Gmat = mp.zeros(2,2) Gmat[0,0] = rgrid_Mmn_vdot(vecRgMgrid, GvecRgMgrid, rsqrgrid,rdiffgrid) Gmat[0,1] = rgrid_Mmn_vdot(vecRgMgrid, GvecImMgrid, rsqrgrid,rdiffgrid) Gmat[1,0] = Gmat[0,1] Gmat[1,1] = rgrid_Mmn_vdot(vecImMgrid,GvecImMgrid, rsqrgrid,rdiffgrid) Uinv = mp.eye(2)*invchi-Gmat unitMvecs.append(GvecRgMgrid) unitMvecs.append(GvecImMgrid) #append unorthogonalized, unnormalized Arnoldi vector for further iterations b = mp.matrix([mp.one]) prevUnorm = 1 / Uinv[0,0] i=2 while i<veclim: Gmat = shell_Green_grid_Arnoldi_RgandImMmn_step_mp(n,k,invchi, rgrid,rsqrgrid,rdiffgrid, RgMgrid, ImMgrid, unitMvecs, Gmat, plotVectors=plotVectors) i += 1 print(i) if i==maxveclim: break if i==veclim: #solve for first column of U and see if its norm has converged Uinv = mp.eye(Gmat.rows)*invchi-Gmat b.rows = i x = mp.lu_solve(Uinv, b) Unorm = mp.norm(x) print('Unorm', Unorm, flush=True) if np.abs(prevUnorm-Unorm) > np.abs(Unorm)*Unormtol: veclim += delveclim prevUnorm = Unorm return rgrid,rsqrgrid,rdiffgrid, RgMgrid, ImMgrid, unitMvecs, Uinv, Gmat
def main(): print('Start at %s ' % time.ctime()) start_time = time.time() handle_input() print_params(logfile) print_params() fp.dps = 12 w, f = readsigma(infile) e = make_e_mesh(emin, de, npts) w1 = [] w2 = [] w3 = [] solutions = [] sets = [] mmts = [] if not use_moments: print('%4s %4s %4s %4s %20s %11s %12s %12s\b' % ('npo', 'nne', 'try', 'ils', 'delta', 'm0', 'm1', 'm2')) else: print('%4s %4s %4s %4s %10s' % ('npo', 'nne', 'try', 'ils', 'delta')) for ipo in range(npo[0], npo[1], 1): for ine in range(ne[0], ne[1]): if ls: qq2 = ipo // 4 else: qq2 = 1 # for q1 in range(0, ipo//4, 2): for q1 in range(0, qq2, 2): sys.stdout.flush() for qq in range(0, nrandomcycle): if (ine + ipo) % 2 != 0: continue sys.stdout.write('%4i %4i %4i %4i %s\r' % (ipo, ine, qq, q1, " " * 16)) if randomp: # tmp = choose_prandom_points(w, f, ine, ipo) # the best shape, the worst moments e1, f1 = choose_prandom_points(w, f, ine, ipo) else: # tmp = choose_seq_points_plus(w, f, ine, ipo) # work only with pade_ls_coeff # tmp = choose_seq_points(w, f, ine, ipo) # work only with pade_ls_coeff e1, f1 = choose_seq_points(w, f, ine, ipo) # tmp = choose_points(w, f, nne, npo) # manual selection # tmp = choose_random_points(w, f, nne, npo) # bad idea if use_moments: f1[:, 0] = make_f_prime(f1[:, 0], e1, m) if ls: pq, success, used_solver = pade_ls_coefficients( f1[:, 0], e1, ipo + ine - q1) else: pq, success = pade_coeficients(f1[:, 0], e1) used_solver = '' if not success: continue # use external information about momenta if use_moments: sigim = pade_m(pq, w, m) else: sigim = pade(pq, w) if not is_imag_negative(w, sigim): continue delta = calc_residual(f, sigim) # if delta > 1.00001: # continue if use_moments: sigre = pade_m(pq, e, m) else: sigre = pade(pq, e) if not is_imag_negative(e, sigre): continue memusage = resource.getrusage( resource.RUSAGE_SELF).ru_maxrss if not use_moments: moments = get_moments(pq) mmts.append(moments) # mmts = np.vstack([mmts, moments]) s = '' for mm in moments: s += '{:12.4f}'.format(mm) # s += '{:18s}'.format(mp.nstr(mm, n=4)) s = '{:4d} {:4d} {:4d} {:4d} {:18.12f} {:s} {:24s}'. \ format(ipo, ine, qq, q1, float(delta), s, used_solver) print('%s' % s) s += '\n' else: s = '{:4d} {:4d} {:4d} {:4d} {:18.12f} {:24s} {:d}'. \ format(ipo, ine, qq, q1, float(delta), used_solver, memusage) print('%s' % s) s += '\n' # Save "good" results for next consideration # with workdps(12): sets.append(s) w1.append(delta) tmp = [] for tt in sigre: tmp.append(complex(tt)) solutions.append(tmp) # Write continued function to the disk if delta < 0.100000005: outfile = 'imG_' + str(ipo) + '_' + str(ine) + '_' + \ str(qq) + '_' + str(q1) + '.dat' write_g_im(outfile, w, sigim) outfile = 'reG_' + str(ipo) + '_' + str(ine) + '_' + \ str(qq) + '_' + str(q1) + '.dat' write_g_re(outfile, e, sigre) # mp.dps = 12 if not use_moments: mmean = calc_mean(mmts) print('Mean moments: %12.3f %12.3f %12.3f' % (float(mmean[0]), float(mmean[1]), float(mmean[2]))) qq = len(w1) with open(logfile, 'a') as f: if not use_moments: s = '{:4s} {:4s} {:4s} {:4s} {:9s} {:15s} {:16s} {:16s}\n'. \ format('npo', 'nne', 'try', 'ils', 'delta', 'm0', 'm1', 'm2') f.write(s) else: s = '{:4s} {:4s} {:4s} {:4s} {:9s}\n'.format( 'npo', 'nne', 'try', 'ils', 'delta') f.write(s) for i in range(0, qq): f.write(sets[i]) f.write('\n') if not use_moments: s = '{0:38s}{1:12.3f}{2:12.3f}{3:12.3f}\n'. \ format('Mean moments', float(mmean[0]), float(mmean[1]), float(mmean[2])) f.write(s) f.write('\n\n\n') if qq == 0: print('There are no solutions') print("Stop at %s" % time.ctime()) end_time = time.time() run_time = end_time - start_time print(run_time) hour = int((run_time / 60) / 60) minute = int((run_time / 60) - (hour * 60)) sec = run_time % 60 print('Program runtime = %2i:%2i:%2i' % (hour, minute, sec)) sys.exit() w = 0.0 w2 = mp.zeros(len(w1), 1) w3 = mp.zeros(len(w1), 1) for i in range(0, qq): # w1[i] = 1 / w1[i] w1[i] = float(w1[i]) w3[i] = 200000 * (min(w1) - w1[i]) w2[i] = math.exp(w3[i]) for i in range(0, qq): w3[i] = w1[i] for i in range(0, qq): w1[i] = 1 / w1[i] w1sum = sum(w1) w2sum = sum(w2) for i in range(0, qq): w1[i] /= w1sum w2[i] /= w2sum with open('weights.dat', 'w') as f: for i in range(0, qq): s = '{:16.12f} {:16.12f} {:16.12f}\n'.format( float(w3[i]), float(w1[i]), float(w2[i])) # s = '{:d} {:16.12f} {:16.12f}\n'.format(i, float(w1[i]), float(w2[i])) f.write(s) sigre = mp.zeros(len(e), 1) for i in range(0, qq): for j in range(0, len(e)): sigre[j] += solutions[i][j] * w1[i] outfile = 'solution_w1.dat' write_g_re(outfile, e, sigre) sigre[:] = 0.0 for i in range(0, qq): for j in range(0, len(e)): sigre[j] += solutions[i][j] * w2[i] outfile = 'solution_w2.dat' write_g_re(outfile, e, sigre) print("Stop at %s" % time.ctime()) end_time = time.time() run_time = end_time - start_time print(run_time) hour = int((run_time / 60) / 60) minute = int((run_time / 60) - (hour * 60)) sec = run_time % 60 print('Program runtime = %i:%i:%i' % (hour, minute, sec)) with open(logfile, 'a') as f: f.write(' %f ' % run_time) f.write('Program runtime = %i:%i:%i' % (hour, minute, sec))
f.write('\n\n\n') if qq == 0: print('There are no solutions') print("Stop at %s" % time.ctime()) end_time = time.time() run_time = end_time - start_time print(run_time) hour = int((run_time / 60) / 60) minute = int((run_time / 60) - (hour * 60)) sec = run_time % 60 print('Program runtime = %2i:%2i:%2i' % (hour, minute, sec)) sys.exit() w = 0.0 w2 = mp.zeros(len(w1), 1) w3 = mp.zeros(len(w1), 1) for i in range(0, qq): # w1[i] = 1 / w1[i] w1[i] = float(w1[i]) w3[i] = 200000 * (min(w1) - w1[i]) w2[i] = math.exp(w3[i]) for i in range(0, qq): w3[i] = w1[i] for i in range(0, qq): w1[i] = 1 / w1[i] w1sum = sum(w1) w2sum = sum(w2) for i in range(0, qq):
def impose(self, basis, L, f, blocked_indices, L_done=False, f_done=False, use_mp=False): r"""Impose the condition by modifying the operator and inhomogeneity. This should be called only during the solving process when the operator matrix `L` and inhomogeneity vector `f` have been built. The caller should supply the indices of rows (via `blocked_indices`) on which previous conditions have been placed to avoid overwriting them with the new conditions. The indices used here will be added to this list (i.e. the caller only needs to repeatedly supply this list, starting with an empty one). @param basis Spectral basis used during solving. @param L Fully populated operator matrix. @param f Inhomogeneity vector. @param blocked_indices List which is updated by this method to store which equations have been replaced (in order not to replace a condition in later calls). @param L_done Whether the operator matrix `L` has already been processed and should be left untouched. May be useful if caching of matrices with conditions already imposed is done, which may speed up computation significantly. @param f_done Whether the inhomogeneity has already been processed and should be left untouched. Might be useful when re-using the same inhomogeneity (and same basis) to solve the same equation with different boundary conditions. @param use_mp Whether to use arbitrary precision math operations (`True`) or faster floating point precision operations (`False`, default). """ if L_done and f_done: return L, f alpha = _make_callable(self._alpha, use_mp=use_mp) beta = _make_callable(self._beta, use_mp=use_mp) value = _make_callable(self._value, use_mp=use_mp) num = basis.num idx = range(num) zero_row = mp.zeros(1, num) if use_mp else np.zeros(num) diff = self._idx + 1 rows_to_append = [] vals_to_append = [] for i, x in self._get_points(basis): i = self._find_free_index(i, num-1, blocked_indices) if i is not None: blocked_indices.append(i) x_phys = basis.transform(x, back=False) a = alpha(x_phys) b = beta(x_phys) v = value(x_phys) if a == b == 0: continue if not L_done: Cx = DCx = zero_row if a != 0: Cx = basis.evaluate_all_at(x, 0) if b != 0: if self._dim > 2: raise NotImplementedError("Neumann or mixed conditions not " "implemented for dim > 2.") DCx = basis.evaluate_all_at(x, diff) # NOTE: Originally, we had sparse indices (e.g. for basis functions # left out from the set). This led to high complexity in # index-types, i.e. the consecutive running indices vs. # the subscript of the actual basis function. This might # be required if we add further functionality to the # system. At this point, one remainder is that the # indices in `idx` could be sparse, whereas `j` can't. row = [a*Cx[j] + b*DCx[j] for j in range(len(idx))] if not any(row): raise NDSolveError("Boundary condition numerically ill-conditioned. " "The condition might already be satisfied by the " "chosen basis or alpha = beta = 0.") if i is None: rows_to_append.append(row) else: self._replace_row(basis.ctx, L, i, row) if not f_done: if i is None: vals_to_append.append([v]) else: f[i] = v if rows_to_append: L = self._append_rows(L, rows_to_append) if vals_to_append: f = self._append_rows(f, vals_to_append) return L, f
def construct_operator_matrix(self, op, as_numpy=False): r"""Compute the operator matrix for the given differential operator. This matrix, `L`, is defined as the matrix that results from applying the differential operator of the given problem to the matrix `M` consisting of the basis functions evaluated at the collocation points, i.e. \f[ M_{ij} = \phi_j(x_i), \f] where \f$\phi_j\f$ is the j'th basis function and \f$x_i\f$ the i'th collocation point. The differential operator is given by a list of coefficient functions `op`, where the elements correspond to the different values of `n` used for calling deriv_mat(). For example (for a 1D basis): \code{.unparsed} op[0]: multiplies the function phi(x) itself op[1]: multiplies phi'(x) op[2]: multiplies phi''(x) ... \endcode Alternatively, `op` may be a callable. In this case, it will be called with the full list of collocation points to evaluate the operator at. It should return an array/list of operator components (as above), each with as many elements as the given points list has. Args: op: Operator coefficient functions or a callable to compute them all at once or a list of lists already containing the evaluated operator. as_numpy: Whether to construct a NumPy operator matrix (if `True`) or an mpmath matrix (default), the latter being much slower and requiring much memory. """ if callable(op): sampled_op = op(np.array(self.pts) if as_numpy else self.pts) elif callable(op[0]): sampled_op = self.sample_operator_funcs(op) else: sampled_op = op real = self._is_op_real(sampled_op) L = self.zero_op(as_numpy, real=real) rows = cols = self._num if self._use_mp: fl = mp.mpf if real else mp.mpc else: fl = float if real else complex zeros = mp.zeros(1, cols) if self._use_mp else np.zeros(cols, dtype=fl) for row_idx in range(rows): row = zeros.copy() for n in range(len(sampled_op)): try: op_coeff = sampled_op[n][row_idx] except TypeError: op_coeff = sampled_op[n] if not self._use_mp: op_coeff = fl(op_coeff) if op_coeff != 0: op_row = self._compute_deriv_mat_row(n, row_idx) if op_coeff is None: # Coefficient failed to compute. Let's hope the basis # is zero there... if any(op_row): raise RuntimeError("Operator (n=%d) evaluated to " "an invalid value at %s." % (n, self.pts[row_idx])) continue row += op_coeff * op_row if as_numpy: L[row_idx, :] = lmap(fl, row) else: L[row_idx, :] = row return L
def modS_opt_mpmath(initdof, S1list, dgHfunc, mineigfunc, opttol=1e-2, modSratio=1e-2, check_iter_period=20): modenum = len(S1list) modSlist = [] epsSlist = [0] * modenum test_modSlist = [] test_S1list = [] test_epsSlist = [ 0 ] * modenum #these test_ lists are for helping to evaluate how big to set future modS for mode in range(modenum): modSlist.append(mp.matrix(S1list[mode].rows, 1)) test_modSlist.append(mp.matrix(S1list[mode].rows, 1)) test_S1list.append(mp.matrix(S1list[mode].rows, 1)) dofnum = initdof.rows dof = initdof.copy( ) #.copy() because we don't wont to modify the initialization array in place dofgrad = mp.matrix(dofnum, 1) dofHess = mp.matrix(dofnum, dofnum) tic = time.time() iternum = 0 prevD = mp.inf alphaopt_grad = mp.one alphaopt_Hess = mp.one tol_orthoS = 1e-5 dualfunc = lambda d: get_spatialProj_dualgradHess_modS(d, [], [], dgHfunc, S1list, modSlist, epsSlist, get_grad=False, get_Hess=False) while True: iternum += 1 print('the iteration number is:', iternum, flush=True) doGD = ( iternum % 2 == 0 ) #flag for deciding whether to do a gradient step or a Newton step dualval = get_spatialProj_dualgradHess_modS(dof, dofgrad, dofHess, dgHfunc, S1list, modSlist, epsSlist, get_grad=True, get_Hess=(not doGD)) objval = dualval - (dof.T * dofgrad)[0] abs_cstrt_sum = mp_dblabsdot(dof, dofgrad) print( 'current dual, objective, absolute sum of constraint violation are', dualval, objval, abs_cstrt_sum) if np.abs(dualval - objval) < opttol * min(np.abs( objval), np.abs(dualval)) and abs_cstrt_sum < opttol * min( np.abs(objval), np.abs(dualval)): #objective convergence termination break if iternum % check_iter_period == 0: print('previous dual is', prevD) if np.abs(prevD - dualval) < np.abs( dualval ) * 1e-3: #dual convergence / stuck optimization terminatino break prevD = dualval normgrad = np.linalg.norm(dofgrad) if not doGD: Ndir = mp.lu_solve(dofHess, -dofgrad) normNdir = np.linalg.norm(Ndir) pdir = Ndir / normNdir print('do regular Hessian step') print('normNdir is', normNdir) print('normgrad is', normgrad) print('Ndir dot grad is', np.dot(Ndir, dofgrad)) if doGD: print('do regular gradient step') pdir = -dofgrad / normgrad print('normgrad is', normgrad) c1 = 0.5 c2 = 0.7 #the parameters for doing line search if doGD: alpha_start = alphaopt_grad else: alpha_start = alphaopt_Hess alpha = alpha_start print('alpha before feasibility backtrack', alpha) while mineigfunc(dof + alpha * pdir)[1] <= 0: alpha *= c2 alpha_feas = alpha print('alpha before backtracking is', alpha_feas) alphaopt = alpha Dopt = mp.inf while True: tmp = dualfunc(dof + alpha * pdir) if tmp < Dopt: #the dual is still decreasing as we backtrack, continue Dopt = tmp alphaopt = alpha else: alphaopt = alpha break if tmp <= dualval + c1 * alpha * np.dot( pdir, dofgrad): #Armijo backtracking condition alphaopt = alpha break alpha *= c2 if alphaopt / alpha_start > ( c2 + 1) / 2: #in this case can start with bigger step alpha_newstart = alphaopt * 2 else: alpha_newstart = alphaopt if alpha_feas / alpha_start < ( c2 + 1 ) / 2 and alphaopt / alpha_feas > ( c2 + 1 ) / 2: #this means we encountered feasibility wall and backtracking linesearch didn't reduce step size, we should add mod source singular_dof = dof + ( alpha_feas / c2) * pdir #dof that is roughly on duality boundary singular_mode, singular_eigw, singular_eigv = mineigfunc( singular_dof, eigvals_only=False) if epsSlist[singular_mode] <= 0: print('new modS aded at mode', singular_mode) test_modSlist[singular_mode] = singular_eigv test_epsSlist[singular_mode] = 1.0 modval = np.abs( get_spatialProj_dualgradHess_modS(singular_dof, [], [], dgHfunc, test_S1list, test_modSlist, test_epsSlist, get_grad=False, get_Hess=False)) modSlist[singular_mode] = singular_eigv epsSlist[singular_mode] = mp.sqrt(modSratio * np.abs(dualval / modval)) test_modSlist[singular_mode] = mp.zeros( S1list[singular_mode].rows, 1) test_epsSlist[singular_mode] = 0 elif np.abs(mp_conjdot(singular_eigv, modSlist[singular_mode])) < tol_orthoS: print('changed modS at mode', singular_mode) modSlist[singular_mode] = mp.sqrt(0.5) * ( modSlist[singular_mode] + singular_eigv) print('stepsize alphaopt is', alphaopt, '\n') dof += alpha * pdir if doGD: alphaopt_grad = alpha_newstart else: alphaopt_Hess = alpha_newstart ####NOW WE GRADUALLY BRING THE MAGNITUDES OF THE MODS DOWN TO ZERO#### alphaopt_grad = max(alphaopt_grad, 5e-5 * mp.one) alphaopt_Hess = max(alphaopt_Hess, 5e-5 * mp.one) minalphatol = 1e-10 olddualval = dualval reductFactor = 1e-1 reductCount = 1 lastReduct = False while True: #gradual reduction of modified source amplitude, outer loop if not lastReduct: for i in range(len(epsSlist)): epsSlist[i] *= reductFactor modSratio *= reductFactor else: for i in range(len(epsSlist)): epsSlist[i] = 0 iternum = 0 while True: iternum += 1 print('reducing modS now, at reduction #', reductCount, 'the iteration number is:', iternum, flush=True) doGD = ( iternum % 2 == 0 ) #flag for deciding whether to do a gradient step or a Newton step dualval = get_spatialProj_dualgradHess_modS(dof, dofgrad, dofHess, dgHfunc, S1list, modSlist, epsSlist, get_grad=True, get_Hess=(not doGD)) objval = dualval - (dof.T * dofgrad)[0] abs_cstrt_sum = mp_dblabsdot(dof, dofgrad) print( 'current dual, objective, absolute sum of constraint violation are', dualval, objval, abs_cstrt_sum) if np.abs(dualval - objval) < opttol * min(np.abs( objval), np.abs(dualval)) and abs_cstrt_sum < opttol * min( np.abs(objval), np.abs(dualval)): #objective convergence termination break if iternum % check_iter_period == 0: print('previous dual is', prevD) if np.abs(prevD - dualval) < np.abs( dualval ) * 1e-3: #dual convergence / stuck optimization termination break if alphaopt_grad < minalphatol and alphaopt_Hess < minalphatol: alphaopt_grad = 5e-5 alphaopt_Hess = 5e-5 #periodically boost the max step size since we are gradually turning off the modified sources ##SHOULD MAKE THIS MORE TRANSPARENT IN THE FUTURE## prevD = dualval normgrad = np.linalg.norm(dofgrad) if not doGD: Ndir = mp.lu_solve(dofHess, -dofgrad) normNdir = np.linalg.norm(Ndir) pdir = Ndir / normNdir print('do regular Hessian step') print('normNdir is', normNdir) print('normgrad is', normgrad) print('Ndir dot grad is', np.dot(Ndir, dofgrad)) if doGD: print('do regular gradient step') pdir = -dofgrad / normgrad print('normgrad is', normgrad) c1 = 0.5 c2 = 0.7 #the parameters for doing line search if doGD: alpha_start = alphaopt_grad else: alpha_start = alphaopt_Hess alpha = alpha_start print('alpha before feasibility backtrack', alpha) while mineigfunc(dof + alpha * pdir)[1] <= 0: alpha *= c2 alpha_feas = alpha print('alpha before backtracking is', alpha_feas) alphaopt = alpha Dopt = mp.inf while True: tmp = dualfunc(dof + alpha * pdir) if tmp < Dopt: #the dual is still decreasing as we backtrack, continue Dopt = tmp alphaopt = alpha else: alphaopt = alpha break if tmp <= dualval + c1 * alpha * np.dot( pdir, dofgrad): #Armijo backtracking condition alphaopt = alpha break alpha *= c2 if alphaopt / alpha_start > ( c2 + 1) / 2: #in this case can start with bigger step alpha_newstart = alphaopt * 2 else: alpha_newstart = alphaopt if (not lastReduct) and alpha_feas / alpha_start < ( c2 + 1 ) / 2 and alphaopt / alpha_feas > ( c2 + 1 ) / 2: #don't bother to modify sources if this is the final reduction iteration singular_dof = dof + ( alpha_feas / c2) * pdir #dof that is roughly on duality boundary singular_mode, singular_eigw, singular_eigv = mineigfunc( singular_dof, eigvals_only=False) if epsSlist[singular_mode] <= 0: print('new modS aded at mode', singular_mode) test_modSlist[singular_mode] = singular_eigv test_epsSlist[singular_mode] = mp.one modval = np.abs( get_spatialProj_dualgradHess_modS(singular_dof, [], [], dgHfunc, test_S1list, test_modSlist, test_epsSlist, get_grad=False, get_Hess=False)) modSlist[singular_mode] = singular_eigv epsSlist[singular_mode] = mp.sqrt( modSratio * np.abs(dualval / modval)) test_modSlist[singular_mode] = mp.zeros( S1list[singular_mode].rows, 1) test_epsSlist[singular_mode] = 0 elif np.abs(np.vdot(singular_eigv, modSlist[singular_mode])) < tol_orthoS: print('changed modS at mode', singular_mode) modSlist[singular_mode] = mp.sqrt(0.5) * ( modSlist[singular_mode] + singular_eigv) print('stepsize alphaopt is', alphaopt, '\n') dof += alpha * pdir if doGD: alphaopt_grad = alpha_newstart else: alphaopt_Hess = alpha_newstart if lastReduct: break if np.abs(olddualval - dualval) < opttol * np.abs(dualval): lastReduct = True olddualval = dualval reductCount += 1 print('time elapsed:', time.time() - tic, flush=True) return dof, dofgrad, dualval, objval
def zdipole_field_xy2D_periodic_array(k0, L, xd, yd, zd, xp, yp, zp, sumtol=1e-12): #field at coord (xp,yp,zp) of z-polarized dipoles in a square array in the z=czd plane #generated by summing over discrete k-vectors; in evanescent region the summand decreases exponentially with increasing kp and abskz #it seems that directly summing over individual dipole fields leads to convergence issues due to relatively slow (polynomial) decay of dipole fields #order of summation in k-space: concentric squares, 0th square the origin, 1st square infinity-norm radius 1, 2nd square infinity-norm radius 2... #since we are using the angular representation for consistency we insist that zp>zd """ if not (xp<L/2 and xp>-L/2 and yp<L/2 and yp>-L/2): return 'target point out of Brillouin zone' """ if zp <= zd: return 'need zp>zd' field = mp.zeros(3, 1) oldfield = mp.zeros(3, 1) deltak = 2 * mp.pi / L deltax = xp - xd deltay = yp - yd deltaz = zp - zd i = 1 #label of which square we are on prefac = -1j / (2 * L**2 * k0**2) while True: #termination condition in loop #sum over the i-th square lkx = -i * deltak rkx = i * deltak for iy in range(-i, i + 1): ky = iy * deltak kpsqr = lkx**2 + ky**2 kz = mp.sqrt(k0**2 - kpsqr) lphasefac = mp.expj(lkx * deltax + ky * deltay + kz * deltaz) rphasefac = mp.expj(rkx * deltax + ky * deltay + kz * deltaz) field[0] += prefac * (lkx * lphasefac + rkx * rphasefac) field[1] += prefac * (lphasefac + rphasefac) * ky field[2] += prefac * (lphasefac + rphasefac) * (-kpsqr / kz) bky = -i * deltak uky = i * deltak for ix in range(-i + 1, i): kx = ix * deltak kpsqr = kx**2 + bky**2 kz = mp.sqrt(k0**2 - kpsqr) bphasefac = mp.expj(kx * deltax + bky * deltay + kz * deltaz) uphasefac = mp.expj(kx * deltax + uky * deltay + kz * deltaz) field[0] += prefac * (bphasefac + uphasefac) * kx field[1] += prefac * (bphasefac * bky + uphasefac * uky) field[2] += prefac * (bphasefac + uphasefac) * (-kpsqr / kz) if mp.norm(field - oldfield) < mp.norm(field) * sumtol: break print('i', i) #mp.nprint(field) oldfield = field.copy() i += 1 return field