Repository: keras-team/keras

Issue:
Lambda layer with tf.fft.fft2d error
I have a problem trying to make a Lambda layer that aplies fft2d to a tensor:
```
from keras.layers import Lambda, Input
import tensorflow as tf


inp = Input(shape=(299,299),dtype='complex64')
tensorTransformada = Lambda(tf.fft2d)(Inp)

```
Even if I have declared the first tensor as 'complex64', the next error appears:

    Traceback (most recent call last):

    File "", line 1, in

    File "/usr/local/lib/python3.6/dist-packages/keras/engine/base_layer.py", line 474, in call

    output_shape = self.compute_output_shape(input_shape)

    File "/usr/local/lib/python3.6/dist-packages/keras/layers/core.py", line 648, in compute_output_shape x = self.call(x) File "/usr/local/lib/python3.6/dist-packages/keras/layers/core.py", line 682, in call return self.function(inputs, **arguments)

    File "/usr/local/lib/python3.6/dist-packages/tensorflow/python/ops/gen_spectral_ops.py", line 437, in fft2d "FFT2D", input=input, name=name)

    File "/usr/local/lib/python3.6/dist-packages/tensorflow/python/framework/op_def_library.py", line 609, in _apply_op_helper param_name=input_name)

    File "/usr/local/lib/python3.6/dist-packages/tensorflow/python/framework/op_def_library.py", line 60, in _SatisfiesTypeConstraint ", ".join(dtypes.as_dtype(x).name for x in allowed_list)))

    TypeError: Value passed to parameter 'input' has DataType float32 not in list of allowed values: complex64, complex128

Please solve this bug

Running on Python 3.6.7, keras 2.2.2, tensorflow 1.11.0 on CPU,


Discussion:
I confirm, the issue is that we create a float32 placeholder to compute the output shape.

Workaround for the users till we fix this : 

* Specify the output_shape directly.
    `tensorTransformada = Lambda(tf.fft2d, output_shape=(None, 299, 299))(Inp)`


Resolving patch:
diff --git a/keras/layers/core.py b/keras/layers/core.py
--- a/keras/layers/core.py
+++ b/keras/layers/core.py
@@ -22,7 +22,6 @@
 from ..utils.generic_utils import func_load
 from ..utils.generic_utils import deserialize_keras_object
 from ..utils.generic_utils import has_arg
-from ..utils import conv_utils
 from ..legacy import interfaces
 
 
@@ -644,6 +643,7 @@ def __init__(self, function, output_shape=None,
                  mask=None, arguments=None, **kwargs):
         super(Lambda, self).__init__(**kwargs)
         self.function = function
+        self._input_dtypes = None
         self.arguments = arguments if arguments else {}
         if mask is not None:
             self.supports_masking = True
@@ -664,10 +664,11 @@ def compute_output_shape(self, input_shape):
             # With TensorFlow or CNTK, we can infer the output shape directly:
             if K.backend() in ('tensorflow', 'cntk'):
                 if isinstance(input_shape, list):
-                    xs = [K.placeholder(shape=shape) for shape in input_shape]
+                    xs = [K.placeholder(shape=shape, dtype=dtype)
+                          for shape, dtype in zip(input_shape, self._input_dtypes)]
                     x = self.call(xs)
                 else:
-                    x = K.placeholder(shape=input_shape)
+                    x = K.placeholder(shape=input_shape, dtype=self._input_dtypes)
                     x = self.call(x)
                 if isinstance(x, list):
                     return [K.int_shape(x_elem) for x_elem in x]
@@ -703,6 +704,10 @@ def call(self, inputs, mask=None):
         arguments = self.arguments
         if has_arg(self.function, 'mask'):
             arguments['mask'] = mask
+        if isinstance(inputs, list):
+            self._input_dtypes = [K.dtype(x) for x in inputs]
+        else:
+            self._input_dtypes = K.dtype(inputs)
         return self.function(inputs, **arguments)
 
     def compute_mask(self, inputs, mask=None):


Test patch:
diff --git a/tests/keras/layers/core_test.py b/tests/keras/layers/core_test.py
--- a/tests/keras/layers/core_test.py
+++ b/tests/keras/layers/core_test.py
@@ -242,6 +242,18 @@ def output_shape(input_shape):
 
     test_multiple_outputs_no_mask()
 
+    def test_dtypes():
+        def func(x):
+            if K.dtype(x) != 'float16':
+                raise TypeError('x dtype is not float16, it is', K.dtype(x))
+            return x
+
+        i = layers.Input(shape=(3, 2, 1), dtype='float16')
+        o = layers.Lambda(func)
+        _ = o(i)
+        assert o._input_dtypes == 'float16'
+    test_dtypes()
+
     # test serialization with function
     def f(x):
         return x + 1