Skip to content

Commit 1baf0e7

Browse files
author
Release Manager
committed
Trac #10038: Wrap Maxima's demoivre and exponentialize
Sage does not appear to have easily accesible analogues of Mathematica's `TrigToExp` and `ExpToTrig`. From [http://ask.sagemath.org/question/148 /complex-exponentialtrigonometric#comment-587 AskSage]: {{{ #!python sage: x = var('x') sage: t1 = cos(x) sage: t2 = e^(I * x) / 2 / I - e^(-I * x) / 2 / I sage: sageobj(t1._maxima_().exponentialize()) 1/2*e^(-I*x) + 1/2*e^(I*x) sage: sageobj(t2._maxima_().demoivre()) sin(x) }}} URL: https://trac.sagemath.org/10038 Reported by: mpatel Ticket author(s): Emmanuel Charpentier Reviewer(s): Travis Scrimshaw
2 parents 383a2f3 + e913bee commit 1baf0e7

File tree

2 files changed

+204
-0
lines changed

2 files changed

+204
-0
lines changed

src/sage/symbolic/expression.pyx

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5714,6 +5714,83 @@ cdef class Expression(CommutativeRingElement):
57145714
from sage.symbolic.expression_conversions import SubstituteFunction
57155715
return SubstituteFunction(self, original, new)()
57165716

5717+
def exponentialize(self):
5718+
r"""
5719+
Return this symbolic expression with all circular and hyperbolic
5720+
functions replaced by their respective exponential
5721+
expressions.
5722+
5723+
EXAMPLES::
5724+
5725+
sage: x = SR.var("x")
5726+
sage: sin(x).exponentialize()
5727+
-1/2*I*e^(I*x) + 1/2*I*e^(-I*x)
5728+
sage: sec(x).exponentialize()
5729+
2/(e^(I*x) + e^(-I*x))
5730+
sage: tan(x).exponentialize()
5731+
(-I*e^(I*x) + I*e^(-I*x))/(e^(I*x) + e^(-I*x))
5732+
sage: sinh(x).exponentialize()
5733+
-1/2*e^(-x) + 1/2*e^x
5734+
sage: sech(x).exponentialize()
5735+
2/(e^(-x) + e^x)
5736+
sage: tanh(x).exponentialize()
5737+
-(e^(-x) - e^x)/(e^(-x) + e^x)
5738+
5739+
TESTS:
5740+
5741+
Check that ``u(x).exponentialize().demoivre(force=True)``
5742+
is identity::
5743+
5744+
sage: x = SR.var("x")
5745+
sage: all([bool(u(x).exponentialize().demoivre(force=True) == u(x))
5746+
....: for u in (sin, cos, tan, csc, sec, cot,
5747+
....: sinh, cosh, tanh, csch, sech, coth)])
5748+
True
5749+
5750+
Check that differentiation and exponentialization commute::
5751+
5752+
sage: x = SR.var("x")
5753+
sage: all([bool(u(x).diff(x).exponentialize() ==
5754+
....: u(x).exponentialize().diff(x))
5755+
....: for u in (sin, cos, tan, csc, sec, cot,
5756+
....: sinh, cosh, tanh, csch, sech, coth)])
5757+
True
5758+
"""
5759+
from sage.symbolic.expression_conversions import Exponentialize
5760+
return Exponentialize(self)()
5761+
5762+
def demoivre(self, force=False):
5763+
r"""
5764+
Return this symbolic expression with complex exponentials
5765+
(optionally all exponentials) replaced by (at least partially)
5766+
trigonometric/hyperbolic expressions.
5767+
5768+
EXAMPLES::
5769+
5770+
sage: x, a, b = SR.var("x, a, b")
5771+
sage: exp(a + I*b).demoivre()
5772+
(cos(b) + I*sin(b))*e^a
5773+
sage: exp(I*x).demoivre()
5774+
cos(x) + I*sin(x)
5775+
sage: exp(x).demoivre()
5776+
e^x
5777+
sage: exp(x).demoivre(force=True)
5778+
cosh(x) + sinh(x)
5779+
5780+
TESTS:
5781+
5782+
Check that de Moivre transformation correctly commutes
5783+
with differentiation::
5784+
5785+
sage: x = SR.var("x")
5786+
sage: f = function("f")
5787+
sage: bool(f(exp(I*x)).diff(x).demoivre() ==
5788+
....: f(exp(I*x)).demoivre().diff(x))
5789+
True
5790+
"""
5791+
from sage.symbolic.expression_conversions import DeMoivre
5792+
return DeMoivre(self, force)()
5793+
57175794
def substitution_delayed(self, pattern, replacement):
57185795
"""
57195796
Replace all occurrences of pattern by the result of replacement.

src/sage/symbolic/expression_conversions.py

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2375,6 +2375,133 @@ def derivative(self, ex, operator):
23752375
else:
23762376
return operator(*[self(_) for _ in ex.operands()])
23772377

2378+
class Exponentialize(ExpressionTreeWalker):
2379+
# Implementation note: this code is executed once at first
2380+
# reference in the code using it, therefore avoiding rebuilding
2381+
# the same canned results dictionary at each call.
2382+
from sage.functions.hyperbolic import sinh, cosh, sech, csch, tanh, coth
2383+
from sage.functions.log import exp
2384+
from sage.functions.trig import sin, cos, sec, csc, tan, cot
2385+
from sage.rings.imaginary_unit import I
2386+
from sage.symbolic.constants import e
2387+
from sage.rings.integer import Integer
2388+
from sage.symbolic.ring import SR
2389+
from sage.calculus.var import function
2390+
half = Integer(1) / Integer(2)
2391+
two = Integer(2)
2392+
x = SR.var("x")
2393+
CircDict = {
2394+
sin: (-half*I*exp(I*x) + half*I*exp(-I*x)).function(x),
2395+
cos: (half*exp(I*x) + half*exp(-I*x)).function(x),
2396+
sec: (two/(exp(I*x) + exp(-I*x))).function(x),
2397+
csc: (two*I/(exp(I*x) - exp(-I*x))).function(x),
2398+
tan: (-I*(exp(I*x) - exp(-I*x))/(exp(I*x) + exp(-I*x))).function(x),
2399+
cot: (I*(exp(I*x) + exp(-I*x))/(exp(I*x) - exp(-I*x))).function(x),
2400+
sinh: (-half*exp(-x) + half*exp(x)).function(x),
2401+
cosh: (half*exp(-x) + half*exp(x)).function(x),
2402+
sech: (two/(exp(-x) + exp(x))).function(x),
2403+
csch: (-two/(exp(-x) - exp(x))).function(x),
2404+
tanh: (-(exp(-x) - exp(x))/(exp(x) + exp(-x))).function(x),
2405+
coth: (-(exp(-x) + exp(x))/(exp(-x) - exp(x))).function(x)
2406+
}
2407+
Circs = list(CircDict.keys())
2408+
2409+
def __init__(self, ex):
2410+
"""
2411+
A class that walks a symbolic expression tree and replace circular
2412+
and hyperbolic functions by their respective exponential
2413+
expressions.
2414+
2415+
EXAMPLES::
2416+
2417+
sage: from sage.symbolic.expression_conversions import Exponentialize
2418+
sage: d=Exponentialize(sin(x))
2419+
sage: d(sin(x))
2420+
-1/2*I*e^(I*x) + 1/2*I*e^(-I*x)
2421+
sage: d(cosh(x))
2422+
1/2*e^(-x) + 1/2*e^x
2423+
"""
2424+
self.ex = ex
2425+
2426+
def composition(self, ex, op):
2427+
r"""
2428+
Return the composition of ``self`` with ``ex`` by ``op``.
2429+
2430+
EXAMPLES::
2431+
2432+
sage: x = SR.var("x")
2433+
sage: from sage.symbolic.expression_conversions import Exponentialize
2434+
sage: p = x
2435+
sage: s = Exponentialize(p)
2436+
sage: q = sin(x)
2437+
sage: s.composition(q, q.operator())
2438+
-1/2*I*e^(I*x) + 1/2*I*e^(-I*x)
2439+
"""
2440+
if op in self.Circs:
2441+
return self.CircDict.get(op)(*[self(oper)
2442+
for oper in ex.operands()])
2443+
return super(Exponentialize, self).composition(ex, op)
2444+
2445+
class DeMoivre(ExpressionTreeWalker):
2446+
def __init__(self, ex, force=False):
2447+
r"""
2448+
A class that walks a symbolic expression tree and replaces
2449+
occurences of complex exponentials (optionally, all
2450+
exponentials) by their respective trigonometric expressions.
2451+
2452+
INPUT:
2453+
2454+
- ``force`` -- boolean (default: ``False``); replace `\exp(x)`
2455+
with `\cosh(x) + \sinh(x)`
2456+
2457+
EXAMPLES::
2458+
2459+
sage: a, b = SR.var("a, b")
2460+
sage: from sage.symbolic.expression_conversions import DeMoivre
2461+
sage: d=DeMoivre(e^a)
2462+
sage: d(e^(a+I*b))
2463+
(cos(b) + I*sin(b))*e^a
2464+
"""
2465+
self.ex = ex
2466+
self.force = force
2467+
2468+
def composition(self, ex, op):
2469+
"""
2470+
Return the composition of ``self`` with ``ex`` by ``op``.
2471+
2472+
EXAMPLES::
2473+
2474+
sage: x, a, b = SR.var('x, a, b')
2475+
sage: from sage.symbolic.expression_conversions import DeMoivre
2476+
sage: p = exp(x)
2477+
sage: s = DeMoivre(p)
2478+
sage: q = exp(a+I*b)
2479+
sage: s.composition(q, q.operator())
2480+
(cos(b) + I*sin(b))*e^a
2481+
"""
2482+
from sage.functions.log import exp
2483+
if op is not exp:
2484+
# return super(DeMoivre, self).composition(ex, op)
2485+
return op(*[self(oper) for oper in ex.operands()])
2486+
2487+
from sage.rings.imaginary_unit import I
2488+
from sage.symbolic.ring import SR
2489+
from sage.functions.hyperbolic import sinh, cosh
2490+
from sage.functions.trig import sin, cos
2491+
arg = self(ex.operands()[0])()
2492+
w0, w1 = (SR.wild(u) for u in range(2))
2493+
D = arg.match(w0 + I*w1)
2494+
if D is not None:
2495+
A = D.get(w1)
2496+
return exp(D.get(w0))*(cos(A) + I*sin(A))
2497+
D = arg.match(I*w0)
2498+
if D is not None:
2499+
A = D.get(w0)
2500+
return cos(A) + I*sin(A)
2501+
if self.force:
2502+
return cosh(arg) + sinh(arg)
2503+
return exp(arg)
2504+
23782505
class HoldRemover(ExpressionTreeWalker):
23792506
def __init__(self, ex, exclude=None):
23802507
"""

0 commit comments

Comments
 (0)