uniform.py 9.0 KB
Newer Older
1
# Copyright (c) 2021 PaddlePaddle Authors. All Rights Reserved.
2
#
3 4 5
# 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
6
#
7
#     http://www.apache.org/licenses/LICENSE-2.0
8
#
9 10 11 12 13 14 15
# 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 numpy as np
16 17

import paddle
18
from paddle import _C_ops
19
from paddle.distribution import distribution
20
from paddle.fluid.data_feeder import check_type, convert_dtype
21
from paddle.fluid.layers import tensor
22
from paddle.framework import in_dynamic_mode
23
from paddle.tensor import random
24 25 26


class Uniform(distribution.Distribution):
27 28 29 30 31 32 33 34
    r"""Uniform distribution with `low` and `high` parameters.

    Mathematical Details

    The probability density function (pdf) is

    .. math::

35
        pdf(x; a, b) = \frac{1}{Z}, \ a <=x <b
36 37 38 39 40 41 42 43 44 45 46 47

    .. math::

        Z = b - a

    In the above equation:

    * :math:`low = a`,
    * :math:`high = b`,
    * :math:`Z`: is the normalizing constant.

    The parameters `low` and `high` must be shaped in a way that supports
I
Infinity_lee 已提交
48 49 50 51 52 53
    `Boardcasting` (e.g., `high - low` is a valid operation).

    Note:
        If you want know more about broadcasting, please refer to `Introduction to Tensor`_ .

        .. _Introduction to Tensor: ../../guides/beginner/tensor_en.html#chapter5-broadcasting-of-tensor
54 55

    Args:
56 57 58 59 60
        low(int|float|list|tuple|numpy.ndarray|Tensor): The lower boundary of
            uniform distribution.The data type is float32 and float64.
        high(int|float|list|tuple|numpy.ndarray|Tensor): The higher boundary
            of uniform distribution.The data type is float32 and float64.
        name (str, optional): For details, please refer to :ref:`api_guide_Name`. Generally, no setting is required. Default: None.
61 62 63 64

    Examples:
        .. code-block:: python

65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
            import paddle
            from paddle.distribution import Uniform

            # Without broadcasting, a single uniform distribution [3, 4]:
            u1 = Uniform(low=3.0, high=4.0)
            # 2 distributions [1, 3], [2, 4]
            u2 = Uniform(low=[1.0, 2.0], high=[3.0, 4.0])
            # 4 distributions
            u3 = Uniform(low=[[1.0, 2.0], [3.0, 4.0]],
                        high=[[1.5, 2.5], [3.5, 4.5]])

            # With broadcasting:
            u4 = Uniform(low=3.0, high=[5.0, 6.0, 7.0])

            # Complete example
            value_tensor = paddle.to_tensor([0.8], dtype="float32")

            uniform = Uniform([0.], [2.])

            sample = uniform.sample([2])
            # a random tensor created by uniform distribution with shape: [2, 1]
            entropy = uniform.entropy()
87
            # [0.6931472] with shape: [1]
88 89 90 91
            lp = uniform.log_prob(value_tensor)
            # [-0.6931472] with shape: [1]
            p = uniform.probs(value_tensor)
            # [0.5] with shape: [1]
92 93 94
    """

    def __init__(self, low, high, name=None):
95
        if not in_dynamic_mode():
96 97 98 99 100 101 102 103 104 105 106 107
            check_type(
                low,
                'low',
                (int, float, np.ndarray, tensor.Variable, list, tuple),
                'Uniform',
            )
            check_type(
                high,
                'high',
                (int, float, np.ndarray, tensor.Variable, list, tuple),
                'Uniform',
            )
108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125

        self.all_arg_is_float = False
        self.batch_size_unknown = False
        self.name = name if name is not None else 'Uniform'
        self.dtype = 'float32'

        if isinstance(low, int):
            low = float(low)
        if isinstance(high, int):
            high = float(high)

        if self._validate_args(low, high):
            self.low = low
            self.high = high
            self.dtype = convert_dtype(low.dtype)
        else:
            if isinstance(low, float) and isinstance(high, float):
                self.all_arg_is_float = True
126 127 128 129
            if isinstance(low, np.ndarray) and str(low.dtype) in [
                'float32',
                'float64',
            ]:
130
                self.dtype = low.dtype
131 132 133 134
            elif isinstance(high, np.ndarray) and str(high.dtype) in [
                'float32',
                'float64',
            ]:
135 136 137 138
                self.dtype = high.dtype
            # pylint: disable=unbalanced-tuple-unpacking
            self.low, self.high = self._to_tensor(low, high)
            if self.dtype != convert_dtype(self.low.dtype):
139 140
                self.low = paddle.cast(self.low, dtype=self.dtype)
                self.high = paddle.cast(self.high, dtype=self.dtype)
141

142
        super().__init__(self.low.shape)
143

144 145 146 147
    def sample(self, shape, seed=0):
        """Generate samples of the specified shape.

        Args:
148 149
            shape (list): 1D `int32`. Shape of the generated samples.
            seed (int): Python integer number.
150 151

        Returns:
152
            Tensor, A tensor with prepended dimensions shape. The data type is float32.
153 154

        """
155
        if not in_dynamic_mode():
156 157 158 159 160
            check_type(shape, 'shape', (list), 'sample')
            check_type(seed, 'seed', (int), 'sample')

        name = self.name + '_sample'
        batch_shape = list((self.low + self.high).shape)
161
        if -1 in batch_shape:
162 163
            output_shape = shape + batch_shape
            zero_tmp = tensor.fill_constant_batch_size_like(
164 165
                self.low + self.high, batch_shape + shape, self.dtype, 0.0
            )
166
            uniform_random_tmp = random.uniform_random_batch_size_like(
167 168 169
                zero_tmp,
                zero_tmp.shape,
                dtype=self.dtype,
170 171 172 173
                min=0.0,
                max=1.0,
                seed=seed,
            )
174 175
            zero_tmp_reshape = paddle.reshape(zero_tmp, output_shape)
            uniform_random_tmp_reshape = paddle.reshape(
176 177 178 179 180
                uniform_random_tmp, output_shape
            )
            output = uniform_random_tmp_reshape * (
                zero_tmp_reshape + self.high - self.low
            )
181
            output = paddle.add(output, self.low, name=name)
182 183 184
            return output
        else:
            output_shape = shape + batch_shape
185
            output = paddle.uniform(
186 187
                output_shape, dtype=self.dtype, min=0.0, max=1.0, seed=seed
            ) * (
188
                paddle.zeros(output_shape, dtype=self.dtype)
189 190
                + (self.high - self.low)
            )
191
            output = paddle.add(output, self.low, name=name)
192
            if self.all_arg_is_float:
193
                return paddle.reshape(output, shape, name=name)
194 195 196 197 198 199 200
            else:
                return output

    def log_prob(self, value):
        """Log probability density/mass function.

        Args:
201
            value (Tensor): The input tensor.
202 203

        Returns:
204
            Tensor, log probability.The data type is same with value.
205 206 207

        """
        value = self._check_values_dtype_in_probs(self.low, value)
208
        if in_dynamic_mode():
209 210 211 212
            # ensure value in [low, high]
            lb_bool = self.low < value
            ub_bool = value < self.high

213 214 215 216 217 218 219
            lb = _C_ops.cast(lb_bool, value.dtype)
            ub = _C_ops.cast(ub_bool, value.dtype)
            return paddle.log(lb * ub) - paddle.log(self.high - self.low)
        else:
            name = self.name + '_log_prob'
            lb_bool = self.low < value
            ub_bool = value < self.high
220 221
            lb = paddle.cast(lb_bool, dtype=value.dtype)
            ub = paddle.cast(ub_bool, dtype=value.dtype)
222 223 224
            return paddle.subtract(
                paddle.log(lb * ub), paddle.log(self.high - self.low), name=name
            )
225 226 227 228 229

    def probs(self, value):
        """Probability density/mass function.

        Args:
230
            value (Tensor): The input tensor.
231 232

        Returns:
233
            Tensor, probability. The data type is same with value.
234 235 236

        """
        value = self._check_values_dtype_in_probs(self.low, value)
237
        if in_dynamic_mode():
238 239
            lb_bool = self.low < value
            ub_bool = value < self.high
240 241 242 243 244 245 246
            lb = _C_ops.cast(lb_bool, value.dtype)
            ub = _C_ops.cast(ub_bool, value.dtype)
            return (lb * ub) / (self.high - self.low)
        else:
            name = self.name + '_probs'
            lb_bool = self.low < value
            ub_bool = value < self.high
247 248
            lb = paddle.cast(lb_bool, dtype=value.dtype)
            ub = paddle.cast(ub_bool, dtype=value.dtype)
249
            return paddle.divide((lb * ub), (self.high - self.low), name=name)
250 251 252 253 254 255 256 257 258 259 260

    def entropy(self):
        r"""Shannon entropy in nats.

        The entropy is

        .. math::

            entropy(low, high) = \\log (high - low)

        Returns:
261
            Tensor, Shannon entropy of uniform distribution.The data type is float32.
262 263 264

        """
        name = self.name + '_entropy'
265
        return paddle.log(self.high - self.low, name=name)