def _random_poly(_type, dim): ''' Generates a random polynomial that has the form c_1x_1 + c_2x_2 + ... + c_nx_n where n = dim and each c_i is a randomly chosen integer between 0 and 1000. Parameters ---------- _type : string Type of Polynomial to generate. "MultiCheb" or "MultiPower". dim : int Degree of polynomial to generate (?). Returns ------- Polynomial Randomly generated Polynomial. ''' _vars = get_var_list(dim) random_poly_shape = [2 for i in range(dim)] random_poly_coeff = np.zeros(tuple(random_poly_shape), dtype=int) for var in _vars: random_poly_coeff[var] = np.random.randint(1000) if _type == 'MultiCheb': return MultiCheb(random_poly_coeff), _vars else: return MultiPower(random_poly_coeff), _vars
def makeBasisDict(matrix, matrix_terms, VB, power): '''Calculates and returns the basisDict. This is a dictionary of the terms on the diagonal of the reduced TVB matrix to the terms in the Vector Basis. It is used to create the multiplication matrix in root_finder. Parameters -------- matrix: numpy array The reduced TVB matrix. matrix_terms : numpy array The terms in the matrix. The i'th row is the term represented by the i'th column of the matrix. VB : numpy array Each row is a term in the vector basis. power : bool If True, the initial polynomials were MultiPower. If False, they were MultiCheb. Returns ----------- basisDict : dict Maps terms on the diagonal of the reduced TVB matrix (tuples) to numpy arrays that represent the terms reduction into the Vector Basis. ''' basisDict = {} VBSet = set() for i in VB: VBSet.add(tuple(i)) if power: #We don't actually need most of the rows, so we only get the ones we need. neededSpots = set() for term, mon in itertools.product(VB,get_var_list(VB.shape[1])): if tuple(term+mon) not in VBSet: neededSpots.add(tuple(term+mon)) spots = list() for dim in range(VB.shape[1]): spots.append(VB.T[dim]) for i in range(matrix.shape[0]): term = tuple(matrix_terms[i]) if power and term not in neededSpots: continue basisDict[term] = matrix[i][matrix.shape[0]:] return basisDict
def makeBasisDict(matrix, matrix_terms, VB): '''Calculates and returns the basisDict. This is a dictionary of the terms on the diagonal of the reduced Macaulay matrix to the terms in the Vector Basis. It is used to create the multiplication matrix in root_finder. Parameters -------- matrix: numpy array The reduced Macaulay matrix. matrix_terms : numpy array The terms in the matrix. The i'th row is the term represented by the i'th column of the matrix. VB : numpy array Each row is a term in the vector basis. Returns ----------- basisDict : dict Maps terms on the diagonal of the reduced Macaulay matrix (tuples) to numpy arrays of the shape remainder_shape that represent the terms reduction into the Vector Basis. ''' basisDict = {} VBSet = set() for i in VB: VBSet.add(tuple(i)) #We don't actually need most of the rows, so we only get the ones we need. neededSpots = set() for term, mon in itertools.product(VB, get_var_list(VB.shape[1])): if tuple(term - mon) not in VBSet: neededSpots.add(tuple(term - mon)) for i in range(matrix.shape[0]): term = tuple(matrix_terms[i]) if term not in neededSpots: continue basisDict[term] = matrix[i][matrix.shape[0]:] return basisDict
def roots(polys, method='Groebner'): ''' Finds the roots of the given list of polynomials. Parameters ---------- polys : list of polynomial objects Polynomials to find the common roots of. method : string The root finding method to be used. Can be either 'Groebner', 'Macaulay', or 'TVB'. returns ------- list of numpy arrays the common roots of the polynomials ''' polys = match_poly_dimensions(polys) # Determine polynomial type poly_type = is_power(polys, return_string=True) if method == 'TVB' or method == 'new_TVB': try: m_f, var_dict = TVBMultMatrix(polys, poly_type, method) except TVBError as e: if str( e ) == "Doesn't have all x^n's on diagonal. Do linear transformation": raise e ''' #Optionally have it do F4 instead in thise case. warnings.warn("TVB method failed. Trying F4 instead. \ Error message from TVB is - {}".format(e), InstabilityWarning) method = 'Groebner' GB, m_f, var_dict = groebnerMultMatrix(polys, poly_type, method) ''' elif str(e) == 'Polys are non-zero dimensional': return -1 else: raise e else: GB, m_f, var_dict = groebnerMultMatrix(polys, poly_type, method) # both TVBMultMatrix and groebnerMultMatrix will return m_f as # -1 if the ideal is not zero dimensional or if there are no roots if type(m_f) == int: return -1 # Get list of indexes of single variables and store vars that were not # in the vector space basis. dim = max(f.dim for f in polys) var_list = get_var_list(dim) var_indexes = [-1] * dim vars_not_in_basis = {} for i in range(len(var_list)): var = var_list[i] # x_i if var in var_dict: var_indexes[i] = var_dict[var] else: # maps the position in the root to its variable vars_not_in_basis[i] = var vnib = False if len(vars_not_in_basis) != 0: if method == 'TVB': print("This isn't working yet...") return -1 vnib = True # Get left eigenvectors e = np.linalg.eig(m_f.T) eig = e[1] num_vectors = eig.shape[1] eig_vectors = [eig[:, i] for i in range(num_vectors)] # columns of eig roots = [] for v in eig_vectors: if v[var_dict[tuple(0 for i in range(dim))]] == 0: continue root = np.zeros(dim, dtype=complex) # This will always work because var_indexes and root have the # same length - dim - and var_indexes has the variables in the # order they should be in the root for i in range(dim): x_i_pos = var_indexes[i] if x_i_pos != -1: root[i] = v[x_i_pos] / v[var_dict[tuple(0 for i in range(dim))]] if vnib: # Go through the indexes of variables not in the basis in # decreasing order. It must be done in decreasing order for the # roots to be calculated correctly, since the vars with lower # indexes depend on the ones with higher indexes for pos in list(vars_not_in_basis.keys())[::-1]: GB_poly = _get_poly_with_LT(vars_not_in_basis[pos], GB) var_value = GB_poly(root) * -1 root[pos] = var_value roots.append(root) #roots.append(newton_polish(polys,root)) return roots
def bounding_parallelepiped(linear): """ A helper function for projecting polynomials. It accepts a linear polynomial and return vectors describing an (n-1)-dimensional parallelepiped that covers the intersection between the linear polynomial (it's variety) and the n-dimensional hypercube. Note: The parallelepiped can be described using just one vertex, and (n-1) vectors, each of dimension n. Second Note: This first attempt is very simple, and can be greatly improved by creating a parallelepiped that much more closely surrounds the points. Currently, it just makes a rectangle. Parameters ---------- linear : numpy array The coefficients of the linear function. Returns ------- p0 : numpy array One vertex of the parallelepiped. edges : numpy array Array of vectors describing the edges of the parallelepiped, from p0. """ dim = linear.ndim coord = np.ones((dim-1,2)) coord[:,0] = -1 const = linear[tuple([0]*dim)] coeff = np.zeros(dim) # flatten the linear coefficients for i,idx in enumerate(get_var_list(dim)): coeff[i] = linear[idx] # get the intersection points with the hypercube edges lower = -np.ones(dim) upper = np.ones(dim) vert = [] for i in range(dim): for pt in product(*coord): val = -const skipped = 0 for j,c in enumerate(coeff): if i==j: skipped = 1 continue val -= c*pt[j-skipped] one_point = list(pt) if not np.isclose(coeff[i], 0): one_point.insert(i,val/coeff[i]) one_point = np.array(one_point) if np.all(lower <= one_point) and np.all(one_point <= upper): vert.append(one_point) # what to do if no intersections if len(vert) == 0: p0 = -const/np.dot(coeff, coeff)*coeff Q, R = np.linalg.qr(np.column_stack([coeff, np.eye(dim)[:,:dim-1]])) edges = Q[:,1:] return p0, edges # raise Exception("What do I do!?") # do the thing vert = np.unique(np.array(vert), axis=0) v0 = vert[0] vert_shift = vert - v0 Q, vert_flat, _ = qr(vert_shift.T, pivoting=True) vert_flat = vert_flat[:-1] # remove flattened dimension min_vals = np.min(vert_flat, axis=1) max_vals = np.max(vert_flat, axis=1) p0 = Q[:,:-1].dot(min_vals) + v0 edges = Q[:,:-1].dot(np.diag(max_vals-min_vals)) return p0, edges