Example #1
0
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
Example #4
0
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]]
Example #8
0
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)]