示例#1
0
def solve_linear(coeffs):
    """Finds the roots when the coeffs are **all** linear.

    Parameters
    ----------
    coeffs : list
        A list of the coefficient arrays. They should all be linear.

    Returns
    -------
    solve_linear : numpy array
        The root, if any.
    """
    dim = len(coeffs[0].shape)
    A = np.zeros([dim, dim])
    B = np.zeros(dim)
    for row in range(dim):
        coeff = coeffs[row]
        spot = tuple([0] * dim)
        B[row] = coeff[spot]
        var_list = get_var_list(dim)
        for col in range(dim):
            if coeff.shape[0] == 1:
                A[row, col] = 0
            else:
                A[row, col] = coeff[var_list[col]]
    #solve the system
    try:
        return np.linalg.solve(A, -B), np.nan
    except np.linalg.LinAlgError as e:
        if str(e) == 'Singular matrix':
            #if the system is dependent, then there are infinitely many roots
            #if the system is inconsistent, there are no roots
            #TODO: this should be more airtight than raising a warning

            #if the rightmost column of U from LU decomposition
            # is a pivot column, system is inconsistent
            # otherwise, it's dependent
            U = lu(np.hstack((A, B.reshape(-1, 1))))[2]
            pivot_columns = [
                np.flatnonzero(U[i, :])[0] for i in range(U.shape[0])
                if np.flatnonzero(U[i, :]).shape[0] > 0
            ]
            if not (U.shape[1] - 1 in pivot_columns):
                #independent
                warnings.warn('System potentially has infinitely many roots')
            return np.zeros([0, dim]), np.zeros([0, dim])
示例#2
0
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 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.
    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 Macaulay 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))

    #We don't actually need most of the rows, so we only get the ones we need
    if power:
        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 power and term not in neededSpots:
            continue
        basisDict[term] = matrix[i][matrix.shape[0]:]

    return basisDict
示例#3
0
def nullspace(linear_polys):
    """Builds a matrix to represent the system of linear polynomials.
    Columns 1:-1 represent coefficients of linear terms, while Column -1 represents
    the constant terms.
    Parameters
    ----------
    linear_polys : list
        list of linear MultiCheb objects
    Returns
    -------
    A: ((n,n) ndarray)
        The RREF of A.
    Pc: ((n,) ndarray)
        The column pivoting array.
    """
    dim = linear_polys[0].dim
    A = np.zeros((len(linear_polys),dim+1))
    for i,p in enumerate(linear_polys):
        A[i,:-1] = p.coeff[tuple(get_var_list(dim))]
        A[i,-1]  = p.coeff[tuple([0]*dim)]

    return rref(A)
示例#4
0
def _random_poly(_type, dim):
    '''
    Generates a random linear 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)

    random_poly_coeff = np.zeros(tuple(random_poly_shape), dtype=float)
    #np.random.seed(42)

    coeffs = np.random.rand(dim)
    coeffs /= np.linalg.norm(coeffs)
    for i, var in enumerate(_vars):
        random_poly_coeff[var] = coeffs[i]

    if _type == 'MultiCheb':
        return MultiCheb(random_poly_coeff), _vars
    else:
        return MultiPower(random_poly_coeff), _vars
示例#5
0
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 an nd-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):
        pts = np.array([(pt[:i]+ (0,) + pt[i:]) for pt in product(*coord)])
        val = -const
        for j,c in enumerate(coeff):
            if i==j:
                continue
            val = val - c*pts[:,j]
        if not np.isclose(coeff[i], 0):
            pts[:,i] = val/coeff[i]
        else:
            pts[:,i] = np.nan
        mask = np.all(lower <= pts, axis=1) & np.all(pts <= upper, axis=1)
        if np.any(mask):
            vert.append(pts[mask])

    # 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

    # do the thing
    vert = np.unique(np.vstack(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
示例#6
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
示例#7
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 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)