extension.py 7.9 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14
#   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.

15
# TODO: define the extention functions
16

17
__all__ = ['diag_embed', 'row_conv']
18

L
Li Fuchen 已提交
19 20
import numpy as np
from ...fluid.data_feeder import check_dtype
21
from ...fluid.layer_helper import LayerHelper
L
Li Fuchen 已提交
22 23 24
from ...fluid.framework import Variable, in_dygraph_mode
from ...fluid.layers.tensor import assign
from ...fluid import core, dygraph_utils
25 26 27
from ...fluid.layers.layer_function_generator import templatedoc


L
Li Fuchen 已提交
28 29 30 31 32
def diag_embed(input, offset=0, dim1=-2, dim2=-1):
    """
    This OP creates a tensor whose diagonals of certain 2D planes (specified by dim1 and dim2) 
    are filled by ``input``. By default, a 2D plane formed by the last two dimensions 
    of the returned tensor will be selected.
33

L
Li Fuchen 已提交
34
    The argument ``offset`` determines which diagonal is generated:
35

L
Li Fuchen 已提交
36 37 38
    - If offset = 0, it is the main diagonal.
    - If offset > 0, it is above the main diagonal.
    - If offset < 0, it is below the main diagonal.
39

L
Li Fuchen 已提交
40
    Args:
41
        input(Tensor|numpy.ndarray): The input tensor. Must be at least 1-dimensional. The input data type should be float32, float64, int32, int64.
L
Li Fuchen 已提交
42 43 44
        offset(int, optional): Which diagonal to consider. Default: 0 (main diagonal).
        dim1(int, optional): The first dimension with respect to which to take diagonal. Default: -2.
        dim2(int, optional): The second dimension with respect to which to take diagonal. Default: -1.
45
    
L
Li Fuchen 已提交
46
    Returns:
47
        Tensor, the output data type is the same as input data type.
48
    
L
Li Fuchen 已提交
49 50
    Examples:
        .. code-block:: python
51

L
Li Fuchen 已提交
52 53 54 55
            import paddle.nn.functional as F
            import numpy as np
            
            diag_embed = np.random.randn(2, 3).astype('float32')
56 57
            # [[ 0.7545889 , -0.25074545,  0.5929117 ],
            #  [-0.6097662 , -0.01753256,  0.619769  ]]
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93

            data1 = F.diag_embed(diag_embed)
            data1.numpy()
            # [[[ 0.7545889 ,  0.        ,  0.        ],
            #  [ 0.        , -0.25074545,  0.        ],
            #   [ 0.        ,  0.        ,  0.5929117 ]],

            # [[-0.6097662 ,  0.        ,  0.        ],
            #  [ 0.        , -0.01753256,  0.        ],
            #  [ 0.        ,  0.        ,  0.619769  ]]]

            data2 = F.diag_embed(diag_embed, offset=-1, dim1=0, dim2=2)
            data2.numpy()
            # [[[ 0.        ,  0.        ,  0.        ,  0.        ],
            #   [ 0.7545889 ,  0.        ,  0.        ,  0.        ],
            #   [ 0.        , -0.25074545,  0.        ,  0.        ],
            #   [ 0.        ,  0.        ,  0.5929117 ,  0.        ]],
            #
            #  [[ 0.        ,  0.        ,  0.        ,  0.        ],
            #   [-0.6097662 ,  0.        ,  0.        ,  0.        ],
            #   [ 0.        , -0.01753256,  0.        ,  0.        ],
            #   [ 0.        ,  0.        ,  0.619769  ,  0.        ]]]

            data3 = F.diag_embed(diag_embed, offset=1, dim1=0, dim2=2)
            data3.numpy()
            # [[[ 0.        ,  0.7545889 ,  0.        ,  0.        ],
            #   [ 0.        , -0.6097662 ,  0.        ,  0.        ]],
            #
            #  [[ 0.        ,  0.        , -0.25074545,  0.        ],
            #   [ 0.        ,  0.        , -0.01753256,  0.        ]],
            #
            #  [[ 0.        ,  0.        ,  0.        ,  0.5929117 ],
            #   [ 0.        ,  0.        ,  0.        ,  0.619769  ]],
            #
            #  [[ 0.        ,  0.        ,  0.        ,  0.        ],
            #   [ 0.        ,  0.        ,  0.        ,  0.        ]]]
L
Li Fuchen 已提交
94 95 96 97 98 99 100 101 102 103 104 105 106
    """
    inputs = {'Input': [input]}
    attrs = {'offset': offset, 'dim1': dim1, 'dim2': dim2}

    if not isinstance(input, Variable):
        input = assign(input)

    def __check_input(input, offset, dim1, dim2):
        check_dtype(input.dtype, 'Input',
                    ['int32', 'int64', 'float16', 'float32', 'float64'],
                    'diag_embed')

        input_shape = list(input.shape)
107
        assert len(input_shape) >= 1,                     \
L
Li Fuchen 已提交
108 109
                "Input must be at least 1-dimensional, "   \
                "But received Input's dimensional: %s.\n" %  \
110
                len(input_shape)
L
Li Fuchen 已提交
111

112 113 114
        assert np.abs(dim1) <= len(input_shape),    \
            "Dim1 is out of range (expected to be in range of [%d, %d], but got %d).\n"  \
            % (-(len(input_shape) + 1), len(input_shape), dim1)
L
Li Fuchen 已提交
115

116 117 118
        assert np.abs(dim2) <= len(input_shape),      \
            "Dim2 is out of range (expected to be in range of [%d, %d], but got %d).\n"  \
            % (-(len(input_shape) + 1), len(input_shape), dim2)
L
Li Fuchen 已提交
119 120 121

        dim1_ = dim1 if dim1 >= 0 else len(input_shape) + dim1 + 1
        dim2_ = dim2 if dim2 >= 0 else len(input_shape) + dim2 + 1
122
        assert dim1_ != dim2_,       \
L
Li Fuchen 已提交
123
               "dim1 and dim2 cannot be the same dimension." \
124
                "But received dim1 = %d, dim2 = %d\n"%(dim1, dim2)
L
Li Fuchen 已提交
125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142

    if not in_dygraph_mode():
        __check_input(input, offset, dim1, dim2)
    helper = LayerHelper("diag_embed", **locals())

    out = helper.create_variable_for_type_inference(dtype=input.dtype)

    helper.append_op(
        type='diag_embed',
        inputs={'Input': [input]},
        attrs={'offset': offset,
               'dim1': dim1,
               'dim2': dim2},
        outputs={'Out': [out]})
    out.stop_gradient = True
    return out


143 144 145
@templatedoc()
def row_conv(input, weight, act=None):
    """
S
swtkiwi 已提交
146

147 148 149
    ${comment}

    Args:
150 151
        input (Tensor):  the input(X) is a LodTensor or tensor, LodTensor(X) 
            supports variable time-length input sequences. The underlying 
152 153 154 155 156 157
            tensor in this LoDTensor is a matrix with shape (T, D), where 
            T is the total time steps in this mini-batch and D is the input 
            data dimension. 
            If the input is a padded minibatch, the shape of the input is 
            (N, T, D), N is batch size, T is the max time steps in the batch,
             D is the input data dimension.
158
        weight (Tensor): The weight. A Tensor with shape 
159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179
            (future_context_size + 1, D), where future_context_size is the 
            context size of the RowConv operator.
        act (str): Non-linear activation to be applied to output variable.

    Returns:
        ${out_comment}.

    Examples:
        .. code-block:: python

            from paddle import fluid, nn
            import paddle.nn.functional as F
            import numpy as np

            batch_size = 4
            time_steps = 8
            feature_size = 6
            context_size = 4
            x = np.random.randn(batch_size, time_steps, feature_size).astype(np.float32)
            weight = np.random.randn(context_size + 1, feature_size).astype(np.float32)

180 181 182 183
            x_var = paddle.to_tensor(x)
            w_var = paddle.to_tensor(weight)
            y_var = F.extension.row_conv(x_var, w_var)
            print(y_var.shape)
184

185
            # [4, 8, 6]
186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201
    """

    if in_dygraph_mode():
        pre_act = core.ops.row_conv(input, weight)
        out = dygraph_utils._append_activation_in_dygraph(pre_act, act)
        return out
    else:
        helper = LayerHelper('row_conv', **locals())
        dtype = helper.input_dtype()

        inputs = {'X': [input], 'Filter': [weight]}
        pre_act = helper.create_variable_for_type_inference(dtype)
        outputs = {'Out': [pre_act]}
        helper.append_op(type='row_conv', inputs=inputs, outputs=outputs)
        out = helper.append_activation(pre_act)
    return out