Init complex number neural network (#24018)
* Init complex number neural network, test=develop * Improve doc writing, test=develop * Fix elementwise add & sub, test=develop * Fix elementwise mul act, test=develop * a) add ut for complex variable; b) remove arg act in elementwise_ops. test=developrevert-22778-infer_var_type
parent
34d7d6aef0
commit
720d18990c
@ -0,0 +1,18 @@
|
|||||||
|
# Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
from . import tensor
|
||||||
|
from .tensor import *
|
||||||
|
|
||||||
|
__all__ = tensor.__all__ + []
|
||||||
@ -0,0 +1,40 @@
|
|||||||
|
# Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
from ..fluid import framework
|
||||||
|
|
||||||
|
|
||||||
|
def is_complex(x):
|
||||||
|
"""
|
||||||
|
Return true if the input(x) is a ComplexVariable.
|
||||||
|
"""
|
||||||
|
return isinstance(x, framework.ComplexVariable)
|
||||||
|
|
||||||
|
|
||||||
|
def is_real(x):
|
||||||
|
"""
|
||||||
|
Return true if the input(x) is a real number Variable.
|
||||||
|
"""
|
||||||
|
return isinstance(x, framework.Variable)
|
||||||
|
|
||||||
|
|
||||||
|
def complex_variable_exists(inputs, layer_name):
|
||||||
|
for inp in inputs:
|
||||||
|
if is_complex(inp):
|
||||||
|
return
|
||||||
|
err_msg = "At least one inputs of layer complex." if len(inputs) > 1 \
|
||||||
|
else "The input of layer complex."
|
||||||
|
raise ValueError(err_msg + layer_name +
|
||||||
|
"() must be ComplexVariable, please "
|
||||||
|
"use the layer for real numher instead.")
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
# Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
from . import math
|
||||||
|
from .math import *
|
||||||
|
|
||||||
|
__all__ = math.__all__ + []
|
||||||
@ -0,0 +1,216 @@
|
|||||||
|
# Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserve.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
from ..helper import is_complex, is_real, complex_variable_exists
|
||||||
|
from ...fluid.framework import ComplexVariable
|
||||||
|
from ...fluid import layers
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
'elementwise_add', 'elementwise_sub', 'elementwise_mul', 'elementwise_div'
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def elementwise_add(x, y, axis=-1, name=None):
|
||||||
|
"""
|
||||||
|
The element-wise addition layer for complex number inputs. At least one of
|
||||||
|
inputs :attr:`x` and :attr:`y` must be a ComplexVariable. See the detailed
|
||||||
|
description for the function and other arguments
|
||||||
|
in :ref:`api_fluid_layers_elementwise_add` .
|
||||||
|
|
||||||
|
Args:
|
||||||
|
x (Variable|ComplexVariable): The first input Variable or ComplexVariable
|
||||||
|
with any number of dimensions. The supported data types include float32
|
||||||
|
and float64 when it is a Variable. Otherwise the supported data types
|
||||||
|
are complex64 or complex128.
|
||||||
|
y (Variable|ComplexVariable): The second input Variable or ComplexVariable
|
||||||
|
with any number of dimensions. The supported data types include float32
|
||||||
|
and float64 when it is a Variable. Otherwise the supported data types
|
||||||
|
are complex64 or complex128.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
import paddle
|
||||||
|
import paddle.fluid.dygraph as dg
|
||||||
|
|
||||||
|
a = np.array([[1.0+1.0j, 2.0+1.0j], [3.0+1.0j, 4.0+1.0j]])
|
||||||
|
b = np.array([[5.0+2.0j, 6.0+2.0j], [7.0+2.0j, 8.0+2.0j]])
|
||||||
|
with dg.guard():
|
||||||
|
x = dg.to_variable(a)
|
||||||
|
y = dg.to_variable(b)
|
||||||
|
out = paddle.complex.elementwise_add(x, y)
|
||||||
|
print(out.numpy())
|
||||||
|
# [[ 6.+3.j 8.+3.j]
|
||||||
|
# [10.+3.j 12.+3.j]]
|
||||||
|
"""
|
||||||
|
complex_variable_exists([x, y], "elementwise_add")
|
||||||
|
(x_real, x_imag) = (x.real, x.imag) if is_complex(x) else (x, None)
|
||||||
|
(y_real, y_imag) = (y.real, y.imag) if is_complex(y) else (y, None)
|
||||||
|
real = layers.elementwise_add(x_real, y_real, axis=axis, name=name)
|
||||||
|
if is_real(x_imag) and is_real(y_imag):
|
||||||
|
imag = layers.elementwise_add(x_imag, y_imag, axis=axis, name=name)
|
||||||
|
elif is_real(x_imag):
|
||||||
|
imag = layers.assign(x_imag)
|
||||||
|
else:
|
||||||
|
imag = layers.elementwise_add(
|
||||||
|
layers.zeros_like(x_real), y_imag, axis=axis, name=name)
|
||||||
|
return ComplexVariable(real, imag)
|
||||||
|
|
||||||
|
|
||||||
|
def elementwise_sub(x, y, axis=-1, name=None):
|
||||||
|
"""
|
||||||
|
The element-wise subtraction layer for complex number inputs. At least one of
|
||||||
|
inputs :attr:`x` and :attr:`y` must be a ComplexVariable. See the detailed
|
||||||
|
description for the function and other arguments
|
||||||
|
in :ref:`api_fluid_layers_elementwise_sub` .
|
||||||
|
|
||||||
|
Args:
|
||||||
|
x (Variable|ComplexVariable): The first input Variable or ComplexVariable
|
||||||
|
with any number of dimensions. The supported data types include float32
|
||||||
|
and float64 when it is a Variable. Otherwise the supported data types
|
||||||
|
are complex64 or complex128.
|
||||||
|
y (Variable|ComplexVariable): The second input Variable or ComplexVariable
|
||||||
|
with any number of dimensions. The supported data types include float32
|
||||||
|
and float64 when it is a Variable. Otherwise the supported data types
|
||||||
|
are complex64 or complex128.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
import paddle
|
||||||
|
import paddle.fluid.dygraph as dg
|
||||||
|
|
||||||
|
a = np.array([[1.0+1.0j, 2.0+1.0j], [3.0+1.0j, 4.0+1.0j]])
|
||||||
|
b = np.array([[5.0+2.0j, 6.0+2.0j], [7.0+2.0j, 8.0+2.0j]])
|
||||||
|
with dg.guard():
|
||||||
|
x = dg.to_variable(a)
|
||||||
|
y = dg.to_variable(b)
|
||||||
|
out = paddle.complex.elementwise_sub(x, y)
|
||||||
|
print(out.numpy())
|
||||||
|
# [[-4.-1.j -4.-1.j]
|
||||||
|
# [-4.-1.j -4.-1.j]]
|
||||||
|
"""
|
||||||
|
complex_variable_exists([x, y], "elementwise_sub")
|
||||||
|
(x_real, x_imag) = (x.real, x.imag) if is_complex(x) else (x, None)
|
||||||
|
(y_real, y_imag) = (y.real, y.imag) if is_complex(y) else (y, None)
|
||||||
|
real = layers.elementwise_sub(x_real, y_real, axis=axis, name=name)
|
||||||
|
if is_real(x_imag) and is_real(y_imag):
|
||||||
|
imag = layers.elementwise_sub(x_imag, y_imag, axis=axis, name=name)
|
||||||
|
elif is_real(x_imag):
|
||||||
|
imag = layers.assign(x_imag)
|
||||||
|
else:
|
||||||
|
imag = layers.elementwise_sub(
|
||||||
|
layers.zeros_like(x_real), y_imag, axis=axis, name=name)
|
||||||
|
return ComplexVariable(real, imag)
|
||||||
|
|
||||||
|
|
||||||
|
def elementwise_mul(x, y, axis=-1, name=None):
|
||||||
|
"""
|
||||||
|
The element-wise multiplication layer for complex number inputs. At least
|
||||||
|
one of inputs :attr:`x` and :attr:`y` must be a ComplexVariable. See the
|
||||||
|
detailed description for the function and other arguments
|
||||||
|
in :ref:`api_fluid_layers_elementwise_mul` .
|
||||||
|
|
||||||
|
Args:
|
||||||
|
x (Variable|ComplexVariable): The first input Variable or ComplexVariable
|
||||||
|
with any number of dimensions. The supported data types include float32
|
||||||
|
and float64 when it is a Variable. Otherwise the supported data types
|
||||||
|
are complex64 or complex128.
|
||||||
|
y (Variable|ComplexVariable): The second input Variable or ComplexVariable
|
||||||
|
with any number of dimensions. The supported data types include float32
|
||||||
|
and float64 when it is a Variable. Otherwise the supported data types
|
||||||
|
are complex64 or complex128.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
import paddle
|
||||||
|
import paddle.fluid.dygraph as dg
|
||||||
|
|
||||||
|
a = np.array([[1.0+1.0j, 2.0+1.0j], [3.0+1.0j, 4.0+1.0j]])
|
||||||
|
b = np.array([[5.0+2.0j, 6.0+2.0j], [7.0+2.0j, 8.0+2.0j]])
|
||||||
|
with dg.guard():
|
||||||
|
x = dg.to_variable(a)
|
||||||
|
y = dg.to_variable(b)
|
||||||
|
out = paddle.complex.elementwise_mul(x, y)
|
||||||
|
print(out.numpy())
|
||||||
|
# [[ 3. +7.j 10.+10.j]
|
||||||
|
# [19.+13.j 30.+16.j]]
|
||||||
|
"""
|
||||||
|
complex_variable_exists([x, y], "elementwise_mul")
|
||||||
|
# (a + bi)(c + di) = (ac - bd) + (bc + ad)i
|
||||||
|
(a, b) = (x.real, x.imag) if is_complex(x) else (x, None)
|
||||||
|
(c, d) = (y.real, y.imag) if is_complex(y) else (y, None)
|
||||||
|
|
||||||
|
ac = layers.elementwise_mul(a, c, axis=axis, name=name)
|
||||||
|
bd = layers.elementwise_mul(
|
||||||
|
b, d, axis=axis, name=name) if is_real(b) and is_real(d) else None
|
||||||
|
bc = layers.elementwise_mul(
|
||||||
|
b, c, axis=axis, name=name) if is_real(b) else None
|
||||||
|
ad = layers.elementwise_mul(
|
||||||
|
a, d, axis=axis, name=name) if is_real(d) else None
|
||||||
|
real = ac - bd if is_real(bd) else ac
|
||||||
|
imag = bc + ad if is_real(bc) and is_real(ad) else bc if is_real(bc) else ad
|
||||||
|
return ComplexVariable(real, imag)
|
||||||
|
|
||||||
|
|
||||||
|
def elementwise_div(x, y, axis=-1, name=None):
|
||||||
|
"""
|
||||||
|
The element-wise division layer for complex number inputs. At least one of
|
||||||
|
inputs :attr:`x` and :attr:`y` must be a ComplexVariable. See the detailed
|
||||||
|
description for the function and other arguments
|
||||||
|
in :ref:`api_fluid_layers_elementwise_div` .
|
||||||
|
|
||||||
|
Args:
|
||||||
|
x (Variable|ComplexVariable): The first input Variable or ComplexVariable
|
||||||
|
with any number of dimensions. The supported data types include float32
|
||||||
|
and float64 when it is a Variable. Otherwise the supported data types
|
||||||
|
are complex64 or complex128.
|
||||||
|
y (Variable|ComplexVariable): The second input Variable or ComplexVariable
|
||||||
|
with any number of dimensions. The supported data types include float32
|
||||||
|
and float64 when it is a Variable. Otherwise the supported data types
|
||||||
|
are complex64 or complex128.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
import paddle
|
||||||
|
import paddle.fluid.dygraph as dg
|
||||||
|
|
||||||
|
a = np.array([[1.0+1.0j, 2.0+1.0j], [3.0+1.0j, 4.0+1.0j]])
|
||||||
|
b = np.array([[5.0+2.0j, 6.0+2.0j], [7.0+2.0j, 8.0+2.0j]])
|
||||||
|
with dg.guard():
|
||||||
|
x = dg.to_variable(a)
|
||||||
|
y = dg.to_variable(b)
|
||||||
|
out = paddle.complex.elementwise_div(x, y)
|
||||||
|
print(out.numpy())
|
||||||
|
# [[0.24137931+0.10344828j 0.35 +0.05j ]
|
||||||
|
# [0.43396226+0.01886792j 0.5 +0.j ]]
|
||||||
|
"""
|
||||||
|
complex_variable_exists([x, y], "elementwise_div")
|
||||||
|
# (a + bi)/(c + di) = (a + bi)(c - di)/(c^2 + d^2)
|
||||||
|
(c, d) = (y.real, y.imag) if is_complex(y) else (y, None)
|
||||||
|
y_conj = ComplexVariable(c, -d) if is_real(d) else c
|
||||||
|
e = 1 / (layers.pow(c, 2.0) + layers.pow(d, 2.0)
|
||||||
|
) if is_real(d) else 1 / layers.pow(c, 2.0)
|
||||||
|
return elementwise_mul(
|
||||||
|
elementwise_mul(
|
||||||
|
x, y_conj, axis=axis, name=name),
|
||||||
|
e,
|
||||||
|
axis=axis,
|
||||||
|
name=name)
|
||||||
@ -0,0 +1,70 @@
|
|||||||
|
# Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
import unittest
|
||||||
|
import numpy as np
|
||||||
|
from numpy.random import random as rand
|
||||||
|
import paddle.complex as cpx
|
||||||
|
import paddle.fluid as fluid
|
||||||
|
import paddle.fluid.dygraph as dg
|
||||||
|
|
||||||
|
layers = {
|
||||||
|
"add": cpx.elementwise_add,
|
||||||
|
"sub": cpx.elementwise_sub,
|
||||||
|
"mul": cpx.elementwise_mul,
|
||||||
|
"div": cpx.elementwise_div,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class TestComplexElementwiseLayers(unittest.TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
self._dtype = "float64"
|
||||||
|
self._places = [fluid.CPUPlace()]
|
||||||
|
if fluid.core.is_compiled_with_cuda():
|
||||||
|
self._places.append(fluid.CUDAPlace(0))
|
||||||
|
|
||||||
|
def calc(self, x, y, layer_type, place):
|
||||||
|
with dg.guard(place):
|
||||||
|
var_x = dg.to_variable(x)
|
||||||
|
var_y = dg.to_variable(y)
|
||||||
|
return layers[layer_type](var_x, var_y).numpy()
|
||||||
|
|
||||||
|
def compare(self, x, y):
|
||||||
|
for place in self._places:
|
||||||
|
self.assertTrue(np.allclose(self.calc(x, y, "add", place), x + y))
|
||||||
|
self.assertTrue(np.allclose(self.calc(x, y, "sub", place), x - y))
|
||||||
|
self.assertTrue(np.allclose(self.calc(x, y, "mul", place), x * y))
|
||||||
|
self.assertTrue(np.allclose(self.calc(x, y, "div", place), x / y))
|
||||||
|
|
||||||
|
def test_complex_xy(self):
|
||||||
|
x = rand([2, 3, 4, 5]).astype(self._dtype) + 1j * rand(
|
||||||
|
[2, 3, 4, 5]).astype(self._dtype)
|
||||||
|
y = rand([2, 3, 4, 5]).astype(self._dtype) + 1j * rand(
|
||||||
|
[2, 3, 4, 5]).astype(self._dtype)
|
||||||
|
self.compare(x, y)
|
||||||
|
|
||||||
|
def test_complex_x_real_y(self):
|
||||||
|
x = rand([2, 3, 4, 5]).astype(self._dtype) + 1j * rand(
|
||||||
|
[2, 3, 4, 5]).astype(self._dtype)
|
||||||
|
y = rand([4, 5]).astype(self._dtype)
|
||||||
|
self.compare(x, y)
|
||||||
|
|
||||||
|
def test_real_x_complex_y(self):
|
||||||
|
x = rand([2, 3, 4, 5]).astype(self._dtype)
|
||||||
|
y = rand([5]).astype(self._dtype) + 1j * rand([5]).astype(self._dtype)
|
||||||
|
self.compare(x, y)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
||||||
@ -0,0 +1,48 @@
|
|||||||
|
# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
import unittest
|
||||||
|
import numpy as np
|
||||||
|
import paddle
|
||||||
|
import paddle.fluid.dygraph as dg
|
||||||
|
|
||||||
|
|
||||||
|
class TestComplexVariable(unittest.TestCase):
|
||||||
|
def compare(self):
|
||||||
|
a = np.array([[1.0 + 1.0j, 2.0 + 1.0j],
|
||||||
|
[3.0 + 1.0j, 4.0 + 1.0j]]).astype(self._dtype)
|
||||||
|
b = np.array([[1.0 + 1.0j, 1.0 + 1.0j]]).astype(self._dtype)
|
||||||
|
|
||||||
|
with dg.guard():
|
||||||
|
x = dg.to_variable(a, "x")
|
||||||
|
y = dg.to_variable(b)
|
||||||
|
out = paddle.complex.elementwise_add(x, y)
|
||||||
|
self.assertIsNotNone("{}".format(out))
|
||||||
|
|
||||||
|
self.assertTrue(np.allclose(out.numpy(), a + b))
|
||||||
|
self.assertEqual(x.name, {'real': 'x.real', 'imag': 'x.imag'})
|
||||||
|
x.name = "new_x"
|
||||||
|
self.assertEqual(x.name, {'real': 'new_x.real', 'imag': 'new_x.imag'})
|
||||||
|
self.assertEqual(out.dtype, self._dtype)
|
||||||
|
self.assertEqual(out.shape, x.shape)
|
||||||
|
|
||||||
|
def test_attrs(self):
|
||||||
|
self._dtype = "complex64"
|
||||||
|
self.compare()
|
||||||
|
self._dtype = "complex128"
|
||||||
|
self.compare()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
||||||
Loading…
Reference in new issue