test_tensor_shape.py 17.7 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 16
# to_tensor api will create 1 less op now, this test was changed

17 18
from __future__ import print_function

19
import numpy as np
20 21

import unittest
22
import paddle
23
import paddle.fluid as fluid
24
from paddle.fluid.dygraph.jit import declarative
25 26 27 28 29 30 31 32 33


def dyfunc_tensor_shape_1(x):
    x = fluid.dygraph.to_variable(x)
    res = fluid.layers.reshape(x, shape=x.shape)
    return res


def dyfunc_tensor_shape_2(x):
34
    x = paddle.to_tensor(x)
35 36
    shape = x.shape
    shape2 = shape
37
    res = paddle.reshape(x, shape2)
38 39 40 41
    return res


def dyfunc_tensor_shape_3(x):
42
    # Transform y.shape but run y.shape actually because y is not Tensor
43
    x = fluid.dygraph.to_variable(x)
44
    y = np.ones(5)
45 46 47 48 49 50 51 52 53 54 55 56
    res = fluid.layers.reshape(x, shape=y.shape)
    return res


def dyfunc_tensor_shape_4(x):
    x = fluid.dygraph.to_variable(x)
    res = fluid.layers.reshape(x, shape=(-1, x.shape[0], len(x.shape)))
    return res


def dyfunc_tensor_shape_5(x):
    # `res = fluid.layers.reshape(x, shape=(-1, s))` to
57
    # `res = fluid.layers.reshape(x, shape=(-1,
58
    #           paddle.jit.dy2static.convert_var_shape(x)[0]))`
59 60 61 62 63 64
    x = fluid.dygraph.to_variable(x)
    s = x.shape[0]
    res = fluid.layers.reshape(x, shape=(-1, s))
    return res


65 66 67 68 69 70 71 72 73 74
def dyfunc_tensor_shape_6(x):
    # `res = fluid.layers.reshape(x, shape=(-1, s))` to
    # `res = fluid.layers.reshape(x, shape=(-1,
    #           paddle.jit.dy2static.convert_var_shape(x)[0:]))`
    x = fluid.dygraph.to_variable(x)
    s = x.shape[0:]
    res = fluid.layers.reshape(x, shape=s)
    return res


75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
def dyfunc_tuple_shape_1(x):
    x = paddle.to_tensor(x)
    a, b = x.shape
    res = paddle.reshape(x, shape=(b, a))
    return res


def dyfunc_tuple_shape_2(x):
    x = paddle.to_tensor(x)
    shape = x.shape
    a, b = shape
    res = paddle.reshape(x, shape=(b, a))
    return res


90 91 92 93 94 95 96
def dyfunc_tuple_shape_3(x):
    x = paddle.to_tensor(x)
    a, b = paddle.shape(x)
    res = paddle.reshape(x, shape=(b, a))
    return res


97 98 99 100 101 102 103 104 105 106 107
def dyfunc_paddle_shape_api(x):
    x = paddle.to_tensor(x)
    # paddle.shape will not be converted.
    a = paddle.shape(x)[0]
    # alias api will also not be converted.
    alias_old_api = paddle.fluid.layers
    b = alias_old_api.shape(x)[1]
    res = paddle.reshape(x, shape=(b, a))
    return res


108 109 110 111 112
def dyfunc_with_if_1(x):
    x = fluid.dygraph.to_variable(x)
    res = fluid.layers.reshape(x, [-1, 1])
    x_shape_0 = x.shape[0]
    if x_shape_0 < 1:
113
        # `res.shape[0]` is transformed into
114
        #   `paddle.jit.dy2static.convert_var_shape(res)[0]`
115
        if res.shape[0] > 1:
116 117 118
            res = fluid.layers.fill_constant(value=2,
                                             shape=x.shape,
                                             dtype="int32")
119
        else:
120 121 122
            res = fluid.layers.fill_constant(value=3,
                                             shape=x.shape,
                                             dtype="int32")
123 124 125 126 127
    return res


def dyfunc_with_if_2(x):
    x = fluid.dygraph.to_variable(x)
128
    # `len(x.shape)` will not be transformed because x.shape is not used by Paddle api.
129 130 131 132 133 134 135 136 137 138 139
    if len(x.shape) < 1:
        res = x
    else:
        res = fluid.layers.fill_constant(value=8, shape=x.shape, dtype="int32")

    return res


def dyfunc_with_for_1(x):
    x = fluid.dygraph.to_variable(x)
    res = fluid.layers.fill_constant(value=0, shape=[1], dtype="int32")
140
    # `x.shape[0]` is transformed into `paddle.jit.dy2static.convert_var_shape(x)[0]`
141 142 143 144 145 146 147 148 149 150
    for i in range(x.shape[0]):
        res += 1
    return res


def dyfunc_with_for_2(x):
    x = fluid.dygraph.to_variable(x)
    x_shape_0 = x.shape[0]
    res = fluid.layers.fill_constant(value=0, shape=[1], dtype="int32")

151
    # `x_shape_0` is transformed into `paddle.jit.dy2static.convert_var_shape(x)[0]`
152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
    for i in range(x_shape_0):
        res += 1
    return res


def dyfunc_with_for_3(x):
    x = fluid.dygraph.to_variable(x)
    res = fluid.layers.fill_constant(value=0, shape=[1], dtype="int32")
    # `len(x.shape)` is not transformed.
    for i in range(len(x.shape)):
        res += 1

    return res


def dyfunc_with_while_1(x):
    x = fluid.dygraph.to_variable(x)
    res = fluid.layers.fill_constant(value=0, shape=[1], dtype="int32")
170
    # `x.shape[0]` is transformed into `paddle.jit.dy2static.convert_var_shape(x)[0]`
171 172 173 174 175 176 177 178 179 180 181 182
    i = 1
    while i < x.shape[0]:
        res += 1
        i = i + 2
    return res


def dyfunc_with_while_2(x):
    x = fluid.dygraph.to_variable(x)
    x_shape_0 = x.shape[0]
    res = fluid.layers.fill_constant(value=0, shape=[1], dtype="int32")
    i = 1
183
    # `x_shape_0` is transformed into `paddle.jit.dy2static.convert_var_shape(x)[0]`
184
    while i < x_shape_0:
185 186 187
        res += 1
        i = i + 2
    return res
188 189


190 191 192 193 194 195 196 197 198 199 200 201 202
def dyfunc_with_while_3(x):
    x = fluid.dygraph.to_variable(x)
    x_shape = x.shape
    res = fluid.layers.fill_constant(value=0, shape=[1], dtype="int32")
    i = 1

    # `len(x.shape)` is not transformed.
    while len(x_shape) > i:
        res += 1
        i += 1
    return res


203
def dyfunc_with_while_4(x):
204
    x = paddle.to_tensor(x)
205
    y = np.ones(5)
206 207 208 209 210 211 212 213 214 215
    y_shape_0 = y.shape[0]
    i = 1

    # Transform y_shape_0 but run y.shape[0] actually because y is not Tensor
    while y_shape_0 > i:
        x += 1
        i += 1
    return x


216 217 218 219 220 221 222 223
def dyfunc_change_shape_after_assign(x):
    x = paddle.to_tensor(x)
    a, b = x.shape
    x = paddle.reshape(x, shape=(-1, 1))
    res = paddle.reshape(x, shape=(b, a))
    return res


224 225 226 227 228 229
def dyfunc_len_paddle_shape():
    x = paddle.to_tensor([1, 2, 3])
    if len(paddle.shape(x)) > 0:
        print(x)


230 231 232 233 234 235
def dyfunc_dict_assign_shape():
    x = paddle.to_tensor([1, 2])
    a = {}
    a['shape'] = x.shape[0]


236 237
# 1. Basic tests without control flow
class TestTensorShapeBasic(unittest.TestCase):
238

239
    def setUp(self):
240
        self.input = np.ones(5).astype("int32")
241 242
        self.place = fluid.CUDAPlace(
            0) if fluid.is_compiled_with_cuda() else fluid.CPUPlace()
243 244
        self._set_input_spec()
        self._set_expected_op_num()
245 246 247 248
        self.init_test_func()

    def init_test_func(self):
        self.dygraph_func = dyfunc_tensor_shape_1
249

250 251 252
    def _set_input_spec(self):
        self.input_spec = [paddle.static.InputSpec(shape=[5], dtype="int32")]

253
    def _run(self, to_static):
254
        with fluid.dygraph.guard():
255 256 257 258
            if to_static:
                res = declarative(self.dygraph_func)(self.input).numpy()
            else:
                res = self.dygraph_func(self.input).numpy()
259 260
            return res

261 262
    def get_dygraph_output(self):
        return self._run(to_static=False)
263

264
    def get_static_output(self):
265
        return self._run(to_static=True)
266 267

    def test_transformed_static_result(self):
268 269
        static_res = self.get_static_output()
        dygraph_res = self.get_dygraph_output()
270
        np.testing.assert_allclose(dygraph_res, static_res, rtol=1e-05)
271

272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295
    def _set_expected_op_num(self):
        self.expected_op_num = 2
        self.expected_shape_op_num = 0
        self.expected_slice_op_num = 0

    def _compute_op_num(self, program):
        self.op_num = sum([len(block.ops) for block in program.blocks])
        self.shape_op_num = 0
        self.slice_op_num = 0

        for block in program.blocks:
            self.shape_op_num += len(
                [op for op in block.ops if op.type == "shape"])
            self.slice_op_num += len(
                [op for op in block.ops if op.type == "slice"])

    def test_op_num(self):
        static_layer = paddle.jit.to_static(self.dygraph_func, self.input_spec)
        program = static_layer.main_program
        self._compute_op_num(program)
        self.assertEqual(self.op_num, self.expected_op_num)
        self.assertEqual(self.shape_op_num, self.expected_shape_op_num)
        self.assertEqual(self.slice_op_num, self.expected_slice_op_num)

296 297

class TestTensorShapeBasic2(TestTensorShapeBasic):
298

299 300 301
    def init_test_func(self):
        self.dygraph_func = dyfunc_tensor_shape_2

302
    def _set_expected_op_num(self):
303
        self.expected_op_num = 1
304
        self.expected_shape_op_num = 0
305 306
        self.expected_slice_op_num = 0

307 308

class TestTensorShapeBasic3(TestTensorShapeBasic):
309

310 311 312 313 314
    def init_test_func(self):
        self.dygraph_func = dyfunc_tensor_shape_3


class TestTensorShapeBasic4(TestTensorShapeBasic):
315

316 317 318 319 320
    def init_test_func(self):
        self.dygraph_func = dyfunc_tensor_shape_4


class TestTensorShapeBasic5(TestTensorShapeBasic):
321

322 323 324
    def init_test_func(self):
        self.dygraph_func = dyfunc_tensor_shape_5

325
    def _set_expected_op_num(self):
326 327 328
        self.expected_op_num = 2
        self.expected_shape_op_num = 0
        self.expected_slice_op_num = 0
329

330

331
class TestTensorShapeBasic6(TestTensorShapeBasic):
332

333 334 335
    def init_test_func(self):
        self.dygraph_func = dyfunc_tensor_shape_6

336
    def _set_expected_op_num(self):
337 338 339
        self.expected_op_num = 2
        self.expected_shape_op_num = 0
        self.expected_slice_op_num = 0
340

341

342
class TestTupleShape1(TestTensorShapeBasic):
343

344
    def init_test_func(self):
345
        self.input = np.ones((5, 7)).astype("int32")
346 347 348
        self.input_spec = [
            paddle.static.InputSpec(shape=[-1, -1], dtype="int32")
        ]
349 350
        self.dygraph_func = dyfunc_tuple_shape_1

351
    def _set_expected_op_num(self):
352
        self.expected_op_num = 4
353
        self.expected_shape_op_num = 1
354 355
        self.expected_slice_op_num = 2

356 357

class TestTupleShape2(TestTensorShapeBasic):
358

359
    def init_test_func(self):
360
        self.input = np.ones((5, 7)).astype("int32")
361 362 363
        self.input_spec = [
            paddle.static.InputSpec(shape=[-1, 7], dtype="int32")
        ]
364
        self.dygraph_func = dyfunc_tuple_shape_2
365 366

    def _set_expected_op_num(self):
367
        self.expected_op_num = 4
368
        self.expected_shape_op_num = 1
369
        self.expected_slice_op_num = 1
370 371 372


class TestTupleShape3(TestTensorShapeBasic):
373

374
    def init_test_func(self):
375
        self.input = np.ones((5, 7)).astype("int32")
376 377
        self.input_spec = [paddle.static.InputSpec(shape=[5, 7], dtype="int32")]
        self.dygraph_func = dyfunc_tuple_shape_3
378

379
    def _set_expected_op_num(self):
380
        self.expected_op_num = 4
381 382 383
        self.expected_shape_op_num = 1
        self.expected_slice_op_num = 2

384

385
class TestPaddleShapeApi(TestTensorShapeBasic):
386

387
    def init_test_func(self):
388
        self.input = np.ones((5, 7)).astype("int32")
389 390 391 392
        self.input_spec = [paddle.static.InputSpec(shape=[5, 7], dtype="int32")]
        self.dygraph_func = dyfunc_paddle_shape_api

    def _set_expected_op_num(self):
393
        self.expected_op_num = 5
394 395 396 397
        self.expected_shape_op_num = 2
        self.expected_slice_op_num = 2


398 399
# 2. Tests with control flow if
class TestTensorShapeInIf1(TestTensorShapeBasic):
400

401 402 403
    def init_test_func(self):
        self.dygraph_func = dyfunc_with_if_1

404
    def _set_expected_op_num(self):
405 406 407
        self.expected_op_num = 2
        self.expected_shape_op_num = 0
        self.expected_slice_op_num = 0
408

409 410

class TestTensorShapeInIf2(TestTensorShapeBasic):
411

412 413 414
    def init_test_func(self):
        self.dygraph_func = dyfunc_with_if_2

415
    def _set_expected_op_num(self):
416 417 418
        self.expected_op_num = 2
        self.expected_shape_op_num = 0
        self.expected_slice_op_num = 0
419

420 421 422

# 3. Tests with control flow for loop
class TestTensorShapeInFor1(TestTensorShapeBasic):
423

424 425 426
    def init_test_func(self):
        self.dygraph_func = dyfunc_with_for_1

427
    def _set_expected_op_num(self):
428 429 430
        self.expected_op_num = 7
        self.expected_shape_op_num = 0
        self.expected_slice_op_num = 0
431

432

433
class TestTensorShapeInFor2(TestTensorShapeInFor1):
434

435 436 437
    def init_test_func(self):
        self.dygraph_func = dyfunc_with_for_2

438
    def _set_expected_op_num(self):
439 440 441
        self.expected_op_num = 7
        self.expected_shape_op_num = 0
        self.expected_slice_op_num = 0
442

443

444
class TestTensorShapeInFor3(TestTensorShapeInFor1):
445

446 447 448 449
    def init_test_func(self):
        self.dygraph_func = dyfunc_with_for_3

    def _set_expected_op_num(self):
450 451 452
        self.expected_op_num = 3
        self.expected_shape_op_num = 0
        self.expected_slice_op_num = 0
453 454


455
# 4. Tests with control flow while loop
456
class TestTensorShapeInWhile1(TestTensorShapeInFor1):
457

458 459 460
    def init_test_func(self):
        self.dygraph_func = dyfunc_with_while_1

461 462 463 464 465
    def _set_expected_op_num(self):
        self.expected_op_num = 4
        self.expected_shape_op_num = 0
        self.expected_slice_op_num = 0

466

467
class TestTensorShapeInWhile2(TestTensorShapeInFor1):
468

469 470
    def init_test_func(self):
        self.dygraph_func = dyfunc_with_while_2
471

472
    def _set_expected_op_num(self):
473 474 475
        self.expected_op_num = 4
        self.expected_shape_op_num = 0
        self.expected_slice_op_num = 0
476

477

478
class TestTensorShapeInWhile3(TestTensorShapeBasic):
479

480 481 482
    def init_test_func(self):
        self.dygraph_func = dyfunc_with_while_3

483
    def _set_expected_op_num(self):
484 485
        self.expected_op_num = 2
        self.expected_shape_op_num = 0
486
        self.expected_slice_op_num = 0
487

488 489

class TestTensorShapeInWhile4(TestTensorShapeBasic):
490

491 492 493
    def init_test_func(self):
        self.dygraph_func = dyfunc_with_while_4

494
    def _set_expected_op_num(self):
495
        self.expected_op_num = 4
496 497 498 499 500 501
        self.expected_shape_op_num = 0
        self.expected_slice_op_num = 0


# 5. Test op num for negetive dim
class TestOpNumBasicWithTensorShape(unittest.TestCase):
502

503 504 505 506 507 508 509
    def setUp(self):
        self._set_input_spec()
        self._set_test_func()
        self._set_expected_op_num()

    def _set_input_spec(self):
        self.input_spec = [
510
            paddle.static.InputSpec(shape=[-1, 5], dtype="int32")
511 512 513 514 515 516
        ]

    def _set_test_func(self):
        self.dygraph_func = dyfunc_tensor_shape_1

    def _set_expected_op_num(self):
517
        self.expected_op_num = 5
518
        self.expected_shape_op_num = 1
519
        self.expected_slice_op_num = 1
520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542

    def _compute_op_num(self, program):
        self.op_num = sum([len(block.ops) for block in program.blocks])
        self.shape_op_num = 0
        self.slice_op_num = 0

        for block in program.blocks:
            self.shape_op_num += len(
                [op for op in block.ops if op.type == "shape"])
            self.slice_op_num += len(
                [op for op in block.ops if op.type == "slice"])

    def test_op_num(self):
        static_layer = paddle.jit.to_static(self.dygraph_func, self.input_spec)
        program = static_layer.main_program

        self._compute_op_num(program)
        self.assertEqual(self.op_num, self.expected_op_num)
        self.assertEqual(self.shape_op_num, self.expected_shape_op_num)
        self.assertEqual(self.slice_op_num, self.expected_slice_op_num)


class TestOpNumBasicWithTensorShape4(TestOpNumBasicWithTensorShape):
543

544 545 546 547
    def _set_test_func(self):
        self.dygraph_func = dyfunc_tensor_shape_4

    def _set_expected_op_num(self):
548 549 550
        self.expected_op_num = 8
        self.expected_shape_op_num = 2
        self.expected_slice_op_num = 2
551 552 553


class TestOpNumWithTensorShapeTuple1(TestOpNumBasicWithTensorShape):
554

555 556 557 558
    def _set_test_func(self):
        self.dygraph_func = dyfunc_tuple_shape_1

    def _set_expected_op_num(self):
559
        self.expected_op_num = 4
560 561
        self.expected_shape_op_num = 1
        self.expected_slice_op_num = 1
562 563 564


class TestOpNumWithTensorShapeInIf1(TestOpNumBasicWithTensorShape):
565

566 567 568 569
    def _set_test_func(self):
        self.dygraph_func = dyfunc_with_if_1

    def _set_expected_op_num(self):
570
        self.expected_op_num = 32
571
        self.expected_shape_op_num = 4
572
        self.expected_slice_op_num = 4
573 574 575


class TestOpNumWithTensorShapeInFor1(TestOpNumBasicWithTensorShape):
576

577 578 579 580
    def _set_test_func(self):
        self.dygraph_func = dyfunc_with_for_1

    def _set_expected_op_num(self):
581 582
        self.expected_op_num = 29
        self.expected_shape_op_num = 2
583 584 585 586
        self.expected_slice_op_num = 3


class TestOpNumWithTensorShapeInWhile1(TestOpNumBasicWithTensorShape):
587

588 589 590 591
    def _set_test_func(self):
        self.dygraph_func = dyfunc_with_while_1

    def _set_expected_op_num(self):
592
        self.expected_op_num = 21
593 594 595
        self.expected_shape_op_num = 3
        self.expected_slice_op_num = 3

596

597
class TestChangeShapeAfterAssign(TestTensorShapeBasic):
598

599
    def init_test_func(self):
600
        self.input = np.ones((2, 3)).astype("int32")
601 602 603
        self.input_spec = [
            paddle.static.InputSpec(shape=[-1, 3], dtype="int32")
        ]
604 605 606
        self.dygraph_func = dyfunc_change_shape_after_assign

    def _set_expected_op_num(self):
607
        self.expected_op_num = 5
608 609
        self.expected_shape_op_num = 1
        self.expected_slice_op_num = 1
610 611


612 613 614 615 616 617 618 619
def dyfunc_with_static_convert_var_shape(x):
    # Note: this will create `batch_size__static_convert_var_shape_suffix_0` firstly.
    batch_size = x.shape[0]
    if len(x.shape) < 1:
        res = x
    else:
        # Test for correctly to find `batch_size__static_convert_var_shape_suffix_0` in
        # deeply nested scope.
620 621 622
        res = fluid.layers.fill_constant(value=8,
                                         shape=[batch_size],
                                         dtype="int32")
623 624 625 626 627

    return res


class TestFindStatiConvertVarShapeSuffixVar(unittest.TestCase):
628

629 630 631 632 633 634 635
    def test(self):
        x_spec = paddle.static.InputSpec(shape=[None, 10])
        func = paddle.jit.to_static(dyfunc_with_if_2, input_spec=[x_spec])
        # Call this function to trigger program translation.
        func.concrete_program


636 637
if __name__ == '__main__':
    unittest.main()