You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
715 lines
26 KiB
715 lines
26 KiB
5 months ago
|
from collections import defaultdict
|
||
|
from functools import reduce
|
||
|
from math import prod
|
||
|
|
||
|
from sympy.core.function import expand_log, count_ops, _coeff_isneg
|
||
|
from sympy.core import sympify, Basic, Dummy, S, Add, Mul, Pow, expand_mul, factor_terms
|
||
|
from sympy.core.sorting import ordered, default_sort_key
|
||
|
from sympy.core.numbers import Integer, Rational
|
||
|
from sympy.core.mul import _keep_coeff
|
||
|
from sympy.core.rules import Transform
|
||
|
from sympy.functions import exp_polar, exp, log, root, polarify, unpolarify
|
||
|
from sympy.matrices.expressions.matexpr import MatrixSymbol
|
||
|
from sympy.polys import lcm, gcd
|
||
|
from sympy.ntheory.factor_ import multiplicity
|
||
|
|
||
|
|
||
|
|
||
|
def powsimp(expr, deep=False, combine='all', force=False, measure=count_ops):
|
||
|
"""
|
||
|
Reduce expression by combining powers with similar bases and exponents.
|
||
|
|
||
|
Explanation
|
||
|
===========
|
||
|
|
||
|
If ``deep`` is ``True`` then powsimp() will also simplify arguments of
|
||
|
functions. By default ``deep`` is set to ``False``.
|
||
|
|
||
|
If ``force`` is ``True`` then bases will be combined without checking for
|
||
|
assumptions, e.g. sqrt(x)*sqrt(y) -> sqrt(x*y) which is not true
|
||
|
if x and y are both negative.
|
||
|
|
||
|
You can make powsimp() only combine bases or only combine exponents by
|
||
|
changing combine='base' or combine='exp'. By default, combine='all',
|
||
|
which does both. combine='base' will only combine::
|
||
|
|
||
|
a a a 2x x
|
||
|
x * y => (x*y) as well as things like 2 => 4
|
||
|
|
||
|
and combine='exp' will only combine
|
||
|
::
|
||
|
|
||
|
a b (a + b)
|
||
|
x * x => x
|
||
|
|
||
|
combine='exp' will strictly only combine exponents in the way that used
|
||
|
to be automatic. Also use deep=True if you need the old behavior.
|
||
|
|
||
|
When combine='all', 'exp' is evaluated first. Consider the first
|
||
|
example below for when there could be an ambiguity relating to this.
|
||
|
This is done so things like the second example can be completely
|
||
|
combined. If you want 'base' combined first, do something like
|
||
|
powsimp(powsimp(expr, combine='base'), combine='exp').
|
||
|
|
||
|
Examples
|
||
|
========
|
||
|
|
||
|
>>> from sympy import powsimp, exp, log, symbols
|
||
|
>>> from sympy.abc import x, y, z, n
|
||
|
>>> powsimp(x**y*x**z*y**z, combine='all')
|
||
|
x**(y + z)*y**z
|
||
|
>>> powsimp(x**y*x**z*y**z, combine='exp')
|
||
|
x**(y + z)*y**z
|
||
|
>>> powsimp(x**y*x**z*y**z, combine='base', force=True)
|
||
|
x**y*(x*y)**z
|
||
|
|
||
|
>>> powsimp(x**z*x**y*n**z*n**y, combine='all', force=True)
|
||
|
(n*x)**(y + z)
|
||
|
>>> powsimp(x**z*x**y*n**z*n**y, combine='exp')
|
||
|
n**(y + z)*x**(y + z)
|
||
|
>>> powsimp(x**z*x**y*n**z*n**y, combine='base', force=True)
|
||
|
(n*x)**y*(n*x)**z
|
||
|
|
||
|
>>> x, y = symbols('x y', positive=True)
|
||
|
>>> powsimp(log(exp(x)*exp(y)))
|
||
|
log(exp(x)*exp(y))
|
||
|
>>> powsimp(log(exp(x)*exp(y)), deep=True)
|
||
|
x + y
|
||
|
|
||
|
Radicals with Mul bases will be combined if combine='exp'
|
||
|
|
||
|
>>> from sympy import sqrt
|
||
|
>>> x, y = symbols('x y')
|
||
|
|
||
|
Two radicals are automatically joined through Mul:
|
||
|
|
||
|
>>> a=sqrt(x*sqrt(y))
|
||
|
>>> a*a**3 == a**4
|
||
|
True
|
||
|
|
||
|
But if an integer power of that radical has been
|
||
|
autoexpanded then Mul does not join the resulting factors:
|
||
|
|
||
|
>>> a**4 # auto expands to a Mul, no longer a Pow
|
||
|
x**2*y
|
||
|
>>> _*a # so Mul doesn't combine them
|
||
|
x**2*y*sqrt(x*sqrt(y))
|
||
|
>>> powsimp(_) # but powsimp will
|
||
|
(x*sqrt(y))**(5/2)
|
||
|
>>> powsimp(x*y*a) # but won't when doing so would violate assumptions
|
||
|
x*y*sqrt(x*sqrt(y))
|
||
|
|
||
|
"""
|
||
|
def recurse(arg, **kwargs):
|
||
|
_deep = kwargs.get('deep', deep)
|
||
|
_combine = kwargs.get('combine', combine)
|
||
|
_force = kwargs.get('force', force)
|
||
|
_measure = kwargs.get('measure', measure)
|
||
|
return powsimp(arg, _deep, _combine, _force, _measure)
|
||
|
|
||
|
expr = sympify(expr)
|
||
|
|
||
|
if (not isinstance(expr, Basic) or isinstance(expr, MatrixSymbol) or (
|
||
|
expr.is_Atom or expr in (exp_polar(0), exp_polar(1)))):
|
||
|
return expr
|
||
|
|
||
|
if deep or expr.is_Add or expr.is_Mul and _y not in expr.args:
|
||
|
expr = expr.func(*[recurse(w) for w in expr.args])
|
||
|
|
||
|
if expr.is_Pow:
|
||
|
return recurse(expr*_y, deep=False)/_y
|
||
|
|
||
|
if not expr.is_Mul:
|
||
|
return expr
|
||
|
|
||
|
# handle the Mul
|
||
|
if combine in ('exp', 'all'):
|
||
|
# Collect base/exp data, while maintaining order in the
|
||
|
# non-commutative parts of the product
|
||
|
c_powers = defaultdict(list)
|
||
|
nc_part = []
|
||
|
newexpr = []
|
||
|
coeff = S.One
|
||
|
for term in expr.args:
|
||
|
if term.is_Rational:
|
||
|
coeff *= term
|
||
|
continue
|
||
|
if term.is_Pow:
|
||
|
term = _denest_pow(term)
|
||
|
if term.is_commutative:
|
||
|
b, e = term.as_base_exp()
|
||
|
if deep:
|
||
|
b, e = [recurse(i) for i in [b, e]]
|
||
|
if b.is_Pow or isinstance(b, exp):
|
||
|
# don't let smthg like sqrt(x**a) split into x**a, 1/2
|
||
|
# or else it will be joined as x**(a/2) later
|
||
|
b, e = b**e, S.One
|
||
|
c_powers[b].append(e)
|
||
|
else:
|
||
|
# This is the logic that combines exponents for equal,
|
||
|
# but non-commutative bases: A**x*A**y == A**(x+y).
|
||
|
if nc_part:
|
||
|
b1, e1 = nc_part[-1].as_base_exp()
|
||
|
b2, e2 = term.as_base_exp()
|
||
|
if (b1 == b2 and
|
||
|
e1.is_commutative and e2.is_commutative):
|
||
|
nc_part[-1] = Pow(b1, Add(e1, e2))
|
||
|
continue
|
||
|
nc_part.append(term)
|
||
|
|
||
|
# add up exponents of common bases
|
||
|
for b, e in ordered(iter(c_powers.items())):
|
||
|
# allow 2**x/4 -> 2**(x - 2); don't do this when b and e are
|
||
|
# Numbers since autoevaluation will undo it, e.g.
|
||
|
# 2**(1/3)/4 -> 2**(1/3 - 2) -> 2**(1/3)/4
|
||
|
if (b and b.is_Rational and not all(ei.is_Number for ei in e) and \
|
||
|
coeff is not S.One and
|
||
|
b not in (S.One, S.NegativeOne)):
|
||
|
m = multiplicity(abs(b), abs(coeff))
|
||
|
if m:
|
||
|
e.append(m)
|
||
|
coeff /= b**m
|
||
|
c_powers[b] = Add(*e)
|
||
|
if coeff is not S.One:
|
||
|
if coeff in c_powers:
|
||
|
c_powers[coeff] += S.One
|
||
|
else:
|
||
|
c_powers[coeff] = S.One
|
||
|
|
||
|
# convert to plain dictionary
|
||
|
c_powers = dict(c_powers)
|
||
|
|
||
|
# check for base and inverted base pairs
|
||
|
be = list(c_powers.items())
|
||
|
skip = set() # skip if we already saw them
|
||
|
for b, e in be:
|
||
|
if b in skip:
|
||
|
continue
|
||
|
bpos = b.is_positive or b.is_polar
|
||
|
if bpos:
|
||
|
binv = 1/b
|
||
|
if b != binv and binv in c_powers:
|
||
|
if b.as_numer_denom()[0] is S.One:
|
||
|
c_powers.pop(b)
|
||
|
c_powers[binv] -= e
|
||
|
else:
|
||
|
skip.add(binv)
|
||
|
e = c_powers.pop(binv)
|
||
|
c_powers[b] -= e
|
||
|
|
||
|
# check for base and negated base pairs
|
||
|
be = list(c_powers.items())
|
||
|
_n = S.NegativeOne
|
||
|
for b, e in be:
|
||
|
if (b.is_Symbol or b.is_Add) and -b in c_powers and b in c_powers:
|
||
|
if (b.is_positive is not None or e.is_integer):
|
||
|
if e.is_integer or b.is_negative:
|
||
|
c_powers[-b] += c_powers.pop(b)
|
||
|
else: # (-b).is_positive so use its e
|
||
|
e = c_powers.pop(-b)
|
||
|
c_powers[b] += e
|
||
|
if _n in c_powers:
|
||
|
c_powers[_n] += e
|
||
|
else:
|
||
|
c_powers[_n] = e
|
||
|
|
||
|
# filter c_powers and convert to a list
|
||
|
c_powers = [(b, e) for b, e in c_powers.items() if e]
|
||
|
|
||
|
# ==============================================================
|
||
|
# check for Mul bases of Rational powers that can be combined with
|
||
|
# separated bases, e.g. x*sqrt(x*y)*sqrt(x*sqrt(x*y)) ->
|
||
|
# (x*sqrt(x*y))**(3/2)
|
||
|
# ---------------- helper functions
|
||
|
|
||
|
def ratq(x):
|
||
|
'''Return Rational part of x's exponent as it appears in the bkey.
|
||
|
'''
|
||
|
return bkey(x)[0][1]
|
||
|
|
||
|
def bkey(b, e=None):
|
||
|
'''Return (b**s, c.q), c.p where e -> c*s. If e is not given then
|
||
|
it will be taken by using as_base_exp() on the input b.
|
||
|
e.g.
|
||
|
x**3/2 -> (x, 2), 3
|
||
|
x**y -> (x**y, 1), 1
|
||
|
x**(2*y/3) -> (x**y, 3), 2
|
||
|
exp(x/2) -> (exp(a), 2), 1
|
||
|
|
||
|
'''
|
||
|
if e is not None: # coming from c_powers or from below
|
||
|
if e.is_Integer:
|
||
|
return (b, S.One), e
|
||
|
elif e.is_Rational:
|
||
|
return (b, Integer(e.q)), Integer(e.p)
|
||
|
else:
|
||
|
c, m = e.as_coeff_Mul(rational=True)
|
||
|
if c is not S.One:
|
||
|
if m.is_integer:
|
||
|
return (b, Integer(c.q)), m*Integer(c.p)
|
||
|
return (b**m, Integer(c.q)), Integer(c.p)
|
||
|
else:
|
||
|
return (b**e, S.One), S.One
|
||
|
else:
|
||
|
return bkey(*b.as_base_exp())
|
||
|
|
||
|
def update(b):
|
||
|
'''Decide what to do with base, b. If its exponent is now an
|
||
|
integer multiple of the Rational denominator, then remove it
|
||
|
and put the factors of its base in the common_b dictionary or
|
||
|
update the existing bases if necessary. If it has been zeroed
|
||
|
out, simply remove the base.
|
||
|
'''
|
||
|
newe, r = divmod(common_b[b], b[1])
|
||
|
if not r:
|
||
|
common_b.pop(b)
|
||
|
if newe:
|
||
|
for m in Mul.make_args(b[0]**newe):
|
||
|
b, e = bkey(m)
|
||
|
if b not in common_b:
|
||
|
common_b[b] = 0
|
||
|
common_b[b] += e
|
||
|
if b[1] != 1:
|
||
|
bases.append(b)
|
||
|
# ---------------- end of helper functions
|
||
|
|
||
|
# assemble a dictionary of the factors having a Rational power
|
||
|
common_b = {}
|
||
|
done = []
|
||
|
bases = []
|
||
|
for b, e in c_powers:
|
||
|
b, e = bkey(b, e)
|
||
|
if b in common_b:
|
||
|
common_b[b] = common_b[b] + e
|
||
|
else:
|
||
|
common_b[b] = e
|
||
|
if b[1] != 1 and b[0].is_Mul:
|
||
|
bases.append(b)
|
||
|
bases.sort(key=default_sort_key) # this makes tie-breaking canonical
|
||
|
bases.sort(key=measure, reverse=True) # handle longest first
|
||
|
for base in bases:
|
||
|
if base not in common_b: # it may have been removed already
|
||
|
continue
|
||
|
b, exponent = base
|
||
|
last = False # True when no factor of base is a radical
|
||
|
qlcm = 1 # the lcm of the radical denominators
|
||
|
while True:
|
||
|
bstart = b
|
||
|
qstart = qlcm
|
||
|
|
||
|
bb = [] # list of factors
|
||
|
ee = [] # (factor's expo. and it's current value in common_b)
|
||
|
for bi in Mul.make_args(b):
|
||
|
bib, bie = bkey(bi)
|
||
|
if bib not in common_b or common_b[bib] < bie:
|
||
|
ee = bb = [] # failed
|
||
|
break
|
||
|
ee.append([bie, common_b[bib]])
|
||
|
bb.append(bib)
|
||
|
if ee:
|
||
|
# find the number of integral extractions possible
|
||
|
# e.g. [(1, 2), (2, 2)] -> min(2/1, 2/2) -> 1
|
||
|
min1 = ee[0][1]//ee[0][0]
|
||
|
for i in range(1, len(ee)):
|
||
|
rat = ee[i][1]//ee[i][0]
|
||
|
if rat < 1:
|
||
|
break
|
||
|
min1 = min(min1, rat)
|
||
|
else:
|
||
|
# update base factor counts
|
||
|
# e.g. if ee = [(2, 5), (3, 6)] then min1 = 2
|
||
|
# and the new base counts will be 5-2*2 and 6-2*3
|
||
|
for i in range(len(bb)):
|
||
|
common_b[bb[i]] -= min1*ee[i][0]
|
||
|
update(bb[i])
|
||
|
# update the count of the base
|
||
|
# e.g. x**2*y*sqrt(x*sqrt(y)) the count of x*sqrt(y)
|
||
|
# will increase by 4 to give bkey (x*sqrt(y), 2, 5)
|
||
|
common_b[base] += min1*qstart*exponent
|
||
|
if (last # no more radicals in base
|
||
|
or len(common_b) == 1 # nothing left to join with
|
||
|
or all(k[1] == 1 for k in common_b) # no rad's in common_b
|
||
|
):
|
||
|
break
|
||
|
# see what we can exponentiate base by to remove any radicals
|
||
|
# so we know what to search for
|
||
|
# e.g. if base were x**(1/2)*y**(1/3) then we should
|
||
|
# exponentiate by 6 and look for powers of x and y in the ratio
|
||
|
# of 2 to 3
|
||
|
qlcm = lcm([ratq(bi) for bi in Mul.make_args(bstart)])
|
||
|
if qlcm == 1:
|
||
|
break # we are done
|
||
|
b = bstart**qlcm
|
||
|
qlcm *= qstart
|
||
|
if all(ratq(bi) == 1 for bi in Mul.make_args(b)):
|
||
|
last = True # we are going to be done after this next pass
|
||
|
# this base no longer can find anything to join with and
|
||
|
# since it was longer than any other we are done with it
|
||
|
b, q = base
|
||
|
done.append((b, common_b.pop(base)*Rational(1, q)))
|
||
|
|
||
|
# update c_powers and get ready to continue with powsimp
|
||
|
c_powers = done
|
||
|
# there may be terms still in common_b that were bases that were
|
||
|
# identified as needing processing, so remove those, too
|
||
|
for (b, q), e in common_b.items():
|
||
|
if (b.is_Pow or isinstance(b, exp)) and \
|
||
|
q is not S.One and not b.exp.is_Rational:
|
||
|
b, be = b.as_base_exp()
|
||
|
b = b**(be/q)
|
||
|
else:
|
||
|
b = root(b, q)
|
||
|
c_powers.append((b, e))
|
||
|
check = len(c_powers)
|
||
|
c_powers = dict(c_powers)
|
||
|
assert len(c_powers) == check # there should have been no duplicates
|
||
|
# ==============================================================
|
||
|
|
||
|
# rebuild the expression
|
||
|
newexpr = expr.func(*(newexpr + [Pow(b, e) for b, e in c_powers.items()]))
|
||
|
if combine == 'exp':
|
||
|
return expr.func(newexpr, expr.func(*nc_part))
|
||
|
else:
|
||
|
return recurse(expr.func(*nc_part), combine='base') * \
|
||
|
recurse(newexpr, combine='base')
|
||
|
|
||
|
elif combine == 'base':
|
||
|
|
||
|
# Build c_powers and nc_part. These must both be lists not
|
||
|
# dicts because exp's are not combined.
|
||
|
c_powers = []
|
||
|
nc_part = []
|
||
|
for term in expr.args:
|
||
|
if term.is_commutative:
|
||
|
c_powers.append(list(term.as_base_exp()))
|
||
|
else:
|
||
|
nc_part.append(term)
|
||
|
|
||
|
# Pull out numerical coefficients from exponent if assumptions allow
|
||
|
# e.g., 2**(2*x) => 4**x
|
||
|
for i in range(len(c_powers)):
|
||
|
b, e = c_powers[i]
|
||
|
if not (all(x.is_nonnegative for x in b.as_numer_denom()) or e.is_integer or force or b.is_polar):
|
||
|
continue
|
||
|
exp_c, exp_t = e.as_coeff_Mul(rational=True)
|
||
|
if exp_c is not S.One and exp_t is not S.One:
|
||
|
c_powers[i] = [Pow(b, exp_c), exp_t]
|
||
|
|
||
|
# Combine bases whenever they have the same exponent and
|
||
|
# assumptions allow
|
||
|
# first gather the potential bases under the common exponent
|
||
|
c_exp = defaultdict(list)
|
||
|
for b, e in c_powers:
|
||
|
if deep:
|
||
|
e = recurse(e)
|
||
|
if e.is_Add and (b.is_positive or e.is_integer):
|
||
|
e = factor_terms(e)
|
||
|
if _coeff_isneg(e):
|
||
|
e = -e
|
||
|
b = 1/b
|
||
|
c_exp[e].append(b)
|
||
|
del c_powers
|
||
|
|
||
|
# Merge back in the results of the above to form a new product
|
||
|
c_powers = defaultdict(list)
|
||
|
for e in c_exp:
|
||
|
bases = c_exp[e]
|
||
|
|
||
|
# calculate the new base for e
|
||
|
|
||
|
if len(bases) == 1:
|
||
|
new_base = bases[0]
|
||
|
elif e.is_integer or force:
|
||
|
new_base = expr.func(*bases)
|
||
|
else:
|
||
|
# see which ones can be joined
|
||
|
unk = []
|
||
|
nonneg = []
|
||
|
neg = []
|
||
|
for bi in bases:
|
||
|
if bi.is_negative:
|
||
|
neg.append(bi)
|
||
|
elif bi.is_nonnegative:
|
||
|
nonneg.append(bi)
|
||
|
elif bi.is_polar:
|
||
|
nonneg.append(
|
||
|
bi) # polar can be treated like non-negative
|
||
|
else:
|
||
|
unk.append(bi)
|
||
|
if len(unk) == 1 and not neg or len(neg) == 1 and not unk:
|
||
|
# a single neg or a single unk can join the rest
|
||
|
nonneg.extend(unk + neg)
|
||
|
unk = neg = []
|
||
|
elif neg:
|
||
|
# their negative signs cancel in groups of 2*q if we know
|
||
|
# that e = p/q else we have to treat them as unknown
|
||
|
israt = False
|
||
|
if e.is_Rational:
|
||
|
israt = True
|
||
|
else:
|
||
|
p, d = e.as_numer_denom()
|
||
|
if p.is_integer and d.is_integer:
|
||
|
israt = True
|
||
|
if israt:
|
||
|
neg = [-w for w in neg]
|
||
|
unk.extend([S.NegativeOne]*len(neg))
|
||
|
else:
|
||
|
unk.extend(neg)
|
||
|
neg = []
|
||
|
del israt
|
||
|
|
||
|
# these shouldn't be joined
|
||
|
for b in unk:
|
||
|
c_powers[b].append(e)
|
||
|
# here is a new joined base
|
||
|
new_base = expr.func(*(nonneg + neg))
|
||
|
# if there are positive parts they will just get separated
|
||
|
# again unless some change is made
|
||
|
|
||
|
def _terms(e):
|
||
|
# return the number of terms of this expression
|
||
|
# when multiplied out -- assuming no joining of terms
|
||
|
if e.is_Add:
|
||
|
return sum([_terms(ai) for ai in e.args])
|
||
|
if e.is_Mul:
|
||
|
return prod([_terms(mi) for mi in e.args])
|
||
|
return 1
|
||
|
xnew_base = expand_mul(new_base, deep=False)
|
||
|
if len(Add.make_args(xnew_base)) < _terms(new_base):
|
||
|
new_base = factor_terms(xnew_base)
|
||
|
|
||
|
c_powers[new_base].append(e)
|
||
|
|
||
|
# break out the powers from c_powers now
|
||
|
c_part = [Pow(b, ei) for b, e in c_powers.items() for ei in e]
|
||
|
|
||
|
# we're done
|
||
|
return expr.func(*(c_part + nc_part))
|
||
|
|
||
|
else:
|
||
|
raise ValueError("combine must be one of ('all', 'exp', 'base').")
|
||
|
|
||
|
|
||
|
def powdenest(eq, force=False, polar=False):
|
||
|
r"""
|
||
|
Collect exponents on powers as assumptions allow.
|
||
|
|
||
|
Explanation
|
||
|
===========
|
||
|
|
||
|
Given ``(bb**be)**e``, this can be simplified as follows:
|
||
|
* if ``bb`` is positive, or
|
||
|
* ``e`` is an integer, or
|
||
|
* ``|be| < 1`` then this simplifies to ``bb**(be*e)``
|
||
|
|
||
|
Given a product of powers raised to a power, ``(bb1**be1 *
|
||
|
bb2**be2...)**e``, simplification can be done as follows:
|
||
|
|
||
|
- if e is positive, the gcd of all bei can be joined with e;
|
||
|
- all non-negative bb can be separated from those that are negative
|
||
|
and their gcd can be joined with e; autosimplification already
|
||
|
handles this separation.
|
||
|
- integer factors from powers that have integers in the denominator
|
||
|
of the exponent can be removed from any term and the gcd of such
|
||
|
integers can be joined with e
|
||
|
|
||
|
Setting ``force`` to ``True`` will make symbols that are not explicitly
|
||
|
negative behave as though they are positive, resulting in more
|
||
|
denesting.
|
||
|
|
||
|
Setting ``polar`` to ``True`` will do simplifications on the Riemann surface of
|
||
|
the logarithm, also resulting in more denestings.
|
||
|
|
||
|
When there are sums of logs in exp() then a product of powers may be
|
||
|
obtained e.g. ``exp(3*(log(a) + 2*log(b)))`` - > ``a**3*b**6``.
|
||
|
|
||
|
Examples
|
||
|
========
|
||
|
|
||
|
>>> from sympy.abc import a, b, x, y, z
|
||
|
>>> from sympy import Symbol, exp, log, sqrt, symbols, powdenest
|
||
|
|
||
|
>>> powdenest((x**(2*a/3))**(3*x))
|
||
|
(x**(2*a/3))**(3*x)
|
||
|
>>> powdenest(exp(3*x*log(2)))
|
||
|
2**(3*x)
|
||
|
|
||
|
Assumptions may prevent expansion:
|
||
|
|
||
|
>>> powdenest(sqrt(x**2))
|
||
|
sqrt(x**2)
|
||
|
|
||
|
>>> p = symbols('p', positive=True)
|
||
|
>>> powdenest(sqrt(p**2))
|
||
|
p
|
||
|
|
||
|
No other expansion is done.
|
||
|
|
||
|
>>> i, j = symbols('i,j', integer=True)
|
||
|
>>> powdenest((x**x)**(i + j)) # -X-> (x**x)**i*(x**x)**j
|
||
|
x**(x*(i + j))
|
||
|
|
||
|
But exp() will be denested by moving all non-log terms outside of
|
||
|
the function; this may result in the collapsing of the exp to a power
|
||
|
with a different base:
|
||
|
|
||
|
>>> powdenest(exp(3*y*log(x)))
|
||
|
x**(3*y)
|
||
|
>>> powdenest(exp(y*(log(a) + log(b))))
|
||
|
(a*b)**y
|
||
|
>>> powdenest(exp(3*(log(a) + log(b))))
|
||
|
a**3*b**3
|
||
|
|
||
|
If assumptions allow, symbols can also be moved to the outermost exponent:
|
||
|
|
||
|
>>> i = Symbol('i', integer=True)
|
||
|
>>> powdenest(((x**(2*i))**(3*y))**x)
|
||
|
((x**(2*i))**(3*y))**x
|
||
|
>>> powdenest(((x**(2*i))**(3*y))**x, force=True)
|
||
|
x**(6*i*x*y)
|
||
|
|
||
|
>>> powdenest(((x**(2*a/3))**(3*y/i))**x)
|
||
|
((x**(2*a/3))**(3*y/i))**x
|
||
|
>>> powdenest((x**(2*i)*y**(4*i))**z, force=True)
|
||
|
(x*y**2)**(2*i*z)
|
||
|
|
||
|
>>> n = Symbol('n', negative=True)
|
||
|
|
||
|
>>> powdenest((x**i)**y, force=True)
|
||
|
x**(i*y)
|
||
|
>>> powdenest((n**i)**x, force=True)
|
||
|
(n**i)**x
|
||
|
|
||
|
"""
|
||
|
from sympy.simplify.simplify import posify
|
||
|
|
||
|
if force:
|
||
|
def _denest(b, e):
|
||
|
if not isinstance(b, (Pow, exp)):
|
||
|
return b.is_positive, Pow(b, e, evaluate=False)
|
||
|
return _denest(b.base, b.exp*e)
|
||
|
reps = []
|
||
|
for p in eq.atoms(Pow, exp):
|
||
|
if isinstance(p.base, (Pow, exp)):
|
||
|
ok, dp = _denest(*p.args)
|
||
|
if ok is not False:
|
||
|
reps.append((p, dp))
|
||
|
if reps:
|
||
|
eq = eq.subs(reps)
|
||
|
eq, reps = posify(eq)
|
||
|
return powdenest(eq, force=False, polar=polar).xreplace(reps)
|
||
|
|
||
|
if polar:
|
||
|
eq, rep = polarify(eq)
|
||
|
return unpolarify(powdenest(unpolarify(eq, exponents_only=True)), rep)
|
||
|
|
||
|
new = powsimp(eq)
|
||
|
return new.xreplace(Transform(
|
||
|
_denest_pow, filter=lambda m: m.is_Pow or isinstance(m, exp)))
|
||
|
|
||
|
_y = Dummy('y')
|
||
|
|
||
|
|
||
|
def _denest_pow(eq):
|
||
|
"""
|
||
|
Denest powers.
|
||
|
|
||
|
This is a helper function for powdenest that performs the actual
|
||
|
transformation.
|
||
|
"""
|
||
|
from sympy.simplify.simplify import logcombine
|
||
|
|
||
|
b, e = eq.as_base_exp()
|
||
|
if b.is_Pow or isinstance(b, exp) and e != 1:
|
||
|
new = b._eval_power(e)
|
||
|
if new is not None:
|
||
|
eq = new
|
||
|
b, e = new.as_base_exp()
|
||
|
|
||
|
# denest exp with log terms in exponent
|
||
|
if b is S.Exp1 and e.is_Mul:
|
||
|
logs = []
|
||
|
other = []
|
||
|
for ei in e.args:
|
||
|
if any(isinstance(ai, log) for ai in Add.make_args(ei)):
|
||
|
logs.append(ei)
|
||
|
else:
|
||
|
other.append(ei)
|
||
|
logs = logcombine(Mul(*logs))
|
||
|
return Pow(exp(logs), Mul(*other))
|
||
|
|
||
|
_, be = b.as_base_exp()
|
||
|
if be is S.One and not (b.is_Mul or
|
||
|
b.is_Rational and b.q != 1 or
|
||
|
b.is_positive):
|
||
|
return eq
|
||
|
|
||
|
# denest eq which is either pos**e or Pow**e or Mul**e or
|
||
|
# Mul(b1**e1, b2**e2)
|
||
|
|
||
|
# handle polar numbers specially
|
||
|
polars, nonpolars = [], []
|
||
|
for bb in Mul.make_args(b):
|
||
|
if bb.is_polar:
|
||
|
polars.append(bb.as_base_exp())
|
||
|
else:
|
||
|
nonpolars.append(bb)
|
||
|
if len(polars) == 1 and not polars[0][0].is_Mul:
|
||
|
return Pow(polars[0][0], polars[0][1]*e)*powdenest(Mul(*nonpolars)**e)
|
||
|
elif polars:
|
||
|
return Mul(*[powdenest(bb**(ee*e)) for (bb, ee) in polars]) \
|
||
|
*powdenest(Mul(*nonpolars)**e)
|
||
|
|
||
|
if b.is_Integer:
|
||
|
# use log to see if there is a power here
|
||
|
logb = expand_log(log(b))
|
||
|
if logb.is_Mul:
|
||
|
c, logb = logb.args
|
||
|
e *= c
|
||
|
base = logb.args[0]
|
||
|
return Pow(base, e)
|
||
|
|
||
|
# if b is not a Mul or any factor is an atom then there is nothing to do
|
||
|
if not b.is_Mul or any(s.is_Atom for s in Mul.make_args(b)):
|
||
|
return eq
|
||
|
|
||
|
# let log handle the case of the base of the argument being a Mul, e.g.
|
||
|
# sqrt(x**(2*i)*y**(6*i)) -> x**i*y**(3**i) if x and y are positive; we
|
||
|
# will take the log, expand it, and then factor out the common powers that
|
||
|
# now appear as coefficient. We do this manually since terms_gcd pulls out
|
||
|
# fractions, terms_gcd(x+x*y/2) -> x*(y + 2)/2 and we don't want the 1/2;
|
||
|
# gcd won't pull out numerators from a fraction: gcd(3*x, 9*x/2) -> x but
|
||
|
# we want 3*x. Neither work with noncommutatives.
|
||
|
|
||
|
def nc_gcd(aa, bb):
|
||
|
a, b = [i.as_coeff_Mul() for i in [aa, bb]]
|
||
|
c = gcd(a[0], b[0]).as_numer_denom()[0]
|
||
|
g = Mul(*(a[1].args_cnc(cset=True)[0] & b[1].args_cnc(cset=True)[0]))
|
||
|
return _keep_coeff(c, g)
|
||
|
|
||
|
glogb = expand_log(log(b))
|
||
|
if glogb.is_Add:
|
||
|
args = glogb.args
|
||
|
g = reduce(nc_gcd, args)
|
||
|
if g != 1:
|
||
|
cg, rg = g.as_coeff_Mul()
|
||
|
glogb = _keep_coeff(cg, rg*Add(*[a/g for a in args]))
|
||
|
|
||
|
# now put the log back together again
|
||
|
if isinstance(glogb, log) or not glogb.is_Mul:
|
||
|
if glogb.args[0].is_Pow or isinstance(glogb.args[0], exp):
|
||
|
glogb = _denest_pow(glogb.args[0])
|
||
|
if (abs(glogb.exp) < 1) == True:
|
||
|
return Pow(glogb.base, glogb.exp*e)
|
||
|
return eq
|
||
|
|
||
|
# the log(b) was a Mul so join any adds with logcombine
|
||
|
add = []
|
||
|
other = []
|
||
|
for a in glogb.args:
|
||
|
if a.is_Add:
|
||
|
add.append(a)
|
||
|
else:
|
||
|
other.append(a)
|
||
|
return Pow(exp(logcombine(Mul(*add))), e*Mul(*other))
|