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, 2017) : simplification functions
Travis Scrimshaw (2016): review tweaks
Marius Gerbershagen (2022) : skip simplification of expressions with a single number or symbolic variable
- 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 notationD[...]
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 asd(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}
- class sage.manifolds.utilities.SimplifyAbsTrig(ex)#
Bases:
sage.symbolic.expression_conversions.ExpressionTreeWalker
Class for simplifying absolute values of cosines or sines (in the real domain), by walking the expression tree.
The end user interface is the function
simplify_abs_trig()
.INPUT:
ex
– a symbolic expression
EXAMPLES:
Let us consider the following symbolic expression with some assumption on the range of the variable \(x\):
sage: assume(pi/2<x, x<pi) sage: a = abs(cos(x)) + abs(sin(x))
The method
simplify_full()
is ineffective on such an expression:sage: a.simplify_full() abs(cos(x)) + abs(sin(x))
We construct a
SimplifyAbsTrig
objects
from the symbolic expressiona
:sage: from sage.manifolds.utilities import SimplifyAbsTrig sage: s = SimplifyAbsTrig(a)
We use the
__call__
method to walk the expression tree and produce a correctly simplified expression, given that \(x\in(\pi/2, \pi)\):sage: s() -cos(x) + sin(x)
Calling the simplifier
s
with an expression actually simplifies this expression:sage: s(a) # same as s() since s is built from a -cos(x) + sin(x) sage: s(abs(cos(x/2)) + abs(sin(x/2))) # pi/4 < x/2 < pi/2 cos(1/2*x) + sin(1/2*x) sage: s(abs(cos(2*x)) + abs(sin(2*x))) # pi < 2 x < 2*pi abs(cos(2*x)) - sin(2*x) sage: s(abs(sin(2+abs(cos(x))))) # nested abs(sin_or_cos(...)) sin(-cos(x) + 2)
See also
simplify_abs_trig()
for more examples withSimplifyAbsTrig
at work.- composition(ex, operator)#
This is the only method of the base class
ExpressionTreeWalker
that is reimplemented, since it manages the composition ofabs
withcos
orsin
.INPUT:
ex
– a symbolic expressionoperator
– an operator
OUTPUT:
a symbolic expression, equivalent to
ex
withabs(cos(...))
andabs(sin(...))
simplified, according to the range of their argument.
EXAMPLES:
sage: from sage.manifolds.utilities import SimplifyAbsTrig sage: assume(-pi/2 < x, x<0) sage: a = abs(sin(x)) sage: s = SimplifyAbsTrig(a) sage: a.operator() abs sage: s.composition(a, a.operator()) sin(-x)
sage: a = exp(function('f')(x)) # no abs(sin_or_cos(...)) sage: a.operator() exp sage: s.composition(a, a.operator()) e^f(x)
sage: forget() # no longer any assumption on x sage: a = abs(cos(sin(x))) # simplifiable since -1 <= sin(x) <= 1 sage: s.composition(a, a.operator()) cos(sin(x)) sage: a = abs(sin(cos(x))) # not simplifiable sage: s.composition(a, a.operator()) abs(sin(cos(x)))
- class sage.manifolds.utilities.SimplifySqrtReal(ex)#
Bases:
sage.symbolic.expression_conversions.ExpressionTreeWalker
Class for simplifying square roots in the real domain, by walking the expression tree.
The end user interface is the function
simplify_sqrt_real()
.INPUT:
ex
– a symbolic expression
EXAMPLES:
Let us consider the square root of an exact square under some assumption:
sage: assume(x<1) sage: a = sqrt(x^2-2*x+1)
The method
simplify_full()
is ineffective on such an expression:sage: a.simplify_full() sqrt(x^2 - 2*x + 1)
and the more aggressive method
canonicalize_radical()
yields a wrong result, given that \(x<1\):sage: a.canonicalize_radical() # wrong output! x - 1
We construct a
SimplifySqrtReal
objects
from the symbolic expressiona
:sage: from sage.manifolds.utilities import SimplifySqrtReal sage: s = SimplifySqrtReal(a)
We use the
__call__
method to walk the expression tree and produce a correctly simplified expression:sage: s() -x + 1
Calling the simplifier
s
with an expression actually simplifies this expression:sage: s(a) # same as s() since s is built from a -x + 1 sage: s(sqrt(x^2)) abs(x) sage: s(sqrt(1+sqrt(x^2-2*x+1))) # nested sqrt's sqrt(-x + 2)
Another example where both
simplify_full()
andcanonicalize_radical()
fail:sage: b = sqrt((x-1)/(x-2))*sqrt(1-x) sage: b.simplify_full() # does not simplify sqrt(-x + 1)*sqrt((x - 1)/(x - 2)) sage: b.canonicalize_radical() # wrong output, given that x<1 (I*x - I)/sqrt(x - 2) sage: SimplifySqrtReal(b)() # OK, given that x<1 -(x - 1)/sqrt(-x + 2)
See also
simplify_sqrt_real()
for more examples withSimplifySqrtReal
at work.- arithmetic(ex, operator)#
This is the only method of the base class
ExpressionTreeWalker
that is reimplemented, since square roots are considered as arithmetic operations withoperator
=pow
andex.operands()[1]
=1/2
or-1/2
.INPUT:
ex
– a symbolic expressionoperator
– an arithmetic operator
OUTPUT:
a symbolic expression, equivalent to
ex
with square roots simplified
EXAMPLES:
sage: from sage.manifolds.utilities import SimplifySqrtReal sage: a = sqrt(x^2+2*x+1) sage: s = SimplifySqrtReal(a) sage: a.operator() <built-in function pow> sage: s.arithmetic(a, a.operator()) abs(x + 1)
sage: a = x + 1 # no square root sage: s.arithmetic(a, a.operator()) x + 1
sage: a = x + 1 + sqrt(function('f')(x)^2) sage: s.arithmetic(a, a.operator()) x + abs(f(x)) + 1
- sage.manifolds.utilities.exterior_derivative(form)#
Exterior derivative of a differential form.
INPUT:
form
– a differential form; this must an instance of eitherDiffScalarField
for a 0-form (scalar field)DiffFormParal
for a \(p\)-form (\(p\geq 1\)) on a parallelizable manifoldDiffForm
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 objectgraph
.INPUT:
graph
–Graphics3d
; a 3D graphic objectxlabel
– string for the x-axis labelylabel
– string for the y-axis labelzlabel
– 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(...))
andabs(cos(...))
in symbolic expressions.EXAMPLES:
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, whileabs(sin(y))
andabs(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
simplify_trig()
norsimplify_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))
Simplification of
abs(cos(...))
:sage: forget() sage: M = Manifold(3, 'M', structure='topological') sage: X.<x,y,z> = M.chart(r'x y:(0,pi/2) z:(pi/4,3*pi/4)') sage: X.coord_range() x: (-oo, +oo); y: (0, 1/2*pi); z: (1/4*pi, 3/4*pi) sage: simplify_abs_trig( abs(cos(x)) + abs(cos(y)) + abs(cos(2*z)) ) abs(cos(x)) + cos(y) - cos(2*z)
Additional tests:
sage: simplify_abs_trig(abs(cos(y-pi/2))) # shall simplify cos(-1/2*pi + y) sage: simplify_abs_trig(abs(cos(y+pi/2))) # shall simplify -cos(1/2*pi + y) sage: simplify_abs_trig(abs(cos(y-pi))) # shall simplify -cos(-pi + y) sage: simplify_abs_trig(abs(cos(2*y))) # must not simplify abs(cos(2*y)) sage: simplify_abs_trig(abs(cos(y/2)) * abs(sin(z))) # shall simplify cos(1/2*y)*sin(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
ChartFunction
.The chain is formed by the following functions, called successively:
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: M = Manifold(2, 'M', structure='topological', field='complex') sage: X.<x,y> = M.chart()
Then neither
x
nory
is assumed to be real:sage: assumptions() []
Accordingly,
simplify_chain_generic
does not simplifysqrt(x^2)
toabs(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_generic_sympy(expr)#
Apply a chain of simplifications to a sympy expression.
This is the simplification chain used in calculus involving coordinate functions on manifolds over fields different from \(\RR\), as implemented in
ChartFunction
.The chain is formed by the following functions, called successively:
combsimp()
trigsimp()
expand()
simplify()
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', calc_method='sympy') sage: X.<x,y> = M.chart()
Then neither
x
nory
is assumed to be real:sage: assumptions() []
Accordingly,
simplify_chain_generic_sympy
does not simplifysqrt(x^2)
toabs(x)
:sage: from sage.manifolds.utilities import simplify_chain_generic_sympy sage: s = (sqrt(x^2))._sympy_() sage: simplify_chain_generic_sympy(s) sqrt(x**2)
This contrasts with the behavior of
simplify_chain_real_sympy()
.Other simplifications:
sage: s = ((x+y)^2 - x^2 -2*x*y - y^2)._sympy_() sage: simplify_chain_generic_sympy(s) 0 sage: s = ((x^2 - 2*x + 1) / (x^2 -1))._sympy_() sage: simplify_chain_generic_sympy(s) (x - 1)/(x + 1) sage: s = (cos(2*x) - 2*cos(x)^2 + 1)._sympy_() sage: simplify_chain_generic_sympy(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
ChartFunction
.The chain is formed by the following functions, called successively:
EXAMPLES:
We consider variables that are coordinates of a chart on a real manifold:
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 bysimplify_real()
as well:sage: s.simplify_real() abs(y) sage: s.simplify_full() abs(y)
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()
norsimplify_full()
, norcanonicalize_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_chain_real_sympy(expr)#
Apply a chain of simplifications to a sympy expression, assuming the real domain.
This is the simplification chain used in calculus involving coordinate functions on real manifolds, as implemented in
ChartFunction
.The chain is formed by the following functions, called successively:
combsimp()
trigsimp()
expand()
simplify()
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',calc_method='sympy') 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_sympy sage: s = (sqrt(y^2))._sympy_() sage: simplify_chain_real_sympy(s) Abs(y)
Furthermore, we have:
sage: s = (sqrt(x^2-2*x+1))._sympy_() sage: simplify_chain_real_sympy(s) 1 - x
Other simplifications:
sage: s = (abs(sin(pi*x)))._sympy_() sage: simplify_chain_real_sympy(s) # correct output since x in (0,1) sin(pi*x)
sage: s = (cos(y)^2 + sin(y)^2)._sympy_() sage: simplify_chain_real_sympy(s) 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
canonicalize_radical()
, which yields incorrect results whenx < 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() -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 -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() x - 1
- sage.manifolds.utilities.xder(form)#
Exterior derivative of a differential form.
INPUT:
form
– a differential form; this must an instance of eitherDiffScalarField
for a 0-form (scalar field)DiffFormParal
for a \(p\)-form (\(p\geq 1\)) on a parallelizable manifoldDiffForm
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