test_while_loop_op.py 22.9 KB
Newer Older
G
guofei 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
# 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.
from __future__ import print_function

import numpy as np
import unittest

19
import paddle
G
guofei 已提交
20 21 22 23 24 25
import paddle.fluid as fluid
import paddle.fluid.core as core
import paddle.fluid.layers as layers
import paddle.fluid.framework as framework
from paddle.fluid.executor import Executor
from paddle.fluid.framework import Program, program_guard
26
from paddle.fluid.backward import append_backward
G
guofei 已提交
27

28 29
paddle.enable_static()

G
guofei 已提交
30 31

class TestApiWhileLoop(unittest.TestCase):
32

G
guofei 已提交
33
    def test_var_tuple(self):
34

G
guofei 已提交
35 36 37 38 39 40 41 42 43 44 45 46 47 48
        def cond(i):
            return layers.less_than(i, ten)

        def body(i):
            return layers.elementwise_add(x=i, y=one)

        main_program = Program()
        startup_program = Program()
        with program_guard(main_program, startup_program):
            i = layers.fill_constant(shape=[1], dtype='int64', value=0)
            one = layers.fill_constant(shape=[1], dtype='int64', value=1)
            ten = layers.fill_constant(shape=[1], dtype='int64', value=10)
            out = layers.while_loop(cond, body, (i, ))

49 50
        place = fluid.CUDAPlace(
            0) if core.is_compiled_with_cuda() else fluid.CPUPlace()
G
guofei 已提交
51 52 53 54 55 56
        exe = fluid.Executor(place)
        res = exe.run(main_program, fetch_list=out)
        self.assertTrue(
            np.allclose(np.asarray(res[0]), np.full((1), 10, np.int64)))

    def test_var_list(self):
57

G
guofei 已提交
58 59 60 61 62 63 64 65 66 67 68 69 70
        def cond(i, mem):
            return layers.less_than(i, ten)

        def body(i, mem):
            mem = layers.elementwise_add(x=mem, y=one)
            i = layers.increment(i)
            return [i, mem]

        main_program = Program()
        startup_program = Program()
        with program_guard(main_program, startup_program):
            i = layers.zeros(shape=[1], dtype='int64')
            ten = layers.fill_constant(shape=[1], dtype='int64', value=10)
71
            mem = fluid.data(name='mem', shape=[10], dtype='float32')
G
guofei 已提交
72 73 74 75 76 77
            one = layers.fill_constant(shape=[10], dtype='float32', value=1)
            out = layers.while_loop(cond, body, [i, mem])

            data = np.random.rand(10).astype('float32')
            data_one = np.ones(10).astype('float32')

78 79
        place = fluid.CUDAPlace(
            0) if core.is_compiled_with_cuda() else fluid.CPUPlace()
G
guofei 已提交
80 81 82 83 84 85
        exe = fluid.Executor(place)
        res = exe.run(main_program, feed={'mem': data}, fetch_list=out)
        for i in range(10):
            data = np.add(data, data_one)
        self.assertTrue(np.allclose(np.asarray(res[1]), data))

86
    def test_var_dict(self):
87

88
        def cond(i, ten, test_dict, test_list, test_list_dict):
89 90
            return layers.less_than(i, ten)

91 92 93 94 95 96 97
        def body(i, ten, test_dict, test_list, test_list_dict):
            test_dict["test_key"] = i
            test_dict["test_key"] += 1

            test_list[0] = fluid.layers.reshape(test_list[0], [2, -1]) + 1

            test_list_dict[0]["test_key"] += 1
98 99
            test_list_dict[0]["test_key"] = fluid.layers.relu(
                test_list_dict[0]["test_key"])
100

101
            i = layers.increment(i)
102
            return [i, ten, test_dict, test_list, test_list_dict]
103 104 105 106 107 108 109

        main_program = Program()
        startup_program = Program()
        with program_guard(main_program, startup_program):
            i = layers.zeros(shape=[1], dtype='int64')
            ten = layers.fill_constant(shape=[1], dtype='int64', value=10)
            test_data = layers.fill_constant(shape=[1], dtype='int64', value=0)
110

111
            test_dict = {"test_key": test_data}
112
            test_list = [
113
                layers.fill_constant(shape=[1, 2], dtype='int64', value=0)
114 115
            ]
            test_list_dict = [{
116 117
                "test_key":
                layers.fill_constant(shape=[1], dtype='float32', value=0)
118 119 120 121
            }]

            i, ten, test_dict, test_list, test_list_dict = layers.while_loop(
                cond, body, [i, ten, test_dict, test_list, test_list_dict])
122 123
        place = fluid.CUDAPlace(
            0) if core.is_compiled_with_cuda() else fluid.CPUPlace()
124
        exe = fluid.Executor(place)
125 126 127 128 129
        res = exe.run(main_program,
                      fetch_list=[
                          test_dict["test_key"], test_list[0],
                          test_list_dict[0]["test_key"]
                      ])
130
        self.assertTrue(
131 132
            np.allclose(np.asarray(res[0]),
                        np.full(shape=(1), fill_value=10, dtype=np.int64)))
133
        self.assertTrue(
134 135
            np.allclose(np.asarray(res[1]),
                        np.full(shape=(2, 1), fill_value=10, dtype=np.int64)))
136
        self.assertTrue(
137 138
            np.allclose(np.asarray(res[2]),
                        np.full(shape=(1), fill_value=10, dtype=np.float32)))
139

G
guofei 已提交
140 141

class TestApiWhileLoop_Nested(unittest.TestCase):
142

G
guofei 已提交
143
    def test_nested_net(self):
144

G
guofei 已提交
145 146 147 148
        def external_cond(i, j, init, sums):
            return layers.less_than(i, loop_len1)

        def external_body(i, j, init, sums):
149

G
guofei 已提交
150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
            def internal_cond(j, init, sums):
                return layers.less_than(j, loop_len2)

            def internal_body(j, init, sums):
                init = layers.elementwise_add(x=init, y=ones)
                sums = layers.elementwise_add(x=init, y=sums)
                j = layers.increment(j)
                return [j, init, sums]

            result = layers.while_loop(internal_cond, internal_body,
                                       [j, init, sums])
            j = result[0]
            init = result[1]
            sums = result[2]
            sums = layers.elementwise_add(x=init, y=sums)
            i = layers.increment(i)
            return [i, j, init, sums]

        main_program = Program()
        startup_program = Program()
        with program_guard(main_program, startup_program):
            i = layers.zeros(shape=[1], dtype='int64')
            j = layers.zeros(shape=[1], dtype='int64')
173 174
            init = fluid.data(name='init', shape=[3, 3], dtype='float32')
            sums = fluid.data(name='sums', shape=[3, 3], dtype='float32')
G
guofei 已提交
175 176 177 178
            loop_len1 = layers.fill_constant(shape=[1], dtype='int64', value=2)
            loop_len2 = layers.fill_constant(shape=[1], dtype='int64', value=3)
            ones = layers.fill_constant(shape=[3, 3], dtype='float32', value=1)

179
            out = layers.while_loop(external_cond, external_body,
G
guofei 已提交
180 181 182 183 184
                                    [i, j, init, sums])

            data = np.random.rand(3, 3).astype('float32')
            data_sums = np.zeros([3, 3]).astype('float32')

185 186
        place = fluid.CUDAPlace(
            0) if core.is_compiled_with_cuda() else fluid.CPUPlace()
G
guofei 已提交
187
        exe = fluid.Executor(place)
188
        res = exe.run(main_program,
189 190 191 192
                      feed={
                          'init': data,
                          'sums': data_sums
                      },
193
                      fetch_list=out)
G
guofei 已提交
194 195 196 197 198
        for i in range(3):
            data = np.add(data, 1)
            data_sums = np.add(data, data_sums)
        for j in range(2):
            data_sums = np.add(data, data_sums)
199 200 201 202
        self.assertTrue(np.allclose(np.asarray(res[3]), data_sums))


class TestApiWhileLoop_Backward(unittest.TestCase):
203

204
    def test_while_loop_backward(self):
205

206 207 208
        def cond(i, x):
            return layers.less_than(i, eleven)

209
        def body(i, x):
210
            x = layers.elementwise_mul(x=i, y=i)
211 212
            i = layers.increment(i)
            return [i, x]
213 214 215 216

        main_program = Program()
        startup_program = Program()
        with fluid.program_guard(main_program, startup_program):
217
            i = fluid.data(name='i', shape=[1], dtype='float32')
218 219 220
            i.stop_gradient = False
            eleven = layers.fill_constant(shape=[1], dtype='float32', value=11)
            one = layers.fill_constant(shape=[1], dtype='float32', value=1)
221
            x = fluid.data(name='x', shape=[1], dtype='float32')
222 223 224
            x.stop_gradient = False

            out = layers.while_loop(cond, body, [i, x])
225
            mean = paddle.mean(out[1])
226 227
            append_backward(mean)

228 229
        place = fluid.CUDAPlace(
            0) if core.is_compiled_with_cuda() else fluid.CPUPlace()
230 231 232 233 234 235 236 237
        exe = fluid.Executor(place)

        feed_i = np.ones(1).astype('float32')
        feed_x = np.ones(1).astype('float32')
        data = np.asarray([100]).astype('float32')
        i_grad = np.asarray([110]).astype('float32')

        res = exe.run(main_program,
238 239 240 241
                      feed={
                          'i': feed_i,
                          'x': feed_x
                      },
242 243
                      fetch_list=[mean.name, i.grad_name])
        self.assertTrue(np.allclose(np.asarray(res[0]), data))
244 245 246
        self.assertTrue(np.allclose(np.asarray(res[1]), i_grad),
                        msg=" \nres = \n{} \n\n ans = \n{}".format(
                            res[1], i_grad))
247 248

    def test_while_loop_backward2(self):
249

250
        def cond(i, x):
251
            return i < 3
252 253

        def body(i, x):
254
            x = x * i
255 256 257 258 259 260 261 262 263 264 265 266
            i = i + 1
            return [i, x]

        main_program = Program()
        startup_program = Program()
        with fluid.program_guard(main_program, startup_program):
            i = fluid.data(name='i', shape=[1], dtype='float32')
            i.stop_gradient = False
            x = fluid.data(name='x', shape=[1], dtype='float32')
            x.stop_gradient = False

            out = layers.while_loop(cond, body, [i, x])
267
            mean = paddle.mean(out[1])
268 269
            append_backward(mean)

270 271
        place = fluid.CUDAPlace(
            0) if core.is_compiled_with_cuda() else fluid.CPUPlace()
272 273 274 275
        exe = fluid.Executor(place)

        feed_i = np.ones(1).astype('float32')
        feed_x = np.ones(1).astype('float32')
276 277 278
        data = np.asarray([2]).astype('float32')
        i_grad = np.asarray([3]).astype('float32')
        x_grad = np.asarray([2]).astype('float32')
279 280

        res = exe.run(main_program,
281 282 283 284
                      feed={
                          'i': feed_i,
                          'x': feed_x
                      },
285
                      fetch_list=[mean.name, i.grad_name, x.grad_name])
286
        self.assertTrue(np.allclose(np.asarray(res[0]), data))
287 288 289 290 291 292
        self.assertTrue(np.allclose(np.asarray(res[1]), i_grad),
                        msg=" \nres = \n{} \n\n ans = \n{}".format(
                            res[1], i_grad))
        self.assertTrue(np.allclose(np.asarray(res[2]), x_grad),
                        msg=" \nres = \n{} \n\n ans = \n{}".format(
                            res[2], x_grad))
293 294


295
class TestApiWhileLoop_NestedWithBackwardAndLoDTensorArray(unittest.TestCase):
296

297
    def test_nested_net_with_backward_and_lodtensor(self):
298

299 300 301 302
        def external_cond(i, j, x, mem_array):
            return layers.less_than(i, array_len)

        def external_body(i, j, x, mem_array):
303

304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324
            def internal_cond(j, x, mem_array):
                return layers.less_than(j, array_len2)

            def internal_body(j, x, mem_array):
                inner_data = layers.array_read(array=data_array, i=j)
                inner_prev = layers.array_read(array=mem_array, i=j)
                inner_sum_0 = layers.elementwise_add(x=inner_data, y=inner_prev)
                inner_sum_1 = layers.elementwise_add(x=x, y=inner_sum_0)
                j = layers.increment(x=j, in_place=True)
                layers.array_write(inner_sum_1, i=j, array=mem_array)
                return [j, x, mem_array]

            outer_data = layers.array_read(array=data_array, i=i)
            outer_prev = layers.array_read(array=mem_array, i=i)
            outer_sum_0 = layers.elementwise_add(x=outer_data, y=outer_prev)
            outer_sum_1 = layers.elementwise_add(x=x, y=outer_sum_0)
            i = layers.increment(x=i, in_place=True)
            layers.array_write(outer_sum_1, i=i, array=mem_array)
            j, x, mem_array = layers.while_loop(internal_cond, internal_body,
                                                [j, x, mem_array])
            return [i, j, x, mem_array]
325 326 327 328

        main_program = Program()
        startup_program = Program()
        with fluid.program_guard(main_program, startup_program):
329 330 331 332
            d0 = fluid.data(name='d0', shape=[10], dtype='float32')
            d1 = fluid.data(name='d1', shape=[10], dtype='float32')
            d2 = fluid.data(name='d2', shape=[10], dtype='float32')
            x = fluid.data(name='x', shape=[10], dtype='float32')
333
            x.stop_gradient = False
334 335 336 337 338 339 340 341 342 343 344 345 346 347 348
            i = layers.zeros(shape=[1], dtype='int64')
            i.stop_gradient = True
            init = layers.zeros(shape=[10], dtype='float32')
            mem_array = layers.array_write(x=init, i=i)
            data_array = layers.array_write(x=d0, i=i)
            i = layers.increment(i)
            layers.array_write(d1, i, array=data_array)
            i = layers.increment(i)
            layers.array_write(d2, i, array=data_array)
            i = layers.zeros(shape=[1], dtype='int64')
            i.stop_gradient = True
            array_len = layers.fill_constant(shape=[1], dtype='int64', value=1)
            j = layers.fill_constant(shape=[1], dtype='int64', value=1)
            j.stop_gradient = True
            array_len2 = layers.fill_constant(shape=[1], dtype='int64', value=3)
349

350 351
            out = layers.while_loop(external_cond, external_body,
                                    [i, j, x, mem_array])
352

353
            sum_result = layers.array_read(array=mem_array, i=j)
354
            mean = paddle.mean(sum_result)
355
            append_backward(mean)
356

357 358
            place = fluid.CUDAPlace(
                0) if core.is_compiled_with_cuda() else fluid.CPUPlace()
359 360 361 362 363 364 365 366
            exe = fluid.Executor(place)

            d = []
            for i in range(3):
                d.append(np.random.random(size=[10]).astype('float32'))
            feed_x = np.ones(10).astype('float32')
            data_sum = d[0] + d[1] + d[2] + 3 * feed_x
            x_grad = [0.3] * 10
367 368 369 370 371 372 373 374
            res = exe.run(main_program,
                          feed={
                              'd0': d[0],
                              'd1': d[1],
                              'd2': d[2],
                              'x': feed_x
                          },
                          fetch_list=[sum_result.name, x.grad_name])
375 376
            self.assertTrue(np.allclose(res[0], data_sum))
            self.assertTrue(np.allclose(res[1], x_grad))
377 378 379


class TestApiWhileLoopWithSwitchCase(unittest.TestCase):
380

381
    def test_with_switch_case(self):
382

383 384 385 386
        def cond(i):
            return layers.less_than(i, ten)

        def body(i):
387

388 389 390 391 392 393 394 395 396 397 398 399
            def fn_add_three():
                data_add_three = layers.elementwise_add(x=i, y=three)
                return data_add_three

            def fn_square():
                data_mul_data = layers.elementwise_mul(x=i, y=i)
                return data_mul_data

            def fn_add_one():
                data_add_one = layers.elementwise_add(x=i, y=one)
                return data_add_one

400 401 402 403 404 405
            return layers.switch_case(branch_index=i,
                                      branch_fns={
                                          2: fn_add_three,
                                          5: fn_square
                                      },
                                      default=fn_add_one)
406 407 408 409 410 411 412 413 414 415

        main_program = Program()
        startup_program = Program()
        with fluid.program_guard(main_program, startup_program):
            i = layers.fill_constant(shape=[1], dtype='int64', value=1)
            ten = layers.fill_constant(shape=[1], dtype='int64', value=10)
            three = layers.fill_constant(shape=[1], dtype='int64', value=3)
            one = layers.fill_constant(shape=[1], dtype='int64', value=1)
            out = layers.while_loop(cond, body, [i])

416 417
        place = fluid.CUDAPlace(
            0) if core.is_compiled_with_cuda() else fluid.CPUPlace()
418 419 420 421 422
        exe = fluid.Executor(place)
        res = exe.run(main_program, fetch_list=out)

        data = np.asarray([25]).astype('int64')
        self.assertTrue(np.allclose(np.asarray(res[0]), data))
G
guofei 已提交
423 424 425


class TestApiWhileLoop_Error(unittest.TestCase):
426

G
guofei 已提交
427
    def test_error(self):
428

G
guofei 已提交
429 430 431 432 433 434 435 436 437 438 439 440
        def cond_returns_constant(i):
            return 1

        def cond_returns_not_bool_tensor(i):
            return layers.increment(i)

        def cond_returns_bool_tensor(i):
            return layers.less_than(i, ten)

        def cond_returns_2d_tensor(i):
            return layers.less_than(i, ten_2d)

441 442 443
        def cond_receives_two_args(i, ten):
            return layers.less_than(i, ten)

G
guofei 已提交
444 445 446
        def body(i):
            return layers.increment(i)

447 448 449 450 451 452 453
        def body_returns_error_length(i):
            i = layers.increment(i)
            return [i, i]

        def body_returns_error_type(i, ten):
            return layers.increment(i)

454 455 456 457
        def cond_returns_with_mutable_dict(i, test_dict):
            return i > 0

        def body_returns_with_mutable_dict(i, test_dict):
458 459 460
            test_dict['new_key'] = layers.fill_constant(shape=[1],
                                                        dtype='int64',
                                                        value=1)
461 462 463 464 465 466 467
            return layers.increment(i), test_dict

        def cond_returns_with_mutable_list(i, test_list):
            return i > 0

        def body_returns_with_mutable_list(i, test_list):
            test_list.append(
468
                layers.fill_constant(shape=[1], dtype='int64', value=1))
469 470
            return layers.increment(i), test_list

G
guofei 已提交
471 472 473 474 475 476 477 478 479
        main_program = Program()
        startup_program = Program()
        with program_guard(main_program, startup_program):
            data = layers.fill_constant(shape=[1], dtype='int64', value=1)
            data_1d = layers.fill_constant(shape=[1], dtype='int64', value=1)
            data_2d = layers.fill_constant(shape=[2, 2], dtype='int64', value=1)
            ten = layers.fill_constant(shape=[1], dtype='int64', value=10)
            ten_2d = layers.fill_constant(shape=[2, 2], dtype='int64', value=10)

480
            # The type of `cond` in Op(while_loop) must be callable
G
guofei 已提交
481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523
            def type_error_cond():
                out = layers.while_loop(data, body, [data_1d])

            self.assertRaises(TypeError, type_error_cond)

            # The type of `body` in Op(while_loop) must be callable
            def type_error_body():
                out = layers.while_loop(cond_returns_bool_tensor, data,
                                        [data_1d])

            self.assertRaises(TypeError, type_error_body)

            # The type of `loop_vars` in Op(while_loop) must be list or tuple
            def type_error_loop_vars():
                out = layers.while_loop(cond_returns_bool_tensor, body, data_1d)

            self.assertRaises(TypeError, type_error_loop_vars)

            # The value of `loop_vars` is empty
            def value_error_loop_vars():
                out = layers.while_loop(cond_returns_bool_tensor, body, [])

            self.assertRaises(ValueError, value_error_loop_vars)

            # The type of `cond` returns in Op(while_loop) must be Variable
            def type_error_cond_returns_not_variable():
                out = layers.while_loop(cond_returns_constant, body, [data_1d])

            self.assertRaises(TypeError, type_error_cond_returns_not_variable)

            # The type of `cond` returns in Op(while_loop) must be a bollean variable
            def type_error_cond_returns_not_boolean():
                out = layers.while_loop(cond_returns_not_bool_tensor, body,
                                        [data_1d])

            self.assertRaises(TypeError, type_error_cond_returns_not_boolean)

            # The shape of `cond` returns in Op(while_loop) must be 1
            def type_error_shape_cond_returns_2d():
                out = layers.while_loop(cond_returns_2d_tensor, body, [data_2d])

            self.assertRaises(TypeError, type_error_shape_cond_returns_2d)

524 525 526 527 528 529 530 531 532 533 534 535 536 537
            # The length of `body` returns in Op(while_loop) must be same as `loop_vars`
            def value_error_body_returns_error_length():
                out = layers.while_loop(cond_returns_bool_tensor,
                                        body_returns_error_length, [data])

            self.assertRaises(ValueError, value_error_body_returns_error_length)

            # The type of `body` returns in Op(while_loop) must be same as `loop_vars`
            def value_error_body_returns_error_type():
                out = layers.while_loop(cond_receives_two_args,
                                        body_returns_error_type, [data, ten])

            self.assertRaises(ValueError, value_error_body_returns_error_type)

538 539 540
            # The length of `output_vars` with mutable value should keep same with `loop_vars`
            def value_error_body_returns_with_mutable_dict():
                test_dict = {
541 542
                    "int_constant":
                    layers.fill_constant(shape=[2, 2], dtype='int64', value=1)
543 544 545 546 547 548 549 550 551 552
                }
                out = layers.while_loop(cond_returns_with_mutable_dict,
                                        body_returns_with_mutable_dict,
                                        [data, test_dict])

            self.assertRaises(ValueError,
                              value_error_body_returns_with_mutable_dict)

            def value_error_body_returns_with_mutable_list():
                test_list = [
553
                    layers.fill_constant(shape=[2, 2], dtype='int64', value=1)
554 555 556 557 558 559 560 561
                ]
                out = layers.while_loop(cond_returns_with_mutable_list,
                                        body_returns_with_mutable_list,
                                        [data, test_list])

            self.assertRaises(ValueError,
                              value_error_body_returns_with_mutable_list)

G
guofei 已提交
562

563
class TestApiWhileLoopSliceInBody(unittest.TestCase):
564

565
    def test_var_slice(self):
566

567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583
        def cond(z, i):
            return i + 1 <= x_shape[0]

        def body(z, i):
            z = z + x[i]
            i += 1
            return z, i

        main_program = Program()
        startup_program = Program()
        with program_guard(main_program, startup_program):
            x = fluid.layers.data(name='x', shape=[5], dtype='int32')
            z = fluid.layers.fill_constant([1], 'int32', 0)
            x_shape = fluid.layers.shape(x)
            i = fluid.layers.fill_constant([1], 'int32', 0)
            z, _ = fluid.layers.while_loop(cond, body, [z, i])

584 585
        place = fluid.CUDAPlace(
            0) if core.is_compiled_with_cuda() else fluid.CPUPlace()
586 587 588 589 590 591 592
        exe = fluid.Executor(place)

        np_x = np.array([1, 2, 3, 4, 5], dtype='int32')
        res = exe.run(main_program, feed={'x': np_x}, fetch_list=[z])
        self.assertTrue(np.array_equal(res[0], [np.sum(np_x)]))


G
guofei 已提交
593 594
if __name__ == '__main__':
    unittest.main()