Utilities for Calculus

This module defines helper functions which are used for simplifications and display of symbolic expressions.

AUTHORS:

  • Michal Bejger (2015) : class ExpressionNice
  • Eric Gourgoulhon (2015) : simplification functions
  • Travis Scrimshaw (2016): review tweaks
class sage.manifolds.utilities.ExpressionNice(ex)

Bases: sage.symbolic.expression.Expression

Subclass of Expression for a “human-friendly” display of partial derivatives and the possibility to shorten the display by skipping the arguments of symbolic functions.

INPUT:

  • ex – symbolic expression

EXAMPLES:

An expression formed with callable symbolic expressions:

sage: var('x y z')
(x, y, z)
sage: f = function('f')(x, y)
sage: g = f.diff(y).diff(x)
sage: h = function('h')(y, z)
sage: k = h.diff(z)
sage: fun = x*g + y*(k-z)^2

The standard Pynac display of partial derivatives:

sage: fun
y*(z - diff(h(y, z), z))^2 + x*diff(f(x, y), x, y)
sage: latex(fun)
y {\left(z - \frac{\partial}{\partial z}h\left(y, z\right)\right)}^{2} + x \frac{\partial^{2}}{\partial x\partial y}f\left(x, y\right)

With ExpressionNice, the Pynac notation D[...] is replaced by textbook-like notation:

sage: from sage.manifolds.utilities import ExpressionNice
sage: ExpressionNice(fun)
y*(z - d(h)/dz)^2 + x*d^2(f)/dxdy
sage: latex(ExpressionNice(fun))
y {\left(z - \frac{\partial\,h}{\partial z}\right)}^{2}
 + x \frac{\partial^2\,f}{\partial x\partial y}

An example when function variables are themselves functions:

sage: f = function('f')(x, y)
sage: g = function('g')(x, f)  # the second variable is the function f
sage: fun = (g.diff(x))*x - x^2*f.diff(x,y)
sage: fun
-x^2*diff(f(x, y), x, y) + (diff(f(x, y), x)*D[1](g)(x, f(x, y)) + D[0](g)(x, f(x, y)))*x
sage: ExpressionNice(fun)
-x^2*d^2(f)/dxdy + (d(f)/dx*d(g)/d(f(x, y)) + d(g)/dx)*x
sage: latex(ExpressionNice(fun))
-x^{2} \frac{\partial^2\,f}{\partial x\partial y}
 + {\left(\frac{\partial\,f}{\partial x}
   \frac{\partial\,g}{\partial \left( f\left(x, y\right) \right)}
 + \frac{\partial\,g}{\partial x}\right)} x

Note that D[1](g)(x, f(x,y)) is rendered as d(g)/d(f(x, y)).

An example with multiple differentiations:

sage: fun = f.diff(x,x,y,y,x)*x
sage: fun
x*diff(f(x, y), x, x, x, y, y)
sage: ExpressionNice(fun)
x*d^5(f)/dx^3dy^2
sage: latex(ExpressionNice(fun))
x \frac{\partial^5\,f}{\partial x ^ 3\partial y ^ 2}

Parentheses are added around powers of partial derivatives to avoid any confusion:

sage: fun = f.diff(y)^2
sage: fun
diff(f(x, y), y)^2
sage: ExpressionNice(fun)
(d(f)/dy)^2
sage: latex(ExpressionNice(fun))
\left(\frac{\partial\,f}{\partial y}\right)^{2}

The explicit mention of function arguments can be omitted for the sake of brevity:

sage: fun = fun*f
sage: ExpressionNice(fun)
f(x, y)*(d(f)/dy)^2
sage: Manifold.options.omit_function_arguments=True
sage: ExpressionNice(fun)
f*(d(f)/dy)^2
sage: latex(ExpressionNice(fun))
f \left(\frac{\partial\,f}{\partial y}\right)^{2}
sage: Manifold.options._reset()
sage: ExpressionNice(fun)
f(x, y)*(d(f)/dy)^2
sage: latex(ExpressionNice(fun))
f\left(x, y\right) \left(\frac{\partial\,f}{\partial y}\right)^{2}
sage.manifolds.utilities.exterior_derivative(form)

Exterior derivative of a differential form.

INPUT:

  • form – a differential form; this must an instance of either
    • DiffScalarField for a 0-form (scalar field)
    • DiffFormParal for a \(p\)-form (\(p\geq 1\)) on a parallelizable manifold
    • DiffForm for a a \(p\)-form (\(p\geq 1\)) on a non-parallelizable manifold

OUTPUT:

  • the \((p+1)\)-form that is the exterior derivative of form

EXAMPLES:

Exterior derivative of a scalar field (0-form):

sage: from sage.manifolds.utilities import exterior_derivative
sage: M = Manifold(3, 'M')
sage: X.<x,y,z> = M.chart()
sage: f = M.scalar_field({X: x+y^2+z^3}, name='f')
sage: df = exterior_derivative(f); df
1-form df on the 3-dimensional differentiable manifold M
sage: df.display()
df = dx + 2*y dy + 3*z^2 dz

An alias is xder:

sage: from sage.manifolds.utilities import xder
sage: df == xder(f)
True

Exterior derivative of a 1-form:

sage: a = M.one_form(name='a')
sage: a[:] = [x+y*z, x-y*z, x*y*z]
sage: da = xder(a); da
2-form da on the 3-dimensional differentiable manifold M
sage: da.display()
da = (-z + 1) dx/\dy + (y*z - y) dx/\dz + (x*z + y) dy/\dz
sage: dda = xder(da); dda
3-form dda on the 3-dimensional differentiable manifold M
sage: dda.display()
dda = 0
sage.manifolds.utilities.set_axes_labels(graph, xlabel, ylabel, zlabel, **kwds)

Set axes labels for a 3D graphics object graph.

This is a workaround for the lack of axes labels in 3D plots. This sets the labels as text3d() objects at locations determined from the bounding box of the graphic object graph.

INPUT:

  • graphGraphics3d; a 3D graphic object
  • xlabel – string for the x-axis label
  • ylabel – string for the y-axis label
  • zlabel – string for the z-axis label
  • **kwds – options (e.g. color) for text3d

OUTPUT:

  • the 3D graphic object with text3d labels added

EXAMPLES:

sage: g = sphere()
sage: g.all
[Graphics3d Object]
sage: from sage.manifolds.utilities import set_axes_labels
sage: ga = set_axes_labels(g, 'X', 'Y', 'Z', color='red')
sage: ga.all  # the 3D frame has now axes labels
[Graphics3d Object, Graphics3d Object,
 Graphics3d Object, Graphics3d Object]
sage.manifolds.utilities.simplify_abs_trig(expr)

Simplify abs(sin(...)) in symbolic expressions.

EXAMPLES:

sage: forget()  # for doctests only
sage: M = Manifold(3, 'M', structure='topological')
sage: X.<x,y,z> = M.chart(r'x y:(0,pi) z:(-pi/3,0)')
sage: X.coord_range()
x: (-oo, +oo); y: (0, pi); z: (-1/3*pi, 0)

Since x spans all \(\RR\), no simplification of abs(sin(x)) occurs, while abs(sin(y)) and abs(sin(3*z)) are correctly simplified, given that \(y \in (0,\pi)\) and \(z \in (-\pi/3,0)\):

sage: from sage.manifolds.utilities import simplify_abs_trig
sage: simplify_abs_trig( abs(sin(x)) + abs(sin(y)) + abs(sin(3*z)) )
abs(sin(x)) + sin(y) - sin(3*z)

Note that neither Sage’s function simplify_trig() nor simplify_full() works in this case:

sage: s = abs(sin(x)) + abs(sin(y)) + abs(sin(3*z))
sage: s.simplify_trig()
abs(4*cos(z)^2 - 1)*abs(sin(z)) + abs(sin(x)) + abs(sin(y))
sage: s.simplify_full()
abs(4*cos(z)^2 - 1)*abs(sin(z)) + abs(sin(x)) + abs(sin(y))

despite the following assumptions hold:

sage: assumptions()
[x is real, y is real, y > 0, y < pi, z is real, z > -1/3*pi, z < 0]

Additional checks are:

sage: simplify_abs_trig( abs(sin(y/2)) )  # shall simplify
sin(1/2*y)
sage: simplify_abs_trig( abs(sin(2*y)) )  # must not simplify
abs(sin(2*y))
sage: simplify_abs_trig( abs(sin(z/2)) )  # shall simplify
-sin(1/2*z)
sage: simplify_abs_trig( abs(sin(4*z)) )  # must not simplify
abs(sin(4*z))
sage.manifolds.utilities.simplify_chain_generic(expr)

Apply a chain of simplifications to a symbolic expression.

This is the simplification chain used in calculus involving coordinate functions on manifolds over fields different from \(\RR\), as implemented in CoordFunctionSymb.

The chain is formed by the following functions, called successively:

  1. simplify_factorial()
  2. simplify_rectform()
  3. simplify_trig()
  4. simplify_rational()
  5. expand_sum()

NB: for the time being, this is identical to simplify_full().

EXAMPLES:

We consider variables that are coordinates of a chart on a complex manifold:

sage: forget()  # for doctest only
sage: M = Manifold(2, 'M', structure='topological', field='complex')
sage: X.<x,y> = M.chart()

Then neither x nor y is assumed to be real:

sage: assumptions()
[]

Accordingly, simplify_chain_generic does not simplify sqrt(x^2) to abs(x):

sage: from sage.manifolds.utilities import simplify_chain_generic
sage: s = sqrt(x^2)
sage: simplify_chain_generic(s)
sqrt(x^2)

This contrasts with the behavior of simplify_chain_real().

Other simplifications:

sage: s = (x+y)^2 - x^2 -2*x*y - y^2
sage: simplify_chain_generic(s)
0
sage: s = (x^2 - 2*x + 1) / (x^2 -1)
sage: simplify_chain_generic(s)
(x - 1)/(x + 1)
sage: s = cos(2*x) - 2*cos(x)^2 + 1
sage: simplify_chain_generic(s)
0
sage.manifolds.utilities.simplify_chain_real(expr)

Apply a chain of simplifications to a symbolic expression, assuming the real domain.

This is the simplification chain used in calculus involving coordinate functions on real manifolds, as implemented in CoordFunctionSymb.

The chain is formed by the following functions, called successively:

  1. simplify_factorial()
  2. simplify_trig()
  3. simplify_rational()
  4. simplify_sqrt_real()
  5. simplify_abs_trig()
  6. canonicalize_radical()
  7. simplify_log()
  8. simplify_rational()
  9. simplify_trig()

EXAMPLES:

We consider variables that are coordinates of a chart on a real manifold:

sage: forget()  # for doctest only
sage: M = Manifold(2, 'M', structure='topological')
sage: X.<x,y> = M.chart('x:(0,1) y')

The following assumptions then hold:

sage: assumptions()
[x is real, x > 0, x < 1, y is real]

and we have:

sage: from sage.manifolds.utilities import simplify_chain_real
sage: s = sqrt(y^2)
sage: simplify_chain_real(s)
abs(y)

The above result is correct since y is real. It is obtained by simplify_real() as well, but not by simplify_full():

sage: s.simplify_real()
abs(y)
sage: s.simplify_full()
sqrt(y^2)

Furthermore, we have:

sage: s = sqrt(x^2-2*x+1)
sage: simplify_chain_real(s)
-x + 1

which is correct since \(x \in (0,1)\). On this example, neither simplify_real() nor simplify_full(), nor canonicalize_radical() give satisfactory results:

sage: s.simplify_real()  # unsimplified output
sqrt(x^2 - 2*x + 1)
sage: s.simplify_full()  # unsimplified output
sqrt(x^2 - 2*x + 1)
sage: s.canonicalize_radical()  # wrong output since x in (0,1)
x - 1

Other simplifications:

sage: s = abs(sin(pi*x))
sage: simplify_chain_real(s)  # correct output since x in (0,1)
sin(pi*x)
sage: s.simplify_real()  # unsimplified output
abs(sin(pi*x))
sage: s.simplify_full()  # unsimplified output
abs(sin(pi*x))
sage: s = cos(y)^2 + sin(y)^2
sage: simplify_chain_real(s)
1
sage: s.simplify_real()  # unsimplified output
cos(y)^2 + sin(y)^2
sage: s.simplify_full()  # OK
1
sage.manifolds.utilities.simplify_sqrt_real(expr)

Simplify sqrt in symbolic expressions in the real domain.

EXAMPLES:

Simplifications of basic expressions:

sage: from sage.manifolds.utilities import simplify_sqrt_real
sage: simplify_sqrt_real( sqrt(x^2) )
abs(x)
sage: assume(x<0)
sage: simplify_sqrt_real( sqrt(x^2) )
-x
sage: simplify_sqrt_real( sqrt(x^2-2*x+1) )
-x + 1
sage: simplify_sqrt_real( sqrt(x^2) + sqrt(x^2-2*x+1) )
-2*x + 1

This improves over Sage’s canonicalize_radical(), which yields incorrect results when x < 0:

sage: forget()  # removes the assumption x<0
sage: sqrt(x^2).canonicalize_radical()
x
sage: assume(x<0)
sage: sqrt(x^2).canonicalize_radical() # wrong output
x
sage: sqrt(x^2-2*x+1).canonicalize_radical() # wrong output
x - 1
sage: ( sqrt(x^2) + sqrt(x^2-2*x+1) ).canonicalize_radical() # wrong output
2*x - 1

Simplification of nested sqrt‘s:

sage: forget()  # removes the assumption x<0
sage: simplify_sqrt_real( sqrt(1 + sqrt(x^2)) )
sqrt(abs(x) + 1)
sage: assume(x<0)
sage: simplify_sqrt_real( sqrt(1 + sqrt(x^2)) )
sqrt(-x + 1)
sage: simplify_sqrt_real( sqrt(x^2 + sqrt(4*x^2) + 1) )
-x + 1

Again, canonicalize_radical() fails on the last one:

sage: (sqrt(x^2 + sqrt(4*x^2) + 1)).canonicalize_radical()  # wrong output
x + 1
sage.manifolds.utilities.xder(form)

Exterior derivative of a differential form.

INPUT:

  • form – a differential form; this must an instance of either
    • DiffScalarField for a 0-form (scalar field)
    • DiffFormParal for a \(p\)-form (\(p\geq 1\)) on a parallelizable manifold
    • DiffForm for a a \(p\)-form (\(p\geq 1\)) on a non-parallelizable manifold

OUTPUT:

  • the \((p+1)\)-form that is the exterior derivative of form

EXAMPLES:

Exterior derivative of a scalar field (0-form):

sage: from sage.manifolds.utilities import exterior_derivative
sage: M = Manifold(3, 'M')
sage: X.<x,y,z> = M.chart()
sage: f = M.scalar_field({X: x+y^2+z^3}, name='f')
sage: df = exterior_derivative(f); df
1-form df on the 3-dimensional differentiable manifold M
sage: df.display()
df = dx + 2*y dy + 3*z^2 dz

An alias is xder:

sage: from sage.manifolds.utilities import xder
sage: df == xder(f)
True

Exterior derivative of a 1-form:

sage: a = M.one_form(name='a')
sage: a[:] = [x+y*z, x-y*z, x*y*z]
sage: da = xder(a); da
2-form da on the 3-dimensional differentiable manifold M
sage: da.display()
da = (-z + 1) dx/\dy + (y*z - y) dx/\dz + (x*z + y) dy/\dz
sage: dda = xder(da); dda
3-form dda on the 3-dimensional differentiable manifold M
sage: dda.display()
dda = 0