def get_variables(n): if n == 0: return [] if n == 1: return [var('t')] names = ['t' + str(i) for i in xrange(n)] return list(var(names))
def Dini(a=1, b=1, name="Dini's surface"): r""" Returns Dini's surface, with parametrization .. MATH:: \begin{aligned} x(u, v) & = a \cos(u)\sin(v); \\ y(u, v) & = a \sin(u)\sin(v); \\ z(u, v) & = u + \log(\tan(v/2)) + \cos(v). \end{aligned} INPUT: - ``a, b`` -- surface parameters. - ``name`` -- string. Name of the surface. EXAMPLES:: sage: dini = surfaces.Dini(a=3, b=4); dini Parametrized surface ('Dini's surface') with equation (3*cos(u)*sin(v), 3*sin(u)*sin(v), 4*u + 3*cos(v) + 3*log(tan(1/2*v))) sage: dini.plot() # not tested -- known bug (see #10132) """ u, v = var('u, v') dini_eq = [a*cos(u)*sin(v), a*sin(u)*sin(v), a*(cos(v) + log(tan(v/2))) + b*u] coords = ((u, 0, 2*pi), (v, 0, 2*pi)) return ParametrizedSurface3D(dini_eq, coords, name)
def spin_polynomial(part, weight, length): """ Returns the spin polynomial associated to ``part``, ``weight``, and ``length``. EXAMPLES:: sage: from sage.combinat.ribbon_tableau import spin_polynomial sage: spin_polynomial([6,6,6],[4,2],3) t^6 + t^5 + 2*t^4 + t^3 + t^2 sage: spin_polynomial([6,6,6],[4,1,1],3) t^6 + 2*t^5 + 3*t^4 + 2*t^3 + t^2 sage: spin_polynomial([3,3,3,2,1], [2,2], 3) t^(7/2) + t^(5/2) sage: spin_polynomial([3,3,3,2,1], [2,1,1], 3) 2*t^(7/2) + 2*t^(5/2) + t^(3/2) sage: spin_polynomial([3,3,3,2,1], [1,1,1,1], 3) 3*t^(7/2) + 5*t^(5/2) + 3*t^(3/2) + sqrt(t) sage: spin_polynomial([5,4,3,2,1,1,1], [2,2,1], 3) 2*t^(9/2) + 6*t^(7/2) + 2*t^(5/2) sage: spin_polynomial([[6]*6, [3,3]], [4,4,2], 3) 3*t^9 + 5*t^8 + 9*t^7 + 6*t^6 + 3*t^5 """ from sage.symbolic.ring import var sp = spin_polynomial_square(part,weight,length) t = var('t') c = sp.coeffs() return sum([c[i]*t**(QQ(i)/2) for i in range(len(c))])
def Enneper(name="Enneper's surface"): r""" Return Enneper's surface, with parametrization .. MATH:: \begin{aligned} x(u, v) & = u(1 - u^2/3 + v^2)/3; \\ y(u, v) & = -v(1 - v^2/3 + u^2)/3; \\ z(u, v) & = (u^2 - v^2)/3. \end{aligned} INPUT: - ``name`` -- string. Name of the surface. For more information, see :wikipedia:`Enneper_surface`. EXAMPLES:: sage: enn = surfaces.Enneper(); enn Parametrized surface ('Enneper's surface') with equation (-1/9*(u^2 - 3*v^2 - 3)*u, -1/9*(3*u^2 - v^2 + 3)*v, 1/3*u^2 - 1/3*v^2) sage: enn.plot() Graphics3d Object """ u, v = var('u, v') enneper_eq = [ u * (1 - u**2 / 3 + v**2) / 3, -v * (1 - v**2 / 3 + u**2) / 3, (u**2 - v**2) / 3 ] coords = ((u, -3, 3), (v, -3, 3)) return ParametrizedSurface3D(enneper_eq, coords, name)
def spin_polynomial(part, weight, length): """ Returns the spin polynomial associated to part, weight, and length. EXAMPLES:: sage: from sage.combinat.ribbon_tableau import spin_polynomial sage: spin_polynomial([6,6,6],[4,2],3) t^6 + t^5 + 2*t^4 + t^3 + t^2 sage: spin_polynomial([6,6,6],[4,1,1],3) t^6 + 2*t^5 + 3*t^4 + 2*t^3 + t^2 sage: spin_polynomial([3,3,3,2,1], [2,2], 3) t^(7/2) + t^(5/2) sage: spin_polynomial([3,3,3,2,1], [2,1,1], 3) 2*t^(7/2) + 2*t^(5/2) + t^(3/2) sage: spin_polynomial([3,3,3,2,1], [1,1,1,1], 3) 3*t^(7/2) + 5*t^(5/2) + 3*t^(3/2) + sqrt(t) sage: spin_polynomial([5,4,3,2,1,1,1], [2,2,1], 3) 2*t^(9/2) + 6*t^(7/2) + 2*t^(5/2) sage: spin_polynomial([[6]*6, [3,3]], [4,4,2], 3) 3*t^9 + 5*t^8 + 9*t^7 + 6*t^6 + 3*t^5 """ from sage.symbolic.ring import var sp = spin_polynomial_square(part, weight, length) t = var('t') c = sp.coeffs() return sum([c[i] * t**(QQ(i) / 2) for i in range(len(c))])
def Klein(r=1, name="Klein bottle"): r""" Returns the Klein bottle, in the figure-8 parametrization given by .. MATH:: \begin{aligned} x(u, v) & = (r + \cos(u/2)\cos(v) - \sin(u/2)\sin(2v)) \cos(u); \\ y(u, v) & = (r + \cos(u/2)\cos(v) - \sin(u/2)\sin(2v)) \sin(u); \\ z(u, v) & = \sin(u/2)\cos(v) + \cos(u/2)\sin(2v). \end{aligned} INPUT: - ``r`` -- radius of the "figure-8" circle. - ``name`` -- string. Name of the surface. EXAMPLES:: sage: klein = surfaces.Klein(); klein Parametrized surface ('Klein bottle') with equation (-(sin(1/2*u)*sin(2*v) - cos(1/2*u)*sin(v) - 1)*cos(u), -(sin(1/2*u)*sin(2*v) - cos(1/2*u)*sin(v) - 1)*sin(u), cos(1/2*u)*sin(2*v) + sin(1/2*u)*sin(v)) sage: klein.plot() Graphics3d Object """ u, v = var("u, v") x = (r + cos(u / 2) * sin(v) - sin(u / 2) * sin(2 * v)) * cos(u) y = (r + cos(u / 2) * sin(v) - sin(u / 2) * sin(2 * v)) * sin(u) z = sin(u / 2) * sin(v) + cos(u / 2) * sin(2 * v) klein_eq = [x, y, z] coords = ((u, 0, 2 * pi), (v, 0, 2 * pi)) return ParametrizedSurface3D(klein_eq, coords, name)
def Helicoid(h=1, name="Helicoid"): r""" Returns a helicoid surface, with parametrization .. MATH:: \begin{aligned} x(\rho, \theta) & = \rho \cos(\theta); \\ y(\rho, \theta) & = \rho \sin(\theta); \\ z(\rho, \theta) & = h\theta/(2\pi). \end{aligned} INPUT: - ``h`` -- distance along the z-axis between two successive turns of the helicoid. - ``name`` -- string. Name of the surface. EXAMPLES:: sage: helicoid = surfaces.Helicoid(h=2); helicoid Parametrized surface ('Helicoid') with equation (rho*cos(theta), rho*sin(theta), theta/pi) sage: helicoid.plot() Graphics3d Object """ rho, theta = var("rho, theta") helicoid_eq = [rho * cos(theta), rho * sin(theta), h * theta / (2 * pi)] coords = ((rho, -2, 2), (theta, 0, 2 * pi)) return ParametrizedSurface3D(helicoid_eq, [rho, theta], name)
def MonkeySaddle(name="Monkey saddle"): r""" Returns a monkey saddle surface, with equation .. MATH:: z = x^3 - 3xy^2. INPUT: - ``name`` -- string. Name of the surface. EXAMPLES:: sage: saddle = surfaces.MonkeySaddle(); saddle Parametrized surface ('Monkey saddle') with equation (u, v, u^3 - 3*u*v^2) sage: saddle.plot() Graphics3d Object """ u, v = var("u, v") monkey_eq = [u, v, u ** 3 - 3 * u * v ** 2] coords = ((u, -2, 2), (v, -2, 2)) return ParametrizedSurface3D(monkey_eq, coords, name)
def Enneper(name="Enneper's surface"): r""" Returns Enneper's surface, with parametrization .. MATH:: \begin{aligned} x(u, v) & = u(1 - u^2/3 + v^2)/3; \\ y(u, v) & = -v(1 - v^2/3 + u^2)/3; \\ z(u, v) & = (u^2 - v^2)/3. \end{aligned} INPUT: - ``name`` -- string. Name of the surface. EXAMPLES:: sage: enn = surfaces.Enneper(); enn Parametrized surface ('Enneper's surface') with equation (-1/9*(u^2 - 3*v^2 - 3)*u, -1/9*(3*u^2 - v^2 + 3)*v, 1/3*u^2 - 1/3*v^2) sage: enn.plot() Graphics3d Object """ u, v = var("u, v") enneper_eq = [u * (1 - u ** 2 / 3 + v ** 2) / 3, -v * (1 - v ** 2 / 3 + u ** 2) / 3, (u ** 2 - v ** 2) / 3] coords = ((u, -3, 3), (v, -3, 3)) return ParametrizedSurface3D(enneper_eq, coords, name)
def WhitneyUmbrella(name="Whitney's umbrella"): r""" Return Whitney's umbrella, with parametric representation .. MATH:: x(u, v) = uv, \quad y(u, v) = u, \quad z(u, v) = v^2. INPUT: - ``name`` -- string. Name of the surface. For more information, see :wikipedia:`Whitney_umbrella`. EXAMPLES:: sage: whitney = surfaces.WhitneyUmbrella(); whitney Parametrized surface ('Whitney's umbrella') with equation (u*v, u, v^2) sage: whitney.plot() Graphics3d Object """ u, v = var('u, v') whitney_eq = [u * v, u, v**2] coords = ((u, -1, 1), (v, -1, 1)) return ParametrizedSurface3D(whitney_eq, coords, name)
def Crosscap(r=1, name="Crosscap"): r""" Return a crosscap surface, with parametrization .. MATH:: \begin{aligned} x(u, v) & = r(1 + \cos(v)) \cos(u); \\ y(u, v) & = r(1 + \cos(v)) \sin(u); \\ z(u, v) & = - r\tanh(u - \pi) \sin(v). \end{aligned} INPUT: - ``r`` -- surface parameter. - ``name`` -- string. Name of the surface. For more information, see :wikipedia:`Cross-cap`. EXAMPLES:: sage: crosscap = surfaces.Crosscap(); crosscap Parametrized surface ('Crosscap') with equation ((cos(v) + 1)*cos(u), (cos(v) + 1)*sin(u), -sin(v)*tanh(-pi + u)) sage: crosscap.plot() Graphics3d Object """ u, v = var('u, v') crosscap_eq = [ r * (1 + cos(v)) * cos(u), r * (1 + cos(v)) * sin(u), -tanh(u - pi) * r * sin(v) ] coords = ((u, 0, 2 * pi), (v, 0, 2 * pi)) return ParametrizedSurface3D(crosscap_eq, coords, name)
def MonkeySaddle(name="Monkey saddle"): r""" Return a monkey saddle surface, with equation .. MATH:: z = x^3 - 3xy^2. INPUT: - ``name`` -- string. Name of the surface. For more information, see :wikipedia:`Monkey_saddle`. EXAMPLES:: sage: saddle = surfaces.MonkeySaddle(); saddle Parametrized surface ('Monkey saddle') with equation (u, v, u^3 - 3*u*v^2) sage: saddle.plot() Graphics3d Object """ u, v = var('u, v') monkey_eq = [u, v, u**3 - 3 * u * v**2] coords = ((u, -2, 2), (v, -2, 2)) return ParametrizedSurface3D(monkey_eq, coords, name)
def Catenoid(c=1, name="Catenoid"): r""" Returns a catenoid surface, with parametric representation .. MATH:: \begin{aligned} x(u, v) & = c \cosh(v/c) \cos(u); \\ y(u, v) & = c \cosh(v/c) \sin(u); \\ z(u, v) & = v. \end{aligned} INPUT: - ``c`` -- surface parameter. - ``name`` -- string. Name of the surface. EXAMPLES:: sage: cat = surfaces.Catenoid(); cat Parametrized surface ('Catenoid') with equation (cos(u)*cosh(v), cosh(v)*sin(u), v) sage: cat.plot() Graphics3d Object """ u, v = var("u, v") catenoid_eq = [c * cosh(v / c) * cos(u), c * cosh(v / c) * sin(u), v] coords = ((u, 0, 2 * pi), (v, -1, 1)) return ParametrizedSurface3D(catenoid_eq, coords, name)
def Torus(r=2, R=3, name="Torus"): r""" Return a torus obtained by revolving a circle of radius ``r`` around a coplanar axis ``R`` units away from the center of the circle. The parametrization used is .. MATH:: \begin{aligned} x(u, v) & = (R + r \cos(v)) \cos(u); \\ y(u, v) & = (R + r \cos(v)) \sin(u); \\ z(u, v) & = r \sin(v). \end{aligned} INPUT: - ``r``, ``R`` -- Minor and major radius of the torus. - ``name`` -- string. Name of the surface. For more information, see :wikipedia:`Torus`. EXAMPLES:: sage: torus = surfaces.Torus(); torus Parametrized surface ('Torus') with equation ((2*cos(v) + 3)*cos(u), (2*cos(v) + 3)*sin(u), 2*sin(v)) sage: torus.plot() Graphics3d Object """ u, v = var('u, v') torus_eq = [(R + r * cos(v)) * cos(u), (R + r * cos(v)) * sin(u), r * sin(v)] coords = ((u, 0, 2 * pi), (v, 0, 2 * pi)) return ParametrizedSurface3D(torus_eq, coords, name)
def Klein(r=1, name="Klein bottle"): r""" Return the Klein bottle, in the figure-8 parametrization given by .. MATH:: \begin{aligned} x(u, v) & = (r + \cos(u/2)\cos(v) - \sin(u/2)\sin(2v)) \cos(u); \\ y(u, v) & = (r + \cos(u/2)\cos(v) - \sin(u/2)\sin(2v)) \sin(u); \\ z(u, v) & = \sin(u/2)\cos(v) + \cos(u/2)\sin(2v). \end{aligned} INPUT: - ``r`` -- radius of the "figure-8" circle. - ``name`` -- string. Name of the surface. For more information, see :wikipedia:`Klein_bottle`. EXAMPLES:: sage: klein = surfaces.Klein(); klein Parametrized surface ('Klein bottle') with equation (-(sin(1/2*u)*sin(2*v) - cos(1/2*u)*sin(v) - 1)*cos(u), -(sin(1/2*u)*sin(2*v) - cos(1/2*u)*sin(v) - 1)*sin(u), cos(1/2*u)*sin(2*v) + sin(1/2*u)*sin(v)) sage: klein.plot() Graphics3d Object """ u, v = var('u, v') x = (r + cos(u / 2) * sin(v) - sin(u / 2) * sin(2 * v)) * cos(u) y = (r + cos(u / 2) * sin(v) - sin(u / 2) * sin(2 * v)) * sin(u) z = sin(u / 2) * sin(v) + cos(u / 2) * sin(2 * v) klein_eq = [x, y, z] coords = ((u, 0, 2 * pi), (v, 0, 2 * pi)) return ParametrizedSurface3D(klein_eq, coords, name)
def Helicoid(h=1, name="Helicoid"): r""" Return a helicoid surface, with parametrization .. MATH:: \begin{aligned} x(\rho, \theta) & = \rho \cos(\theta); \\ y(\rho, \theta) & = \rho \sin(\theta); \\ z(\rho, \theta) & = h\theta/(2\pi). \end{aligned} INPUT: - ``h`` -- distance along the z-axis between two successive turns of the helicoid. - ``name`` -- string. Name of the surface. For more information, see :wikipedia:`Helicoid`. EXAMPLES:: sage: helicoid = surfaces.Helicoid(h=2); helicoid Parametrized surface ('Helicoid') with equation (rho*cos(theta), rho*sin(theta), theta/pi) sage: helicoid.plot() Graphics3d Object """ rho, theta = var('rho, theta') helicoid_eq = [ rho * cos(theta), rho * sin(theta), h * theta / (2 * pi) ] coords = ((rho, -2, 2), (theta, 0, 2 * pi)) return ParametrizedSurface3D(helicoid_eq, coords, name)
def __init__(self, coordinate_patch = None): """ Construct the algebra of differential forms on a given coordinate patch. See ``DifferentialForms`` for details. INPUT:: - ``coordinate_patch`` -- Coordinate patch where the algebra lives. If no coordinate patch is given, a default coordinate patch with coordinates (x, y, z) is used. EXAMPLES:: sage: p, q = var('p, q') sage: U = CoordinatePatch((p, q)); U Open subset of R^2 with coordinates p, q sage: F = DifferentialForms(U); F Algebra of differential forms in the variables p, q """ from sage.categories.graded_algebras_with_basis \ import GradedAlgebrasWithBasis from sage.structure.parent_gens import ParentWithGens if not coordinate_patch: x, y, z = var('x, y, z') coordinate_patch = CoordinatePatch((x, y, z)) if not isinstance(coordinate_patch, CoordinatePatch): raise TypeError("%s not a valid Coordinate Patch" % coordinate_patch) self._patch = coordinate_patch ParentWithGens.__init__(self, SR, \ category = GradedAlgebrasWithBasis(SR))
def Dini(a=1, b=1, name="Dini's surface"): r""" Returns Dini's surface, with parametrization .. MATH:: \begin{aligned} x(u, v) & = a \cos(u)\sin(v); \\ y(u, v) & = a \sin(u)\sin(v); \\ z(u, v) & = u + \log(\tan(v/2)) + \cos(v). \end{aligned} INPUT: - ``a, b`` -- surface parameters. - ``name`` -- string. Name of the surface. EXAMPLES:: sage: dini = surfaces.Dini(a=3, b=4); dini Parametrized surface ('Dini's surface') with equation (3*cos(u)*sin(v), 3*sin(u)*sin(v), 4*u + 3*cos(v) + 3*log(tan(1/2*v))) sage: dini.plot() # not tested -- known bug (see #10132) """ u, v = var("u, v") dini_eq = [a * cos(u) * sin(v), a * sin(u) * sin(v), a * (cos(v) + log(tan(v / 2))) + b * u] coords = ((u, 0, 2 * pi), (v, 0, 2 * pi)) return ParametrizedSurface3D(dini_eq, coords, name)
def Crosscap(r=1, name="Crosscap"): r""" Returns a crosscap surface, with parametrization .. MATH:: \begin{aligned} x(u, v) & = r(1 + \cos(v)) \cos(u); \\ y(u, v) & = r(1 + \cos(v)) \sin(u); \\ z(u, v) & = - r\tanh(u - \pi) \sin(v). \end{aligned} INPUT: - ``r`` -- surface parameter. - ``name`` -- string. Name of the surface. EXAMPLES:: sage: crosscap = surfaces.Crosscap(); crosscap Parametrized surface ('Crosscap') with equation ((cos(v) + 1)*cos(u), (cos(v) + 1)*sin(u), -sin(v)*tanh(-pi + u)) sage: crosscap.plot() Graphics3d Object """ u, v = var("u, v") crosscap_eq = [r * (1 + cos(v)) * cos(u), r * (1 + cos(v)) * sin(u), -tanh(u - pi) * r * sin(v)] coords = ((u, 0, 2 * pi), (v, 0, 2 * pi)) return ParametrizedSurface3D(crosscap_eq, coords, name)
def WhitneyUmbrella(name="Whitney's umbrella"): r""" Returns Whitney's umbrella, with parametric representation .. MATH:: x(u, v) = uv, \quad y(u, v) = u, \quad z(u, v) = v^2. INPUT: - ``name`` -- string. Name of the surface. EXAMPLES:: sage: whitney = surfaces.WhitneyUmbrella(); whitney Parametrized surface ('Whitney's umbrella') with equation (u*v, u, v^2) sage: whitney.plot() Graphics3d Object """ u, v = var("u, v") whitney_eq = [u * v, u, v ** 2] coords = ((u, -1, 1), (v, -1, 1)) return ParametrizedSurface3D(whitney_eq, coords, name)
def Torus(r=2, R=3, name="Torus"): r""" Returns a torus obtained by revolving a circle of radius ``r`` around a coplanar axis ``R`` units away from the center of the circle. The parametrization used is .. MATH:: \begin{aligned} x(u, v) & = (R + r \cos(v)) \cos(u); \\ y(u, v) & = (R + r \cos(v)) \sin(u); \\ z(u, v) & = r \sin(v). \end{aligned} INPUT: - ``r``, ``R`` -- Minor and major radius of the torus. - ``name`` -- string. Name of the surface. EXAMPLES:: sage: torus = surfaces.Torus(); torus Parametrized surface ('Torus') with equation ((2*cos(v) + 3)*cos(u), (2*cos(v) + 3)*sin(u), 2*sin(v)) sage: torus.plot() Graphics3d Object """ u, v = var("u, v") torus_eq = [(R + r * cos(v)) * cos(u), (R + r * cos(v)) * sin(u), r * sin(v)] coords = ((u, 0, 2 * pi), (v, 0, 2 * pi)) return ParametrizedSurface3D(torus_eq, coords, name)
def Catenoid(c=1, name="Catenoid"): r""" Return a catenoid surface, with parametric representation .. MATH:: \begin{aligned} x(u, v) & = c \cosh(v/c) \cos(u); \\ y(u, v) & = c \cosh(v/c) \sin(u); \\ z(u, v) & = v. \end{aligned} INPUT: - ``c`` -- surface parameter. - ``name`` -- string. Name of the surface. For more information, see :wikipedia:`Catenoid`. EXAMPLES:: sage: cat = surfaces.Catenoid(); cat Parametrized surface ('Catenoid') with equation (cos(u)*cosh(v), cosh(v)*sin(u), v) sage: cat.plot() Graphics3d Object """ u, v = var('u, v') catenoid_eq = [c * cosh(v / c) * cos(u), c * cosh(v / c) * sin(u), v] coords = ((u, 0, 2 * pi), (v, -1, 1)) return ParametrizedSurface3D(catenoid_eq, coords, name)
def Dini(a=1, b=1, name="Dini's surface"): r""" Return Dini's surface, with parametrization .. MATH:: \begin{aligned} x(u, v) & = a \cos(u)\sin(v); \\ y(u, v) & = a \sin(u)\sin(v); \\ z(u, v) & = u + \log(\tan(v/2)) + \cos(v). \end{aligned} INPUT: - ``a, b`` -- surface parameters. - ``name`` -- string. Name of the surface. For more information, see :wikipedia:`Dini%27s_surface`. EXAMPLES:: sage: dini = surfaces.Dini(a=3, b=4); dini Parametrized surface ('Dini's surface') with equation (3*cos(u)*sin(v), 3*sin(u)*sin(v), 4*u + 3*cos(v) + 3*log(tan(1/2*v))) sage: dini.plot() Graphics3d Object """ u, v = var('u, v') dini_eq = [ a * cos(u) * sin(v), a * sin(u) * sin(v), a * (cos(v) + log(tan(v / 2))) + b * u ] coords = ((u, 0, 2 * pi), (v, 0, 2 * pi)) return ParametrizedSurface3D(dini_eq, coords, name)
def _real_or_imaginary_part_of_power_of_complex_number(n, start): """ Let z = x + y * I. If start = 0, return Re(z^n). If start = 1, return Im(z^n). The result is a sage symbolic expression in x and y with rational coefficients. """ # By binomial theorem, we have # # n n n n n-i i # (x + y * I) = sum ( ) * I * x * y # i = 0 i # # The real/imaginary part consists of all even/odd terms in the sum: return sum([ binomial(n, i) * (-1) ** (i/2) * var('x') ** (n - i) * var('y') ** i for i in range(start, n + 1, 2)])
def Paraboloid(a=1, b=1, c=1, elliptic=True, name=None): r""" Returns a paraboloid with equation .. MATH:: \frac{z}{c} = \pm \frac{x^2}{a^2} + \frac{y^2}{b^2} When the plus sign is selected, the paraboloid is elliptic. Otherwise the surface is a hyperbolic paraboloid. INPUT: - ``a``, ``b``, ``c`` -- Surface parameters. - ``elliptic`` (default: True) -- whether to create an elliptic or hyperbolic paraboloid. - ``name`` -- string. Name of the surface. EXAMPLES:: sage: epar = surfaces.Paraboloid(1, 3, 2); epar Parametrized surface ('Elliptic paraboloid') with equation (u, v, 2*u^2 + 2/9*v^2) sage: epar.plot() Graphics3d Object sage: hpar = surfaces.Paraboloid(2, 3, 1, elliptic=False); hpar Parametrized surface ('Hyperbolic paraboloid') with equation (u, v, -1/4*u^2 + 1/9*v^2) sage: hpar.plot() Graphics3d Object """ u, v = var("u, v") x = u y = v if elliptic: z = c * (v ** 2 / b ** 2 + u ** 2 / a ** 2) else: z = c * (v ** 2 / b ** 2 - u ** 2 / a ** 2) paraboloid_eq = [x, y, z] coords = ((u, -3, 3), (v, -3, 3)) if name is None: if elliptic: name = "Elliptic paraboloid" else: name = "Hyperbolic paraboloid" return ParametrizedSurface3D(paraboloid_eq, coords, name)
def Paraboloid(a=1, b=1, c=1, elliptic=True, name=None): r""" Return a paraboloid with equation .. MATH:: \frac{z}{c} = \pm \frac{x^2}{a^2} + \frac{y^2}{b^2} When the plus sign is selected, the paraboloid is elliptic. Otherwise the surface is a hyperbolic paraboloid. INPUT: - ``a``, ``b``, ``c`` -- Surface parameters. - ``elliptic`` (default: True) -- whether to create an elliptic or hyperbolic paraboloid. - ``name`` -- string. Name of the surface. For more information, see :wikipedia:`Paraboloid`. EXAMPLES:: sage: epar = surfaces.Paraboloid(1, 3, 2); epar Parametrized surface ('Elliptic paraboloid') with equation (u, v, 2*u^2 + 2/9*v^2) sage: epar.plot() Graphics3d Object sage: hpar = surfaces.Paraboloid(2, 3, 1, elliptic=False); hpar Parametrized surface ('Hyperbolic paraboloid') with equation (u, v, -1/4*u^2 + 1/9*v^2) sage: hpar.plot() Graphics3d Object """ u, v = var('u, v') x = u y = v if elliptic: z = c * (v**2 / b**2 + u**2 / a**2) else: z = c * (v**2 / b**2 - u**2 / a**2) paraboloid_eq = [x, y, z] coords = ((u, -3, 3), (v, -3, 3)) if name is None: if elliptic: name = "Elliptic paraboloid" else: name = "Hyperbolic paraboloid" return ParametrizedSurface3D(paraboloid_eq, coords, name)
def __init__(self, L, name, **kwds): r""" Initialize ``self``. TESTS:: sage: L = lie_algebras.Heisenberg(QQ, 2) sage: G = L.lie_group() sage: TestSuite(G).run() """ required_cat = LieAlgebras(L.base_ring()).FiniteDimensional() required_cat = required_cat.WithBasis().Nilpotent() if L not in required_cat: raise TypeError("L needs to be a finite dimensional nilpotent " "Lie algebra with basis") self._lie_algebra = L R = L.base_ring() category = kwds.pop('category', None) category = LieGroups(R).or_subcategory(category) if isinstance(R, RealField_class): structure = RealDifferentialStructure() else: structure = DifferentialStructure() DifferentiableManifold.__init__(self, L.dimension(), name, R, structure, category=category) # initialize exponential coordinates of the first kind basis_strs = [str(X) for X in L.basis()] split = list(zip(*[s.split('_') for s in basis_strs])) if len(split) == 2 and all(sk == split[0][0] for sk in split[0]): self._var_indexing = split[1] else: self._var_indexing = [str(k) for k in range(L.dimension())] variables = ' '.join('x_%s' % k for k in self._var_indexing) self._Exp1 = self.chart(variables) # compute a symbolic formula for the group law L_SR = _symbolic_lie_algebra_copy(L) n = L.dimension() a, b = (tuple(var('%s_%d' % (s, j)) for j in range(n)) for s in ['a', 'b']) self._group_law_vars = (a, b) bch = L_SR.bch(L_SR.from_vector(a), L_SR.from_vector(b), L.step()) self._group_law = vector(SR, (zk.expand() for zk in bch.to_vector()))
def __init__(self,divisor): self.divisor = divisor self.poly_ring = divisor.parent() hw = homogenous_wieghts(divisor) self.wieghts = hw[1:] self.degree = hw[0] #Setup patch for differential form var_names = [ g.__repr__() for g in self.poly_ring.gens()] self.form_vars = var(",".join(var_names)) self.form_patch = CoordinatePatch(self.form_vars) self.form_space = DifferentialForms(self.form_patch) #compute the generators of the logarithmic p-forms self._p_modules = {} # modules of logarithmic differential p-forms self._p_gens = {} # the generators of the module of logarithmic differential p-forms self._p_zero_part_basis = {}
def __init__(self, coordinate_patch=None): """ Construct the algebra of differential forms on a given coordinate patch. See ``DifferentialForms`` for details. INPUT: - ``coordinate_patch`` -- Coordinate patch where the algebra lives. If no coordinate patch is given, a default coordinate patch with coordinates (x, y, z) is used. EXAMPLES:: sage: p, q = var('p, q') sage: U = CoordinatePatch((p, q)); U doctest:...: DeprecationWarning: Use Manifold instead. See http://trac.sagemath.org/24444 for details. Open subset of R^2 with coordinates p, q sage: F = DifferentialForms(U); F doctest:...: DeprecationWarning: For the set of differential forms of degree p, use U.diff_form_module(p), where U is the base manifold (type U.diff_form_module? for details). See http://trac.sagemath.org/24444 for details. Algebra of differential forms in the variables p, q """ from sage.categories.graded_algebras_with_basis \ import GradedAlgebrasWithBasis from sage.structure.parent_gens import ParentWithGens from sage.misc.superseded import deprecation deprecation( 24444, 'For the set of differential forms of degree p, ' + 'use U.diff_form_module(p), where U is the base ' + 'manifold (type U.diff_form_module? for details).') if not coordinate_patch: x, y, z = var('x, y, z') coordinate_patch = CoordinatePatch((x, y, z)) if not isinstance(coordinate_patch, CoordinatePatch): raise TypeError("%s not a valid Coordinate Patch" % coordinate_patch) self._patch = coordinate_patch ParentWithGens.__init__(self, SR, \ category = GradedAlgebrasWithBasis(SR))
def __init__(self, coordinate_patch = None): """ Construct the algebra of differential forms on a given coordinate patch. See ``DifferentialForms`` for details. INPUT: - ``coordinate_patch`` -- Coordinate patch where the algebra lives. If no coordinate patch is given, a default coordinate patch with coordinates (x, y, z) is used. EXAMPLES:: sage: p, q = var('p, q') sage: U = CoordinatePatch((p, q)); U doctest:...: DeprecationWarning: Use Manifold instead. See http://trac.sagemath.org/24444 for details. Open subset of R^2 with coordinates p, q sage: F = DifferentialForms(U); F doctest:...: DeprecationWarning: For the set of differential forms of degree p, use U.diff_form_module(p), where U is the base manifold (type U.diff_form_module? for details). See http://trac.sagemath.org/24444 for details. Algebra of differential forms in the variables p, q """ from sage.categories.graded_algebras_with_basis \ import GradedAlgebrasWithBasis from sage.structure.parent_gens import ParentWithGens from sage.misc.superseded import deprecation deprecation(24444, 'For the set of differential forms of degree p, ' + 'use U.diff_form_module(p), where U is the base ' + 'manifold (type U.diff_form_module? for details).') if not coordinate_patch: x, y, z = var('x, y, z') coordinate_patch = CoordinatePatch((x, y, z)) if not isinstance(coordinate_patch, CoordinatePatch): raise TypeError("%s not a valid Coordinate Patch" % coordinate_patch) self._patch = coordinate_patch ParentWithGens.__init__(self, SR, \ category = GradedAlgebrasWithBasis(SR))
def Ellipsoid(center=(0,0,0), axes=(1,1,1), name="Ellipsoid"): r""" Returns an ellipsoid centered at ``center`` whose semi-principal axes have lengths given by the components of ``axes``. The parametrization of the ellipsoid is given by .. MATH:: \begin{aligned} x(u, v) & = x_0 + a \cos(u) \cos(v); \\ y(u, v) & = y_0 + b \sin(u) \cos(v); \\ z(u, v) & = z_0 + c \sin(v). \end{aligned} INPUT: - ``center`` -- 3-tuple. Coordinates of the center of the ellipsoid. - ``axes`` -- 3-tuple. Lengths of the semi-principal axes. - ``name`` -- string. Name of the ellipsoid. EXAMPLES:: sage: ell = surfaces.Ellipsoid(axes=(1, 2, 3)); ell Parametrized surface ('Ellipsoid') with equation (cos(u)*cos(v), 2*cos(v)*sin(u), 3*sin(v)) sage: ell.plot() Graphics3d Object """ u, v = var ('u, v') x, y, z = center a, b, c = axes ellipsoid_parametric_eq = [x + a*cos(u)*cos(v), y + b*sin(u)*cos(v), z + c*sin(v)] coords = ((u, 0, 2*pi), (v, -pi/2, pi/2)) return ParametrizedSurface3D(ellipsoid_parametric_eq, coords, name)
def fixed_charpoly(M, variables): """ Calcualate the characterictic polynomials of a matrix with entries in a group ring. The problem is that Sage doesn't do a good job at calculating characteristic polynomials of matrices over Symbolic Ring in certain cases. To overcome this, we create a new matrix over a Polynomial Ring where inverses of the variables are replaced with new variable, calculate the charpoly, then substitute back. INPUT: - ``M`` -- a square matrix over Symbolic Ring - ``variables`` -- the list of variables appearing in the entries of ``M`` OUTPUT: the characterictic polynomial of ``M`` """ from sage.rings.polynomial.polynomial_ring_constructor import \ PolynomialRing from sage.rings.integer_ring import ZZ sub_vars = {v : var(str(v) + 'inv') for v in variables} ring = PolynomialRing(ZZ, variables + sub_vars.values()) \ if len(variables) > 0 else ZZ new_M = matrix(ring, M.nrows(), M.ncols()) for i in xrange(M.nrows()): for j in xrange(M.ncols()): exp = M[i,j] a = exp.numerator() b = exp.denominator() new_M[i,j] = a * b.subs(sub_vars) poly = new_M.charpoly('u') back_sub = {str(sub_vars[v]) : 1/v for v in sub_vars} poly = poly.subs(**back_sub) return poly
def Ellipsoid(center=(0,0,0), axes=(1,1,1), name="Ellipsoid"): r""" Returns an ellipsoid centered at ``center`` whose semi-principal axes have lengths given by the components of ``axes``. The parametrization of the ellipsoid is given by .. MATH:: \begin{aligned} x(u, v) & = x_0 + a \cos(u) \cos(v); \\ y(u, v) & = y_0 + b \sin(u) \cos(v); \\ z(u, v) & = z_0 + c \sin(v). \end{aligned} INPUT: - ``center`` -- 3-tuple. Coordinates of the center of the ellipsoid. - ``axes`` -- 3-tuple. Lengths of the semi-principal axes. - ``name`` -- string. Name of the ellipsoid. EXAMPLES:: sage: ell = surfaces.Ellipsoid(axes=(1, 2, 3)); ell Parametrized surface ('Ellipsoid') with equation (cos(u)*cos(v), 2*cos(v)*sin(u), 3*sin(v)) sage: ell.plot() """ u, v = var ('u, v') x, y, z = center a, b, c = axes ellipsoid_parametric_eq = [x + a*cos(u)*cos(v), y + b*sin(u)*cos(v), z + c*sin(v)] coords = ((u, 0, 2*pi), (v, -pi/2, pi/2)) return ParametrizedSurface3D(ellipsoid_parametric_eq, coords, name)
def __call__(self, *args): """ EXAMPLES:: sage: from sage.symbolic.operators import FDerivativeOperator sage: x,y = var('x,y') sage: f = function('foo') sage: op = FDerivativeOperator(f, [0,1]) sage: op(x,y) D[0, 1](foo)(x, y) sage: op(x,x^2) D[0, 1](foo)(x, x^2) TESTS: We should be able to operate on functions evaluated at a point, not just a symbolic variable, :trac:`12796`:: sage: from sage.symbolic.operators import FDerivativeOperator sage: f = function('f') sage: op = FDerivativeOperator(f, [0]) sage: op(1) D[0](f)(1) """ if (not all(is_SymbolicVariable(x) for x in args) or len(args) != len(set(args))): # An evaluated derivative of the form f'(1) is not a # symbolic variable, yet we would like to treat it # like one. So, we replace the argument `1` with a # temporary variable e.g. `t0` and then evaluate the # derivative f'(t0) symbolically at t0=1. See trac # #12796. temp_args=[var("t%s"%i) for i in range(len(args))] vars=[temp_args[i] for i in self._parameter_set] return self._f(*temp_args).diff(*vars).function(*temp_args)(*args) vars = [args[i] for i in self._parameter_set] return self._f(*args).diff(*vars)
def setUp(self): self.poly_ring = PolynomialRing(QQ, "x", 3) self.x = self.poly_ring.gens()[0] self.y = self.poly_ring.gens()[1] self.z = self.poly_ring.gens()[2] self.vars = var('x,y,z')
def dynatomic_polynomial(self, period): r""" Compute the (affine) dynatomic polynomial of a dynamical system `f: \mathbb{A}^1 \to \mathbb{A}^1`. The dynatomic polynomial is the analog of the cyclotomic polynomial and its roots are the points of formal period `n`. ALGORITHM: Homogenize to a map `f: \mathbb{P}^1 \to \mathbb{P}^1` and compute the dynatomic polynomial there. Then, dehomogenize. INPUT: - ``period`` -- a positive integer or a list/tuple `[m,n]`, where `m` is the preperiod and `n` is the period OUTPUT: If possible, a single variable polynomial in the coordinate ring of the polynomial. Otherwise a fraction field element of the coordinate ring of the polynomial. EXAMPLES:: sage: A.<x,y> = AffineSpace(QQ, 2) sage: f = DynamicalSystem_affine([x^2+y^2, y^2]) sage: f.dynatomic_polynomial(2) Traceback (most recent call last): ... TypeError: does not make sense in dimension >1 :: sage: A.<x> = AffineSpace(ZZ, 1) sage: f = DynamicalSystem_affine([(x^2+1)/x]) sage: f.dynatomic_polynomial(4) 2*x^12 + 18*x^10 + 57*x^8 + 79*x^6 + 48*x^4 + 12*x^2 + 1 :: sage: A.<x> = AffineSpace(CC, 1) sage: f = DynamicalSystem_affine([(x^2+1)/(3*x)]) sage: f.dynatomic_polynomial(3) 13.0000000000000*x^6 + 117.000000000000*x^4 + 78.0000000000000*x^2 + 1.00000000000000 :: sage: A.<x> = AffineSpace(QQ, 1) sage: f = DynamicalSystem_affine([x^2-10/9]) sage: f.dynatomic_polynomial([2, 1]) 531441*x^4 - 649539*x^2 - 524880 :: sage: A.<x> = AffineSpace(CC, 1) sage: f = DynamicalSystem_affine([x^2+CC.0]) sage: f.dynatomic_polynomial(2) x^2 + x + 1.00000000000000 + 1.00000000000000*I :: sage: K.<c> = FunctionField(QQ) sage: A.<x> = AffineSpace(K, 1) sage: f = DynamicalSystem_affine([x^2 + c]) sage: f.dynatomic_polynomial(4) x^12 + 6*c*x^10 + x^9 + (15*c^2 + 3*c)*x^8 + 4*c*x^7 + (20*c^3 + 12*c^2 + 1)*x^6 + (6*c^2 + 2*c)*x^5 + (15*c^4 + 18*c^3 + 3*c^2 + 4*c)*x^4 + (4*c^3 + 4*c^2 + 1)*x^3 + (6*c^5 + 12*c^4 + 6*c^3 + 5*c^2 + c)*x^2 + (c^4 + 2*c^3 + c^2 + 2*c)*x + c^6 + 3*c^5 + 3*c^4 + 3*c^3 + 2*c^2 + 1 :: sage: A.<z> = AffineSpace(QQ, 1) sage: f = DynamicalSystem_affine([z^2+3/z+1/7]) sage: f.dynatomic_polynomial(1).parent() Multivariate Polynomial Ring in z over Rational Field :: sage: R.<c> = QQ[] sage: A.<z> = AffineSpace(R,1) sage: f = DynamicalSystem_affine([z^2 + c]) sage: f.dynatomic_polynomial([1,1]) z^2 + z + c :: sage: A.<x> = AffineSpace(CC,1) sage: F = DynamicalSystem_affine([1/2*x^2 + CC(sqrt(3))]) sage: F.dynatomic_polynomial([1,1]) (0.125000000000000*x^4 + 0.366025403784439*x^2 + 1.50000000000000)/(0.500000000000000*x^2 - x + 1.73205080756888) TESTS:: sage: R.<c> = QQ[] sage: Pc.<x,y> = ProjectiveSpace(R, 1) sage: G = DynamicalSystem_projective([(1/2*c + 1/2)*x^2 + (-2*c)*x*y + 2*c*y^2 , \ (1/4*c + 1/2)*x^2 + (-c - 1)*x*y + (c + 1)*y^2]) sage: G.dehomogenize(1).dynatomic_polynomial(2) (1/4*c + 1/4)*x^2 + (-c - 1/2)*x + c + 1 """ from sage.schemes.affine.affine_space import is_AffineSpace if not is_AffineSpace(self.domain()): raise NotImplementedError("not implemented for subschemes") if self.domain().dimension_relative() > 1: raise TypeError("does not make sense in dimension >1") G = self.homogenize(1) F = G.dynatomic_polynomial(period) T = G.domain().coordinate_ring() S = self.domain().coordinate_ring() if is_SymbolicExpressionRing(F.parent()): u = var(self.domain().coordinate_ring().variable_name()) return F.subs({F.variables()[0]: u, F.variables()[1]: 1}) elif T(F.denominator()).degree() == 0: R = F.parent() phi = R.hom([S.gen(0), 1], S) return phi(F) else: R = F.numerator().parent() phi = R.hom([S.gen(0), 1], S) return phi(F.numerator()) / phi(F.denominator())
def field_containing_real_and_imaginary_part_of_number_field(number_field): """ Given a Sage number field number_field with a complex embedding z, return (real_number_field, real_part, imag_part). The number field real_number_field is the smallest number field containing the real part and imaginary part of every element in number_field. real_part and imag_part are elements in real_number_field which comes with a real embedding such that under this embedding, we have z = real_part + imag_part * I. sage: from sage.rings.complex_field import ComplexField sage: CF = ComplexField() sage: x = var('x') sage: nf = NumberField(x**2 + 1, 'x', embedding = CF(1.0j)) sage: field_containing_real_and_imaginary_part_of_number_field(nf) (Number Field in x with defining polynomial x with x = 0, 0, 1) sage: nf = NumberField(x**2 + 7, 'x', embedding = CF(2.64575j)) sage: field_containing_real_and_imaginary_part_of_number_field(nf) (Number Field in x with defining polynomial x^2 - 7 with x = 2.645751311064591?, 0, x) sage: nf = NumberField(x**3 + x**2 + 23, 'x', embedding = CF(1.1096 + 2.4317j)) sage: field_containing_real_and_imaginary_part_of_number_field(nf) (Number Field in x with defining polynomial x^6 + 2*x^5 + 2*x^4 - 113/2*x^3 - 229/4*x^2 - 115/4*x - 575/8 with x = 3.541338405550421?, -20/14377*x^5 + 382/14377*x^4 + 526/14377*x^3 + 1533/14377*x^2 - 18262/14377*x - 10902/14377, 20/14377*x^5 - 382/14377*x^4 - 526/14377*x^3 - 1533/14377*x^2 + 32639/14377*x + 10902/14377) """ # Let p be the defining polynomial of the given number field. # Given the one complex equation p(z) = 0, translate it into two # real equations Re(p(x+y*I)) = 0, Im(p(x+y*I)) = 0. # equations are sage symbolic expressions in x and y. equations = [ _real_or_imaginary_part_for_polynomial_in_complex_variable( number_field.defining_polynomial(), start) for start in [0, 1] ] # In _solve_two_equations, we implemented a method that can solve # a system of two polynomial equations in two variables x and y # provided that y is in the number field generated by x. # If we are lucky, this is the case. # If we are unlucky, the number field containing x and y is generated # by x' = x + k * y where k is some small natural number. # The k mentioned above. We start with 0 and increase until we # succeed. k = 0 # The amount of extra precision beyond double precision we are working # with. We increase it if one of the above methods fails to find the right # factor of the above polynomials. extra_prec = 0 # Initialize the intervals for x and y with double prescision intervals CIF = ComplexIntervalField() z_val = CIF(number_field.gen_embedding()) x_val = z_val.real() y_val = z_val.imag() # Keep trying to find a k or increase precision until we succeed # Give up if k reaches 100 or we are at precision 16 times greater than # that of a double while k < 100 and extra_prec < 5: # Compute the interval for x' xprime_val = x_val + k * y_val # From the equations for x and y, get the equations for x' and y # where x' = x + k * y as abover equations_for_xprime = [ eqn.substitute(x=var('x') - k * var('y')) for eqn in equations ] try: # Try to find a solution to the two equations solution = _solve_two_equations(equations_for_xprime[0], equations_for_xprime[1], xprime_val, y_val) if solution: # We succeeded. We have a solution for the equations in # x' and y, thus, we need to do x = x' - k * y real_number_field, y_expression = solution x_expression = real_number_field.gen() - k * y_expression return real_number_field, x_expression, y_expression else: # No solution found. This means that y is not in the # number field generated by x'. Try a higher k in the # next iteration k += 1 except _IsolateFactorError: # We did not use enough precision. The given intervals for # x and y that are supposed to isolate a solution to the # system of two equations did not have enough precision to # succeed and give a unique answer. # Double the precision we will use from now on extra_prec += 1 # Recompute the intervals for x and y with the new precision. CIF = ComplexIntervalField(53 * 2**extra_prec) z_val = CIF(number_field.gen_embedding()) x_val = z_val.real() y_val = z_val.imag() # Give up return None
def dynatomic_polynomial(self, period): r""" Compute the (affine) dynatomic polynomial of a dynamical system `f: \mathbb{A}^1 \to \mathbb{A}^1`. The dynatomic polynomial is the analog of the cyclotomic polynomial and its roots are the points of formal period `n`. ALGORITHM: Homogenize to a map `f: \mathbb{P}^1 \to \mathbb{P}^1` and compute the dynatomic polynomial there. Then, dehomogenize. INPUT: - ``period`` -- a positive integer or a list/tuple `[m,n]`, where `m` is the preperiod and `n` is the period OUTPUT: If possible, a single variable polynomial in the coordinate ring of the polynomial. Otherwise a fraction field element of the coordinate ring of the polynomial. EXAMPLES:: sage: A.<x,y> = AffineSpace(QQ, 2) sage: f = DynamicalSystem_affine([x^2+y^2, y^2]) sage: f.dynatomic_polynomial(2) Traceback (most recent call last): ... TypeError: does not make sense in dimension >1 :: sage: A.<x> = AffineSpace(ZZ, 1) sage: f = DynamicalSystem_affine([(x^2+1)/x]) sage: f.dynatomic_polynomial(4) 2*x^12 + 18*x^10 + 57*x^8 + 79*x^6 + 48*x^4 + 12*x^2 + 1 :: sage: A.<x> = AffineSpace(CC, 1) sage: f = DynamicalSystem_affine([(x^2+1)/(3*x)]) sage: f.dynatomic_polynomial(3) 13.0000000000000*x^6 + 117.000000000000*x^4 + 78.0000000000000*x^2 + 1.00000000000000 :: sage: A.<x> = AffineSpace(QQ, 1) sage: f = DynamicalSystem_affine([x^2-10/9]) sage: f.dynatomic_polynomial([2, 1]) 531441*x^4 - 649539*x^2 - 524880 :: sage: A.<x> = AffineSpace(CC, 1) sage: f = DynamicalSystem_affine([x^2+CC.0]) sage: f.dynatomic_polynomial(2) x^2 + x + 1.00000000000000 + 1.00000000000000*I :: sage: K.<c> = FunctionField(QQ) sage: A.<x> = AffineSpace(K, 1) sage: f = DynamicalSystem_affine([x^2 + c]) sage: f.dynatomic_polynomial(4) x^12 + 6*c*x^10 + x^9 + (15*c^2 + 3*c)*x^8 + 4*c*x^7 + (20*c^3 + 12*c^2 + 1)*x^6 + (6*c^2 + 2*c)*x^5 + (15*c^4 + 18*c^3 + 3*c^2 + 4*c)*x^4 + (4*c^3 + 4*c^2 + 1)*x^3 + (6*c^5 + 12*c^4 + 6*c^3 + 5*c^2 + c)*x^2 + (c^4 + 2*c^3 + c^2 + 2*c)*x + c^6 + 3*c^5 + 3*c^4 + 3*c^3 + 2*c^2 + 1 :: sage: A.<z> = AffineSpace(QQ, 1) sage: f = DynamicalSystem_affine([z^2+3/z+1/7]) sage: f.dynatomic_polynomial(1).parent() Multivariate Polynomial Ring in z over Rational Field :: sage: R.<c> = QQ[] sage: A.<z> = AffineSpace(R,1) sage: f = DynamicalSystem_affine([z^2 + c]) sage: f.dynatomic_polynomial([1,1]) z^2 + z + c :: sage: A.<x> = AffineSpace(CC,1) sage: F = DynamicalSystem_affine([1/2*x^2 + CC(sqrt(3))]) sage: F.dynatomic_polynomial([1,1]) (0.125000000000000*x^4 + 0.366025403784439*x^2 + 1.50000000000000)/(0.500000000000000*x^2 - x + 1.73205080756888) """ from sage.schemes.affine.affine_space import is_AffineSpace if not is_AffineSpace(self.domain()): raise NotImplementedError("not implemented for subschemes") if self.domain().dimension_relative() > 1: raise TypeError("does not make sense in dimension >1") G = self.homogenize(1) F = G.dynatomic_polynomial(period) T = G.domain().coordinate_ring() S = self.domain().coordinate_ring() if is_SymbolicExpressionRing(F.parent()): u = var(self.domain().coordinate_ring().variable_name()) return F.subs({F.variables()[0]:u,F.variables()[1]:1}) elif T(F.denominator()).degree() == 0: R = F.parent() phi = R.hom([S.gen(0), 1], S) return phi(F) else: R = F.numerator().parent() phi = R.hom([S.gen(0), 1], S) return phi(F.numerator())/phi(F.denominator())
def revolution_plot3d(curve,trange,phirange=None,parallel_axis='z',axis=(0,0),print_vector=False,show_curve=False,**kwds): """ Return a plot of a revolved curve. There are three ways to call this function: - ``revolution_plot3d(f,trange)`` where `f` is a function located in the `x z` plane. - ``revolution_plot3d((f_x,f_z),trange)`` where `(f_x,f_z)` is a parametric curve on the `x z` plane. - ``revolution_plot3d((f_x,f_y,f_z),trange)`` where `(f_x,f_y,f_z)` can be any parametric curve. INPUT: - ``curve`` - A curve to be revolved, specified as a function, a 2-tuple or a 3-tuple. - ``trange`` - A 3-tuple `(t,t_{\min},t_{\max})` where t is the independent variable of the curve. - ``phirange`` - A 2-tuple of the form `(\phi_{\min},\phi_{\max})`, (default `(0,\pi)`) that specifies the angle in which the curve is to be revolved. - ``parallel_axis`` - A string (Either 'x', 'y', or 'z') that specifies the coordinate axis parallel to the revolution axis. - ``axis`` - A 2-tuple that specifies the position of the revolution axis. If parallel is: - 'z' - then axis is the point in which the revolution axis intersects the `x y` plane. - 'x' - then axis is the point in which the revolution axis intersects the `y z` plane. - 'y' - then axis is the point in which the revolution axis intersects the `x z` plane. - ``print_vector`` - If True, the parametrization of the surface of revolution will be printed. - ``show_curve`` - If True, the curve will be displayed. EXAMPLES: Let's revolve a simple function around different axes:: sage: u = var('u') sage: f=u^2 sage: revolution_plot3d(f,(u,0,2),show_curve=True,opacity=0.7).show(aspect_ratio=(1,1,1)) If we move slightly the axis, we get a goblet-like surface:: sage: revolution_plot3d(f,(u,0,2),axis=(1,0.2),show_curve=True,opacity=0.5).show(aspect_ratio=(1,1,1)) A common problem in calculus books, find the volume within the following revolution solid:: sage: line=u sage: parabola=u^2 sage: sur1=revolution_plot3d(line,(u,0,1),opacity=0.5,rgbcolor=(1,0.5,0),show_curve=True,parallel_axis='x') sage: sur2=revolution_plot3d(parabola,(u,0,1),opacity=0.5,rgbcolor=(0,1,0),show_curve=True,parallel_axis='x') sage: (sur1+sur2).show() Now let's revolve a parametrically defined circle. We can play with the topology of the surface by changing the axis, an axis in `(0,0)` (as the previous one) will produce a sphere-like surface:: sage: u = var('u') sage: circle=(cos(u),sin(u)) sage: revolution_plot3d(circle,(u,0,2*pi),axis=(0,0),show_curve=True,opacity=0.5).show(aspect_ratio=(1,1,1)) An axis on `(0,y)` will produce a cylinder-like surface:: sage: revolution_plot3d(circle,(u,0,2*pi),axis=(0,2),show_curve=True,opacity=0.5).show(aspect_ratio=(1,1,1)) And any other axis will produce a torus-like surface:: sage: revolution_plot3d(circle,(u,0,2*pi),axis=(2,0),show_curve=True,opacity=0.5).show(aspect_ratio=(1,1,1)) Now, we can get another goblet-like surface by revolving a curve in 3d:: sage: u = var('u') sage: curve=(u,cos(4*u),u^2) sage: revolution_plot3d(curve,(u,0,2),show_curve=True,parallel_axis='z',axis=(1,.2),opacity=0.5).show(aspect_ratio=(1,1,1)) A curvy curve with only a quarter turn:: sage: u = var('u') sage: curve=(sin(3*u),.8*cos(4*u),cos(u)) sage: revolution_plot3d(curve,(u,0,pi),(0,pi/2),show_curve=True,parallel_axis='z',opacity=0.5).show(aspect_ratio=(1,1,1),frame=False) """ from sage.symbolic.ring import var from sage.symbolic.constants import pi from sage.functions.other import sqrt from sage.functions.trig import sin from sage.functions.trig import cos from sage.functions.trig import atan2 if parallel_axis not in ['x','y','z']: raise ValueError, "parallel_axis must be either 'x', 'y', or 'z'." vart=trange[0] if str(vart)=='phi': phi=var('fi') else: phi=var('phi') if phirange is None:#this if-else provides a phirange phirange=(phi,0,2*pi) elif len(phirange)==3: phi=phirange[0] pass else: phirange=(phi,phirange[0],phirange[1]) if isinstance(curve,tuple) or isinstance(curve,list): #this if-else provides a vector v to be plotted #if curve is a tuple or a list of length 2, it is interpreted as a parametric curve #in the x-z plane. #if it is of length 3 it is interpreted as a parametric curve in 3d space if len(curve) ==2: x=curve[0] y=0 z=curve[1] elif len(curve)==3: x=curve[0] y=curve[1] z=curve[2] else: x=vart y=0 z=curve if parallel_axis=='z': x0=axis[0] y0=axis[1] # (0,0) must be handled separately for the phase value phase=0 if x0!=0 or y0!=0: phase=atan2((y-y0),(x-x0)) R=sqrt((x-x0)**2+(y-y0)**2) v=(R*cos(phi+phase)+x0,R*sin(phi+phase)+y0,z) elif parallel_axis=='x': y0=axis[0] z0=axis[1] # (0,0) must be handled separately for the phase value phase=0 if z0!=0 or y0!=0: phase=atan2((z-z0),(y-y0)) R=sqrt((y-y0)**2+(z-z0)**2) v=(x,R*cos(phi+phase)+y0,R*sin(phi+phase)+z0) elif parallel_axis=='y': x0=axis[0] z0=axis[1] # (0,0) must be handled separately for the phase value phase=0 if z0!=0 or x0!=0: phase=atan2((z-z0),(x-x0)) R=sqrt((x-x0)**2+(z-z0)**2) v=(R*cos(phi+phase)+x0,y,R*sin(phi+phase)+z0) if print_vector: print v if show_curve: curveplot=parametric_plot3d((x,y,z),trange,thickness=2,rgbcolor=(1,0,0)) return parametric_plot3d(v,trange,phirange,**kwds)+curveplot return parametric_plot3d(v,trange,phirange,**kwds)
def revolution_plot3d(curve, trange, phirange=None, parallel_axis='z', axis=(0, 0), print_vector=False, show_curve=False, **kwds): """ Return a plot of a revolved curve. There are three ways to call this function: - ``revolution_plot3d(f,trange)`` where `f` is a function located in the `x z` plane. - ``revolution_plot3d((f_x,f_z),trange)`` where `(f_x,f_z)` is a parametric curve on the `x z` plane. - ``revolution_plot3d((f_x,f_y,f_z),trange)`` where `(f_x,f_y,f_z)` can be any parametric curve. INPUT: - ``curve`` - A curve to be revolved, specified as a function, a 2-tuple or a 3-tuple. - ``trange`` - A 3-tuple `(t,t_{\min},t_{\max})` where t is the independent variable of the curve. - ``phirange`` - A 2-tuple of the form `(\phi_{\min},\phi_{\max})`, (default `(0,\pi)`) that specifies the angle in which the curve is to be revolved. - ``parallel_axis`` - A string (Either 'x', 'y', or 'z') that specifies the coordinate axis parallel to the revolution axis. - ``axis`` - A 2-tuple that specifies the position of the revolution axis. If parallel is: - 'z' - then axis is the point in which the revolution axis intersects the `x y` plane. - 'x' - then axis is the point in which the revolution axis intersects the `y z` plane. - 'y' - then axis is the point in which the revolution axis intersects the `x z` plane. - ``print_vector`` - If True, the parametrization of the surface of revolution will be printed. - ``show_curve`` - If True, the curve will be displayed. EXAMPLES: Let's revolve a simple function around different axes:: sage: u = var('u') sage: f=u^2 sage: revolution_plot3d(f,(u,0,2),show_curve=True,opacity=0.7).show(aspect_ratio=(1,1,1)) If we move slightly the axis, we get a goblet-like surface:: sage: revolution_plot3d(f,(u,0,2),axis=(1,0.2),show_curve=True,opacity=0.5).show(aspect_ratio=(1,1,1)) A common problem in calculus books, find the volume within the following revolution solid:: sage: line=u sage: parabola=u^2 sage: sur1=revolution_plot3d(line,(u,0,1),opacity=0.5,rgbcolor=(1,0.5,0),show_curve=True,parallel_axis='x') sage: sur2=revolution_plot3d(parabola,(u,0,1),opacity=0.5,rgbcolor=(0,1,0),show_curve=True,parallel_axis='x') sage: (sur1+sur2).show() Now let's revolve a parametrically defined circle. We can play with the topology of the surface by changing the axis, an axis in `(0,0)` (as the previous one) will produce a sphere-like surface:: sage: u = var('u') sage: circle=(cos(u),sin(u)) sage: revolution_plot3d(circle,(u,0,2*pi),axis=(0,0),show_curve=True,opacity=0.5).show(aspect_ratio=(1,1,1)) An axis on `(0,y)` will produce a cylinder-like surface:: sage: revolution_plot3d(circle,(u,0,2*pi),axis=(0,2),show_curve=True,opacity=0.5).show(aspect_ratio=(1,1,1)) And any other axis will produce a torus-like surface:: sage: revolution_plot3d(circle,(u,0,2*pi),axis=(2,0),show_curve=True,opacity=0.5).show(aspect_ratio=(1,1,1)) Now, we can get another goblet-like surface by revolving a curve in 3d:: sage: u = var('u') sage: curve=(u,cos(4*u),u^2) sage: revolution_plot3d(curve,(u,0,2),show_curve=True,parallel_axis='z',axis=(1,.2),opacity=0.5).show(aspect_ratio=(1,1,1)) A curvy curve with only a quarter turn:: sage: u = var('u') sage: curve=(sin(3*u),.8*cos(4*u),cos(u)) sage: revolution_plot3d(curve,(u,0,pi),(0,pi/2),show_curve=True,parallel_axis='z',opacity=0.5).show(aspect_ratio=(1,1,1),frame=False) """ from sage.symbolic.ring import var from sage.symbolic.constants import pi from sage.functions.other import sqrt from sage.functions.trig import sin from sage.functions.trig import cos from sage.functions.trig import atan2 if parallel_axis not in ['x', 'y', 'z']: raise ValueError("parallel_axis must be either 'x', 'y', or 'z'.") vart = trange[0] if str(vart) == 'phi': phi = var('fi') else: phi = var('phi') if phirange is None: #this if-else provides a phirange phirange = (phi, 0, 2 * pi) elif len(phirange) == 3: phi = phirange[0] pass else: phirange = (phi, phirange[0], phirange[1]) if isinstance(curve, tuple) or isinstance(curve, list): #this if-else provides a vector v to be plotted #if curve is a tuple or a list of length 2, it is interpreted as a parametric curve #in the x-z plane. #if it is of length 3 it is interpreted as a parametric curve in 3d space if len(curve) == 2: x = curve[0] y = 0 z = curve[1] elif len(curve) == 3: x = curve[0] y = curve[1] z = curve[2] else: x = vart y = 0 z = curve if parallel_axis == 'z': x0 = axis[0] y0 = axis[1] # (0,0) must be handled separately for the phase value phase = 0 if x0 != 0 or y0 != 0: phase = atan2((y - y0), (x - x0)) R = sqrt((x - x0)**2 + (y - y0)**2) v = (R * cos(phi + phase) + x0, R * sin(phi + phase) + y0, z) elif parallel_axis == 'x': y0 = axis[0] z0 = axis[1] # (0,0) must be handled separately for the phase value phase = 0 if z0 != 0 or y0 != 0: phase = atan2((z - z0), (y - y0)) R = sqrt((y - y0)**2 + (z - z0)**2) v = (x, R * cos(phi + phase) + y0, R * sin(phi + phase) + z0) elif parallel_axis == 'y': x0 = axis[0] z0 = axis[1] # (0,0) must be handled separately for the phase value phase = 0 if z0 != 0 or x0 != 0: phase = atan2((z - z0), (x - x0)) R = sqrt((x - x0)**2 + (z - z0)**2) v = (R * cos(phi + phase) + x0, y, R * sin(phi + phase) + z0) if print_vector: print v if show_curve: curveplot = parametric_plot3d((x, y, z), trange, thickness=2, rgbcolor=(1, 0, 0)) return parametric_plot3d(v, trange, phirange, **kwds) + curveplot return parametric_plot3d(v, trange, phirange, **kwds)
def symbolic_velocities(self, left='D', right=None): r""" Return a list of symbolic variables ready to be used by the user as the derivatives of the coordinate functions with respect to a curve parameter (i.e. the velocities along the curve). It may actually serve to denote anything else than velocities, with a name including the coordinate functions. The choice of strings provided as 'left' and 'right' arguments is not entirely free since it must comply with Python prescriptions. INPUT: - ``left`` -- (default: ``D``) string to concatenate to the left of each coordinate functions of the chart - ``right`` -- (default: ``None``) string to concatenate to the right of each coordinate functions of the chart OUTPUT: - a list of symbolic expressions with the desired names EXAMPLES: Symbolic derivatives of the Cartesian coordinates of the 3-dimensional Euclidean space:: sage: R3 = Manifold(3, 'R3', start_index=1) sage: cart.<X,Y,Z> = R3.chart() sage: D = cart.symbolic_velocities(); D [DX, DY, DZ] sage: D = cart.symbolic_velocities(left='d', right="/dt"); D Traceback (most recent call last): ... ValueError: The name "dX/dt" is not a valid Python identifier. sage: D = cart.symbolic_velocities(left='d', right="_dt"); D [dX_dt, dY_dt, dZ_dt] sage: D = cart.symbolic_velocities(left='', right="'"); D Traceback (most recent call last): ... ValueError: The name "X'" is not a valid Python identifier. sage: D = cart.symbolic_velocities(left='', right="_dot"); D [X_dot, Y_dot, Z_dot] sage: R.<t> = RealLine() sage: canon_chart = R.default_chart() sage: D = canon_chart.symbolic_velocities() ; D [Dt] """ from sage.symbolic.ring import var # The case len(self[:]) = 1 is treated apart due to the # following fact. # In the case of several coordinates, the argument of 'var' (as # implemented below after the case len(self[:]) = 1) is a list # of strings of the form ['Dx1', 'Dx2', ...] and not a unique # string of the form 'Dx1 Dx2 ...'. # Although 'var' is supposed to accept both syntaxes, the first # one causes an error when it contains only one argument, due to # line 784 of sage/symbolic/ring.pyx : # "return self.symbol(name, latex_name=formatted_latex_name, domain=domain)" # In this line, the first argument 'name' of 'symbol' is a list # and not a string if the argument of 'var' is a list of one # string (of the type ['Dt']), which causes error in 'symbol'. # This might be corrected. if len(self[:]) == 1: string_vel = left + format(self[:][0]) # will raise an error # in case left is not a string if right is not None: string_vel += right # will raise an error in case right # is not a string # If the argument of 'var' contains only one word, for # instance:: # var('Dt') # then 'var' does not return a tuple containing one symbolic # expression, but the symbolic expression itself. # This is taken into account below in order to return a list # containing one symbolic expression. return [var(string_vel)] list_strings_velocities = [left + format(coord_func) for coord_func in self[:]] # will # raise an error in case left is not a string if right is not None: list_strings_velocities = [str_vel + right for str_vel in list_strings_velocities] # will # raise an error in case right is not a string return list(var(list_strings_velocities))
def setUp(self): self.poly_ring = PolynomialRing(QQ,"x",3); self.x = self.poly_ring.gens()[0]; self.y = self.poly_ring.gens()[1]; self.z = self.poly_ring.gens()[2]; self.vars = var('x,y,z')
def sr_to_max(expr): r""" Convert a symbolic expression into a Maxima object. INPUT: - ``expr`` - symbolic expression OUTPUT: ECL object EXAMPLES:: sage: from sage.interfaces.maxima_lib import sr_to_max sage: var('x') x sage: sr_to_max(x) <ECL: $X> sage: sr_to_max(cos(x)) <ECL: ((%COS) $X)> sage: f = function('f',x) sage: sr_to_max(f.diff()) <ECL: ((%DERIVATIVE) (($F) $X) $X 1)> TESTS: We should be able to convert derivatives evaluated at a point, :trac:`12796`:: sage: from sage.interfaces.maxima_lib import sr_to_max, max_to_sr sage: f = function('f') sage: f_prime = f(x).diff(x) sage: max_to_sr(sr_to_max(f_prime(x = 1))) D[0](f)(1) """ global sage_op_dict, max_op_dict global sage_sym_dict, max_sym_dict if isinstance(expr,list) or isinstance(expr,tuple): return EclObject(([mlist],[sr_to_max(e) for e in expr])) op = expr.operator() if op: # Stolen from sage.symbolic.expression_conversion # Should be defined in a function and then put in special_sage_to_max # For that, we should change the API of the functions there # (we need to have access to op, not only to expr.operands() if isinstance(op, FDerivativeOperator): from sage.symbolic.ring import is_SymbolicVariable args = expr.operands() if (not all(is_SymbolicVariable(v) for v in args) or len(args) != len(set(args))): # An evaluated derivative of the form f'(1) is not a # symbolic variable, yet we would like to treat it # like one. So, we replace the argument `1` with a # temporary variable e.g. `t0` and then evaluate the # derivative f'(t0) symbolically at t0=1. See trac # #12796. temp_args=[var("t%s"%i) for i in range(len(args))] f =sr_to_max(op.function()(*temp_args)) params = op.parameter_set() deriv_max = [[mdiff],f] for i in set(params): deriv_max.extend([sr_to_max(temp_args[i]), EclObject(params.count(i))]) at_eval=sr_to_max([temp_args[i]==args[i] for i in range(len(args))]) return EclObject([[max_at],deriv_max,at_eval]) f = sr_to_max(op.function()(*args)) params = op.parameter_set() deriv_max = [] [deriv_max.extend([sr_to_max(args[i]), EclObject(params.count(i))]) for i in set(params)] l = [[mdiff],f] l.extend(deriv_max) return EclObject(l) elif (op in special_sage_to_max): return EclObject(special_sage_to_max[op](*[sr_to_max(o) for o in expr.operands()])) elif not (op in sage_op_dict): # Maxima does some simplifications automatically by default # so calling maxima(expr) can change the structure of expr #op_max=caar(maxima(expr).ecl()) # This should be safe if we treated all special operators above op_max=maxima(op).ecl() sage_op_dict[op]=op_max max_op_dict[op_max]=op return EclObject(([sage_op_dict[op]], [sr_to_max(o) for o in expr.operands()])) elif expr.is_symbol() or expr.is_constant(): if not expr in sage_sym_dict: sym_max=maxima(expr).ecl() sage_sym_dict[expr]=sym_max max_sym_dict[sym_max]=expr return sage_sym_dict[expr] else: try: return pyobject_to_max(expr.pyobject()) except TypeError: return maxima(expr).ecl()
def dynatomic_polynomial(self, period): r""" For a map `f:\mathbb{A}^1 \to \mathbb{A}^1` this function computes the (affine) dynatomic polynomial. The dynatomic polynomial is the analog of the cyclotomic polynomial and its roots are the points of formal period `n`. ALGORITHM: Homogenize to a map `f:\mathbb{P}^1 \to \mathbb{P}^1` and compute the dynatomic polynomial there. Then, dehomogenize. INPUT: - ``period`` -- a positive integer or a list/tuple `[m,n]`, where `m` is the preperiod and `n` is the period. OUTPUT: - If possible, a single variable polynomial in the coordinate ring of the polynomial. \ Otherwise a fraction field element of the coordinate ring of the polynomial. EXAMPLES:: sage: A.<x,y> = AffineSpace(QQ, 2) sage: H = Hom(A, A) sage: f = H([x^2+y^2, y^2]) sage: f.dynatomic_polynomial(2) Traceback (most recent call last): ... TypeError: does not make sense in dimension >1 :: sage: A.<x> = AffineSpace(ZZ, 1) sage: H = Hom(A, A) sage: f = H([(x^2+1)/x]) sage: f.dynatomic_polynomial(4) 2*x^12 + 18*x^10 + 57*x^8 + 79*x^6 + 48*x^4 + 12*x^2 + 1 :: sage: A.<x> = AffineSpace(CC, 1) sage: H = Hom(A, A) sage: f = H([(x^2+1)/(3*x)]) sage: f.dynatomic_polynomial(3) 13.0000000000000*x^6 + 117.000000000000*x^4 + 78.0000000000000*x^2 + 1.00000000000000 :: sage: A.<x> = AffineSpace(QQ, 1) sage: H = Hom(A, A) sage: f = H([x^2-10/9]) sage: f.dynatomic_polynomial([2, 1]) 531441*x^4 - 649539*x^2 - 524880 :: sage: A.<x> = AffineSpace(CC, 1) sage: H = Hom(A, A) sage: f = H([x^2+CC.0]) sage: f.dynatomic_polynomial(2) x^2 + x + 1.00000000000000 + 1.00000000000000*I :: sage: K.<c> = FunctionField(QQ) sage: A.<x> = AffineSpace(K, 1) sage: H = Hom(A, A) sage: f = H([x^2 + c]) sage: f.dynatomic_polynomial(4) x^12 + 6*c*x^10 + x^9 + (15*c^2 + 3*c)*x^8 + 4*c*x^7 + (20*c^3 + 12*c^2 + 1)*x^6 + (6*c^2 + 2*c)*x^5 + (15*c^4 + 18*c^3 + 3*c^2 + 4*c)*x^4 + (4*c^3 + 4*c^2 + 1)*x^3 + (6*c^5 + 12*c^4 + 6*c^3 + 5*c^2 + c)*x^2 + (c^4 + 2*c^3 + c^2 + 2*c)*x + c^6 + 3*c^5 + 3*c^4 + 3*c^3 + 2*c^2 + 1 :: sage: A.<z> = AffineSpace(QQ, 1) sage: H = End(A) sage: f = H([z^2+3/z+1/7]) sage: f.dynatomic_polynomial(1).parent() Multivariate Polynomial Ring in z over Rational Field :: sage: R.<c> = QQ[] sage: A.<z> = AffineSpace(R,1) sage: H = End(A) sage: f = H([z^2 + c]) sage: f.dynatomic_polynomial([1,1]) z^2 + z + c :: sage: A.<x> = AffineSpace(CC,1) sage: H = Hom(A,A) sage: F = H([1/2*x^2 + sqrt(3)]) sage: F.dynatomic_polynomial([1,1]) (2.00000000000000*x^4 + 5.85640646055102*x^2 + 24.0000000000000)/(x^2 + (-2.00000000000000)*x + 3.46410161513775) """ if self.domain() != self.codomain(): raise TypeError("must have same domain and codomain to iterate") from sage.schemes.affine.affine_space import is_AffineSpace if is_AffineSpace(self.domain())==False: raise NotImplementedError("not implemented for subschemes") if self.domain().dimension_relative()>1: raise TypeError("does not make sense in dimension >1") G = self.homogenize(1) F = G.dynatomic_polynomial(period) T = G.domain().coordinate_ring() S = self.domain().coordinate_ring() if is_SymbolicExpressionRing(F.parent()): u = var(self.domain().coordinate_ring().variable_name()) return F.subs({F.variables()[0]:u,F.variables()[1]:1}) elif T(F.denominator()).degree() == 0: R = F.parent() phi = R.hom([S.gen(0), 1], S) return(phi(F)) else: R = F.numerator().parent() phi = R.hom([S.gen(0), 1], S) return(phi(F.numerator())/phi(F.denominator()))