def _add_splines(c, b1, d, b2): """Construct c*b1 + d*b2.""" if b1 == S.Zero or c == S.Zero: return expand(piecewise_fold(d * b2)) if b2 == S.Zero or d == S.Zero: return expand(piecewise_fold(c * b1)) new_args = [] n_intervals = len(b1.args) assert n_intervals == len(b2.args) new_args.append((expand(c * b1.args[0].expr), b1.args[0].cond)) for i in range(1, n_intervals - 1): new_args.append((expand(c * b1.args[i].expr + d * b2.args[i - 1].expr), b1.args[i].cond)) new_args.append((expand(d * b2.args[-2].expr), b2.args[-2].cond)) new_args.append(b2.args[-1]) return Piecewise(*new_args)
def test_object_from_equation(): from sympy.abc import x, y, a, b, c, d, e assert Circle(x**2 + y**2 + 3 * x + 4 * y - 8) == Circle( Point2D(S(-3) / 2, -2), sqrt(57) / 2) assert Circle(x**2 + y**2 + 6 * x + 8 * y + 25) == Circle( Point2D(-3, -4), 0) assert Circle(a**2 + b**2 + 6 * a + 8 * b + 25, x='a', y='b') == Circle(Point2D(-3, -4), 0) assert Circle(x**2 + y**2 - 25) == Circle(Point2D(0, 0), 5) assert Circle(x**2 + y**2) == Circle(Point2D(0, 0), 0) assert Circle(a**2 + b**2, x='a', y='b') == Circle(Point2D(0, 0), 0) assert Circle(x**2 + y**2 + 6 * x + 8) == Circle(Point2D(-3, 0), 1) assert Circle(x**2 + y**2 + 6 * y + 8) == Circle(Point2D(0, -3), 1) assert Circle((x - 1)**2 + y**2 - 9) == Circle(Point2D(1, 0), 3) assert Circle(6 * (x**2) + 6 * (y**2) + 6 * x + 8 * y - 25) == Circle( Point2D(Rational(-1, 2), Rational(-2, 3)), 5 * sqrt(7) / 6) assert Circle(Eq(a**2 + b**2, 25), x='a', y=b) == Circle(Point2D(0, 0), 5) raises(GeometryError, lambda: Circle(x**2 + y**2 + 3 * x + 4 * y + 26)) raises(GeometryError, lambda: Circle(x**2 + y**2 + 25)) raises(GeometryError, lambda: Circle(a**2 + b**2 + 25, x='a', y='b')) raises(GeometryError, lambda: Circle(x**2 + 6 * y + 8)) raises(GeometryError, lambda: Circle(6 * (x**2) + 4 * (y**2) + 6 * x + 8 * y + 25)) raises(ValueError, lambda: Circle(a**2 + b**2 + 3 * a + 4 * b - 8)) # .equation() adds 'real=True' assumption; '==' would fail if assumptions differed x, y = symbols('x y', real=True) eq = a * x**2 + a * y**2 + c * x + d * y + e assert expand(Circle(eq).equation() * a) == eq
def test_reciprocal_frame_test(): with GA_Printer(): metric = '1 # #,' + \ '# 1 #,' + \ '# # 1,' (e1, e2, e3) = MV.setup('e1 e2 e3', metric) E = e1 ^ e2 ^ e3 Esq = (E*E).scalar() assert str(E) == 'e1^e2^e3' assert str(Esq) == '(e1.e2)**2 - 2*(e1.e2)*(e1.e3)*(e2.e3) + (e1.e3)**2 + (e2.e3)**2 - 1' Esq_inv = 1/Esq E1 = (e2 ^ e3)*E E2 = (-1)*(e1 ^ e3)*E E3 = (e1 ^ e2)*E assert str(E1) == '((e2.e3)**2 - 1)*e1 + ((e1.e2) - (e1.e3)*(e2.e3))*e2 + (-(e1.e2)*(e2.e3) + (e1.e3))*e3' assert str(E2) == '((e1.e2) - (e1.e3)*(e2.e3))*e1 + ((e1.e3)**2 - 1)*e2 + (-(e1.e2)*(e1.e3) + (e2.e3))*e3' assert str(E3) == '(-(e1.e2)*(e2.e3) + (e1.e3))*e1 + (-(e1.e2)*(e1.e3) + (e2.e3))*e2 + ((e1.e2)**2 - 1)*e3' w = (E1 | e2) w = w.expand() assert str(w) == '0' w = (E1 | e3) w = w.expand() assert str(w) == '0' w = (E2 | e1) w = w.expand() assert str(w) == '0' w = (E2 | e3) w = w.expand() assert str(w) == '0' w = (E3 | e1) w = w.expand() assert str(w) == '0' w = (E3 | e2) w = w.expand() assert str(w) == '0' w = (E1 | e1) w = (w.expand()).scalar() Esq = expand(Esq) assert str(simplify(w/Esq)) == '1' w = (E2 | e2) w = (w.expand()).scalar() assert str(simplify(w/Esq)) == '1' w = (E3 | e3) w = (w.expand()).scalar() assert str(simplify(w/Esq)) == '1' return
def test_reciprocal_frame(): """ Test of formula for general reciprocal frame of three vectors. Let three independent vectors be e1, e2, and e3. The reciprocal vectors E1, E2, and E3 obey the relations: e_i.E_j = delta_ij*(e1^e2^e3)**2 """ g = '1 # #,'+ \ '# 1 #,'+ \ '# # 1' g3dn = Ga('e1 e2 e3',g=g) (e1,e2,e3) = g3dn.mv() E = e1^e2^e3 Esq = (E*E).scalar() Esq_inv = 1 / Esq E1 = (e2^e3)*E E2 = (-1)*(e1^e3)*E E3 = (e1^e2)*E w = (E1|e2) w = w.expand() assert w.scalar() == 0 w = (E1|e3) w = w.expand() assert w.scalar() == 0 w = (E2|e1) w = w.expand() assert w.scalar() == 0 w = (E2|e3) w = w.expand() assert w.scalar() == 0 w = (E3|e1) w = w.expand() assert w.scalar() == 0 w = (E3|e2) w = w.expand() assert w.scalar() == 0 w = (E1|e1) w = (w.expand()).scalar() Esq = expand(Esq) assert simplify(w/Esq) == 1 w = (E2|e2) w = (w.expand()).scalar() assert simplify(w/Esq) == 1 w = (E3|e3) w = (w.expand()).scalar() assert simplify(w/Esq) == 1
def _add_splines(c, b1, d, b2): """Construct c*b1 + d*b2.""" if b1 == S.Zero or c == S.Zero: return expand(piecewise_fold(d * b2)) if b2 == S.Zero or d == S.Zero: return expand(piecewise_fold(c * b1)) new_args = [] n_intervals = len(b1.args) assert (n_intervals == len(b2.args)) new_args.append((expand(c * b1.args[0].expr), b1.args[0].cond)) for i in range(1, n_intervals - 1): new_args.append((expand(c * b1.args[i].expr + d * b2.args[i - 1].expr), b1.args[i].cond)) new_args.append((expand(d * b2.args[-2].expr), b2.args[-2].cond)) new_args.append(b2.args[-1]) return Piecewise(*new_args)
def test_reciprocal_frame_test(): with GA_Printer(): metric = "1 # #," + "# 1 #," + "# # 1," (e1, e2, e3) = MV.setup("e1 e2 e3", metric) E = e1 ^ e2 ^ e3 Esq = (E * E).scalar() assert str(E) == "e1^e2^e3" assert str(Esq) == "(e1.e2)**2 - 2*(e1.e2)*(e1.e3)*(e2.e3) + (e1.e3)**2 + (e2.e3)**2 - 1" Esq_inv = 1 / Esq E1 = (e2 ^ e3) * E E2 = (-1) * (e1 ^ e3) * E E3 = (e1 ^ e2) * E assert str(E1) == "((e2.e3)**2 - 1)*e1 + ((e1.e2) - (e1.e3)*(e2.e3))*e2 + (-(e1.e2)*(e2.e3) + (e1.e3))*e3" assert str(E2) == "((e1.e2) - (e1.e3)*(e2.e3))*e1 + ((e1.e3)**2 - 1)*e2 + (-(e1.e2)*(e1.e3) + (e2.e3))*e3" assert str(E3) == "(-(e1.e2)*(e2.e3) + (e1.e3))*e1 + (-(e1.e2)*(e1.e3) + (e2.e3))*e2 + ((e1.e2)**2 - 1)*e3" w = E1 | e2 w = w.expand() assert str(w) == "0" w = E1 | e3 w = w.expand() assert str(w) == "0" w = E2 | e1 w = w.expand() assert str(w) == "0" w = E2 | e3 w = w.expand() assert str(w) == "0" w = E3 | e1 w = w.expand() assert str(w) == "0" w = E3 | e2 w = w.expand() assert str(w) == "0" w = E1 | e1 w = (w.expand()).scalar() Esq = expand(Esq) assert str(simplify(w / Esq)) == "1" w = E2 | e2 w = (w.expand()).scalar() assert str(simplify(w / Esq)) == "1" w = E3 | e3 w = (w.expand()).scalar() assert str(simplify(w / Esq)) == "1" return
def ground(self, rlpProblem): self.lpmodel = LpProblem(rlpProblem.sense) self.add_objective_to_lp(self.ground_expression(rlpProblem.objective)) for constraint in rlpProblem.constraints: if isinstance(constraint, Rel): lhs = constraint.lhs - constraint.rhs ground_result = constraint.__class__( expand(self.ground_expression(lhs)), 0.0) self.add_constraint_to_lp(ground_result) else: # maybe pre-ground here? # result = self.ground_expression(constraint.relation, bound=constraint.query_symbols) result = constraint.ground(self.logkb) for expr in result: ground = self.ground_expression(expr) ground_result = ground.__class__( expand(self.ground_expression(ground.lhs)), ground.rhs) self.add_constraint_to_lp(ground_result) return self.lpmodel.get_scipy_matrices( rlpProblem), self.lpmodel.lp_variables
def conic_coeff(variables, equation): if total_degree(equation) != 2: raise ValueError() x = variables[0] y = variables[1] equation = expand(equation) a = equation.coeff(x**2) b = equation.coeff(x * y) c = equation.coeff(y**2) d = equation.coeff(x, 1).coeff(y, 0) e = equation.coeff(y, 1).coeff(x, 0) f = equation.coeff(x, 0).coeff(y, 0) return a, b, c, d, e, f
def multiplicity(self, point): """ Returns the multiplicity of a singular point on the region. A singular point (x,y) of region is said to be of multiplicity m if all the partial derivatives off to order m - 1 vanish there. Examples ======== >>> from sympy.abc import x, y, z >>> from sympy.vector import ImplicitRegion >>> I = ImplicitRegion((x, y, z), x**2 + y**3 - z**4) >>> I.singular_points() {(0, 0, 0)} >>> I.multiplicity((0, 0, 0)) 2 """ if isinstance(point, Point): point = point.args modified_eq = self.equation for i, var in enumerate(self.variables): modified_eq = modified_eq.subs(var, var + point[i]) modified_eq = expand(modified_eq) if len(modified_eq.args) != 0: terms = modified_eq.args m = min([total_degree(term) for term in terms]) else: terms = modified_eq m = total_degree(terms) return m
def test_rs_series(): x, a, b, c = symbols('x, a, b, c') assert rs_series(a, a, 5).as_expr() == a assert rs_series(sin(1/a), a, 5).as_expr() == sin(1/a) assert rs_series(sin(a), a, 5).as_expr() == (sin(a).series(a, 0, 5)).removeO() assert rs_series(sin(a) + cos(a), a, 5).as_expr() == ((sin(a) + cos(a)).series(a, 0, 5)).removeO() assert rs_series(sin(a)*cos(a), a, 5).as_expr() == ((sin(a)* cos(a)).series(a, 0, 5)).removeO() p = (sin(a) - a)*(cos(a**2) + a**4/2) assert expand(rs_series(p, a, 10).as_expr()) == expand(p.series(a, 0, 10).removeO()) p = sin(a**2/2 + a/3) + cos(a/5)*sin(a/2)**3 assert expand(rs_series(p, a, 5).as_expr()) == expand(p.series(a, 0, 5).removeO()) p = sin(x**2 + a)*(cos(x**3 - 1) - a - a**2) assert expand(rs_series(p, a, 5).as_expr()) == expand(p.series(a, 0, 5).removeO()) p = sin(a**2 - a/3 + 2)**5*exp(a**3 - a/2) assert expand(rs_series(p, a, 10).as_expr()) == expand(p.series(a, 0, 10).removeO()) p = sin(a + b + c) assert expand(rs_series(p, a, 5).as_expr()) == expand(p.series(a, 0, 5).removeO()) p = tan(sin(a**2 + 4) + b + c) assert expand(rs_series(p, a, 6).as_expr()) == expand(p.series(a, 0, 6).removeO())
def test_noneuclidian_distance_calculation(): from sympy import solve, sqrt GA_Printer.on() metric = '0 # #,# 0 #,# # 1' (X, Y, e) = MV.setup('X Y e', metric) assert str((X ^ Y) * (X ^ Y)) == '(X.Y)**2' L = X ^ Y ^ e B = L * e assert str(B) == 'X^Y - (Y.e)*X^e + (X.e)*Y^e' Bsq = B * B assert str(Bsq) == '(X.Y)*((X.Y) - 2*(X.e)*(Y.e))' Bsq = Bsq.scalar() assert str(B) == 'X^Y - (Y.e)*X^e + (X.e)*Y^e' BeBr = B * e * B.rev() assert str(BeBr) == '((X.Y)*(-(X.Y) + 2*(X.e)*(Y.e)))*e' assert str(B * B) == '(X.Y)*((X.Y) - 2*(X.e)*(Y.e))' assert str(L * L) == '(X.Y)*((X.Y) - 2*(X.e)*(Y.e))' (s, c, Binv, M, BigS, BigC, alpha, XdotY, Xdote, Ydote) = symbols('s c (1/B) M S C alpha (X.Y) (X.e) (Y.e)') Bhat = Binv * B R = c + s * Bhat assert str(R) == 'c + (1/B)*s*X^Y - (1/B)*(Y.e)*s*X^e + (1/B)*(X.e)*s*Y^e' Z = R * X * R.rev() Z.obj = expand(Z.obj) Z.obj = Z.obj.collect([Binv, s, c, XdotY]) assert str( Z ) == '((1/B)**2*(X.Y)**2*s**2 - 2*(1/B)**2*(X.Y)*(X.e)*(Y.e)*s**2 + 2*(1/B)*(X.Y)*c*s - 2*(1/B)*(X.e)*(Y.e)*c*s + c**2)*X + 2*(1/B)*(X.e)**2*c*s*Y + (2*(1/B)*(X.Y)*(X.e)*s*(-(1/B)*(X.Y)*s + 2*(1/B)*(X.e)*(Y.e)*s - c))*e' W = Z | Y # From this point forward all calculations are with sympy scalars W = W.scalar() assert str( W ) == '(1/B)**2*(X.Y)**3*s**2 - 4*(1/B)**2*(X.Y)**2*(X.e)*(Y.e)*s**2 + 4*(1/B)**2*(X.Y)*(X.e)**2*(Y.e)**2*s**2 + 2*(1/B)*(X.Y)**2*c*s - 4*(1/B)*(X.Y)*(X.e)*(Y.e)*c*s + (X.Y)*c**2' W = expand(W) W = simplify(W) W = W.collect([s * Binv]) M = 1 / Bsq W = W.subs(Binv**2, M) W = simplify(W) Bmag = sqrt(XdotY**2 - 2 * XdotY * Xdote * Ydote) W = W.collect([Binv * c * s, XdotY]) #Double angle substitutions W = W.subs(2 * XdotY**2 - 4 * XdotY * Xdote * Ydote, 2 / (Binv**2)) W = W.subs(2 * c * s, BigS) W = W.subs(c**2, (BigC + 1) / 2) W = W.subs(s**2, (BigC - 1) / 2) W = simplify(W) W = expand(W) W = W.subs(1 / Binv, Bmag) assert str( W ) == '(X.Y)*C - (X.e)*(Y.e)*C + (X.e)*(Y.e) + S*sqrt((X.Y)**2 - 2*(X.Y)*(X.e)*(Y.e))' Wd = collect(W, [BigC, BigS], exact=True, evaluate=False) Wd_1 = Wd[S.One] Wd_C = Wd[BigC] Wd_S = Wd[BigS] assert str(Wd_1) == '(X.e)*(Y.e)' assert str(Wd_C) == '(X.Y) - (X.e)*(Y.e)' assert str(Wd_S) == 'sqrt((X.Y)**2 - 2*(X.Y)*(X.e)*(Y.e))' assert str(Bmag) == 'sqrt((X.Y)**2 - 2*(X.Y)*(X.e)*(Y.e))' Wd_1 = Wd_1.subs(Bmag, 1 / Binv) Wd_C = Wd_C.subs(Bmag, 1 / Binv) Wd_S = Wd_S.subs(Bmag, 1 / Binv) lhs = Wd_1 + Wd_C * BigC rhs = -Wd_S * BigS lhs = lhs**2 rhs = rhs**2 W = expand(lhs - rhs) W = expand(W.subs(1 / Binv**2, Bmag**2)) W = expand(W.subs(BigS**2, BigC**2 - 1)) W = W.collect([BigC, BigC**2], evaluate=False) a = simplify(W[BigC**2]) b = simplify(W[BigC]) c = simplify(W[S.One]) assert str(a) == '(X.e)**2*(Y.e)**2' assert str(b) == '2*(X.e)*(Y.e)*((X.Y) - (X.e)*(Y.e))' assert str(c) == '(X.Y)**2 - 2*(X.Y)*(X.e)*(Y.e) + (X.e)**2*(Y.e)**2' x = Symbol('x') C = solve(a * x**2 + b * x + c, x)[0] assert str(expand(simplify(expand(C)))) == '-(X.Y)/((X.e)*(Y.e)) + 1' GA_Printer.off() return
def test_rs_series(): x, a, b, c = symbols('x, a, b, c') assert rs_series(a, a, 5).as_expr() == a assert rs_series(sin(1 / a), a, 5).as_expr() == sin(1 / a) assert rs_series(sin(a), a, 5).as_expr() == (sin(a).series(a, 0, 5)).removeO() assert rs_series(sin(a) + cos(a), a, 5).as_expr() == ((sin(a) + cos(a)).series(a, 0, 5)).removeO() assert rs_series(sin(a) * cos(a), a, 5).as_expr() == ((sin(a) * cos(a)).series(a, 0, 5)).removeO() p = (sin(a) - a) * (cos(a**2) + a**4 / 2) assert expand(rs_series(p, a, 10).as_expr()) == expand( p.series(a, 0, 10).removeO()) p = sin(a**2 / 2 + a / 3) + cos(a / 5) * sin(a / 2)**3 assert expand(rs_series(p, a, 5).as_expr()) == expand( p.series(a, 0, 5).removeO()) p = sin(x**2 + a) * (cos(x**3 - 1) - a - a**2) assert expand(rs_series(p, a, 5).as_expr()) == expand( p.series(a, 0, 5).removeO()) p = sin(a**2 - a / 3 + 2)**5 * exp(a**3 - a / 2) assert expand(rs_series(p, a, 10).as_expr()) == expand( p.series(a, 0, 10).removeO()) p = sin(a + b + c) assert expand(rs_series(p, a, 5).as_expr()) == expand( p.series(a, 0, 5).removeO()) p = tan(sin(a**2 + 4) + b + c) assert expand(rs_series(p, a, 6).as_expr()) == expand( p.series(a, 0, 6).removeO())
def test_noneuclidian_distance_calculation(): from sympy import solve, sqrt with GA_Printer(): metric = '0 # #,# 0 #,# # 1' (X, Y, e) = MV.setup('X Y e', metric) assert str((X ^ Y)*(X ^ Y)) == '(X.Y)**2' L = X ^ Y ^ e B = L*e assert str(B) == 'X^Y - (Y.e)*X^e + (X.e)*Y^e' Bsq = B*B assert str(Bsq) == '(X.Y)*((X.Y) - 2*(X.e)*(Y.e))' Bsq = Bsq.scalar() assert str(B) == 'X^Y - (Y.e)*X^e + (X.e)*Y^e' BeBr = B*e*B.rev() assert str(BeBr) == '((X.Y)*(-(X.Y) + 2*(X.e)*(Y.e)))*e' assert str(B*B) == '(X.Y)*((X.Y) - 2*(X.e)*(Y.e))' assert str(L*L) == '(X.Y)*((X.Y) - 2*(X.e)*(Y.e))' (s, c, Binv, M, BigS, BigC, alpha, XdotY, Xdote, Ydote) = symbols('s c (1/B) M S C alpha (X.Y) (X.e) (Y.e)') Bhat = Binv*B R = c + s*Bhat assert str(R) == 'c + (1/B)*s*X^Y - (1/B)*(Y.e)*s*X^e + (1/B)*(X.e)*s*Y^e' Z = R*X*R.rev() Z.obj = expand(Z.obj) Z.obj = Z.obj.collect([Binv, s, c, XdotY]) assert str(Z) == '((1/B)**2*(X.Y)**2*s**2 - 2*(1/B)**2*(X.Y)*(X.e)*(Y.e)*s**2 + 2*(1/B)*(X.Y)*c*s - 2*(1/B)*(X.e)*(Y.e)*c*s + c**2)*X + 2*(1/B)*(X.e)**2*c*s*Y + (2*(1/B)*(X.Y)*(X.e)*s*(-(1/B)*(X.Y)*s + 2*(1/B)*(X.e)*(Y.e)*s - c))*e' W = Z | Y # From this point forward all calculations are with sympy scalars W = W.scalar() assert str(W) == '(1/B)**2*(X.Y)**3*s**2 - 4*(1/B)**2*(X.Y)**2*(X.e)*(Y.e)*s**2 + 4*(1/B)**2*(X.Y)*(X.e)**2*(Y.e)**2*s**2 + 2*(1/B)*(X.Y)**2*c*s - 4*(1/B)*(X.Y)*(X.e)*(Y.e)*c*s + (X.Y)*c**2' W = expand(W) W = simplify(W) W = W.collect([s*Binv]) M = 1/Bsq W = W.subs(Binv**2, M) W = simplify(W) Bmag = sqrt(XdotY**2 - 2*XdotY*Xdote*Ydote) W = W.collect([Binv*c*s, XdotY]) #Double angle substitutions W = W.subs(2*XdotY**2 - 4*XdotY*Xdote*Ydote, 2/(Binv**2)) W = W.subs(2*c*s, BigS) W = W.subs(c**2, (BigC + 1)/2) W = W.subs(s**2, (BigC - 1)/2) W = simplify(W) W = expand(W) W = W.subs(1/Binv, Bmag) assert str(W) == '(X.Y)*C - (X.e)*(Y.e)*C + (X.e)*(Y.e) + S*sqrt((X.Y)**2 - 2*(X.Y)*(X.e)*(Y.e))' Wd = collect(W, [BigC, BigS], exact=True, evaluate=False) Wd_1 = Wd[S.One] Wd_C = Wd[BigC] Wd_S = Wd[BigS] assert str(Wd_1) == '(X.e)*(Y.e)' assert str(Wd_C) == '(X.Y) - (X.e)*(Y.e)' assert str(Wd_S) == 'sqrt((X.Y)**2 - 2*(X.Y)*(X.e)*(Y.e))' assert str(Bmag) == 'sqrt((X.Y)**2 - 2*(X.Y)*(X.e)*(Y.e))' Wd_1 = Wd_1.subs(Bmag, 1/Binv) Wd_C = Wd_C.subs(Bmag, 1/Binv) Wd_S = Wd_S.subs(Bmag, 1/Binv) lhs = Wd_1 + Wd_C*BigC rhs = -Wd_S*BigS lhs = lhs**2 rhs = rhs**2 W = expand(lhs - rhs) W = expand(W.subs(1/Binv**2, Bmag**2)) W = expand(W.subs(BigS**2, BigC**2 - 1)) W = W.collect([BigC, BigC**2], evaluate=False) a = simplify(W[BigC**2]) b = simplify(W[BigC]) c = simplify(W[S.One]) assert str(a) == '(X.e)**2*(Y.e)**2' assert str(b) == '2*(X.e)*(Y.e)*((X.Y) - (X.e)*(Y.e))' assert str(c) == '(X.Y)**2 - 2*(X.Y)*(X.e)*(Y.e) + (X.e)**2*(Y.e)**2' x = Symbol('x') C = solve(a*x**2 + b*x + c, x)[0] assert str(expand(simplify(expand(C)))) == '-(X.Y)/((X.e)*(Y.e)) + 1' return
def rational_parametrization(self, parameters=('t', 's'), reg_point=None): """ Returns the rational parametrization of implict region. Examples ======== >>> from sympy import Eq >>> from sympy.abc import x, y, z, s, t >>> from sympy.vector import ImplicitRegion >>> parabola = ImplicitRegion((x, y), y**2 - 4*x) >>> parabola.rational_parametrization() (4/t**2, 4/t) >>> circle = ImplicitRegion((x, y), Eq(x**2 + y**2, 4)) >>> circle.rational_parametrization() (4*t/(t**2 + 1), 4*t**2/(t**2 + 1) - 2) >>> I = ImplicitRegion((x, y), x**3 + x**2 - y**2) >>> I.rational_parametrization() (t**2 - 1, t*(t**2 - 1)) >>> cubic_curve = ImplicitRegion((x, y), x**3 + x**2 - y**2) >>> cubic_curve.rational_parametrization(parameters=(t)) (t**2 - 1, t*(t**2 - 1)) >>> sphere = ImplicitRegion((x, y, z), x**2 + y**2 + z**2 - 4) >>> sphere.rational_parametrization(parameters=(t, s)) (-2 + 4/(s**2 + t**2 + 1), 4*s/(s**2 + t**2 + 1), 4*t/(s**2 + t**2 + 1)) For some conics, regular_points() is unable to find a point on curve. To calulcate the parametric representation in such cases, user need to determine a point on the region and pass it using reg_point. >>> c = ImplicitRegion((x, y), (x - 1/2)**2 + (y)**2 - (1/4)**2) >>> c.rational_parametrization(reg_point=(3/4, 0)) (0.75 - 0.5/(t**2 + 1), -0.5*t/(t**2 + 1)) References ========== - Christoph M. Hoffmann, "Conversion Methods between Parametric and Implicit Curves and Surfaces", Purdue e-Pubs, 1990. Available: https://docs.lib.purdue.edu/cgi/viewcontent.cgi?article=1827&context=cstech """ equation = self.equation degree = self.degree if degree == 1: if len(self.variables) == 1: return (equation, ) elif len(self.variables) == 2: x, y = self.variables y_par = list(solveset(equation, y))[0] return x, y_par else: raise NotImplementedError() point = () # Finding the (n - 1) fold point of the monoid of degree if degree == 2: # For degree 2 curves, either a regular point or a singular point can be used. if reg_point is not None: # Using point provided by the user as regular point point = reg_point else: if len(self.singular_points()) != 0: point = list(self.singular_points())[0] else: point = self.regular_point() if len(self.singular_points()) != 0: singular_points = self.singular_points() for spoint in singular_points: syms = Tuple(*spoint).free_symbols rep = {s: 2 for s in syms} if len(syms) != 0: spoint = tuple(s.subs(rep) for s in spoint) if self.multiplicity(spoint) == degree - 1: point = spoint break if len(point) == 0: # The region in not a monoid raise NotImplementedError() modified_eq = equation # Shifting the region such that fold point moves to origin for i, var in enumerate(self.variables): modified_eq = modified_eq.subs(var, var + point[i]) modified_eq = expand(modified_eq) hn = hn_1 = 0 for term in modified_eq.args: if total_degree(term) == degree: hn += term else: hn_1 += term hn_1 = -1 * hn_1 if not isinstance(parameters, tuple): parameters = (parameters, ) if len(self.variables) == 2: parameter1 = parameters[0] if parameter1 == 's': # To avoid name conflict between parameters s = _symbol('s_', real=True) else: s = _symbol('s', real=True) t = _symbol(parameter1, real=True) hn = hn.subs({self.variables[0]: s, self.variables[1]: t}) hn_1 = hn_1.subs({self.variables[0]: s, self.variables[1]: t}) x_par = (s * (hn_1 / hn)).subs(s, 1) + point[0] y_par = (t * (hn_1 / hn)).subs(s, 1) + point[1] return x_par, y_par elif len(self.variables) == 3: parameter1, parameter2 = parameters if parameter1 == 'r' or parameter2 == 'r': # To avoid name conflict between parameters r = _symbol('r_', real=True) else: r = _symbol('r', real=True) s = _symbol(parameter2, real=True) t = _symbol(parameter1, real=True) hn = hn.subs({ self.variables[0]: r, self.variables[1]: s, self.variables[2]: t }) hn_1 = hn_1.subs({ self.variables[0]: r, self.variables[1]: s, self.variables[2]: t }) x_par = (r * (hn_1 / hn)).subs(r, 1) + point[0] y_par = (s * (hn_1 / hn)).subs(r, 1) + point[1] z_par = (t * (hn_1 / hn)).subs(r, 1) + point[2] return x_par, y_par, z_par raise NotImplementedError()
def __trigsimp(expr, deep=False): """recursive helper for trigsimp""" from sympy.simplify.fu import TR10i if _trigpat is None: _trigpats() a, b, c, d, matchers_division, matchers_add, \ matchers_identity, artifacts = _trigpat if expr.is_Mul: # do some simplifications like sin/cos -> tan: if not expr.is_commutative: com, nc = expr.args_cnc() expr = _trigsimp(Mul._from_args(com), deep) * Mul._from_args(nc) else: for i, (pattern, simp, ok1, ok2) in enumerate(matchers_division): if not _dotrig(expr, pattern): continue newexpr = _match_div_rewrite(expr, i) if newexpr is not None: if newexpr != expr: expr = newexpr break else: continue # use SymPy matching instead res = expr.match(pattern) if res and res.get(c, 0): if not res[c].is_integer: ok = ok1.subs(res) if not ok.is_positive: continue ok = ok2.subs(res) if not ok.is_positive: continue # if "a" contains any of trig or hyperbolic funcs with # argument "b" then skip the simplification if any(w.args[0] == res[b] for w in res[a].atoms( TrigonometricFunction, HyperbolicFunction)): continue # simplify and finish: expr = simp.subs(res) break # process below if expr.is_Add: args = [] for term in expr.args: if not term.is_commutative: com, nc = term.args_cnc() nc = Mul._from_args(nc) term = Mul._from_args(com) else: nc = S.One term = _trigsimp(term, deep) for pattern, result in matchers_identity: res = term.match(pattern) if res is not None: term = result.subs(res) break args.append(term * nc) if args != expr.args: expr = Add(*args) expr = min(expr, expand(expr), key=count_ops) if expr.is_Add: for pattern, result in matchers_add: if not _dotrig(expr, pattern): continue expr = TR10i(expr) if expr.has(HyperbolicFunction): res = expr.match(pattern) # if "d" contains any trig or hyperbolic funcs with # argument "a" or "b" then skip the simplification; # this isn't perfect -- see tests if res is None or not (a in res and b in res) or any( w.args[0] in (res[a], res[b]) for w in res[d].atoms(TrigonometricFunction, HyperbolicFunction)): continue expr = result.subs(res) break # Reduce any lingering artifacts, such as sin(x)**2 changing # to 1 - cos(x)**2 when sin(x)**2 was "simpler" for pattern, result, ex in artifacts: if not _dotrig(expr, pattern): continue # Substitute a new wild that excludes some function(s) # to help influence a better match. This is because # sometimes, for example, 'a' would match sec(x)**2 a_t = Wild('a', exclude=[ex]) pattern = pattern.subs(a, a_t) result = result.subs(a, a_t) m = expr.match(pattern) was = None while m and was != expr: was = expr if m[a_t] == 0 or \ -m[a_t] in m[c].args or m[a_t] + m[c] == 0: break if d in m and m[a_t] * m[d] + m[c] == 0: break expr = result.subs(m) m = expr.match(pattern) m.setdefault(c, S.Zero) elif expr.is_Mul or expr.is_Pow or deep and expr.args: expr = expr.func(*[_trigsimp(a, deep) for a in expr.args]) try: if not expr.has(*_trigs): raise TypeError e = expr.atoms(exp) new = expr.rewrite(exp, deep=deep) if new == e: raise TypeError fnew = factor(new) if fnew != new: new = sorted([new, factor(new)], key=count_ops)[0] # if all exp that were introduced disappeared then accept it if not (new.atoms(exp) - e): expr = new except TypeError: pass return expr
def __init__(self, expr, logkb): self.logkb = logkb self.lp_variables = set([]) expanded_expr = expand(expr) self._result = self.visit(expanded_expr)
def test_rs_series(): x, a, b, c = symbols('x, a, b, c') assert rs_series(a, a, 5).as_expr() == a assert rs_series(sin(a), a, 5).as_expr() == (sin(a).series(a, 0, 5)).removeO() assert rs_series(sin(a) + cos(a), a, 5).as_expr() == ((sin(a) + cos(a)).series(a, 0, 5)).removeO() assert rs_series(sin(a) * cos(a), a, 5).as_expr() == ((sin(a) * cos(a)).series(a, 0, 5)).removeO() p = (sin(a) - a) * (cos(a**2) + a**4 / 2) assert expand(rs_series(p, a, 10).as_expr()) == expand( p.series(a, 0, 10).removeO()) p = sin(a**2 / 2 + a / 3) + cos(a / 5) * sin(a / 2)**3 assert expand(rs_series(p, a, 5).as_expr()) == expand( p.series(a, 0, 5).removeO()) p = sin(x**2 + a) * (cos(x**3 - 1) - a - a**2) assert expand(rs_series(p, a, 5).as_expr()) == expand( p.series(a, 0, 5).removeO()) p = sin(a**2 - a / 3 + 2)**5 * exp(a**3 - a / 2) assert expand(rs_series(p, a, 10).as_expr()) == expand( p.series(a, 0, 10).removeO()) p = sin(a + b + c) assert expand(rs_series(p, a, 5).as_expr()) == expand( p.series(a, 0, 5).removeO()) p = tan(sin(a**2 + 4) + b + c) assert expand(rs_series(p, a, 6).as_expr()) == expand( p.series(a, 0, 6).removeO()) p = a**QQ(2, 5) + a**QQ(2, 3) + a r = rs_series(tan(p), a, 2) assert r.as_expr() == a**QQ(9,5) + a**QQ(26,15) + a**QQ(22,15) + a**QQ(6,5)/3 + \ a + a**QQ(2,3) + a**QQ(2,5) r = rs_series(exp(p), a, 1) assert r.as_expr() == a**QQ(4, 5) / 2 + a**QQ(2, 3) + a**QQ(2, 5) + 1 r = rs_series(sin(p), a, 2) assert r.as_expr() == -a**QQ(9,5)/2 - a**QQ(26,15)/2 - a**QQ(22,15)/2 - \ a**QQ(6,5)/6 + a + a**QQ(2,3) + a**QQ(2,5) r = rs_series(cos(p), a, 2) assert r.as_expr() == a**QQ(28,15)/6 - a**QQ(5,3) + a**QQ(8,5)/24 - a**QQ(7,5) - \ a**QQ(4,3)/2 - a**QQ(16,15) - a**QQ(4,5)/2 + 1 assert rs_series(sin(a) / 7, a, 5).as_expr() == (sin(a) / 7).series(a, 0, 5).removeO() assert rs_series(log(1 + x), x, 5).as_expr() == -x**4/4 + x**3/3 - \ x**2/2 + x assert rs_series(log(1 + 4*x), x, 5).as_expr() == -64*x**4 + 64*x**3/3 - \ 8*x**2 + 4*x assert rs_series(log(1 + x + x**2), x, 10).as_expr() == -2*x**9/9 + \ x**8/8 + x**7/7 - x**6/3 + x**5/5 + x**4/4 - 2*x**3/3 + \ x**2/2 + x assert rs_series(log(1 + x*a**2), x, 7).as_expr() == -x**6*a**12/6 + \ x**5*a**10/5 - x**4*a**8/4 + x**3*a**6/3 - \ x**2*a**4/2 + x*a**2
def fast_substitute(monomial, old_sub, new_sub): """Experimental fast substitution routine that considers only restricted cases of noncommutative algebras. In rare cases, it fails to find a substitution. Use it with proper testing. :param monomial: The monomial with parts need to be substituted. :param old_sub: The part to be replaced. :param new_sub: The replacement. """ if is_number_type(monomial): return monomial if monomial.is_Add: return sum([fast_substitute(element, old_sub, new_sub) for element in monomial.as_ordered_terms()]) comm_factors, ncomm_factors = split_commutative_parts(monomial) old_comm_factors, old_ncomm_factors = split_commutative_parts(old_sub) # This is a temporary hack if not isinstance(new_sub, int) and not isinstance(new_sub, float): new_comm_factors, _ = split_commutative_parts(new_sub) comm_monomial = 1 is_constant_term = False if len(comm_factors) == 1 and isinstance(comm_factors[0], Number): is_constant_term = True comm_monomial = comm_factors[0] if not is_constant_term and len(comm_factors) > 0: for comm_factor in comm_factors: comm_monomial *= comm_factor if len(old_comm_factors) > 0: comm_old_sub = 1 for comm_factor in old_comm_factors: comm_old_sub *= comm_factor comm_new_sub = 1 for comm_factor in new_comm_factors: comm_new_sub *= comm_factor comm_monomial = comm_monomial.subs(comm_old_sub, comm_new_sub) if len(ncomm_factors) == 0 or len(old_ncomm_factors) == 0: return comm_monomial # old_factors = old_sub.as_ordered_factors() # factors = monomial.as_ordered_factors() new_var_list = [] new_monomial = 1 match = False left_remainder = 1 right_remainder = 1 for i in range(len(ncomm_factors) - len(old_ncomm_factors) + 1): for j in range(len(old_ncomm_factors)): if isinstance(ncomm_factors[i + j], Number) and \ ((not isinstance(old_ncomm_factors[j], Number) or ncomm_factors[i + j] != old_ncomm_factors[j])): break if isinstance(ncomm_factors[i + j], Symbol) and \ (not isinstance(old_ncomm_factors[j], Operator) or (isinstance(old_ncomm_factors[j], Symbol) and ncomm_factors[i + j] != old_ncomm_factors[j])): break if isinstance(ncomm_factors[i + j], Operator) and \ isinstance(old_ncomm_factors[j], Operator) and \ ncomm_factors[i + j] != old_ncomm_factors[j]: break if isinstance(ncomm_factors[i + j], Dagger) and \ (not isinstance(old_ncomm_factors[j], Dagger) or ncomm_factors[i + j] != old_ncomm_factors[j]): break if not isinstance(ncomm_factors[i + j], Dagger) and \ not isinstance(ncomm_factors[i + j], Pow) and \ isinstance(old_ncomm_factors[j], Dagger): break if isinstance(ncomm_factors[i + j], Pow): if isinstance(old_ncomm_factors[j], Pow): old_base = old_ncomm_factors[j].base old_degree = old_ncomm_factors[j].exp else: old_base = old_ncomm_factors[j] old_degree = 1 if old_base != ncomm_factors[i + j].base: break if old_degree > ncomm_factors[i + j].exp: break if old_degree < ncomm_factors[i + j].exp: if j != len(old_ncomm_factors) - 1: if j != 0: break else: left_remainder = old_base ** ( ncomm_factors[i + j].exp - old_degree) else: right_remainder = old_base ** ( ncomm_factors[i + j].exp - old_degree) if isinstance(ncomm_factors[i + j], Operator) and \ isinstance(old_ncomm_factors[j], Pow): break else: match = True if not match: new_var_list.append(ncomm_factors[i]) else: new_monomial = 1 for var in new_var_list: new_monomial *= var new_monomial *= left_remainder * new_sub * right_remainder for j in range(i + len(old_ncomm_factors), len(ncomm_factors)): new_monomial *= ncomm_factors[j] new_monomial *= comm_monomial break else: if not is_constant_term and len(comm_factors) > 0: new_monomial = comm_monomial for factor in ncomm_factors: new_monomial *= factor else: return monomial if not isinstance(new_sub, (float, int, complex)) and new_sub.is_Add: return expand(new_monomial) else: return new_monomial
def test_rs_series(): x, a, b, c = symbols('x, a, b, c') assert rs_series(a, a, 5).as_expr() == a assert rs_series(sin(a), a, 5).as_expr() == (sin(a).series(a, 0, 5)).removeO() assert rs_series(sin(a) + cos(a), a, 5).as_expr() == ((sin(a) + cos(a)).series(a, 0, 5)).removeO() assert rs_series(sin(a)*cos(a), a, 5).as_expr() == ((sin(a)* cos(a)).series(a, 0, 5)).removeO() p = (sin(a) - a)*(cos(a**2) + a**4/2) assert expand(rs_series(p, a, 10).as_expr()) == expand(p.series(a, 0, 10).removeO()) p = sin(a**2/2 + a/3) + cos(a/5)*sin(a/2)**3 assert expand(rs_series(p, a, 5).as_expr()) == expand(p.series(a, 0, 5).removeO()) p = sin(x**2 + a)*(cos(x**3 - 1) - a - a**2) assert expand(rs_series(p, a, 5).as_expr()) == expand(p.series(a, 0, 5).removeO()) p = sin(a**2 - a/3 + 2)**5*exp(a**3 - a/2) assert expand(rs_series(p, a, 10).as_expr()) == expand(p.series(a, 0, 10).removeO()) p = sin(a + b + c) assert expand(rs_series(p, a, 5).as_expr()) == expand(p.series(a, 0, 5).removeO()) p = tan(sin(a**2 + 4) + b + c) assert expand(rs_series(p, a, 6).as_expr()) == expand(p.series(a, 0, 6).removeO()) p = a**QQ(2,5) + a**QQ(2,3) + a r = rs_series(tan(p), a, 2) assert r.as_expr() == a**QQ(9,5) + a**QQ(26,15) + a**QQ(22,15) + a**QQ(6,5)/3 + \ a + a**QQ(2,3) + a**QQ(2,5) r = rs_series(exp(p), a, 1) assert r.as_expr() == a**QQ(4,5)/2 + a**QQ(2,3) + a**QQ(2,5) + 1 r = rs_series(sin(p), a, 2) assert r.as_expr() == -a**QQ(9,5)/2 - a**QQ(26,15)/2 - a**QQ(22,15)/2 - \ a**QQ(6,5)/6 + a + a**QQ(2,3) + a**QQ(2,5) r = rs_series(cos(p), a, 2) assert r.as_expr() == a**QQ(28,15)/6 - a**QQ(5,3) + a**QQ(8,5)/24 - a**QQ(7,5) - \ a**QQ(4,3)/2 - a**QQ(16,15) - a**QQ(4,5)/2 + 1 assert rs_series(sin(a)/7, a, 5).as_expr() == (sin(a)/7).series(a, 0, 5).removeO()
def __trigsimp(expr, deep=False): """recursive helper for trigsimp""" from sympy.simplify.fu import TR10i if _trigpat is None: _trigpats() a, b, c, d, matchers_division, matchers_add, \ matchers_identity, artifacts = _trigpat if expr.is_Mul: # do some simplifications like sin/cos -> tan: if not expr.is_commutative: com, nc = expr.args_cnc() expr = _trigsimp(Mul._from_args(com), deep)*Mul._from_args(nc) else: for i, (pattern, simp, ok1, ok2) in enumerate(matchers_division): if not _dotrig(expr, pattern): continue newexpr = _match_div_rewrite(expr, i) if newexpr is not None: if newexpr != expr: expr = newexpr break else: continue # use SymPy matching instead res = expr.match(pattern) if res and res.get(c, 0): if not res[c].is_integer: ok = ok1.subs(res) if not ok.is_positive: continue ok = ok2.subs(res) if not ok.is_positive: continue # if "a" contains any of trig or hyperbolic funcs with # argument "b" then skip the simplification if any(w.args[0] == res[b] for w in res[a].atoms( TrigonometricFunction, HyperbolicFunction)): continue # simplify and finish: expr = simp.subs(res) break # process below if expr.is_Add: args = [] for term in expr.args: if not term.is_commutative: com, nc = term.args_cnc() nc = Mul._from_args(nc) term = Mul._from_args(com) else: nc = S.One term = _trigsimp(term, deep) for pattern, result in matchers_identity: res = term.match(pattern) if res is not None: term = result.subs(res) break args.append(term*nc) if args != expr.args: expr = Add(*args) expr = min(expr, expand(expr), key=count_ops) if expr.is_Add: for pattern, result in matchers_add: if not _dotrig(expr, pattern): continue expr = TR10i(expr) if expr.has(HyperbolicFunction): res = expr.match(pattern) # if "d" contains any trig or hyperbolic funcs with # argument "a" or "b" then skip the simplification; # this isn't perfect -- see tests if res is None or not (a in res and b in res) or any( w.args[0] in (res[a], res[b]) for w in res[d].atoms( TrigonometricFunction, HyperbolicFunction)): continue expr = result.subs(res) break # Reduce any lingering artifacts, such as sin(x)**2 changing # to 1 - cos(x)**2 when sin(x)**2 was "simpler" for pattern, result, ex in artifacts: if not _dotrig(expr, pattern): continue # Substitute a new wild that excludes some function(s) # to help influence a better match. This is because # sometimes, for example, 'a' would match sec(x)**2 a_t = Wild('a', exclude=[ex]) pattern = pattern.subs(a, a_t) result = result.subs(a, a_t) m = expr.match(pattern) was = None while m and was != expr: was = expr if m[a_t] == 0 or \ -m[a_t] in m[c].args or m[a_t] + m[c] == 0: break if d in m and m[a_t]*m[d] + m[c] == 0: break expr = result.subs(m) m = expr.match(pattern) m.setdefault(c, S.Zero) elif expr.is_Mul or expr.is_Pow or deep and expr.args: expr = expr.func(*[_trigsimp(a, deep) for a in expr.args]) try: if not expr.has(*_trigs): raise TypeError e = expr.atoms(exp) new = expr.rewrite(exp, deep=deep) if new == e: raise TypeError fnew = factor(new) if fnew != new: new = sorted([new, factor(new)], key=count_ops)[0] # if all exp that were introduced disappeared then accept it if not (new.atoms(exp) - e): expr = new except TypeError: pass return expr
def test_rs_series(): x, a, b, c = symbols('x, a, b, c') assert rs_series(a, a, 5).as_expr() == a assert rs_series(sin(a), a, 5).as_expr() == (sin(a).series(a, 0, 5)).removeO() assert rs_series(sin(a) + cos(a), a, 5).as_expr() == ((sin(a) + cos(a)).series(a, 0, 5)).removeO() assert rs_series(sin(a)*cos(a), a, 5).as_expr() == ((sin(a)* cos(a)).series(a, 0, 5)).removeO() p = (sin(a) - a)*(cos(a**2) + a**4/2) assert expand(rs_series(p, a, 10).as_expr()) == expand(p.series(a, 0, 10).removeO()) p = sin(a**2/2 + a/3) + cos(a/5)*sin(a/2)**3 assert expand(rs_series(p, a, 5).as_expr()) == expand(p.series(a, 0, 5).removeO()) p = sin(x**2 + a)*(cos(x**3 - 1) - a - a**2) assert expand(rs_series(p, a, 5).as_expr()) == expand(p.series(a, 0, 5).removeO()) p = sin(a**2 - a/3 + 2)**5*exp(a**3 - a/2) assert expand(rs_series(p, a, 10).as_expr()) == expand(p.series(a, 0, 10).removeO()) p = sin(a + b + c) assert expand(rs_series(p, a, 5).as_expr()) == expand(p.series(a, 0, 5).removeO()) p = tan(sin(a**2 + 4) + b + c) assert expand(rs_series(p, a, 6).as_expr()) == expand(p.series(a, 0, 6).removeO()) p = a**QQ(2,5) + a**QQ(2,3) + a r = rs_series(tan(p), a, 2) assert r.as_expr() == a**QQ(9,5) + a**QQ(26,15) + a**QQ(22,15) + a**QQ(6,5)/3 + \ a + a**QQ(2,3) + a**QQ(2,5) r = rs_series(exp(p), a, 1) assert r.as_expr() == a**QQ(4,5)/2 + a**QQ(2,3) + a**QQ(2,5) + 1 r = rs_series(sin(p), a, 2) assert r.as_expr() == -a**QQ(9,5)/2 - a**QQ(26,15)/2 - a**QQ(22,15)/2 - \ a**QQ(6,5)/6 + a + a**QQ(2,3) + a**QQ(2,5) r = rs_series(cos(p), a, 2) assert r.as_expr() == a**QQ(28,15)/6 - a**QQ(5,3) + a**QQ(8,5)/24 - a**QQ(7,5) - \ a**QQ(4,3)/2 - a**QQ(16,15) - a**QQ(4,5)/2 + 1 assert rs_series(sin(a)/7, a, 5).as_expr() == (sin(a)/7).series(a, 0, 5).removeO() assert rs_series(log(1 + x), x, 5).as_expr() == -x**4/4 + x**3/3 - \ x**2/2 + x assert rs_series(log(1 + 4*x), x, 5).as_expr() == -64*x**4 + 64*x**3/3 - \ 8*x**2 + 4*x assert rs_series(log(1 + x + x**2), x, 10).as_expr() == -2*x**9/9 + \ x**8/8 + x**7/7 - x**6/3 + x**5/5 + x**4/4 - 2*x**3/3 + \ x**2/2 + x assert rs_series(log(1 + x*a**2), x, 7).as_expr() == -x**6*a**12/6 + \ x**5*a**10/5 - x**4*a**8/4 + x**3*a**6/3 - \ x**2*a**4/2 + x*a**2