Repository: numba/numba

Issue:
abs(complex) unsupported
```
>>> def f(x): return abs(x)
... 
>>> jitted = jit(nopython=True)(f)
>>> 
>>> jitted(-5.0)
5.0
>>> jitted(-5.0 + 1j)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/antoine/numba/numba/dispatcher.py", line 125, in _compile_and_call
    self.jit(sig)
  File "/home/antoine/numba/numba/dispatcher.py", line 120, in jit
    return self.compile(sig, **kws)
  File "/home/antoine/numba/numba/dispatcher.py", line 108, in compile
    flags=flags, locals=locs)
  File "/home/antoine/numba/numba/compiler.py", line 94, in compile_extra
    return_type, flags, locals)
  File "/home/antoine/numba/numba/compiler.py", line 120, in compile_bytecode
    locals)
  File "/home/antoine/numba/numba/compiler.py", line 283, in type_inference_stage
    infer.propagate()
  File "/home/antoine/numba/numba/typeinfer.py", line 312, in propagate
    self.constrains.propagate(self.context, self.typevars)
  File "/home/antoine/numba/numba/typeinfer.py", line 110, in propagate
    constrain(context, typevars)
  File "/home/antoine/numba/numba/typeinfer.py", line 162, in __call__
    self.resolve(context, typevars, fnty)
  File "/home/antoine/numba/numba/typeinfer.py", line 174, in resolve
    raise TypingError(msg, loc=self.loc)
numba.typeinfer.TypingError: Undeclared abs(complex128,)
File "<stdin>", line 1
```



Discussion:

Resolving patch:
diff --git a/numba/targets/builtins.py b/numba/targets/builtins.py
--- a/numba/targets/builtins.py
+++ b/numba/targets/builtins.py
@@ -9,6 +9,7 @@
 from numba import types, typing, cgutils, utils
 from numba.targets.imputils import (builtin, builtin_attr, implement,
                                     impl_attribute, impl_attribute_generic)
+from numba.typing import signature
 
 #-------------------------------------------------------------------------------
 
@@ -730,16 +731,12 @@ class Complex128(cgutils.Structure):
 def get_complex_info(ty):
     if ty == types.complex64:
         cmplxcls = Complex64
-        flty = types.float32
-
     elif ty == types.complex128:
         cmplxcls = Complex128
-        flty = types.float64
-
     else:
         raise TypeError(ty)
 
-    return cmplxcls, flty
+    return cmplxcls, ty.underlying_float
 
 
 @builtin_attr
@@ -936,6 +933,21 @@ def complex_ne_impl(context, builder, sig, args):
     return builder.or_(reals_are_ne, imags_are_ne)
 
 
+def complex_abs_impl(context, builder, sig, args):
+    """
+    abs(z) := hypot(z.real, z.imag)
+    """
+    [typ] = sig.args
+    [val] = args
+    cmplxcls = context.make_complex(typ)
+    flty = typ.underlying_float
+    cmplx = cmplxcls(context, builder, val)
+    [x, y] = cmplx.real, cmplx.imag
+    hypotsig = signature(sig.return_type, flty, flty)
+    hypotimp = context.get_function(math.hypot, hypotsig)
+    return hypotimp(builder, [x, y])
+
+
 for ty, cls in zip([types.complex64, types.complex128],
                    [Complex64, Complex128]):
     builtin(implement("+", ty, ty)(complex_add_impl))
@@ -950,6 +962,8 @@ def complex_ne_impl(context, builder, sig, args):
     builtin(implement('==', ty, ty)(complex_eq_impl))
     builtin(implement('!=', ty, ty)(complex_ne_impl))
 
+    builtin(implement(types.abs_type, ty)(complex_abs_impl))
+
 
 #------------------------------------------------------------------------------
 
diff --git a/numba/types.py b/numba/types.py
--- a/numba/types.py
+++ b/numba/types.py
@@ -107,6 +107,10 @@ def cast_python_value(self, value):
 
 
 class Complex(Type):
+    def __init__(self, name, underlying_float, **kwargs):
+        super(Complex, self).__init__(name, **kwargs)
+        self.underlying_float = underlying_float
+
     def cast_python_value(self, value):
         return getattr(numpy, self.name)(value)
 
@@ -520,8 +524,8 @@ def is_int_tuple(x):
 float32 = Float('float32')
 float64 = Float('float64')
 
-complex64 = Complex('complex64')
-complex128 = Complex('complex128')
+complex64 = Complex('complex64', float32)
+complex128 = Complex('complex128', float64)
 
 len_type = Dummy('len')
 range_type = Dummy('range')
diff --git a/numba/typing/builtins.py b/numba/typing/builtins.py
--- a/numba/typing/builtins.py
+++ b/numba/typing/builtins.py
@@ -44,9 +44,11 @@ def generic(self, args, kws):
 @builtin
 class Abs(ConcreteTemplate):
     key = types.abs_type
-    intcases = [signature(ty, ty) for ty in types.signed_domain]
-    realcases = [signature(ty, ty) for ty in types.real_domain]
-    cases = intcases + realcases
+    int_cases = [signature(ty, ty) for ty in types.signed_domain]
+    real_cases = [signature(ty, ty) for ty in types.real_domain]
+    complex_cases = [signature(ty.underlying_float, ty)
+                     for ty in types.complex_domain]
+    cases = int_cases + real_cases + complex_cases
 
 
 @builtin


Test patch:
diff --git a/numba/tests/test_builtins.py b/numba/tests/test_builtins.py
--- a/numba/tests/test_builtins.py
+++ b/numba/tests/test_builtins.py
@@ -127,6 +127,16 @@ def test_abs(self, flags=enable_pyobj_flags):
         for x in [-1.1, 0.0, 1.1]:
             self.assertAlmostEqual(cfunc(x), pyfunc(x))
 
+        complex_values = [-1.1 + 0.5j, 0.0 + 0j, 1.1 + 3j]
+        cr = compile_isolated(pyfunc, (types.complex64,), flags=flags)
+        cfunc = cr.entry_point
+        for x in complex_values:
+            self.assertAlmostEqual(cfunc(x), pyfunc(x), places=6)
+        cr = compile_isolated(pyfunc, (types.complex128,), flags=flags)
+        cfunc = cr.entry_point
+        for x in complex_values:
+            self.assertAlmostEqual(cfunc(x), pyfunc(x), places=15)
+
     def test_abs_npm(self):
         self.test_abs(flags=no_pyobj_flags)