Repository: sympy/sympy
Pull request: expand log terms using factor.
#### References to other Issues or PRs
Fixes #18674, #18865 


#### Brief description of what is fixed or changed
expand log term into its prime factors if factor flag is True
example:
`x = log(12)/log(2)`
`expand(x, factor=True) = log(3)/log(2) + 2
`

### Release Notes
<!-- BEGIN RELEASE NOTES -->
* functions
    - expand log term into its prime factors if the factor flag is True.
<!-- END RELEASE NOTES -->

Problem statement:
rewrite log(12)/log(2) as 2 + log(3)/log(2)
I am not sure how to rewrite the quotient of logs to remove powers of the denominator's factors. I thought it could be recognized in `logcombine` but that function already does this
```python
>>> logcombine(log(12)/log(2))
log(12**(1/log(2)))
```
So maybe this is something that should be done by `expand_log` with a keyword `factor` is used to factor arguments before running.
```
>>> (log(12)/log(2)).replace(
...    lambda x: x.func is log and x.args[0].is_Integer, 
...    lambda x: log(factorint(x.args[0], visual=True)))
log(2**2*3**1)/log(2**1)
>>> expand_log(_)
(log(3) + 2*log(2))/log(2)
>>> expand(_)
log(3)/log(2) + 2
```
Not sure...seems like there should be an easy way to expand logs of integers.

Hints from issue discussion:
I think logcombine is too aggressive with the power rule. I've noticed in other places that it can mess up other potential simplifications. Perhaps a good start here would be to have flags in logcombine() to selectively disable the two types of combination rules it does. But more generally, it should try to do power combination last, so that other types of combinations can be done first. 
We can maybe write a new function, if that sees two log functions are being divided. Then it treats the denominator value as the base of numerator log function
Suppose in this case, if we can make
`log(12)/log(2) = log(12, 2)`
somehow, then we can easily reach
` log(3)/log(2) + 2`
Hello, Is this issue still open? I would like to contribute to this issue. I have been well versed with the codebase of sympy and have sufficient proficiency over python language. I also have a basic idea on how to move forward with this issue. Can I work on this issue?
SymPy cannot represent two-argument logs yet. log(x, y) is automatically rewritten as log(x)/log(y). I'm having a hard time finding the issue for this. 
https://github.com/sympy/sympy/issues/6888
We can check the number in the numerator and reduce it to the product of its prime factors. 
For instance log(12) could be written as log( 2 x 2 x 3) and then pass this numerator to logcombine() to check if it is further simplified.
So here the numerator becomes log(2) + log(2) + log(3) = 2*log(2) + log(3).
> reduce it to the product of its prime factors

yes...see OP example using `replace`

Commit message: update expand_log to expand when factor is false.

Patch to review:
diff --git a/sympy/core/function.py b/sympy/core/function.py
index 95301137d113..11e7ad07dc85 100644
--- a/sympy/core/function.py
+++ b/sympy/core/function.py
@@ -2820,7 +2820,7 @@ def expand_multinomial(expr, deep=True):
     power_base=False, basic=False, multinomial=True, log=False)
 
 
-def expand_log(expr, deep=True, force=False):
+def expand_log(expr, deep=True, factor=False, force=False):
     """
     Wrapper around expand that only uses the log hint.  See the expand
     docstring for more information.
@@ -2834,9 +2834,16 @@ def expand_log(expr, deep=True, force=False):
     (x + y)*(log(x) + 2*log(y))*exp(x + y)
 
     """
+    from sympy import Mul, log
+    if factor is False:
+        expr = expr.replace(
+        lambda x: x.is_Mul and all(any(isinstance(i, log) and i.args[0].is_Rational
+        for i in Mul.make_args(j)) for j in x.as_numer_denom()),
+        lambda x: expand(x, factor=True))
+
     return sympify(expr).expand(deep=deep, log=True, mul=False,
         power_exp=False, power_base=False, multinomial=False,
-        basic=False, force=force)
+        basic=False, force=force, factor=factor)
 
 
 def expand_func(expr, deep=True):
diff --git a/sympy/functions/elementary/exponential.py b/sympy/functions/elementary/exponential.py
index bfd2e98f55f1..f552ff8f05cf 100644
--- a/sympy/functions/elementary/exponential.py
+++ b/sympy/functions/elementary/exponential.py
@@ -754,17 +754,28 @@ def taylor_term(n, x, *previous_terms):  # of log(1+x)
         return (1 - 2*(n % 2)) * x**(n + 1)/(n + 1)
 
     def _eval_expand_log(self, deep=True, **hints):
-        from sympy import unpolarify, expand_log
+        from sympy import unpolarify, expand_log, factorint
         from sympy.concrete import Sum, Product
         force = hints.get('force', False)
+        factor = hints.get('factor', False)
         if (len(self.args) == 2):
             return expand_log(self.func(*self.args), deep=deep, force=force)
         arg = self.args[0]
         if arg.is_Integer:
             # remove perfect powers
             p = perfect_power(int(arg))
+            logarg = None
+            coeff = 1
             if p is not False:
-                return p[1]*self.func(p[0])
+                arg, coeff = p
+                logarg = self.func(arg)
+            # expand as product of its prime factors if factor=True
+            if factor:
+                p = factorint(int(arg))
+                if int(arg) not in p.keys():
+                    logarg = sum(n*log(val) for val, n in p.items())
+            if logarg is not None:
+                return coeff*logarg
         elif arg.is_Rational:
             return log(arg.p) - log(arg.q)
         elif arg.is_Mul:


Code review comments:
diff_hunk: @@ -754,17 +754,28 @@ def taylor_term(n, x, *previous_terms):  # of log(1+x)
         return (1 - 2*(n % 2)) * x**(n + 1)/(n + 1)
 
     def _eval_expand_log(self, deep=True, **hints):
-        from sympy import unpolarify, expand_log
+        from sympy import unpolarify, expand_log, factorint
         from sympy.concrete import Sum, Product
         force = hints.get('force', False)
+        factor = hints.get('factor', False)
         if (len(self.args) == 2):
             return expand_log(self.func(*self.args), deep=deep, force=force)
         arg = self.args[0]
         if arg.is_Integer:
             # remove perfect powers
             p = perfect_power(int(arg))
path: sympy/functions/elementary/exponential.py
text: @user1:
```suggestion
            p = perfect_power(arg)
```

diff_hunk: @@ -754,17 +754,28 @@ def taylor_term(n, x, *previous_terms):  # of log(1+x)
         return (1 - 2*(n % 2)) * x**(n + 1)/(n + 1)
 
     def _eval_expand_log(self, deep=True, **hints):
-        from sympy import unpolarify, expand_log
+        from sympy import unpolarify, expand_log, factorint
         from sympy.concrete import Sum, Product
         force = hints.get('force', False)
+        factor = hints.get('factor', False)
         if (len(self.args) == 2):
             return expand_log(self.func(*self.args), deep=deep, force=force)
         arg = self.args[0]
         if arg.is_Integer:
             # remove perfect powers
             p = perfect_power(int(arg))
+            logarg = None
+            coeff = 1
             if p is not False:
-                return p[1]*self.func(p[0])
+                arg, coeff = p
+                logarg = self.func(arg)
+            # expand as product of its prime factors if factor=True
+            if factor:
+                p = factorint(int(arg))
+                if int(arg) not in p.keys():
path: sympy/functions/elementary/exponential.py
text: @user1:
```suggestion
                if arg not in p:
```

diff_hunk: @@ -754,17 +754,28 @@ def taylor_term(n, x, *previous_terms):  # of log(1+x)
         return (1 - 2*(n % 2)) * x**(n + 1)/(n + 1)
 
     def _eval_expand_log(self, deep=True, **hints):
-        from sympy import unpolarify, expand_log
+        from sympy import unpolarify, expand_log, factorint
         from sympy.concrete import Sum, Product
         force = hints.get('force', False)
+        factor = hints.get('factor', False)
         if (len(self.args) == 2):
             return expand_log(self.func(*self.args), deep=deep, force=force)
         arg = self.args[0]
         if arg.is_Integer:
             # remove perfect powers
             p = perfect_power(int(arg))
+            logarg = None
+            coeff = 1
             if p is not False:
-                return p[1]*self.func(p[0])
+                arg, coeff = p
+                logarg = self.func(arg)
+            # expand as product of its prime factors if factor=True
+            if factor:
+                p = factorint(int(arg))
path: sympy/functions/elementary/exponential.py
text: @user1:
```suggestion
                p = factorint(arg)
```

diff_hunk: @@ -2834,9 +2834,16 @@ def expand_log(expr, deep=True, force=False):
     (x + y)*(log(x) + 2*log(y))*exp(x + y)
 
     """
+    from sympy import Mul, log
+    if factor is False:
+        expr = expr.replace(
+        lambda x: x.is_Mul and all(any(isinstance(i, log) and i.args[0].is_Rational
+        for i in Mul.make_args(j)) for j in x.as_numer_denom()),
+        lambda x: expand(x, factor=True))
path: sympy/core/function.py
text: @user1:
Since this is just the `expand_log` hint, we probably shouldn't apply the general `expand`, just `expand_log`