From 5a2d15a1d06a526dd6904d004b665cee77566f84 Mon Sep 17 00:00:00 2001 From: zhupengyang Date: Fri, 17 Jul 2020 14:22:46 +0800 Subject: [PATCH] arange API: start default is 0, end default is None (#25452) --- python/paddle/fluid/layers/tensor.py | 81 ++++++++------ .../fluid/tests/unittests/test_arange.py | 65 +++++++---- python/paddle/tensor/creation.py | 103 +++++++++--------- 3 files changed, 144 insertions(+), 105 deletions(-) diff --git a/python/paddle/fluid/layers/tensor.py b/python/paddle/fluid/layers/tensor.py index dedee1fdfd4..eea2d82bf81 100644 --- a/python/paddle/fluid/layers/tensor.py +++ b/python/paddle/fluid/layers/tensor.py @@ -1322,25 +1322,35 @@ def isfinite(x): return out -def range(start, end, step, dtype): +def range(start, end, step, dtype, name=None): """ Return evenly spaced values within a given interval. - Values are generated within the half-open interval [start, stop) (in other words, - the interval including start but excluding stop). + Values are generated within the half-open interval [start, stop) (in other + words, the interval including start but excluding stop). + + If dtype is float32 or float64, we advise adding a small epsilon to end to + avoid floating point rounding errors when comparing against end. Parameters: - start(float32 | float64 | int32 | int64 | Variable): Start of interval. The interval includes this value. - when start is Variable, it is a 1-D Tensor with shape [1]. - end(float32 | float64 | int32 | int64 | Variable): End of interval. The interval does not include this - value, except in some cases where step is not an integer - and floating point round-off affects the length of out. When end is Variable, - it is a 1-D Tensor with shape [1]. - step(float32 | float64 | int32 | int64 | Variable): Spacing between values. For any output out, this is the - distance between two adjacent values, out[i+1] - out[i]. - dtype(str|core.VarDesc.VarType): the data type of the output tensor, can be float32, float64, int32, int64. - - Returns: a 1-D Tensor which is evenly spaced values within a given interval. Its data type is set by dtype. + start(float|int|Variable): Start of interval. The interval includes + this value. If start is Variable, it is a 1-D Tensor with shape [1], + and it's data type should be one of int32, int64, float32, float64. + end(float|int|Variable): End of interval. The interval does not include + this value. When end is Variable, it is a 1-D Tensor with shape [1], + and it's data type should be int32, int64, float32, float64. + step(float|int|Variable): Spacing between values. For any out, this is + the istance between two adjacent values, out[i+1] - out[i]. + When end is Variable, it is a 1-D Tensor with shape [1], and it's + data type should be one of int32, int64, float32, float64. + dtype(str|np.dtype|core.VarDesc.VarType): The data type of the output + tensor, can be float32, float64, int32, int64. + name(str, optional): Normally there is no need for user to set this property. + For more information, please refer to :ref:`api_guide_Name` . + Default is None. + + Returns: a 1-D Tensor which is evenly spaced values within a given interval. + Its data type is set by dtype. Return type: Variable @@ -1348,44 +1358,47 @@ def range(start, end, step, dtype): .. code-block:: python - import paddle.fluid as fluid - data = fluid.layers.range(0, 10, 2, 'int32') + import paddle.fluid as fluid - """ - check_type(start, 'start', (float, int, Variable), 'range') - check_type(end, 'end', (float, int, Variable), 'range') - check_type(step, 'step', (float, int, Variable), 'range') - helper = LayerHelper("range", **locals()) + out1 = fluid.layers.range(0, 10, 2, 'int32') + # [0, 2, 4, 6, 8] - check_dtype(dtype, 'create data type', - ['float32', 'float64', 'int32', 'int64'], 'range') + start_var = fluid.layers.fill_constant([1], 'int64', 3) + out2 = fluid.layers.range(start_var, 7, 1, 'int64') + # [3, 4, 5, 6] + + """ + if not isinstance(dtype, core.VarDesc.VarType): + dtype = convert_np_dtype_to_dtype_(dtype) - dtype = convert_dtype(dtype) if not isinstance(start, Variable): start = fill_constant([1], dtype, start) - elif convert_dtype(start.dtype) != dtype: - # make sure that start, end, step has the same dtype as - # `dtype` - start = cast(x=start, dtype=dtype) + elif start.dtype != dtype: + start = cast(start, dtype) if not isinstance(end, Variable): end = fill_constant([1], dtype, end) - elif convert_dtype(end.dtype) != dtype: - end = cast(x=end, dtype=dtype) + elif end.dtype != dtype: + end = cast(end, dtype) if not isinstance(step, Variable): step = fill_constant([1], dtype, step) - elif convert_dtype(step.dtype) != dtype: - step = cast(x=step, dtype=dtype) + elif step.dtype != dtype: + step = cast(step, dtype) - out = helper.create_variable_for_type_inference(dtype=start.dtype) + if in_dygraph_mode(): + return core.ops.range(start, end, step) + check_dtype(dtype, 'dtype', ['float32', 'float64', 'int32', 'int64'], + 'range/arange') + helper = LayerHelper('range', **locals()) + out = helper.create_variable_for_type_inference(dtype) helper.append_op( type='range', inputs={'Start': start, 'End': end, 'Step': step}, - outputs={'Out': [out]}) + outputs={'Out': out}) out.stop_gradient = True return out diff --git a/python/paddle/fluid/tests/unittests/test_arange.py b/python/paddle/fluid/tests/unittests/test_arange.py index d715744b02a..1736e49f3b6 100644 --- a/python/paddle/fluid/tests/unittests/test_arange.py +++ b/python/paddle/fluid/tests/unittests/test_arange.py @@ -15,7 +15,8 @@ from __future__ import print_function import paddle -import paddle.fluid as fluid +from paddle.fluid import core +from paddle import program_guard, Program import unittest import numpy as np from op_test import OpTest @@ -44,47 +45,67 @@ class TestArangeOp(OpTest): self.check_output() -class TestFloatArangeOpCase0(TestArangeOp): +class TestFloatArangeOp(TestArangeOp): def init_config(self): self.dtype = np.float32 self.case = (0, 5, 1) -class TestInt32ArangeOpCase0(TestArangeOp): +class TestInt32ArangeOp(TestArangeOp): def init_config(self): self.dtype = np.int32 self.case = (0, 5, 2) -class TestInt32ArangeOpCase1(TestArangeOp): +class TestFloat64ArangeOp(TestArangeOp): def init_config(self): - self.dtype = np.int32 + self.dtype = np.float64 self.case = (10, 1, -2) -class TestInt32ArangeOpCase2(TestArangeOp): +class TestInt64ArangeOp(TestArangeOp): def init_config(self): - self.dtype = np.int32 + self.dtype = np.int64 self.case = (-1, -10, -2) +class TestArangeOpError(unittest.TestCase): + def test_errors(self): + with program_guard(Program(), Program()): + self.assertRaises(TypeError, paddle.arange, 10, dtype='int8') + + class TestArangeAPI(unittest.TestCase): def test_out(self): - with fluid.program_guard(fluid.Program()): - data = paddle.arange(0, 5, 1) - place = fluid.CPUPlace() - exe = fluid.Executor(place) - result, = exe.run(fetch_list=[data]) - expected_data = np.arange(0, 5, 1).astype(np.float32) - self.assertEqual((result == expected_data).all(), True) - - with fluid.program_guard(fluid.Program()): - data = paddle.arange(0.0, 5.0, 1.0, 'int32') - place = fluid.CPUPlace() - exe = fluid.Executor(place) - result, = exe.run(fetch_list=[data]) - expected_data = np.arange(0, 5, 1).astype(np.int32) - self.assertEqual((result == expected_data).all(), True) + with program_guard(Program(), Program()): + x1 = paddle.arange(0, 5, 1, 'float32') + + place = paddle.CUDAPlace(0) if core.is_compiled_with_cuda( + ) else paddle.CPUPlace() + exe = paddle.Executor(place) + out = exe.run(fetch_list=[x1]) + + expected_data = np.arange(0, 5, 1).astype(np.float32) + self.assertEqual((out == expected_data).all(), True) + + +class TestArangeImperative(unittest.TestCase): + def test_out(self): + place = paddle.CUDAPlace(0) if core.is_compiled_with_cuda( + ) else paddle.CPUPlace() + with paddle.imperative.guard(place): + x1 = paddle.arange(0, 5, 1) + x2 = paddle.tensor.arange(5) + x3 = paddle.tensor.creation.arange(5) + + start = paddle.imperative.to_variable(np.array([0], 'float32')) + end = paddle.imperative.to_variable(np.array([5], 'float32')) + step = paddle.imperative.to_variable(np.array([1], 'float32')) + x4 = paddle.arange(start, end, step, 'int64') + + expected_data = np.arange(0, 5, 1).astype(np.int64) + for i in [x1, x2, x3, x4]: + self.assertEqual((i.numpy() == expected_data).all(), True) if __name__ == "__main__": diff --git a/python/paddle/tensor/creation.py b/python/paddle/tensor/creation.py index 4de8b90c029..e84fe6b4e0c 100644 --- a/python/paddle/tensor/creation.py +++ b/python/paddle/tensor/creation.py @@ -21,6 +21,7 @@ from ..fluid.data_feeder import check_variable_and_dtype, check_type, check_dtyp from ..fluid.framework import convert_np_dtype_to_dtype_, in_dygraph_mode, _varbase_creator, device_guard, OpProtoHolder from ..fluid.layers import fill_constant from paddle.common_ops_import import * +import paddle # TODO: define functions to get create a tensor from ..fluid.layers import crop_tensor #DEFINE_ALIAS @@ -413,76 +414,80 @@ def full(shape, fill_value, dtype=None, name=None): return fill_constant(shape=shape, dtype=dtype, value=fill_value, name=name) -def arange(start, end, step=1, dtype=None, name=None): +def arange(start=0, end=None, step=1, dtype=None, name=None): """ :alias_main: paddle.arange :alias: paddle.arange,paddle.tensor.arange,paddle.tensor.creation.arange Return evenly spaced values within a given interval. - Values are generated within the half-open interval [start, stop) (in other words, - the interval including start but excluding stop). + Values are generated into the half-open interval [start, stop) with the step. + (the interval including start but excluding stop). + + If dtype is float32 or float64, we advise adding a small epsilon to end to + avoid floating point rounding errors when comparing against end. Parameters: - start(float32 | float64 | int32 | int64 | Variable): Start of interval. The interval includes this value. - when start is Variable, it is a 1-D Tensor with shape [1]. - end(float32 | float64 | int32 | int64 | Variable): End of interval. The interval does not include this - value, except in some cases where step is not an integer - and floating point round-off affects the length of out. When end is Variable, - it is a 1-D Tensor with shape [1]. - step(float32 | float64 | int32 | int64 | Variable): Spacing between values. For any output out, this is the - distance between two adjacent values, out[i+1] - out[i]. - dtype(str|core.VarDesc.VarType): the data type of the output tensor, can be float32, float64, int32, int64. - - Returns: a 1-D Tensor which is evenly spaced values within a given interval. Its data type is set by dtype. + start(float|int|Variable): Start of interval. The interval includes + this value. If end is None, the half-open interval is [0, start). + If start is Variable, it is a 1-D Tensor with shape [1], and it's + data type should be one of int32, int64, float32, float64. Default + is 0. + end(float|int|Variable, optional): End of interval. The interval does + not include this value. When end is Variable, it is a 1-D Tensor + with shape [1], and it's data type should be one of int32, int64, + float32, float64. If end is None, the half-open interval is [0, start). + Default is None. + step(float|int|Variable, optional): Spacing between values. For any + out, this is the istance between two adjacent values, out[i+1] - out[i]. + When end is Variable, it is a 1-D Tensor with shape [1], and it's + data type should be one of int32, int64, float32, float64. Default is 1. + dtype(str|np.dtype|core.VarDesc.VarType, optional): The data type of + the output tensor, can be float32, float64, int32, int64. If dtype + is `None` , the data type of out tensor is `int64` . Defaule is None + name(str, optional): Normally there is no need for user to set this property. + For more information, please refer to :ref:`api_guide_Name` . + Default is None. + + Returns: a 1-D Tensor which is evenly spaced values within a given interval. + Its data type is set by dtype. Return type: Variable + Raises: + TypeError: If dtype is not float32, float64, int32 or int64. + examples: .. code-block:: python - import paddle - # expected out put: [0, 2, 4, 6, 8] - data = paddle.arange(0, 10, 2, 'int32') - - #dygraph mode - import paddle - import paddle.fluid as fluid - with fluid.dygraph.guard(): - x = paddle.arange(0, 6, 2) - # x: [0, 2, 4] - # x dtype: float32 - - """ - helper = LayerHelper("range", **locals()) - - if dtype is None: - dtype = 'float32' + import paddle + import numpy as np - check_dtype(dtype, 'create data type', - ['float32', 'float64', 'int32', 'int64'], 'range') + paddle.enable_imperative() - dtype = convert_dtype(dtype) - if not isinstance(start, Variable): - start = fill_constant([1], dtype, start) + out1 = paddle.arange(5) + # [0, 1, 2, 3, 4] - if not isinstance(end, Variable): - end = fill_constant([1], dtype, end) + out2 = paddle.arange(3, 9, 2.0) + # [3, 5, 7] - if not isinstance(step, Variable): - step = fill_constant([1], dtype, step) + # use 4.999 instead of 5.0 to avoid floating point rounding errors + out3 = paddle.arange(4.999, dtype='float32') + # [0., 1., 2., 3., 4.] - out = helper.create_variable_for_type_inference(dtype=start.dtype) + start_var = paddle.imperative.to_variable(np.array([3])) + out4 = paddle.arange(start_var, 7) + # [3, 4, 5, 6] + + """ + if dtype is None: + dtype = 'int64' + if end is None: + end = start + start = 0 - helper.append_op( - type='range', - inputs={'Start': start, - 'End': end, - 'Step': step}, - outputs={'Out': [out]}) - out.stop_gradient = True - return out + return paddle.fluid.layers.range(start, end, step, dtype, name) def _tril_triu_op(helper): -- GitLab