def groebner(f, var=None, order=None, reduced=True): """Computes the (reduced) Groebner base of given polynomials. Thin wrapper returning SymPy expressions from L{groebner_.groebner}. """ g = groebner_.groebner(f, var, order, reduced) return map(lambda p: p.sympy_expr, g)
def lcm(f, g, var=None, order=None, coeff=None): """Least common multiple. Usage: ====== Input are two polynomials, either as SymPy expressions or as instances of Polynomial. In the first case, the variables and monomial order can be specified with 'var' and 'order', respectively. If 'coeff' is set to 'int', the content of each polynomial is checked and their lcm multiplied to the result. Otherwise it is monic, that is, of leading coefficient 1. The output's type is Polynomial, but there is a wrapper, see L{wrapper.lcm}. Notes: ====== With only one variable, the gcd is used to get the lcm from the product, via f*g = gcd(f, g)*lcm(f, g). In the univariate case, we compute the unique generator of the intersection of the two ideals, generated by f and g. This is done by computing a lexicographic Groebner base of [t*f. (t-1)*g], with t a dummy variable at the first place, then filtering trough the base elements not containing t. Examples: ========= >>> x, y = symbols('xy') >>> print lcm(4*x**2*y, 6*x*y**2) x**2*y**2 >>> print lcm(4*x**2*y, 6*x*y**2, coeff='int') 12*x**2*y**2 References: =========== Cox, Little, O'Shea: Ideals, Varieties and Algorithms, Springer, 2. edition, p. 187 See also L{div}, L{gcd}. """ # Check if f is a Polynomial already, g is assumed to match. if not isinstance(f, Polynomial): f = sympify(f) g = sympify(g) if var is None: var = merge_var(f.atoms(type=Symbol), g.atoms(type=Symbol)) f = Polynomial(f, var=var, order=order) g = Polynomial(g, var=var, order=order) # Check if we need to keep an integer factor. if coeff == 'int': cf, f = f.as_primitive() cg, g = g.as_primitive() cf, cg = int(cf), int(cg) c = Integer(cf*cg/numbers.gcd(cf, cg)) else: c = S.One if len(f.var) == 0: # Constant result. return Polynomial(c, var=f.var, order=f.order) elif len(f.var) == 1: # Use gcd to get univariate lcm. gcd_temp = gcd(f, g) q, r = div(f*g, gcd_temp) assert r.sympy_expr is S.Zero lc, f = q.as_monic() else: # Compute a lexicographic Groebner base of the ideal generated # by t*f and (t-1)*g, with unrelated t. from sympy.polynomials import groebner_ t = Symbol('t', dummy=True) var = [t] + f.var G = groebner_.groebner([Polynomial(t*f.sympy_expr, var=var, order='1-el'), Polynomial((t-1)*g.sympy_expr, var=var, order='1-el')], reduced=True) # Now intersect this result with the polynomial ring in the # var in `var', that is, eliminate t. I = filter(lambda p: t not in p.sympy_expr.atoms(type=Symbol), G) # The intersection should be a principal ideal, that is generated # by a single polynomial. if not len(I) == 1: raise PolynomialException("No single generator for intersection.") f = Polynomial(I[0].sympy_expr, var=f.var, order=f.order) return Polynomial(coeffs=tuple([(c*t[0],) + t[1:] for t in f.coeffs]), var=f.var, order=f.order)
def solve_system(eqs, var=None, order=None): """Solves a system of polynomial equations. Usage: ====== Assumes to get a list of polynomials, either as SymPy expressions or instances of Polynomial. In the first case, you should specify the variables and monomial order through 'var' and 'order'. Otherwise, the polynomials should have matching variables and orders already. Only the first polynomial is checked for its type. This algorithm uses variable elimination and only works for zero-dimensional varieties, that is, a finite number of solutions, which is currently not tested. Examples: ========= >>> x, y = symbols('xy') >>> f = y - x >>> g = x**2 + y**2 - 1 >>> solve_system([f, g]) [(-(1/2)**(1/2), -(1/2)**(1/2)), ((1/2)**(1/2), (1/2)**(1/2))] References: =========== Cox, Little, O'Shea: Ideals, Varieties and Algorithms, Springer, 2. edition, p. 113 """ def is_uv(f): """Is an instance of Polynomial univariate in its last variable?""" for term in f.coeffs: for exponent in term[1:-1]: if exponent > 0: return False return True if not isinstance(eqs, list): eqs = [eqs] if not isinstance(eqs[0], Polynomial): if var is None: var = merge_var(*[f.atoms(type=Symbol) for f in eqs]) eqs = [Polynomial(f, var=var, order='lex') for f in eqs] else: eqs = [Polynomial(f.sympy_expr, var=f.var, order='lex') for f in eqs] # First compute a Groebner base with the polynomials, # with lexicographic ordering, so that the last polynomial is # univariate and can be solved. gb = groebner_.groebner(eqs) # Now filter the the base elements, to get only the univariate ones. eliminated = filter(is_uv, gb) if len(eliminated) != 1: raise PolynomialException("System currently not solvable.") # Try to solve the polynomials with var eliminated. f = eliminated[0] partial_solutions = set(roots(f.sympy_expr, var=f.var[-1])) # No solutions were found. # TODO: Check if there exist some anyways? if len(partial_solutions) == 0: return [] # Is this the last equation, that is, deepest hierarchy? if len(gb) == 1: return map(lambda s:(s,), partial_solutions) # Finally, call this function recursively for each root replacing # the corresponding variable in the system. result = [] for r in partial_solutions: new_system = [] for eq in gb[:-1]: new_eq = eq.sympy_expr.subs(eq.var[-1], r).expand() if new_eq is not S.Zero: new_system.append( Polynomial(new_eq, var=eq.var[:-1], order='lex')) if not new_system: return [] for nps in solve_system(new_system): result.append(nps + (r,)) # With symbols, __nonzero__ returns a StrictInequality, Exception. try: result.sort() except: pass return result