def division(polys, divisor_var=0, max_cond_num=1.e6, macaulay_zero_tol=1.e-12, verbose=False, polish=False, return_all_roots=True): '''Calculates the common zeros of polynomials using a division matrix. Parameters -------- polys: list of MultiCheb Polynomials The polynomials for which the common roots are found. divisor_var : int What variable is being divided by. 0 is x, 1 is y, etc. Defaults to x. max_cond_num : float The maximum condition number of the Macaulay Matrix Reduction macaulay_zero_tol : float What is considered 0 in the macaulay matrix reduction. verbose : bool If True prints information about the solve. polish: bool If True runs a newton polish on the zeros before returning. return_all_roots : bool If True returns all the roots, otherwise just the ones in the unit box. Returns ----------- zeros : numpy array The common roots of the polynomials. Each row is a root. ''' #This first section creates the Macaulay Matrix with the monomials that don't have #the divisor variable in the first columns. polys, transform, is_projected = polys, lambda x: x, False if len(polys) == 1: from yroots.OneDimension import solve return transform(solve(polys[0], MSmatrix=0)) power = is_power(polys) dim = polys[0].dim matrix_degree = np.sum([poly.degree for poly in polys]) - len(polys) + 1 poly_coeff_list = [] for poly in polys: poly_coeff_list = add_polys(matrix_degree, poly, poly_coeff_list) matrix, matrix_terms, cuts = create_matrix(poly_coeff_list, matrix_degree, dim, divisor_var) if verbose: np.set_printoptions(suppress=False, linewidth=200) print('\nStarting Macaulay Matrix\n', matrix) print( '\nColumns in Macaulay Matrix\nFirst element in tuple is degree of x monomial, Second element is degree of y monomial \n', matrix_terms) print( '\nLocation of Cuts in the Macaulay Matrix into [ Mb | M1* | M2* ]\n', cuts) #If bottom left is zero only does the first QR reduction on top part of matrix (for speed). Otherwise does it on the whole thing if np.allclose(matrix[cuts[0]:, :cuts[0]], 0): matrix, matrix_terms = rrqr_reduceMacaulay( matrix, matrix_terms, cuts, max_cond_num=max_cond_num, macaulay_zero_tol=macaulay_zero_tol) else: matrix, matrix_terms = rrqr_reduceMacaulay( matrix, matrix_terms, cuts, max_cond_num=max_cond_num, macaulay_zero_tol=macaulay_zero_tol) if isinstance(matrix, int): return -1 VB = matrix_terms[matrix.shape[0]:] if verbose: np.set_printoptions(suppress=True, linewidth=200) print("\nFinal Macaulay Matrix\n", matrix) print("\nColumns in Macaulay Matrix\n", matrix_terms) #------------> chebyshev if not power: #Builds the inverse matrix. The terms are the vector basis as well as y^k/x terms for all k. Reducing #this matrix allows the y^k/x terms to be reduced back into the vector basis. x_pows_over_y = matrix_terms[np.where( matrix_terms[:, divisor_var] == 0)[0]] x_pows_over_y[:, divisor_var] = -np.ones(x_pows_over_y.shape[0], dtype='int') inv_matrix_terms = np.vstack((x_pows_over_y, VB)) inv_matrix = np.zeros([len(x_pows_over_y), len(inv_matrix_terms)]) #A bunch of different dictionaries are used below for speed purposes and to prevent repeat calculations. #A dictionary of term in inv_matrix_terms to their spot in inv_matrix_terms. inv_spot_dict = dict() spot = 0 for term in inv_matrix_terms: inv_spot_dict[tuple(term)] = spot spot += 1 #A dictionary of terms on the diagonal to their reduction in the vector basis. diag_reduction_dict = dict() for i in range(matrix.shape[0]): term = matrix_terms[i] diag_reduction_dict[tuple(term)] = matrix[i][-len(VB):] #A dictionary of terms to the terms in their quotient when divided by x. (symbolically) divisor_terms_dict = dict() for term in matrix_terms: divisor_terms_dict[tuple(term)] = get_divisor_terms( term, divisor_var) #A dictionary of terms to their quotient when divided by x. (in the vector basis) term_divide_dict = dict() for term in matrix_terms[-len(VB):]: term_divide_dict[tuple(term)] = divide_term( term, inv_matrix_terms, inv_spot_dict, diag_reduction_dict, len(VB), divisor_terms_dict) #Builds the inv_matrix by dividing the rows of matrix by x. for i in range(cuts[0]): inv_matrix[i] = divide_row(matrix[i][-len(VB):], matrix_terms[-len(VB):], term_divide_dict, len(inv_matrix_terms)) spot = matrix_terms[i] spot[divisor_var] -= 1 inv_matrix[i][inv_spot_dict[tuple(spot)]] += 1 #Reduces the inv_matrix to solve for the y^k/x terms in the vector basis. Q, R = qr(inv_matrix) if np.linalg.cond(R[:, :R.shape[0]]) > max_cond_num: return -1 inv_solutions = np.hstack((np.eye(R.shape[0]), solve_triangular(R[:, :R.shape[0]], R[:, R.shape[0]:]))) #A dictionary of term in the vector basis to their spot in the vector basis. VB_spot_dict = dict() spot = 0 for row in VB: VB_spot_dict[tuple(row)] = spot spot += 1 #A dictionary of terms of type y^k/x to their reduction in the vector basis. inv_reduction_dict = dict() for i in range(len(inv_solutions)): inv_reduction_dict[tuple( inv_matrix_terms[i])] = inv_solutions[i][len(inv_solutions):] #Builds the division matrix and finds the eigenvalues and eigenvectors. division_matrix = build_division_matrix(VB, VB_spot_dict, diag_reduction_dict, inv_reduction_dict, divisor_terms_dict) #<---------end Chebyshev else: #--------->Power basisDict = makeBasisDict(matrix, matrix_terms, VB) #Dictionary of terms in the vector basis their spots in the matrix. VBdict = {} spot = 0 for row in VB: VBdict[tuple(row)] = spot spot += 1 # Build division matrix division_matrix = np.zeros((len(VB), len(VB))) for i in range(VB.shape[0]): var = np.zeros(dim) var[divisor_var] = 1 term = tuple(VB[i] - var) if term in VBdict: division_matrix[VBdict[term]][i] += 1 else: division_matrix[:, i] -= basisDict[term] #<----------end Power vals, vecs = eig(division_matrix, left=True, right=False) #conjugate because scipy gives the conjugate eigenvector vecs = vecs.conj() # if len(vals) > len(np.unique(np.round(vals, 10))): # return -1 # eigenvalue_cond = np.linalg.cond(vecs) # if eigenvalue_cond*tol > 1: # return -1 # if verbose: # print("\nDivision Matrix\n", np.round(division_matrix[::-1,::-1], 2)) # print("\nLeft Eigenvectors (as rows)\n", vecs.T) # if not power: # if np.max(np.abs(vals)) > 1.e6: # return -1 #Calculates the zeros, the x values from the eigenvalues and the y values from the eigenvectors. zeros = list() for i in range(len(vals)): # if power and abs(vecs[-1][i]) < 1.e-3: # #This root has magnitude greater than 1, will possibly generate a false root due to instability # continue # if np.abs(vals[i]) < 1.e-5: # continue root = np.zeros(dim, dtype=complex) for spot in range(0, divisor_var): root[spot] = vecs[-(2 + spot)][i] / vecs[-1][i] for spot in range(divisor_var + 1, dim): root[spot] = vecs[-(1 + spot)][i] / vecs[-1][i] root[divisor_var] = 1 / vals[i] # conditions = condeigv(division_matrix.T) # if np.abs(vals[i]) > 1: # print(root, conditions[i]) if polish: root = newton_polish(polys, root) #throw out bad roots in cheb if not power: if np.any([abs(poly(root)) > 1.e-1 for poly in polys]): continue zeros.append(root) if return_all_roots: return transform(np.array(zeros)) else: # only return roots in the unit complex hyperbox zeros = transform(np.array(zeros)) return zeros[np.all(np.abs(zeros) <= 1, axis=0)]
def multiplication(polys, max_cond_num, macaulay_zero_tol, verbose=False, MSmatrix=0, return_all_roots=True): ''' Finds the roots of the given list of multidimensional polynomials using a multiplication matrix. Parameters ---------- polys : list of polynomial objects Polynomials to find the common roots of. verbose : bool Prints information about how the roots are computed. MSmatrix : int Controls which Moller-Stetter matrix is constructed. The options are: 0 (default) -- The Moller-Stetter matrix of a random polynomial Some positive integer i < dimension -- The Moller-Stetter matrix of x_i return_all_roots : bool If True returns all the roots, otherwise just the ones in the unit box. max_cond_num : float The maximum condition number of the Macaulay Matrix Reduction macaulay_zero_tol : float What is considered 0 in the macaulay matrix reduction. returns ------- roots : numpy array The common roots of the polynomials. Each row is a root. Raises ------ ConditioningError if MSMultMatrix(...) raises a ConditioningError. ''' #We don't want to use Linear Projection right now # polys, transform, is_projected = polys, lambda x:x, False if len(polys) == 1: from yroots.OneDimension import solve return transform(solve(polys[0], MSmatrix=0)) poly_type = is_power(polys, return_string=True) dim = polys[0].dim if MSmatrix not in list(range(dim + 1)): raise ValueError( 'MSmatrix must be 0 (random polynomial), or the index of a variable' ) #By Bezout's Theorem. Useful for making sure that the reduced Macaulay Matrix is as we expect degrees = [poly.degree for poly in polys] max_number_of_roots = np.prod(degrees) try: m_f, var_dict, basisDict, VB = MSMultMatrix( polys, poly_type, verbose=verbose, MSmatrix=MSmatrix, max_cond_num=max_cond_num, macaulay_zero_tol=macaulay_zero_tol) except ConditioningError as e: raise e if verbose: print("\nM_f:\n", m_f[::-1, ::-1]) # Get list of indexes of single variables and store vars that were not # in the vector space basis. var_spots = [] removed_var_order = [] removed_var_spots = [] var_mask = [] for order, spot in enumerate(get_var_list(dim)): if spot in var_dict: var_spots.append(var_dict[tuple(spot)]) var_mask.append(True) else: removed_var_order.append(order) removed_var_spots.append(spot) var_mask.append(False) # Get left eigenvectors (come in conjugate pairs) vals, vecs = eig(m_f, left=True, right=False) if verbose: print('\nLeft Eigenvectors (as rows)\n', vecs.T) print('\nEigenvals\n', vals) zeros_spot = var_dict[tuple(0 for i in range(dim))] #throw out roots that were calculated unstably # vecs = vecs[:,np.abs(vecs[zeros_spot]) > 1.e-10] if verbose: print('\nVariable Spots in the Vector\n', var_spots) print('\nEigeinvecs at the Variable Spots:\n', vecs[var_spots]) print('\nConstant Term Spot in the Vector\n', zeros_spot) print('\nEigeinvecs at the Constant Term\n', vecs[zeros_spot]) roots = np.zeros([len(vecs), dim], dtype=complex) roots[:, var_mask] = (vecs[var_spots] / vecs[zeros_spot]).T #Compute the removed variables for order, spot in zip(removed_var_order, removed_var_spots): for coeff, pows in zip(basisDict[spot], VB): temp = coeff for place, num in enumerate(pows): if num > 0: temp *= roots[:, place]**num roots[:, order] -= temp #Check if too many roots assert roots.shape[ 0] <= max_number_of_roots, "Found too many roots,{}/{}/{}:{}".format( roots.shape, max_number_of_roots, degrees, roots) if return_all_roots: roots = np.array(roots, dtype=complex) # #print(roots) # REMARK: We don't always have good information about the derivatives, # so we can't use Newton polishing on our roots. # for i in range(len(roots)): # roots[i] = newton_polish(polys,roots[i],niter=100,tol=1e-20) # #print(roots) return roots else: # only return roots in the unit complex hyperbox return roots[np.all(np.abs(roots) <= 1, axis=0)]
def MacaulayReduction(initial_poly_list, max_cond_num, macaulay_zero_tol, verbose=False): """Reduces the Macaulay matrix to find a vector basis for the system of polynomials. Parameters -------- initial_poly_list: list The polynomials in the system we are solving. max_cond_num : float The maximum condition number of the Macaulay Matrix Reduction macaulay_zero_tol : float What is considered 0 in the macaulay matrix reduction. verbose : bool Prints information about how the roots are computed. Returns ----------- basisDict : dict A dictionary of terms not in the vector basis a matrixes of things in the vector basis that the term can be reduced to. VB : numpy array The terms in the vector basis, each row being a term. varsToRemove : list The variables to remove from the basis because we have linear polysnomials Raises ------ ConditioningError if rrqr_reduceMacaulay(...) raises a ConditioningError. """ power = is_power(initial_poly_list) dim = initial_poly_list[0].dim poly_coeff_list = [] degree = find_degree(initial_poly_list) linear_polys = [poly for poly in initial_poly_list if poly.degree == 1] nonlinear_polys = [poly for poly in initial_poly_list if poly.degree != 1] #Choose which variables to remove if things are linear, and add linear polys to matrix if len(linear_polys) == 1: #one linear varsToRemove = [ np.argmax(np.abs(linear_polys[0].coeff[get_var_list(dim)])) ] poly_coeff_list = add_polys(degree, linear_polys[0], poly_coeff_list) elif len(linear_polys) > 1: #multiple linear #get the row rededuced linear coefficients A, Pc = nullspace(linear_polys) varsToRemove = Pc[:len(A)].copy() #add to macaulay matrix for row in A: #reconstruct a polynomial for each row coeff = np.zeros([2] * dim) coeff[get_var_list(dim)] = row[:-1] coeff[tuple([0] * dim)] = row[-1] if power: poly = MultiPower(coeff) else: poly = MultiCheb(coeff) poly_coeff_list = add_polys(degree, poly, poly_coeff_list) else: #no linear varsToRemove = [] #add nonlinear polys to poly_coeff_list for poly in nonlinear_polys: poly_coeff_list = add_polys(degree, poly, poly_coeff_list) #Creates the matrix matrix, matrix_terms, cuts = create_matrix(poly_coeff_list, degree, dim, varsToRemove) if verbose: np.set_printoptions(suppress=False, linewidth=200) print('\nStarting Macaulay Matrix\n', matrix) print( '\nColumns in Macaulay Matrix\nFirst element in tuple is degree of x, Second element is degree of y\n', matrix_terms) print( '\nLocation of Cuts in the Macaulay Matrix into [ Mb | M1* | M2* ]\n', cuts) try: matrix, matrix_terms = rrqr_reduceMacaulay( matrix, matrix_terms, cuts, max_cond_num=max_cond_num, macaulay_zero_tol=macaulay_zero_tol) except ConditioningError as e: raise e # TODO: rrqr_reduceMacaulay2 is not working when expected. # if np.allclose(matrix[cuts[0]:,:cuts[0]], 0): # matrix, matrix_terms = rrqr_reduceMacaulay2(matrix, matrix_terms, cuts, accuracy = accuracy) # else: # matrix, matrix_terms = rrqr_reduceMacaulay(matrix, matrix_terms, cuts, accuracy = accuracy) if verbose: np.set_printoptions(suppress=True, linewidth=200) print("\nFinal Macaulay Matrix\n", matrix) print("\nColumns in Macaulay Matrix\n", matrix_terms) VB = matrix_terms[matrix.shape[0]:] basisDict = makeBasisDict(matrix, matrix_terms, VB, power) return basisDict, VB, varsToRemove
def solve(polys, MSmatrix=0, eigvals=True, verbose=False, return_all_roots=True, max_cond_num=1.e6, macaulay_zero_tol=1.e-12): ''' Finds the roots of the given list of polynomials. Parameters ---------- polys : list of polynomial objects Polynomials to find the common roots of. MSmatrix : int Controls which Moller-Stetter matrix is constructed For a univariate polynomial, the options are: 0 (default) -- The companion or colleague matrix, rotated 180 degrees 1 -- The unrotated companion or colleague matrix -1 -- The inverse of the companion or colleague matrix For a multivariate polynomial, the options are: 0 (default) -- The Moller-Stetter matrix of a random polynomial Some positive integer i <= dimension -- The Moller-Stetter matrix of x_i, where variables are index from x1, ..., xn Some negative integer i >= -dimension -- The Moller-Stetter matrix of x_i-inverse eigvals : bool Whether to compute roots of univariate polynomials from eigenvalues (True) or eigenvectors (False). Roots of multivariate polynomials are always comptued from eigenvectors verbose : bool Prints information about how the roots are computed. return_all_roots : bool If True returns all the roots, otherwise just the ones in the unit box. max_cond_num : float The maximum condition number of the Macaulay Matrix Reduction macaulay_zero_tol : float What is considered 0 in the macaulay matrix reduction. returns ------- roots : numpy array The common roots of the polynomials. Each row is a root. ''' polys = match_poly_dimensions(polys) # Determine polynomial type and dimension of the system poly_type = is_power(polys, return_string=True) dim = polys[0].dim if dim == 1: if len(polys) == 1: return oneD.solve(polys[0], MSmatrix=MSmatrix, eigvals=eigvals, verbose=verbose) else: zeros = np.unique( oneD.solve(polys[0], MSmatrix=MSmatrix, eigvals=eigvals, verbose=verbose)) #Finds the roots of each succesive polynomial and checks which roots are common. for poly in polys[1:]: if len(zeros) == 0: break zeros2 = np.unique( oneD.solve(poly, MSmatrix=MSmatrix, eigvals=eigvals, verbose=verbose)) common = list() tol = 1.e-10 for zero in zeros2: spot = np.where(np.abs(zeros - zero) < tol) if len(spot[0]) > 0: common.append(zero) zeros = common return zeros else: if MSmatrix < 0: return division(polys, verbose=verbose, divisor_var=-MSmatrix - 1, return_all_roots=return_all_roots, max_cond_num=max_cond_num, macaulay_zero_tol=macaulay_zero_tol) else: return multiplication(polys, verbose=verbose, MSmatrix=MSmatrix, return_all_roots=return_all_roots, max_cond_num=max_cond_num, macaulay_zero_tol=macaulay_zero_tol)
def build_macaulay(initial_poly_list, verbose=False): """Constructs the unreduced Macaulay matrix. Removes linear polynomials by substituting in for a number of variables equal to the number of linear polynomials. Parameters -------- initial_poly_list: list The polynomials in the system we are solving. verbose : bool Prints information about how the roots are computed. Returns ----------- matrix : 2d ndarray The Macaulay matrix matrix_terms : 2d integer ndarray Array containing the ordered basis, where the ith row contains the exponent/degree of the ith basis monomial cut : int Where to cut the Macaulay matrix for the highest-degree monomials varsToRemove : list The variables removed with removing linear polynomials A : 2d ndarray A matrix giving the linear relations between the removed variables and the remaining variables Pc : 1d integer ndarray Array containing the order of the variables as the appear in the columns of A """ power = is_power(initial_poly_list) dim = initial_poly_list[0].dim poly_coeff_list = [] degree = find_degree(initial_poly_list) linear_polys = [poly for poly in initial_poly_list if poly.degree == 1] nonlinear_polys = [poly for poly in initial_poly_list if poly.degree != 1] #Choose which variables to remove if things are linear, and add linear polys to matrix if len(linear_polys) >= 1: #Linear polys involved #get the row rededuced linear coefficients A, Pc = nullspace(linear_polys) varsToRemove = Pc[:len(A)].copy() #add to macaulay matrix for row in A: #reconstruct a polynomial for each row coeff = np.zeros([2] * dim) coeff[tuple(get_var_list(dim))] = row[:-1] coeff[tuple([0] * dim)] = row[-1] if not power: poly = MultiCheb(coeff) else: poly = MultiPower(coeff) poly_coeff_list = add_polys(degree, poly, poly_coeff_list) else: #no linear A, Pc = None, None varsToRemove = [] #add nonlinear polys to poly_coeff_list for poly in nonlinear_polys: poly_coeff_list = add_polys(degree, poly, poly_coeff_list) #Creates the matrix # return (*create_matrix(poly_coeff_list, degree, dim, varsToRemove), A, Pc) return create_matrix(poly_coeff_list, degree, dim, varsToRemove)
def multiplication(polys, max_cond_num, verbose=False, return_all_roots=True, method='svd'): ''' Finds the roots of the given list of multidimensional polynomials using a multiplication matrix. Parameters ---------- polys : list of polynomial objects Polynomials to find the common roots of. max_cond_num : float The maximum condition number of the Macaulay Matrix Reduction verbose : bool Prints information about how the roots are computed. return_all_roots : bool If True returns all the roots, otherwise just the ones in the unit box. returns ------- roots : numpy array The common roots of the polynomials. Each row is a root. Raises ------ ConditioningError if reduce_macaulay() raises a ConditioningError. TooManyRoots if the macaulay matrix returns more roots than the Bezout bound. ''' #We don't want to use Linear Projection right now # polys, transform, is_projected = polys, lambda x:x, False if len(polys) == 1: from yroots.OneDimension import solve return transform(solve(polys[0], MSmatrix=0)) poly_type = is_power(polys, return_string=True) dim = polys[0].dim #By Bezout's Theorem. Useful for making sure that the reduced Macaulay Matrix is as we expect degrees = [poly.degree for poly in polys] max_number_of_roots = np.prod(degrees) matrix, matrix_terms, cut = build_macaulay(polys, verbose) # Attempt to reduce the Macaulay matrix if method == 'qrt': try: E, Q, cond, cond_back = reduce_macaulay_qrt( matrix, cut, max_cond_num) except ConditioningError as e: raise e elif method == 'tvb': try: E, Q, cond, cond_back = reduce_macaulay_tvb( matrix, cut, max_cond_num) except ConditioningError as e: raise e elif method == 'svd': try: E, Q, cond, cond_back = reduce_macaulay_svd( matrix, cut, max_cond_num) except ConditioningError as e: raise e # Construct the Möller-Stetter matrices # M is a 3d array containing the multiplication-by-x_i matrix in M[...,i] if poly_type == "MultiCheb": if method == 'qrt' or method == 'svd': M = ms_matrices_cheb(E, Q, matrix_terms, dim) elif method == 'tvb': M = ms_matrices_p_cheb(E, Q, matrix_terms, dim, cut) else: if method == 'qrt' or method == 'svd': M = ms_matrices(E, Q, matrix_terms, dim) elif method == 'tvb': M = ms_matrices_p(E, Q, matrix_terms, dim, cut) # Compute the roots using eigenvalues of the Möller-Stetter matrices roots, cond_eig = msroots(M) # Check if too many roots if roots.shape[0] > max_number_of_roots: raise TooManyRoots("Found too many roots,{}/{}/{}:{}".format( roots.shape, max_number_of_roots, degrees, roots)) if return_all_roots: return roots else: # only return roots in the unit complex hyperbox return roots[np.all(np.abs(roots) <= 1, axis=0)]
def multiplication(polys, max_cond_num, verbose=False, return_all_roots=True,method='svd'): ''' Finds the roots of the given list of multidimensional polynomials using a multiplication matrix. Parameters ---------- polys : list of polynomial objects Polynomials to find the common roots of. max_cond_num : float The maximum condition number of the Macaulay Matrix Reduction verbose : bool Prints information about how the roots are computed. return_all_roots : bool If True returns all the roots, otherwise just the ones in the unit box. returns ------- roots : numpy array The common roots of the polynomials. Each row is a root. ''' #We don't want to use Linear Projection right now # polys, transform, is_projected = polys, lambda x:x, False if len(polys) == 1: from yroots.OneDimension import solve return transform(solve(polys[0], MSmatrix=0)) poly_type = is_power(polys, return_string = True) dim = polys[0].dim #By Bezout's Theorem. Useful for making sure that the reduced Macaulay Matrix is as we expect bezout_bound = np.prod([poly.degree for poly in polys]) matrix, matrix_terms, cut = build_macaulay(polys, verbose) roots = np.array([]) # If cut is zero, then all the polynomials are linear and we solve # using solve_linear. if cut == 0: roots, cond = solve_linear([p.coeff for p in polys]) # Make sure roots is a 2D array. roots = np.array([roots]) else: # Attempt to reduce the Macaulay matrix if method == 'svd': res = reduce_macaulay_svd(matrix,cut,bezout_bound,max_cond_num) if res[0] is None: return res E,Q = res elif method == 'qrt': res = reduce_macaulay_qrt(matrix,cut,bezout_bound,max_cond_num) if res[0] is None: return res E,Q = res elif method == 'tvb': res = reduce_macaulay_tvb(matrix,cut,bezout_bound,max_cond_num) if res[0] is None: return res E,Q = res else: raise ValueError("Method must be one of 'svd','qrt' or 'tvb'") # Construct the Möller-Stetter matrices # M is a 3d array containing the multiplication-by-x_i matrix in M[...,i] if poly_type == "MultiCheb": if method == 'qrt' or method == 'svd': M = ms_matrices_cheb(E,Q,matrix_terms,dim) elif method == 'tvb': M = ms_matrices_p_cheb(E,Q,matrix_terms,dim,cut) else: if method == 'qrt' or method == 'svd': M = ms_matrices(E,Q,matrix_terms,dim) elif method == 'tvb': M = ms_matrices_p(E,Q,matrix_terms,dim,cut) # Compute the roots using eigenvalues of the Möller-Stetter matrices roots = msroots(M) if return_all_roots: return roots else: # only return roots in the unit complex hyperbox return roots[[np.all(np.abs(root) <= 1) for root in roots]]
def divisionNew(polys, divisor_var=0, tol=1.e-12, verbose=False, polish=False, return_all_roots=True): '''Calculates the common zeros of polynomials using a division matrix. Parameters -------- polys: list of MultiCheb Polynomials The polynomials for which the common roots are found. divisor_var : int What variable is being divided by. 0 is x, 1 is y, etc. Defaults to x. tol : float The tolerance parameter for the Macaulay Reduce. verbose : bool If True prints information about the solve. polish: bool If True runs a newton polish on the zeros before returning. Returns ----------- zeros : numpy array The common roots of the polynomials. Each row is a root. ''' #This first section creates the Macaulay Matrix with the monomials that don't have #the divisor variable in the first columns. polys, transform, is_projected = LinearProjection.remove_linear( polys, 1e-4, 1e-8) if len(polys) == 1: from yroots.OneDimension import solve return transform(solve(polys[0], MSmatrix=0)) power = is_power(polys) if power: raise ValueError("This only works for Chebyshev polynomials") dim = polys[0].dim matrix_degree = np.sum([poly.degree for poly in polys]) - len(polys) + 1 poly_coeff_list = [] for poly in polys: poly_coeff_list = add_polys(matrix_degree, poly, poly_coeff_list) matrix, matrix_terms, cuts = create_matrix(poly_coeff_list, matrix_degree,\ dim, divisor_var) x_pows_over_y = matrix_terms[:cuts[0]].copy() x_pows_over_y[:, divisor_var] = -np.ones(cuts[0], dtype='int') inv_matrix_terms = np.vstack((x_pows_over_y, matrix_terms)) num_rows = matrix.shape[0] inv_matrix = np.zeros([num_rows + matrix.shape[0], len(inv_matrix_terms)]) cuts = tuple([cuts[0], cuts[0] + cuts[1]]) #A dictionary of term in inv_matrix_terms to their spot in inv_matrix_terms. inv_spot_dict = dict() spot = 0 for term in inv_matrix_terms: inv_spot_dict[tuple(term)] = spot spot += 1 #A dictionary of terms to the terms in their quotient when divided by x. (symbolically) divisor_terms_dict = dict() for term in matrix_terms: divisor_terms_dict[tuple(term)] = get_divisor_terms(term, divisor_var) #A dictionary of terms to their quotient when divided by x. (in the vector basis) term_divide_dict = dict() for term in matrix_terms: term_divide_dict[tuple(term)] = divide_term(term, inv_matrix_terms, inv_spot_dict, divisor_terms_dict) #Builds the inv_matrix by dividing the rows of matrix by x. for i in range(num_rows): inv_matrix[i] = divide_row(matrix[i], matrix_terms, term_divide_dict, len(inv_matrix_terms)) inv_matrix[num_rows:, cuts[0]:] = matrix matrix, matrix_terms, perm = rrqr_reduceMacaulay(inv_matrix, inv_matrix_terms, cuts, accuracy=tol, return_perm=True) if isinstance(matrix, int): return -1 for term in term_divide_dict: term_divide_dict[tuple(term)] = term_divide_dict[tuple(term)][perm] VB = matrix_terms[matrix.shape[0]:] basisDict = makeBasisDict2(matrix, matrix_terms) # print(len(VB)) #Dictionary of terms in the vector basis their spots in the matrix. VBdict = {} spot = 0 for row in VB: VBdict[tuple(row)] = spot spot += 1 #Builds the division matrix and finds the eigenvalues and eigenvectors. division_matrix = build_division_matrix(VB, VBdict, basisDict, term_divide_dict, matrix_terms) vals, vecs = eig(division_matrix, left=True, right=False) #conjugate because scipy gives the conjugate eigenvector vecs = vecs.conj() if len(vals) > len(np.unique(np.round(vals, 10))): return -1 vals2, vecs2 = eig(vecs) sorted_vals2 = np.sort(np.abs(vals2)) #Sorted smallest to biggest if sorted_vals2[0] < sorted_vals2[-1] * tol: return -1 if verbose: print("\nDivision Matrix\n", np.round(division_matrix[::-1, ::-1], 2)) print("\nLeft Eigenvectors (as rows)\n", vecs.T) if np.max(np.abs(vals)) > 1.e6: return -1 #Calculates the zeros, the x values from the eigenvalues and the y values from the eigenvectors. zeros = list() for i in range(len(vals)): if np.abs(vals[i]) < 1.e-5: continue root = np.zeros(dim, dtype=complex) for spot in range(0, divisor_var): root[spot] = vecs[-(2 + spot)][i] / vecs[-1][i] for spot in range(divisor_var + 1, dim): root[spot] = vecs[-(1 + spot)][i] / vecs[-1][i] root[divisor_var] = 1 / vals[i] if polish: root = newton_polish(polys, root, tol=tol) #throw out bad roots in cheb if np.any([abs(poly(root)) > 1.e-1 for poly in polys]): continue zeros.append(root) if return_all_roots: return transform(np.array(zeros)) else: # only return roots in the unit complex hyperbox zeros = transform(np.array(zeros)) return zeros[np.all(np.abs(zeros) <= 1, axis=0)]