Repository: sympy/sympy

Issue:
solve_univariate_inequality(x**2 >= 0, x) raises AttributeError
```
>>> x = Symbol('x')
>>> solve_univariate_inequality(x**2 >= 0, x)

---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-5-d005c642db25> in <module>()
----> 1 solve_univariate_inequality(x**2 >= 0, x)

/home/gxyd/Public/sympy/sympy/solvers/inequalities.py in solve_univariate_inequality(expr, gen, relational)
    407     gen = d
    408 
--> 409     e = expr.lhs - expr.rhs
    410     parts = n, d = e.as_numer_denom()
    411     if all(i.is_polynomial(gen) for i in parts):

AttributeError: 'BooleanTrue' object has no attribute 'lhs'
```

Using `git-bisect` reveals [this](https://github.com/shivamvats/sympy/commit/b4596e3e2fe339062d27a0a1104fea298e25dfdf) commit  by @shivamvats  
```
gxyd@swap:~/Public/sympy$ git bisect good 
Bisecting: 263 revisions left to test after this (roughly 8 steps)
[3517ef5e50bf0510c74c0434aff14766e160343c] Merge pull request #9803 from leosartaj/diffDelta
gxyd@swap:~/Public/sympy$ git bisect bad
Bisecting: 137 revisions left to test after this (roughly 7 steps)
[f4ad439372f377e4041b2a94e582815292616c3c] Fix test and docstring
gxyd@swap:~/Public/sympy$ git bisect bad
Bisecting: 64 revisions left to test after this (roughly 6 steps)
[0a850aaff35c353370400185a39cc958ae7b1e38] Merge pull request #9716 from aktech/sets-replace-solve
gxyd@swap:~/Public/sympy$ git bisect good 
Bisecting: 32 revisions left to test after this (roughly 5 steps)
[49e2de008e4b8d0a666fd659510f49c24296971a] Merge pull request #9735 from gxyd/sphx_docs
gxyd@swap:~/Public/sympy$ git bisect good 
Bisecting: 20 revisions left to test after this (roughly 4 steps)
[12c5f496815e58de8dd56ba9a8ef2518f401ecc4] `solveset_real` used instead of `singularities`
gxyd@swap:~/Public/sympy$ git bisect good 
Bisecting: 10 revisions left to test after this (roughly 3 steps)
[7c696fcfdfcb0ed5c0b959163f64b21dfc1d61e2] Merge pull request #9719 from gxyd/Range_Function
gxyd@swap:~/Public/sympy$ git bisect good 
Bisecting: 5 revisions left to test after this (roughly 3 steps)
[d2baf540c8341541c1a2197f7d7e76028f751242] Replace use of  assumption with domain as argument to solveset
gxyd@swap:~/Public/sympy$ git bisect good 
Bisecting: 2 revisions left to test after this (roughly 2 steps)
[5d1bf976f6b6eddd42463cdd4d1044d3a3ba68c9] Fix failing tests and use assumption with solve_univariate_inequality
gxyd@swap:~/Public/sympy$ git bisect good 
Bisecting: 0 revisions left to test after this (roughly 1 step)
[f986e5950607118cb216488871a4d7a3e49851a3] Update docstring
gxyd@swap:~/Public/sympy$ git bisect bad
Bisecting: 0 revisions left to test after this (roughly 0 steps)
[b4596e3e2fe339062d27a0a1104fea298e25dfdf] Fix doc and use dummy variable in solve_univariate_inequality
gxyd@swap:~/Public/sympy$ git bisect bad
b4596e3e2fe339062d27a0a1104fea298e25dfdf is the first bad commit
commit b4596e3e2fe339062d27a0a1104fea298e25dfdf
Author: Shivam Vats <shivamvats.iitkgp@gmail.com>
Date:   Sun Aug 2 08:44:22 2015 +0530

    Fix doc and use dummy variable in solve_univariate_inequality

:040000 040000 79e7b8b24aeee0d1700295b7e7b1f3d863d73f3c e5fc32bc649a14e349f935b3963b125c1d2af690 M	sympy
```

Discussion:

Patch:
diff --git a/sympy/solvers/inequalities.py b/sympy/solvers/inequalities.py
index 2013ed0fb9..ac58e9af99 100644
--- a/sympy/solvers/inequalities.py
+++ b/sympy/solvers/inequalities.py
@@ -406,65 +406,71 @@ def solve_univariate_inequality(expr, gen, relational=True):
     _gen = gen
     gen = d
 
-    e = expr.lhs - expr.rhs
-    parts = n, d = e.as_numer_denom()
-    if all(i.is_polynomial(gen) for i in parts):
-        solns = solve(n, gen, check=False)
-        singularities = solve(d, gen, check=False)
+    if expr is S.true:
+        rv = S.Reals
+    elif expr is S.false:
+        rv = S.EmptySet
     else:
-        solns = solve(e, gen, check=False)
-        singularities = []
-        for d in denoms(e):
-            singularities.extend(solve(d, gen))
+        e = expr.lhs - expr.rhs
+        parts = n, d = e.as_numer_denom()
+        if all(i.is_polynomial(gen) for i in parts):
+            solns = solve(n, gen, check=False)
+            singularities = solve(d, gen, check=False)
+        else:
+            solns = solve(e, gen, check=False)
+            singularities = []
+            for d in denoms(e):
+                singularities.extend(solve(d, gen))
+
+        include_x = expr.func(0, 0)
 
-    include_x = expr.func(0, 0)
+        def valid(x):
+            v = e.subs(gen, x)
+            try:
+                r = expr.func(v, 0)
+            except TypeError:
+                r = S.false
+            if r in (S.true, S.false):
+                return r
+            if v.is_real is False:
+                return S.false
+            else:
+                v = v.n(2)
+                if v.is_comparable:
+                    return expr.func(v, 0)
+                return S.false
 
-    def valid(x):
-        v = e.subs(gen, x)
+        start = S.NegativeInfinity
+        sol_sets = [S.EmptySet]
         try:
-            r = expr.func(v, 0)
-        except TypeError:
-            r = S.false
-        if r in (S.true, S.false):
-            return r
-        if v.is_real is False:
-            return S.false
-        else:
-            v = v.n(2)
-            if v.is_comparable:
-                return expr.func(v, 0)
-            return S.false
+            reals = _nsort(set(solns + singularities), separated=True)[0]
+        except NotImplementedError:
+            raise NotImplementedError('sorting of these roots is not supported')
+        for x in reals:
+            end = x
 
-    start = S.NegativeInfinity
-    sol_sets = [S.EmptySet]
-    try:
-        reals = _nsort(set(solns + singularities), separated=True)[0]
-    except NotImplementedError:
-        raise NotImplementedError('sorting of these roots is not supported')
-    for x in reals:
-        end = x
-
-        if end in [S.NegativeInfinity, S.Infinity]:
-            if valid(S(0)):
-                sol_sets.append(Interval(start, S.Infinity, True, True))
-                break
+            if end in [S.NegativeInfinity, S.Infinity]:
+                if valid(S(0)):
+                    sol_sets.append(Interval(start, S.Infinity, True, True))
+                    break
 
-        if valid((start + end)/2 if start != S.NegativeInfinity else end - 1):
-            sol_sets.append(Interval(start, end, True, True))
+            if valid((start + end)/2 if start != S.NegativeInfinity else end - 1):
+                sol_sets.append(Interval(start, end, True, True))
+
+            if x in singularities:
+                singularities.remove(x)
+            elif include_x:
+                sol_sets.append(FiniteSet(x))
 
-        if x in singularities:
-            singularities.remove(x)
-        elif include_x:
-            sol_sets.append(FiniteSet(x))
+            start = end
 
-        start = end
+        end = S.Infinity
 
-    end = S.Infinity
+        if valid(start + 1):
+            sol_sets.append(Interval(start, end, True, True))
 
-    if valid(start + 1):
-        sol_sets.append(Interval(start, end, True, True))
+        rv = Union(*sol_sets).subs(gen, _gen)
 
-    rv = Union(*sol_sets).subs(gen, _gen)
     return rv if not relational else rv.as_relational(_gen)
 
 


Test patch:
diff --git a/sympy/solvers/tests/test_inequalities.py b/sympy/solvers/tests/test_inequalities.py
index d0b6346062..13569bc9bc 100644
--- a/sympy/solvers/tests/test_inequalities.py
+++ b/sympy/solvers/tests/test_inequalities.py
@@ -284,6 +284,13 @@ def test_solve_univariate_inequality():
         Or(And(-oo < x, x < 1), And(S(1) < x, x < 2))
 
 
+def test_issue_9954():
+    assert isolve(x**2 >= 0, x, relational=False) == S.Reals
+    assert isolve(x**2 >= 0, x, relational=True) == S.Reals.as_relational(x)
+    assert isolve(x**2 < 0, x, relational=False) == S.EmptySet
+    assert isolve(x**2 < 0, x, relational=True) == S.EmptySet.as_relational(x)
+
+
 @slow
 def test_slow_general_univariate():
     r = RootOf(x**5 - x**2 + 1, 0)