Repository: sympy/sympy

Issue:
Sympy solve returns `NoneType object not scriptable`
Reposting here from [StackOverflow](https://stackoverflow.com/q/65584940/1164096) per recommendation from @oscarbenjamin 

I have tested this with both `sympy==1.7.1` and `sympy==1.5.1` and get the same result.

    from sympy import *
    import inspect

    THT,HT,DBH,dib, c0,c1, c2,c3,c4  = symbols('THT HT DBH dib c0 c1 c2 c3 c4')
    init_printing()

    f = c2 + (c3*DBH) + (c4 * THT)
    rhs = 1 - (((HT-1)/(THT - 1))**c1)* (1- exp(c0/f))

    eqn = solve( dib - (DBH * (c0 - (f * ln(rhs)))), HT)

Which returns

```
# Out [37]: 
# output

TypeErrorTraceback (most recent call last)
<ipython-input-37-619e8281e2ae> in <module>
      9 # original rhs = 1 - (((HT-1)/(THT - 1))**c1)* (1- exp(c0/f))
     10 
---> 11 eqn = solve( dib - (DBH * (c0 - (f * ln(rhs)))), HT)
     12 
     13 #eqn = solve( dib - (DBH * (b1+b2 * ln(1-(lam*p)))), HT)

~/.pyenv/versions/3.7.0/lib/python3.7/site-packages/sympy/solvers/solvers.py in solve(f, *symbols, **flags)
   1095         ok = False
   1096         if fi.has(*symset):
-> 1097             ok = True
   1098         else:
   1099             if fi.is_number:

~/.pyenv/versions/3.7.0/lib/python3.7/site-packages/sympy/solvers/solvers.py in _solve(f, *symbols, **flags)
   1458         result = list(result)
   1459         if check:
-> 1460             # all solutions have been checked but now we must
   1461             # check that the solutions do not set denominators
   1462             # in any factor to zero

TypeError: 'NoneType' object is not subscriptable
:end:
```

I can solve the following paired-down version:


```
In [47]: solve( f * ln(rhs), HT)
Out[47]: 
⎡c1___       c1___    ⎤
⎣╲╱ 0 ⋅THT - ╲╱ 0  + 1⎦
```

and adding an integer in place of `c0` it will solve:

```
solve( 2-(f * ln(rhs)), HT)
```
[![enter image description here][1]][1]

But adding a symbol causes the `NoneType` error. Any suggestions would be much appreciated.

  [1]: https://i.stack.imgur.com/9Jp2e.png

Discussion:

Patch:
diff --git a/doc/src/modules/assumptions/handlers/common.rst b/doc/src/modules/assumptions/handlers/common.rst
new file mode 100644
index 0000000000..d7093b9a16
--- /dev/null
+++ b/doc/src/modules/assumptions/handlers/common.rst
@@ -0,0 +1,6 @@
+======
+Common
+======
+
+.. automodule:: sympy.assumptions.handlers.common
+   :members:
diff --git a/doc/src/modules/assumptions/handlers/index.rst b/doc/src/modules/assumptions/handlers/index.rst
new file mode 100644
index 0000000000..eae68d66d8
--- /dev/null
+++ b/doc/src/modules/assumptions/handlers/index.rst
@@ -0,0 +1,13 @@
+========
+Handlers
+========
+
+.. automodule:: sympy.assumptions.handlers
+
+Contents
+========
+
+.. toctree::
+    :maxdepth: 3
+
+    common.rst
diff --git a/doc/src/modules/assumptions/index.rst b/doc/src/modules/assumptions/index.rst
index 90ba36bffc..9a41b79917 100644
--- a/doc/src/modules/assumptions/index.rst
+++ b/doc/src/modules/assumptions/index.rst
@@ -61,6 +61,7 @@ Contents
     ask.rst
     assume.rst
     refine.rst
+    handlers/index.rst
     predicates.rst
 
 
diff --git a/doc/src/modules/assumptions/predicates.rst b/doc/src/modules/assumptions/predicates.rst
index cc526a642d..b725d6b091 100644
--- a/doc/src/modules/assumptions/predicates.rst
+++ b/doc/src/modules/assumptions/predicates.rst
@@ -6,19 +6,6 @@
 Predicates
 ==========
 
-Common
-======
-
-Tautological
-------------
-
-.. autoclass:: sympy.assumptions.predicates.common.IsTruePredicate
-
-Commutative
------------
-
-.. autoclass:: sympy.assumptions.predicates.common.CommutativePredicate
-
 Calculus
 ========
 
diff --git a/sympy/assumptions/ask.py b/sympy/assumptions/ask.py
index fb00dac5b3..b0e7de5c42 100644
--- a/sympy/assumptions/ask.py
+++ b/sympy/assumptions/ask.py
@@ -9,7 +9,6 @@
 from sympy.logic.boolalg import (to_cnf, And, Not, Or, Implies, Equivalent)
 from sympy.logic.inference import satisfiable
 from sympy.utilities.decorator import memoize_property
-from sympy.utilities.exceptions import SymPyDeprecationWarning
 from sympy.assumptions.cnf import CNF, EncodedCNF, Literal
 
 
@@ -146,13 +145,40 @@ def composite(self):
 
     @memoize_property
     def commutative(self):
-        from .handlers.common import CommutativePredicate
-        return CommutativePredicate()
+        """
+        Commutative predicate.
+
+        Explanation
+        ===========
+
+        ``ask(Q.commutative(x))`` is true iff ``x`` commutes with any other
+        object with respect to multiplication operation.
+
+        """
+        # TODO: Add examples
+        return Predicate('commutative')
 
     @memoize_property
     def is_true(self):
-        from .handlers.common import IsTruePredicate
-        return IsTruePredicate()
+        """
+        Generic predicate.
+
+        Explanation
+        ===========
+
+        ``ask(Q.is_true(x))`` is true iff ``x`` is true. This only makes
+        sense if ``x`` is a predicate.
+
+        Examples
+        ========
+
+        >>> from sympy import ask, Q, symbols
+        >>> x = symbols('x')
+        >>> ask(Q.is_true(True))
+        True
+
+        """
+        return Predicate('is_true')
 
     @memoize_property
     def symmetric(self):
@@ -437,18 +463,22 @@ def ask_full_inference(proposition, assumptions, known_facts_cnf):
 def register_handler(key, handler):
     """
     Register a handler in the ask system. key must be a string and handler a
-    class inheriting from AskHandler.
-
-    .. deprecated:: 1.8.
-        Use multipledispatch handler instead. See :obj:`~.Predicate`.
+    class inheriting from AskHandler::
+
+        >>> from sympy.assumptions import register_handler, ask, Q
+        >>> from sympy.assumptions.handlers import AskHandler
+        >>> class MersenneHandler(AskHandler):
+        ...     # Mersenne numbers are in the form 2**n - 1, n integer
+        ...     @staticmethod
+        ...     def Integer(expr, assumptions):
+        ...         from sympy import log
+        ...         return ask(Q.integer(log(expr + 1, 2)))
+        >>> register_handler('mersenne', MersenneHandler)
+        >>> ask(Q.mersenne(7))
+        True
 
     """
-    SymPyDeprecationWarning(
-        feature="register_handler() function",
-        useinstead="multipledispatch handler of Predicate",
-        issue=20873,
-        deprecated_since_version="1.8"
-    ).warn()
+    # Will be deprecated
     if isinstance(key, Predicate):
         key = key.name.name
     Qkey = getattr(Q, key, None)
@@ -459,19 +489,8 @@ class inheriting from AskHandler.
 
 
 def remove_handler(key, handler):
-    """
-    Removes a handler from the ask system. Same syntax as register_handler
-
-    .. deprecated:: 1.8.
-        Use multipledispatch handler instead. See :obj:`~.Predicate`.
-
-    """
-    SymPyDeprecationWarning(
-        feature="remove_handler() function",
-        useinstead="multipledispatch handler of Predicate",
-        issue=20873,
-        deprecated_since_version="1.8"
-    ).warn()
+    """Removes a handler from the ask system. Same syntax as register_handler"""
+    # Will be deprecated
     if isinstance(key, Predicate):
         key = key.name.name
     getattr(Q, key).remove_handler(handler)
@@ -556,6 +575,17 @@ def get_known_facts_dict():
         for k, v in zip(keys, values)]) + ','
     return fact_string % (p, c, m)
 
+# handlers tells us what ask handler we should use
+# for a particular key
+_val_template = 'sympy.assumptions.handlers.%s'
+_handlers = [
+    ("commutative",       "AskCommutativeHandler"),
+    ("is_true",           "common.TautologicalHandler"),
+]
+
+for name, value in _handlers:
+    register_handler(name, _val_template % value)
+
 @cacheit
 def get_known_facts_keys():
     return [
diff --git a/sympy/assumptions/assume.py b/sympy/assumptions/assume.py
index 1accc5d152..cf0730c1e5 100644
--- a/sympy/assumptions/assume.py
+++ b/sympy/assumptions/assume.py
@@ -9,7 +9,6 @@
 from sympy.multipledispatch.dispatcher import (
     Dispatcher, MDNotImplementedError, str_signature
 )
-from sympy.utilities.exceptions import SymPyDeprecationWarning
 from sympy.utilities.iterables import is_sequence
 from sympy.utilities.source import get_class
 
@@ -418,21 +417,11 @@ def __call__(self, expr):
         return AppliedPredicate(self, expr)
 
     def add_handler(self, handler):
-        SymPyDeprecationWarning(
-            feature="Predicate.add_handler() method",
-            useinstead="multipledispatch handler of Predicate",
-            issue=20873,
-            deprecated_since_version="1.8"
-        ).warn()
+        # Will be deprecated
         self.handlers.append(handler)
 
     def remove_handler(self, handler):
-        SymPyDeprecationWarning(
-            feature="Predicate.remove_handler() method",
-            useinstead="multipledispatch handler of Predicate",
-            issue=20873,
-            deprecated_since_version="1.8"
-        ).warn()
+        # Will be deprecated
         self.handlers.remove(handler)
 
     def eval(self, args, assumptions=True):
diff --git a/sympy/assumptions/handlers/__init__.py b/sympy/assumptions/handlers/__init__.py
index 0fbe618eb8..8ac5898179 100644
--- a/sympy/assumptions/handlers/__init__.py
+++ b/sympy/assumptions/handlers/__init__.py
@@ -5,9 +5,9 @@
 """
 
 from .common import (AskHandler, CommonHandler,
-    test_closed_group)
+    AskCommutativeHandler, TautologicalHandler, test_closed_group)
 
 __all__ = [
-    'AskHandler', 'CommonHandler',
-    'test_closed_group'
+    'AskHandler', 'CommonHandler', 'AskCommutativeHandler',
+    'TautologicalHandler', 'test_closed_group'
 ]
diff --git a/sympy/assumptions/handlers/common.py b/sympy/assumptions/handlers/common.py
index 329377b8bb..3b8dcac903 100644
--- a/sympy/assumptions/handlers/common.py
+++ b/sympy/assumptions/handlers/common.py
@@ -3,27 +3,14 @@
 ``Q.commutative`` and ``Q.is_true``.
 """
 
-from sympy.assumptions import Q, ask, AppliedPredicate
-from sympy.core import Basic, Symbol
+from sympy.assumptions import Q, ask
 from sympy.core.logic import _fuzzy_group
-from sympy.core.numbers import NaN, Number
-from sympy.logic.boolalg import (And, BooleanTrue, BooleanFalse, conjuncts,
-    Equivalent, Implies, Not, Or)
-from sympy.utilities.exceptions import SymPyDeprecationWarning
-
-from ..predicates.common import CommutativePredicate, IsTruePredicate
+from sympy.logic.boolalg import conjuncts
 
 
 class AskHandler:
     """Base class that all Ask Handlers must inherit."""
-    def __new__(cls, *args, **kwargs):
-        SymPyDeprecationWarning(
-            feature="AskHandler() class",
-            useinstead="multipledispatch handler",
-            issue=20873,
-            deprecated_since_version="1.8"
-        ).warn()
-        return super().__new__(cls, *args, **kwargs)
+    pass
 
 
 class CommonHandler(AskHandler):
@@ -44,99 +31,98 @@ def AlwaysNone(expr, assumptions):
     NaN = AlwaysFalse
 
 
-# CommutativePredicate
+class AskCommutativeHandler(CommonHandler):
+    """
+    Handler for key 'commutative'
+    """
 
-@CommutativePredicate.register(Symbol)
-def _(expr, assumptions):
-    """Objects are expected to be commutative unless otherwise stated"""
-    assumps = conjuncts(assumptions)
-    if expr.is_commutative is not None:
-        return expr.is_commutative and not ~Q.commutative(expr) in assumps
-    if Q.commutative(expr) in assumps:
+    @staticmethod
+    def Symbol(expr, assumptions):
+        """Objects are expected to be commutative unless otherwise stated"""
+        assumps = conjuncts(assumptions)
+        if expr.is_commutative is not None:
+            return expr.is_commutative and not ~Q.commutative(expr) in assumps
+        if Q.commutative(expr) in assumps:
+            return True
+        elif ~Q.commutative(expr) in assumps:
+            return False
         return True
-    elif ~Q.commutative(expr) in assumps:
-        return False
-    return True
 
-@CommutativePredicate.register(Basic)
-def _(expr, assumptions):
-    for arg in expr.args:
-        if not ask(Q.commutative(arg), assumptions):
-            return False
-    return True
+    @staticmethod
+    def Basic(expr, assumptions):
+        for arg in expr.args:
+            if not ask(Q.commutative(arg), assumptions):
+                return False
+        return True
+
+    @staticmethod
+    def Number(expr, assumptions):
+        return True
+
+    @staticmethod
+    def NaN(expr, assumptions):
+        return True
 
-@CommutativePredicate.register(Number)
-def _(expr, assumptions):
-    return True
 
-@CommutativePredicate.register(NaN)
-def _(expr, assumptions):
-    return True
+class TautologicalHandler(AskHandler):
+    """Wrapper allowing to query the truth value of a boolean expression."""
 
+    @staticmethod
+    def bool(expr, assumptions):
+        return expr
 
-# IsTruePredicate
+    BooleanTrue = staticmethod(CommonHandler.AlwaysTrue)
+    BooleanFalse = staticmethod(CommonHandler.AlwaysFalse)
 
-@IsTruePredicate.register(bool)
-def _(expr, assumptions):
-    return expr
+    @staticmethod
+    def AppliedPredicate(expr, assumptions):
+        return ask(expr, assumptions)
 
-@IsTruePredicate.register(BooleanTrue)
-def _(expr, assumptions):
-    return True
+    @staticmethod
+    def Not(expr, assumptions):
+        value = ask(expr.args[0], assumptions=assumptions)
+        if value in (True, False):
+            return not value
+        else:
+            return None
 
-@IsTruePredicate.register(BooleanFalse)
-def _(expr, assumptions):
-    return False
+    @staticmethod
+    def Or(expr, assumptions):
+        result = False
+        for arg in expr.args:
+            p = ask(arg, assumptions=assumptions)
+            if p is True:
+                return True
+            if p is None:
+                result = None
+        return result
 
-@IsTruePredicate.register(AppliedPredicate)
-def _(expr, assumptions):
-    return ask(expr, assumptions)
+    @staticmethod
+    def And(expr, assumptions):
+        result = True
+        for arg in expr.args:
+            p = ask(arg, assumptions=assumptions)
+            if p is False:
+                return False
+            if p is None:
+                result = None
+        return result
 
-@IsTruePredicate.register(Not)
-def _(expr, assumptions):
-    value = ask(expr.args[0], assumptions=assumptions)
-    if value in (True, False):
-        return not value
-    else:
-        return None
+    @staticmethod
+    def Implies(expr, assumptions):
+        p, q = expr.args
+        return ask(~p | q, assumptions=assumptions)
 
-@IsTruePredicate.register(Or)
-def _(expr, assumptions):
-    result = False
-    for arg in expr.args:
-        p = ask(arg, assumptions=assumptions)
-        if p is True:
-            return True
-        if p is None:
-            result = None
-    return result
-
-@IsTruePredicate.register(And)
-def _(expr, assumptions):
-    result = True
-    for arg in expr.args:
-        p = ask(arg, assumptions=assumptions)
-        if p is False:
-            return False
-        if p is None:
-            result = None
-    return result
-
-@IsTruePredicate.register(Implies)
-def _(expr, assumptions):
-    p, q = expr.args
-    return ask(~p | q, assumptions=assumptions)
-
-@IsTruePredicate.register(Equivalent)
-def _(expr, assumptions):
-    p, q = expr.args
-    pt = ask(p, assumptions=assumptions)
-    if pt is None:
-        return None
-    qt = ask(q, assumptions=assumptions)
-    if qt is None:
-        return None
-    return pt == qt
+    @staticmethod
+    def Equivalent(expr, assumptions):
+        p, q = expr.args
+        pt = ask(p, assumptions=assumptions)
+        if pt is None:
+            return None
+        qt = ask(q, assumptions=assumptions)
+        if qt is None:
+            return None
+        return pt == qt
 
 
 #### Helper methods
diff --git a/sympy/assumptions/predicates/common.py b/sympy/assumptions/predicates/common.py
deleted file mode 100644
index 771993d574..0000000000
--- a/sympy/assumptions/predicates/common.py
+++ /dev/null
@@ -1,44 +0,0 @@
-from sympy.assumptions import Predicate
-from sympy.multipledispatch import Dispatcher
-
-
-class CommutativePredicate(Predicate):
-    """
-    Commutative predicate.
-
-    Explanation
-    ===========
-
-    ``ask(Q.commutative(x))`` is true iff ``x`` commutes with any other
-    object with respect to multiplication operation.
-
-    """
-    # TODO: Add examples
-    name = 'commutative'
-    handler = Dispatcher("FiniteHandler", doc="Handler for key 'commutative'.")
-
-
-class IsTruePredicate(Predicate):
-    """
-    Generic predicate.
-
-    Explanation
-    ===========
-
-    ``ask(Q.is_true(x))`` is true iff ``x`` is true. This only makes
-    sense if ``x`` is a predicate.
-
-    Examples
-    ========
-
-    >>> from sympy import ask, Q, symbols
-    >>> x = symbols('x')
-    >>> ask(Q.is_true(True))
-    True
-
-    """
-    name = 'is_true'
-    handler = Dispatcher(
-        "FiniteHandler",
-        doc="Wrapper allowing to query the truth value of a boolean expression."
-    )
diff --git a/sympy/codegen/array_utils.py b/sympy/codegen/array_utils.py
index 51fef64440..71a45ba583 100644
--- a/sympy/codegen/array_utils.py
+++ b/sympy/codegen/array_utils.py
@@ -588,9 +588,6 @@ def __new__(cls, *args):
         if len({i for i in shapes if i is not None}) > 1:
             raise ValueError("mismatching shapes in addition")
 
-        # Flatten:
-        args = cls._flatten_args(args)
-
         args = [arg for arg in args if not isinstance(arg, (ZeroArray, ZeroMatrix))]
         if len(args) == 0:
             if any(i for i in shapes if i is None):
@@ -607,16 +604,6 @@ def __new__(cls, *args):
             obj._shape = shapes[0]
         return obj
 
-    @classmethod
-    def _flatten_args(cls, args):
-        new_args = []
-        for arg in args:
-            if isinstance(arg, CodegenArrayElementwiseAdd):
-                new_args.extend(arg.args)
-            else:
-                new_args.append(arg)
-        return new_args
-
 
 class CodegenArrayPermuteDims(_CodegenArrayAbstract):
     r"""
diff --git a/sympy/core/mul.py b/sympy/core/mul.py
index 193f43c00f..5fb540a627 100644
--- a/sympy/core/mul.py
+++ b/sympy/core/mul.py
@@ -1197,6 +1197,7 @@ def _combine_inverse(lhs, rhs):
         like oo/oo return 1 (instead of a nan) and ``I`` behaves like
         a symbol instead of sqrt(-1).
         """
+        from sympy.simplify.simplify import signsimp
         from .symbol import Dummy
         if lhs == rhs:
             return S.One
@@ -1227,7 +1228,7 @@ def check(l, r):
             if len(b) != blen:
                 lhs = Mul(*[k**v for k, v in a.items()]).xreplace(i_)
                 rhs = Mul(*[k**v for k, v in b.items()]).xreplace(i_)
-        return lhs/rhs
+        return signsimp(lhs/rhs)
 
     def as_powers_dict(self):
         d = defaultdict(int)


Test patch:
diff --git a/sympy/assumptions/tests/test_query.py b/sympy/assumptions/tests/test_query.py
index 79537f5375..ef18d8011f 100644
--- a/sympy/assumptions/tests/test_query.py
+++ b/sympy/assumptions/tests/test_query.py
@@ -1,7 +1,7 @@
 from sympy.abc import t, w, x, y, z, n, k, m, p, i
 from sympy.assumptions import (ask, AssumptionsContext, Q, register_handler,
         remove_handler)
-from sympy.assumptions.assume import assuming, global_assumptions, Predicate
+from sympy.assumptions.assume import global_assumptions, Predicate
 from sympy.assumptions.ask import compute_known_facts, single_fact_lookup
 from sympy.assumptions.handlers import AskHandler
 from sympy.core.add import Add
@@ -17,7 +17,8 @@
     acos, acot, asin, atan, cos, cot, sin, tan)
 from sympy.logic.boolalg import Equivalent, Implies, Xor, And, to_cnf
 from sympy.matrices import Matrix, SparseMatrix
-from sympy.testing.pytest import XFAIL, slow, raises, warns_deprecated_sympy
+from sympy.testing.pytest import XFAIL, slow, raises
+from sympy.assumptions.assume import assuming
 import math
 
 
@@ -2026,19 +2027,18 @@ def test_key_extensibility():
     raises(AttributeError, lambda: ask(Q.my_key(x)))
 
     # Old handler system
-    with warns_deprecated_sympy():
-        class MyAskHandler(AskHandler):
-            @staticmethod
-            def Symbol(expr, assumptions):
-                return True
-        try:
-            register_handler('my_key', MyAskHandler)
-            assert ask(Q.my_key(x)) is True
-            assert ask(Q.my_key(x + 1)) is None
-        finally:
-            remove_handler('my_key', MyAskHandler)
-            del Q.my_key
-        raises(AttributeError, lambda: ask(Q.my_key(x)))
+    class MyAskHandler(AskHandler):
+        @staticmethod
+        def Symbol(expr, assumptions):
+            return True
+    try:
+        register_handler('my_key', MyAskHandler)
+        assert ask(Q.my_key(x)) is True
+        assert ask(Q.my_key(x + 1)) is None
+    finally:
+        remove_handler('my_key', MyAskHandler)
+        del Q.my_key
+    raises(AttributeError, lambda: ask(Q.my_key(x)))
 
     # New handler system
     class MyPredicate(Predicate):
@@ -2252,24 +2252,23 @@ def test_custom_AskHandler():
     from sympy.logic.boolalg import conjuncts
 
     # Old handler system
-    with warns_deprecated_sympy():
-        class MersenneHandler(AskHandler):
-            @staticmethod
-            def Integer(expr, assumptions):
-                from sympy import log
-                if ask(Q.integer(log(expr + 1, 2))):
-                    return True
-            @staticmethod
-            def Symbol(expr, assumptions):
-                if expr in conjuncts(assumptions):
-                    return True
-        try:
-            register_handler('mersenne', MersenneHandler)
-            n = Symbol('n', integer=True)
-            assert ask(Q.mersenne(7))
-            assert ask(Q.mersenne(n), Q.mersenne(n))
-        finally:
-            del Q.mersenne
+    class MersenneHandler(AskHandler):
+        @staticmethod
+        def Integer(expr, assumptions):
+            from sympy import log
+            if ask(Q.integer(log(expr + 1, 2))):
+                return True
+        @staticmethod
+        def Symbol(expr, assumptions):
+            if expr in conjuncts(assumptions):
+                return True
+    try:
+        register_handler('mersenne', MersenneHandler)
+        n = Symbol('n', integer=True)
+        assert ask(Q.mersenne(7))
+        assert ask(Q.mersenne(n), Q.mersenne(n))
+    finally:
+        del Q.mersenne
 
     # New handler system
     class MersennePredicate(Predicate):
diff --git a/sympy/codegen/tests/test_array_utils.py b/sympy/codegen/tests/test_array_utils.py
index 5a3393c738..58db01afee 100644
--- a/sympy/codegen/tests/test_array_utils.py
+++ b/sympy/codegen/tests/test_array_utils.py
@@ -180,12 +180,6 @@ def test_codegen_array_flatten():
     cgnested = CodegenArrayDiagonal(cg4, (1, 2))
     assert cgnested == CodegenArrayDiagonal(CodegenArrayTensorProduct(M, N, P, Q), (1, 5), (3, 7), (2, 4))
 
-    cg = CodegenArrayElementwiseAdd(M, N)
-    cg2 = CodegenArrayElementwiseAdd(cg, P)
-    assert isinstance(cg2, CodegenArrayElementwiseAdd)
-    assert cg2.args == (M, N, P)
-    assert cg2.shape == (k, k)
-
 
 def test_codegen_array_parse():
     expr = M[i, j]
diff --git a/sympy/core/tests/test_match.py b/sympy/core/tests/test_match.py
index 255ce674d5..6d08f763b9 100644
--- a/sympy/core/tests/test_match.py
+++ b/sympy/core/tests/test_match.py
@@ -473,6 +473,7 @@ def test__combine_inverse():
     assert Mul._combine_inverse(oo*I*y, oo*I) == y
     assert Mul._combine_inverse(oo*y, -oo) == -y
     assert Mul._combine_inverse(-oo*y, oo) == -y
+    assert Mul._combine_inverse((1-exp(x/y)),(exp(x/y)-1)) == -1
     assert Add._combine_inverse(oo, oo) is S.Zero
     assert Add._combine_inverse(oo*I, oo*I) is S.Zero
     assert Add._combine_inverse(x*oo, x*oo) is S.Zero
@@ -714,6 +715,8 @@ def test_match_terms():
     assert (5*y - x).match(5*X - Y) == {X: y, Y: x}
     # 15907
     assert (x + (y - 1)*z).match(x + X*z) == {X: y - 1}
+    # 20747
+    assert (x - log(x/y)*(1-exp(x/y))).match(x - log(X/y)*(1-exp(x/y))) == {X: x}
 
 
 def test_match_bound():
diff --git a/sympy/solvers/tests/test_solvers.py b/sympy/solvers/tests/test_solvers.py
index b4d262d8db..be7a567d29 100644
--- a/sympy/solvers/tests/test_solvers.py
+++ b/sympy/solvers/tests/test_solvers.py
@@ -2290,3 +2290,13 @@ def test_issue_19509():
         -d + a + sqrt(-b + c),
         d + a - sqrt(-b - c),
         d + a + sqrt(-b - c)]
+
+def test_issue_20747():
+    THT, HT, DBH, dib, c0, c1, c2, c3, c4  = symbols('THT HT DBH dib c0 c1 c2 c3 c4')
+    f = DBH*c3 + THT*c4 + c2
+    rhs = 1 - ((HT - 1)/(THT - 1))**c1*(1 - exp(c0/f))
+    eq = dib - DBH*(c0 - f*log(rhs))
+    term = ((1 - exp((DBH*c0 - dib)/(DBH*(DBH*c3 + THT*c4 + c2))))
+            / (1 - exp(c0/(DBH*c3 + THT*c4 + c2))))
+    sol = [THT*term**(1/c1) - term**(1/c1) + 1]
+    assert solve(eq, HT) == sol