4

To motivate the question, sympy.concrete has some efficient tools to manipulate symbolic sums. In order to apply these tools to symbolic products, one has to take a logarithm. However, straightforward taking the logarithm doesn't automatically give the transformation:

import sympy as sp
sp.init_printing() # display math as latex
z = sp.Symbol('z')
j,k = sp.symbols('j,k')
Prod = sp.Product( (z + sp.sqrt(1-4*j*z**2))**(-1), (j,1,k) )
sp.log(Prod)

gives

\log{\left (\prod_{j=1}^{k} \frac{1}{z + \sqrt{- 4 j z^{2} + 1}} \right )}

in all possible variations:

sp.log(Prod)
sp.log(Prod).expand()
sp.log(Prod).simplify()
sp.expand_log(sp.log(Prod),force=True)

Question. How to convert it into sum of logarithms?

Related:

How to simplify logarithm of exponent in sympy?

Sergey Dovgal
  • 614
  • 6
  • 21
  • Valid question, although in cases like this I'd resort to good hand-made math and do that step myself before further processing with Sympy. Certainly this would not be a solution if this is part of a pipeline of actions. – Ignacio Vergara Kausel Sep 20 '17 at 09:28
  • Sure, this is rather a "toy example". I try to use CAS to avoid doing stupid mistakes by hand in large expressions, even it takes more time. – Sergey Dovgal Sep 20 '17 at 09:31
  • Is z a complex number, or perhaps \sqrt{1-4jz^2} is? If one uses the principal branch of logarithm in the complex plane, then it's not true that the logarithm of product is the sum of logarithms. SymPy correctly avoids making that mistake. –  Sep 20 '17 at 11:52
  • Yes, again I am caught on the same mistake :) Sympy default assumptions sometimes seem a bit confusing to me. For example, what if `z` and `j` don't commute? What if `z` is a quaternion? The fact that `z` is complex is always assumed by default, but I suspect that a human assumes that a variable is real by default and would specify a flag `is_complex = True` instead. Well, this is a detail of implementation, I have to get used to it. Thanks for the remark. – Sergey Dovgal Sep 20 '17 at 12:11
  • I edited the question to show that `expand_log` with a `force` option also fails – Sergey Dovgal Sep 20 '17 at 12:13
  • Wait, if variables `a` and `b` are complex, then `log(ab)` IS equal to `log(a) + log(b)` as a multivariate function, isn't it? ;) Okay, maybe not the principal branch, but still... – Sergey Dovgal Sep 20 '17 at 12:14

1 Answers1

6

Assuming that there is no standard function with desired behaviour yet, I wrote my own, mimicking the behaviour of

sp.expand_log(expr, force=True)

This code recursively goes over expression trying to locate patterns log(product) and replaces them by sum(log). This also supports multi-index summation.

Code.

def concrete_expand_log(expr, first_call = True):
    import sympy as sp
    if first_call:
        expr = sp.expand_log(expr, force=True)
    func = expr.func
    args = expr.args
    if args == ():
        return expr
    if func == sp.log:
        if args[0].func == sp.concrete.products.Product:
            Prod = args[0]
            term = Prod.args[0]
            indices = Prod.args[1:]
            return sp.Sum(sp.log(term), *indices)
    return func(*map(lambda x:concrete_expand_log(x, False), args))

Example.

import sympy as sp
from IPython.display import display
sp.init_printing() # display math as latex
z = sp.Symbol('z')
j,k,n = sp.symbols('j,k,n')
Prod = sp.Product( (z + sp.sqrt(1-4*j*z**2))**(-1), (j,0,k))
expr = sp.log(z**(n-k) * (1 - sp.sqrt((1 - 4*(k+2)*z**2)/(1-4*(k+1)*z**2)) ) * Prod)
display(expr)

\log{\left (z^{- k + n} \left(- \sqrt{\frac{- z^{2} \left(4 k + 8\right) + 1}{- z^{2} \left(4 k + 4\right) + 1}} + 1\right) \prod_{j=0}^{k} \frac{1}{z + \sqrt{- 4 j z^{2} + 1}} \right )}

display(concrete_expand_log(expr))

\left(- k + n\right) \log{\left (z \right )} + \log{\left (- \sqrt{\frac{- z^{2} \left(4 k + 8\right) + 1}{- z^{2} \left(4 k + 4\right) + 1}} + 1 \right )} + \sum_{j=0}^{k} \log{\left (\frac{1}{z + \sqrt{- 4 j z^{2} + 1}} \right )}

Sergey Dovgal
  • 614
  • 6
  • 21
  • ```x,y = sympy.symbols('x y', positive=True);``` and ```sympy.expand_log(sympy.log(x*y))``` gives me ```log(x) + log(y)```. The `positive=True` as suggested in the related question seems to make a difference. I don't know if that's been added to sympy after your answer. – 2bigpigs Jul 28 '21 at 21:02
  • 1
    My question is how to apply this to a symbolic Sympy product, and not just a product of two terms. I do not have a way to force the whole expression to be positive by passing some kind of flag. This works for two sympy variables, though. – Sergey Dovgal Jul 31 '21 at 16:43