Repository: Pyomo/pyomo

Issue:
FBBT should use float('inf') instead of math.inf for python 2/3 compatibility
See the conversation in #1076 .


Discussion:

Resolving patch:
diff --git a/pyomo/contrib/fbbt/fbbt.py b/pyomo/contrib/fbbt/fbbt.py
--- a/pyomo/contrib/fbbt/fbbt.py
+++ b/pyomo/contrib/fbbt/fbbt.py
@@ -17,9 +17,6 @@
 
 logger = logging.getLogger(__name__)
 
-if not hasattr(math, 'inf'):
-    math.inf = float('inf')
-
 
 """
 The purpose of this file is to perform feasibility based bounds 
@@ -204,6 +201,26 @@ def _prop_bnds_leaf_to_root_log(node, bnds_dict, feasibility_tol):
     bnds_dict[node] = interval.log(lb1, ub1)
 
 
+def _prop_bnds_leaf_to_root_log10(node, bnds_dict, feasibility_tol):
+    """
+
+    Parameters
+    ----------
+    node: pyomo.core.expr.numeric_expr.UnaryFunctionExpression
+    bnds_dict: ComponentMap
+    feasibility_tol: float
+        If the bounds computed on the body of a constraint violate the bounds of the constraint by more than
+        feasibility_tol, then the constraint is considered infeasible and an exception is raised. This tolerance
+        is also used when performing certain interval arithmetic operations to ensure that none of the feasible
+        region is removed due to floating point arithmetic and to prevent math domain errors (a larger value
+        is more conservative).
+    """
+    assert len(node.args) == 1
+    arg = node.args[0]
+    lb1, ub1 = bnds_dict[arg]
+    bnds_dict[node] = interval.log10(lb1, ub1)
+
+
 def _prop_bnds_leaf_to_root_sin(node, bnds_dict, feasibility_tol):
     """
 
@@ -281,7 +298,7 @@ def _prop_bnds_leaf_to_root_asin(node, bnds_dict, feasibility_tol):
     assert len(node.args) == 1
     arg = node.args[0]
     lb1, ub1 = bnds_dict[arg]
-    bnds_dict[node] = interval.asin(lb1, ub1, -math.inf, math.inf)
+    bnds_dict[node] = interval.asin(lb1, ub1, -interval.inf, interval.inf)
 
 
 def _prop_bnds_leaf_to_root_acos(node, bnds_dict, feasibility_tol):
@@ -301,7 +318,7 @@ def _prop_bnds_leaf_to_root_acos(node, bnds_dict, feasibility_tol):
     assert len(node.args) == 1
     arg = node.args[0]
     lb1, ub1 = bnds_dict[arg]
-    bnds_dict[node] = interval.acos(lb1, ub1, -math.inf, math.inf)
+    bnds_dict[node] = interval.acos(lb1, ub1, -interval.inf, interval.inf)
 
 
 def _prop_bnds_leaf_to_root_atan(node, bnds_dict, feasibility_tol):
@@ -321,7 +338,7 @@ def _prop_bnds_leaf_to_root_atan(node, bnds_dict, feasibility_tol):
     assert len(node.args) == 1
     arg = node.args[0]
     lb1, ub1 = bnds_dict[arg]
-    bnds_dict[node] = interval.atan(lb1, ub1, -math.inf, math.inf)
+    bnds_dict[node] = interval.atan(lb1, ub1, -interval.inf, interval.inf)
 
 
 def _prop_bnds_leaf_to_root_sqrt(node, bnds_dict, feasibility_tol):
@@ -347,6 +364,7 @@ def _prop_bnds_leaf_to_root_sqrt(node, bnds_dict, feasibility_tol):
 _unary_leaf_to_root_map = dict()
 _unary_leaf_to_root_map['exp'] = _prop_bnds_leaf_to_root_exp
 _unary_leaf_to_root_map['log'] = _prop_bnds_leaf_to_root_log
+_unary_leaf_to_root_map['log10'] = _prop_bnds_leaf_to_root_log10
 _unary_leaf_to_root_map['sin'] = _prop_bnds_leaf_to_root_sin
 _unary_leaf_to_root_map['cos'] = _prop_bnds_leaf_to_root_cos
 _unary_leaf_to_root_map['tan'] = _prop_bnds_leaf_to_root_tan
@@ -373,7 +391,7 @@ def _prop_bnds_leaf_to_root_UnaryFunctionExpression(node, bnds_dict, feasibility
     if node.getname() in _unary_leaf_to_root_map:
         _unary_leaf_to_root_map[node.getname()](node, bnds_dict, feasibility_tol)
     else:
-        bnds_dict[node] = (-math.inf, math.inf)
+        bnds_dict[node] = (-interval.inf, interval.inf)
 
 
 def _prop_bnds_leaf_to_root_GeneralExpression(node, bnds_dict, feasibility_tol):
@@ -691,6 +709,32 @@ def _prop_bnds_root_to_leaf_log(node, bnds_dict, feasibility_tol):
     bnds_dict[arg] = (lb1, ub1)
 
 
+def _prop_bnds_root_to_leaf_log10(node, bnds_dict, feasibility_tol):
+    """
+
+    Parameters
+    ----------
+    node: pyomo.core.expr.numeric_expr.ProductExpression
+    bnds_dict: ComponentMap
+    feasibility_tol: float
+        If the bounds computed on the body of a constraint violate the bounds of the constraint by more than
+        feasibility_tol, then the constraint is considered infeasible and an exception is raised. This tolerance
+        is also used when performing certain interval arithmetic operations to ensure that none of the feasible
+        region is removed due to floating point arithmetic and to prevent math domain errors (a larger value
+        is more conservative).
+    """
+    assert len(node.args) == 1
+    arg = node.args[0]
+    lb0, ub0 = bnds_dict[node]
+    lb1, ub1 = bnds_dict[arg]
+    _lb1, _ub1 = interval.power(10, 10, lb0, ub0)
+    if _lb1 > lb1:
+        lb1 = _lb1
+    if _ub1 < ub1:
+        ub1 = _ub1
+    bnds_dict[arg] = (lb1, ub1)
+
+
 def _prop_bnds_root_to_leaf_sin(node, bnds_dict, feasibility_tol):
     """
 
@@ -850,6 +894,7 @@ def _prop_bnds_root_to_leaf_atan(node, bnds_dict, feasibility_tol):
 _unary_root_to_leaf_map = dict()
 _unary_root_to_leaf_map['exp'] = _prop_bnds_root_to_leaf_exp
 _unary_root_to_leaf_map['log'] = _prop_bnds_root_to_leaf_log
+_unary_root_to_leaf_map['log10'] = _prop_bnds_root_to_leaf_log10
 _unary_root_to_leaf_map['sin'] = _prop_bnds_root_to_leaf_sin
 _unary_root_to_leaf_map['cos'] = _prop_bnds_root_to_leaf_cos
 _unary_root_to_leaf_map['tan'] = _prop_bnds_root_to_leaf_tan
@@ -946,7 +991,7 @@ def visit(self, node, values):
         if node.__class__ in _prop_bnds_leaf_to_root_map:
             _prop_bnds_leaf_to_root_map[node.__class__](node, self.bnds_dict, self.feasibility_tol)
         else:
-            self.bnds_dict[node] = (-math.inf, math.inf)
+            self.bnds_dict[node] = (-interval.inf, interval.inf)
         return None
 
     def visiting_potential_leaf(self, node):
@@ -962,9 +1007,9 @@ def visiting_potential_leaf(self, node):
                 lb = value(node.lb)
                 ub = value(node.ub)
                 if lb is None:
-                    lb = -math.inf
+                    lb = -interval.inf
                 if ub is None:
-                    ub = math.inf
+                    ub = interval.inf
                 if lb - self.feasibility_tol > ub:
                     raise InfeasibleConstraintException('Variable has a lower bound which is larger than its upper bound: {0}'.format(str(node)))
             self.bnds_dict[node] = (lb, ub)
@@ -1037,14 +1082,14 @@ def visiting_potential_leaf(self, node):
 
             lb, ub = self.bnds_dict[node]
             if lb - self.feasibility_tol > ub:
-                raise InfeasibleConstraintException('Lower bound computed for variable {0} is larger than the computed upper bound.'.format(node))
-            if lb == math.inf:
+                raise InfeasibleConstraintException('Lower bound ({1}) computed for variable {0} is larger than the computed upper bound ({2}).'.format(node, lb, ub))
+            if lb == interval.inf:
                 raise InfeasibleConstraintException('Computed a lower bound of +inf for variable {0}'.format(node))
-            if ub == -math.inf:
+            if ub == -interval.inf:
                 raise InfeasibleConstraintException('Computed an upper bound of -inf for variable {0}'.format(node))
-            if lb != -math.inf:
+            if lb != -interval.inf:
                 node.setlb(lb)
-            if ub != math.inf:
+            if ub != interval.inf:
                 node.setub(ub)
             return True, None
 
@@ -1111,9 +1156,9 @@ def _fbbt_con(con, config):
     _lb = value(con.lower)
     _ub = value(con.upper)
     if _lb is None:
-        _lb = -math.inf
+        _lb = -interval.inf
     if _ub is None:
-        _ub = math.inf
+        _ub = interval.inf
 
     lb, ub = bnds_dict[con.body]
 
@@ -1142,9 +1187,9 @@ def _fbbt_con(con, config):
             continue
         if _node.is_variable_type():
             lb, ub = bnds_dict[_node]
-            if lb == -math.inf:
+            if lb == -interval.inf:
                 lb = None
-            if ub == math.inf:
+            if ub == interval.inf:
                 ub = None
             new_var_bounds[_node] = (lb, ub)
     return new_var_bounds
@@ -1184,11 +1229,11 @@ def _fbbt_block(m, config):
             if v not in var_to_con_map:
                 var_to_con_map[v] = list()
             if v.lb is None:
-                var_lbs[v] = -math.inf
+                var_lbs[v] = -interval.inf
             else:
                 var_lbs[v] = value(v.lb)
             if v.ub is None:
-                var_ubs[v] = math.inf
+                var_ubs[v] = interval.inf
             else:
                 var_ubs[v] = value(v.ub)
             var_to_con_map[v].append(c)
diff --git a/pyomo/contrib/fbbt/interval.py b/pyomo/contrib/fbbt/interval.py
--- a/pyomo/contrib/fbbt/interval.py
+++ b/pyomo/contrib/fbbt/interval.py
@@ -4,6 +4,7 @@
 from pyomo.common.errors import DeveloperError, InfeasibleConstraintException, PyomoException
 
 logger = logging.getLogger(__name__)
+inf = float('inf')
 
 
 class IntervalException(PyomoException):
@@ -22,17 +23,17 @@ def mul(xl, xu, yl, yu):
     lb = min(xl*yl, xl*yu, xu*yl, xu*yu)
     ub = max(xl*yl, xl*yu, xu*yl, xu*yu)
     if math.isnan(lb):
-        lb = -math.inf
+        lb = -inf
     if math.isnan(ub):
-        ub = math.inf
+        ub = inf
     return lb, ub
 
 
 def inv(xl, xu, feasibility_tol):
     if xl <= feasibility_tol and xu >= -feasibility_tol:
         # if the denominator (x) can include 0, then 1/x is unbounded.
-        lb = -math.inf
-        ub = math.inf
+        lb = -inf
+        ub = inf
     else:
         ub = 1.0 / xl
         lb = 1.0 / xu
@@ -42,8 +43,8 @@ def inv(xl, xu, feasibility_tol):
 def div(xl, xu, yl, yu, feasibility_tol):
     if yl <= feasibility_tol and yu >= -feasibility_tol:
         # if the denominator (y) can include 0, then x/y is unbounded.
-        lb = -math.inf
-        ub = math.inf
+        lb = -inf
+        ub = inf
     else:
         lb, ub = mul(xl, xu, *inv(yl, yu, feasibility_tol))
     return lb, ub
@@ -72,21 +73,21 @@ def power(xl, xu, yl, yu):
         # this section is only needed so we do not encounter math domain errors;
         # The logic is essentially the same as above (xl > 0)
         if xu == 0 and yl < 0:
-            _lba = math.inf
+            _lba = inf
         else:
             _lba = xu ** yl
         if yu < 0:
-            _lbb = math.inf
+            _lbb = inf
         else:
             _lbb = xl ** yu
         lb = min(_lba, _lbb)
 
         if yl < 0:
-            _uba = math.inf
+            _uba = inf
         else:
             _uba = xl ** yl
         if xu == 0 and yu < 0:
-            _ubb = math.inf
+            _ubb = inf
         else:
             _ubb = xu ** yu
         ub = max(_uba, _ubb)
@@ -106,12 +107,12 @@ def power(xl, xu, yl, yu):
                 if y % 2 == 0:
                     lb = xl ** y
                     if xu == 0:
-                        ub = math.inf
+                        ub = inf
                     else:
                         ub = xu ** y
                 else:
                     if xu == 0:
-                        lb = -math.inf
+                        lb = -inf
                     else:
                         lb = xu ** y
                     ub = xl ** y
@@ -126,10 +127,10 @@ def power(xl, xu, yl, yu):
             if y < 0:
                 if y % 2 == 0:
                     lb = min(xl ** y, xu ** y)
-                    ub = math.inf
+                    ub = inf
                 else:
-                    lb = - math.inf
-                    ub = math.inf
+                    lb = - inf
+                    ub = inf
             else:
                 if y % 2 == 0:
                     lb = 0
@@ -173,8 +174,8 @@ def _inverse_power1(zl, zu, yl, yu, orig_xl, orig_xu, feasibility_tol):
         if y == 0:
             # Anything to the power of 0 is 1, so if y is 0, then x can be anything
             # (assuming zl <= 1 <= zu, which is enforced when traversing the tree in the other direction)
-            xl = -math.inf
-            xu = math.inf
+            xl = -inf
+            xu = inf
         elif y % 2 == 0:
             """
             if y is even, then there are two primary cases (note that it is much easier to walk through these
@@ -213,8 +214,8 @@ def _inverse_power1(zl, zu, yl, yu, orig_xl, orig_xu, feasibility_tol):
                 if zu == 0:
                     raise InfeasibleConstraintException('Infeasible. Anything to the power of {0} must be positive.'.format(y))
                 elif zl <= 0:
-                    _xl = -math.inf
-                    _xu = math.inf
+                    _xl = -inf
+                    _xu = inf
                 else:
                     if orig_xl <= -xl:
                         _xl = -xu
@@ -248,16 +249,16 @@ def _inverse_power1(zl, zu, yl, yu, orig_xl, orig_xu, feasibility_tol):
                     pass
                 elif zu <= 0:
                     if zu == 0:
-                        xl = -math.inf
+                        xl = -inf
                     else:
                         xl = -abs(zu)**(1.0/y)
                     if zl == 0:
-                        xu = -math.inf
+                        xu = -inf
                     else:
                         xu = -abs(zl)**(1.0/y)
                 else:
-                    xl = -math.inf
-                    xu = math.inf
+                    xl = -inf
+                    xu = inf
 
     return xl, xu
 
@@ -288,11 +289,23 @@ def log(xl, xu):
     if xl > 0:
         lb = math.log(xl)
     else:
-        lb = -math.inf
+        lb = -inf
     if xu > 0:
         ub = math.log(xu)
     else:
-        ub = -math.inf
+        ub = -inf
+    return lb, ub
+
+
+def log10(xl, xu):
+    if xl > 0:
+        lb = math.log10(xl)
+    else:
+        lb = -inf
+    if xu > 0:
+        ub = math.log10(xu)
+    else:
+        ub = -inf
     return lb, ub
 
 
@@ -314,7 +327,7 @@ def sin(xl, xu):
     # find the minimum value of i such that 2*pi*i - pi/2 >= xl. Then round i up. If 2*pi*i - pi/2 is still less
     # than or equal to xu, then there is a minimum between xl and xu. Thus the lb is -1. Otherwise, the minimum
     # occurs at either xl or xu
-    if xl <= -math.inf or xu >= math.inf:
+    if xl <= -inf or xu >= inf:
         return -1, 1
     pi = math.pi
     i = (xl + pi / 2) / (2 * pi)
@@ -355,7 +368,7 @@ def cos(xl, xu):
     # find the minimum value of i such that 2*pi*i - pi >= xl. Then round i up. If 2*pi*i - pi/2 is still less
     # than or equal to xu, then there is a minimum between xl and xu. Thus the lb is -1. Otherwise, the minimum
     # occurs at either xl or xu
-    if xl <= -math.inf or xu >= math.inf:
+    if xl <= -inf or xu >= inf:
         return -1, 1
     pi = math.pi
     i = (xl + pi) / (2 * pi)
@@ -396,15 +409,15 @@ def tan(xl, xu):
     # the lb is -inf and the ub is inf. Otherwise the minimum occurs at xl and the maximum occurs at xu.
     # find the minimum value of i such that pi*i + pi/2 >= xl. Then round i up. If pi*i + pi/2 is still less
     # than or equal to xu, then there is an undefined point between xl and xu.
-    if xl <= -math.inf or xu >= math.inf:
-        return -math.inf, math.inf
+    if xl <= -inf or xu >= inf:
+        return -inf, inf
     pi = math.pi
     i = (xl - pi / 2) / (pi)
     i = math.ceil(i)
     x_at_undefined = pi * i + pi / 2
     if x_at_undefined <= xu:
-        lb = -math.inf
-        ub = math.inf
+        lb = -inf
+        ub = inf
     else:
         lb = math.tan(xl)
         ub = math.tan(xu)
@@ -436,7 +449,7 @@ def asin(xl, xu, yl, yu):
 
     pi = math.pi
 
-    if yl <= -math.inf:
+    if yl <= -inf:
         lb = yl
     elif xl <= math.sin(yl) <= xu:
         # if sin(yl) >= xl then yl satisfies the bounds on x, and the lower bound of y cannot be improved
@@ -486,7 +499,7 @@ def asin(xl, xu, yl, yu):
             lb = lb2
 
     # use the same logic for the maximum
-    if yu >= math.inf:
+    if yu >= inf:
         ub = yu
     elif xl <= math.sin(yu) <= xu:
         ub = yu
@@ -547,7 +560,7 @@ def acos(xl, xu, yl, yu):
 
     pi = math.pi
 
-    if yl <= -math.inf:
+    if yl <= -inf:
         lb = yl
     elif xl <= math.cos(yl) <= xu:
         # if xl <= cos(yl) <= xu then yl satisfies the bounds on x, and the lower bound of y cannot be improved
@@ -598,7 +611,7 @@ def acos(xl, xu, yl, yu):
             lb = lb2
 
     # use the same logic for the maximum
-    if yu >= math.inf:
+    if yu >= inf:
         ub = yu
     elif xl <= math.cos(yu) <= xu:
         ub = yu
@@ -656,7 +669,7 @@ def atan(xl, xu, yl, yu):
     pi = math.pi
 
     # tan goes to -inf and inf at every pi*i + pi/2 (integer i).
-    if xl <= -math.inf or yl <= -math.inf:
+    if xl <= -inf or yl <= -inf:
         lb = yl
     else:
         i = (yl - pi / 2) / pi
@@ -666,7 +679,7 @@ def atan(xl, xu, yl, yu):
         dist = y_tmp - (-pi/2)
         lb = i + dist
 
-    if xu >= math.inf or yu >= math.inf:
+    if xu >= inf or yu >= inf:
         ub = yu
     else:
         i = (yu - pi / 2) / pi


Test patch:
diff --git a/pyomo/contrib/fbbt/tests/test_fbbt.py b/pyomo/contrib/fbbt/tests/test_fbbt.py
--- a/pyomo/contrib/fbbt/tests/test_fbbt.py
+++ b/pyomo/contrib/fbbt/tests/test_fbbt.py
@@ -535,6 +535,29 @@ def test_log(self):
             self.assertTrue(np.all(xl <= x))
             self.assertTrue(np.all(xu >= x))
 
+    @unittest.skipIf(not numpy_available, 'Numpy is not available.')
+    def test_log10(self):
+        c_bounds = [(-2.5, 2.8), (-2.5, -0.5), (0.5, 2.8), (-2.5, 0), (0, 2.8), (-2.5, -1), (1, 2.8), (-1, -0.5), (0.5, 1)]
+        for cl, cu in c_bounds:
+            m = pe.Block(concrete=True)
+            m.x = pe.Var()
+            m.c = pe.Constraint(expr=pe.inequality(body=pe.log10(m.x), lower=cl, upper=cu))
+            fbbt(m)
+            z = np.linspace(pe.value(m.c.lower), pe.value(m.c.upper), 100)
+            if m.x.lb is None:
+                xl = -np.inf
+            else:
+                xl = pe.value(m.x.lb)
+            if m.x.ub is None:
+                xu = np.inf
+            else:
+                xu = pe.value(m.x.ub)
+            x = 10**z
+            print(xl, xu, cl, cu)
+            print(x)
+            self.assertTrue(np.all(xl <= x))
+            self.assertTrue(np.all(xu >= x))
+
     def test_sin(self):
         m = pe.Block(concrete=True)
         m.x = pe.Var(bounds=(-math.pi/2, math.pi/2))
diff --git a/pyomo/contrib/fbbt/tests/test_interval.py b/pyomo/contrib/fbbt/tests/test_interval.py
--- a/pyomo/contrib/fbbt/tests/test_interval.py
+++ b/pyomo/contrib/fbbt/tests/test_interval.py
@@ -58,32 +58,32 @@ def test_inv(self):
         self.assertAlmostEqual(ub, 10)
 
         lb, ub = interval.inv(0, 0.1, feasibility_tol=1e-8)
-        self.assertEqual(lb, -math.inf)
-        self.assertEqual(ub, math.inf)
+        self.assertEqual(lb, -interval.inf)
+        self.assertEqual(ub, interval.inf)
 
         lb, ub = interval.inv(0, 0, feasibility_tol=1e-8)
-        self.assertEqual(lb, -math.inf)
-        self.assertEqual(ub, math.inf)
+        self.assertEqual(lb, -interval.inf)
+        self.assertEqual(ub, interval.inf)
 
         lb, ub = interval.inv(-0.1, 0, feasibility_tol=1e-8)
-        self.assertEqual(lb, -math.inf)
-        self.assertEqual(ub, math.inf)
+        self.assertEqual(lb, -interval.inf)
+        self.assertEqual(ub, interval.inf)
 
         lb, ub = interval.inv(-0.2, -0.1, feasibility_tol=1e-8)
         self.assertAlmostEqual(lb, -10)
         self.assertAlmostEqual(ub, -5)
 
         lb, ub = interval.inv(0, -1e-16, feasibility_tol=1e-8)
-        self.assertEqual(lb, -math.inf)
-        self.assertEqual(ub, math.inf)
+        self.assertEqual(lb, -interval.inf)
+        self.assertEqual(ub, interval.inf)
 
         lb, ub = interval.inv(1e-16, 0, feasibility_tol=1e-8)
-        self.assertEqual(lb, -math.inf)
-        self.assertAlmostEqual(ub, math.inf)
+        self.assertEqual(lb, -interval.inf)
+        self.assertAlmostEqual(ub, interval.inf)
 
         lb, ub = interval.inv(-1, 1, feasibility_tol=1e-8)
-        self.assertAlmostEqual(lb, -math.inf)
-        self.assertAlmostEqual(ub, math.inf)
+        self.assertAlmostEqual(lb, -interval.inf)
+        self.assertAlmostEqual(ub, interval.inf)
 
     @unittest.skipIf(not numpy_available, 'Numpy is not available.')
     def test_div(self):
@@ -200,9 +200,9 @@ def test_tan(self):
 
     @unittest.skipIf(not numpy_available, 'Numpy is not available.')
     def test_asin(self):
-        yl, yu = interval.asin(-0.5, 0.5, -math.inf, math.inf)
-        self.assertEqual(yl, -math.inf)
-        self.assertEqual(yu, math.inf)
+        yl, yu = interval.asin(-0.5, 0.5, -interval.inf, interval.inf)
+        self.assertEqual(yl, -interval.inf)
+        self.assertEqual(yu, interval.inf)
         yl, yu = interval.asin(-0.5, 0.5, -math.pi, math.pi)
         self.assertAlmostEqual(yl, -math.pi, 12)
         self.assertAlmostEqual(yu, math.pi, 12)
@@ -227,9 +227,9 @@ def test_asin(self):
 
     @unittest.skipIf(not numpy_available, 'Numpy is not available.')
     def test_acos(self):
-        yl, yu = interval.acos(-0.5, 0.5, -math.inf, math.inf)
-        self.assertEqual(yl, -math.inf)
-        self.assertEqual(yu, math.inf)
+        yl, yu = interval.acos(-0.5, 0.5, -interval.inf, interval.inf)
+        self.assertEqual(yl, -interval.inf)
+        self.assertEqual(yu, interval.inf)
         yl, yu = interval.acos(-0.5, 0.5, -0.5*math.pi, 0.5*math.pi)
         self.assertAlmostEqual(yl, -0.5*math.pi, 12)
         self.assertAlmostEqual(yu, 0.5*math.pi, 12)
@@ -254,9 +254,9 @@ def test_acos(self):
 
     @unittest.skipIf(not numpy_available, 'Numpy is not available.')
     def test_atan(self):
-        yl, yu = interval.atan(-0.5, 0.5, -math.inf, math.inf)
-        self.assertEqual(yl, -math.inf)
-        self.assertEqual(yu, math.inf)
+        yl, yu = interval.atan(-0.5, 0.5, -interval.inf, interval.inf)
+        self.assertEqual(yl, -interval.inf)
+        self.assertEqual(yu, interval.inf)
         yl, yu = interval.atan(-0.5, 0.5, -0.1, 0.1)
         self.assertAlmostEqual(yl, -0.1, 12)
         self.assertAlmostEqual(yu, 0.1, 12)