480 lines
12 KiB
Python
480 lines
12 KiB
Python
# 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.
|
|
|
|
import paddle
|
|
|
|
|
|
def add_fn(x):
|
|
x = x + 1
|
|
return x
|
|
|
|
|
|
def loss_fn(x, label):
|
|
loss = paddle.nn.functional.cross_entropy(
|
|
x, label, reduction='none', use_softmax=False
|
|
)
|
|
return loss
|
|
|
|
|
|
def dyfunc_empty_nonlocal(x):
|
|
flag = True
|
|
if flag:
|
|
print("It's a test for empty nonlocal stmt")
|
|
|
|
if paddle.mean(x) < 0:
|
|
x + 1
|
|
|
|
out = x * 2
|
|
return out
|
|
|
|
|
|
def dyfunc_with_if_else(x_v, label=None):
|
|
if paddle.mean(x_v).numpy() > 5:
|
|
x_v = x_v - 1
|
|
else:
|
|
x_v = x_v + 1
|
|
# plain if in python
|
|
if label is not None:
|
|
loss = paddle.nn.functional.cross_entropy(
|
|
x_v, label, reduction='none', use_softmax=False
|
|
)
|
|
return loss
|
|
return x_v
|
|
|
|
|
|
def dyfunc_with_if_else2(x, col=100):
|
|
row = 0
|
|
if abs(col) > x.shape[-1]:
|
|
# TODO: Don't support return non-Tensor in Tensor-dependent `if` statement currently.
|
|
# `x` is Tensor, `col` is not Tensor, and `col` is the return value of `true_fn` after transformed.
|
|
# col = -1
|
|
col = paddle.tensor.fill_constant(shape=[], value=-1, dtype="int64")
|
|
else:
|
|
col = paddle.tensor.fill_constant(shape=[], value=1, dtype="int64")
|
|
if paddle.mean(x).numpy() > x.numpy()[row][col]:
|
|
x_pow = paddle.pow(x, 2)
|
|
y = paddle.nn.functional.relu(x_pow)
|
|
else:
|
|
x_pow = paddle.pow(x, 2)
|
|
y = paddle.tanh(x_pow)
|
|
return y
|
|
|
|
|
|
def dyfunc_with_if_else3(x):
|
|
# Create new var in parent scope, return it in true_fn and false_fn.
|
|
# The var is created only in one of If.body or If.orelse node, and it used as gast.Load firstly after gast.If node.
|
|
# The transformed code:
|
|
"""
|
|
q = paddle.jit.dy2static.UndefinedVar('q')
|
|
z = paddle.jit.dy2static.UndefinedVar('z')
|
|
|
|
def true_fn_0(q, x, y):
|
|
x = x + 1
|
|
z = x + 2
|
|
q = x + 3
|
|
return q, x, y, z
|
|
|
|
def false_fn_0(q, x, y):
|
|
y = y + 1
|
|
z = x - 2
|
|
m = x + 2
|
|
n = x + 3
|
|
return q, x, y, z
|
|
q, x, y, z = paddle.static.nn.cond(paddle.mean(x) < 5, lambda :
|
|
paddle.jit.dy2static.convert_call(true_fn_0)(q, x, y),
|
|
lambda : paddle.jit.dy2static.convert_call(false_fn_0)(q,
|
|
x, y))
|
|
"""
|
|
y = x + 1
|
|
# NOTE: x_v[0] < 5 is True
|
|
if paddle.mean(x).numpy() < 5:
|
|
x = x + 1
|
|
z = x + 2
|
|
q = x + 3
|
|
m = x + 2
|
|
n = x + 3
|
|
else:
|
|
y = y + 1
|
|
z = x - 2
|
|
q = x + 3
|
|
m = x + 2
|
|
n = x + 3
|
|
|
|
q = q + 1
|
|
n = q + 2
|
|
x = n
|
|
return x
|
|
|
|
|
|
def dyfunc_with_if_else_early_return1():
|
|
x = paddle.to_tensor([10])
|
|
if x == 0:
|
|
a = paddle.zeros([2, 2])
|
|
b = paddle.zeros([3, 3])
|
|
return a, b
|
|
a = paddle.zeros([2, 2]) + 1
|
|
return a, paddle.zeros([3, 3]) + 1
|
|
|
|
|
|
def dyfunc_with_if_else_early_return2():
|
|
x = paddle.to_tensor([10])
|
|
if x == 0:
|
|
a = paddle.zeros([2, 2])
|
|
b = paddle.zeros([3, 3])
|
|
return a, b
|
|
elif x == 1:
|
|
c = paddle.zeros([2, 2]) + 1
|
|
d = paddle.zeros([3, 3]) + 1
|
|
return c, d
|
|
e = paddle.zeros([2, 2]) + 3
|
|
return e, paddle.zeros([3, 3]) + 3
|
|
|
|
|
|
def dyfunc_with_if_else_with_list_generator(x):
|
|
if 10 > 5:
|
|
y = paddle.add_n(
|
|
[paddle.full(shape=[2], fill_value=v) for v in range(5)]
|
|
)
|
|
else:
|
|
y = x
|
|
return y
|
|
|
|
|
|
def nested_if_else(x_v):
|
|
batch_size = 16
|
|
feat_size = x_v.shape[-1]
|
|
bias = paddle.tensor.fill_constant([feat_size], dtype='float32', value=1)
|
|
if x_v.shape[0] != batch_size:
|
|
# TODO: Don't support return non-Tensor in Tensor-dependent `if` statement currently.
|
|
# `x_v.shape[0]` is not Tensor, and `batch_size` is the return value of `true_fn` after transformed.
|
|
# col = -1
|
|
# batch_size = x_v.shape[0]
|
|
batch_size = paddle.shape(x_v)[0]
|
|
|
|
# if tensor.shape is [1], now support to compare with numpy.
|
|
if paddle.mean(x_v).numpy() < 0:
|
|
y = x_v + bias
|
|
w = paddle.tensor.fill_constant([feat_size], dtype='float32', value=10)
|
|
if y.numpy()[0] < 10:
|
|
tmp = y * w
|
|
y = paddle.nn.functional.relu(tmp)
|
|
if paddle.mean(y).numpy() < batch_size:
|
|
tmp = paddle.tensor.fill_constant(
|
|
y.shape, dtype='float32', value=-1
|
|
)
|
|
y = paddle.abs(y)
|
|
else:
|
|
tmp = paddle.tensor.fill_constant(
|
|
y.shape, dtype='float32', value=-1
|
|
)
|
|
y = y - tmp
|
|
else:
|
|
tmp = y * w
|
|
y = paddle.nn.functional.relu(tmp)
|
|
if paddle.mean(y).numpy() < batch_size:
|
|
tmp = paddle.tensor.fill_constant(
|
|
y.shape, dtype='float32', value=-1
|
|
)
|
|
y = paddle.abs(y)
|
|
else:
|
|
tmp = paddle.tensor.fill_constant(
|
|
y.shape, dtype='float32', value=-1
|
|
)
|
|
y = y - tmp
|
|
else:
|
|
y = x_v - bias
|
|
w = paddle.tensor.fill_constant([feat_size], dtype='float32', value=10)
|
|
tmp = y * w
|
|
y = paddle.nn.functional.relu(tmp)
|
|
tmp = paddle.tensor.fill_constant(y.shape, dtype='float32', value=-1)
|
|
y = paddle.abs(y)
|
|
return y
|
|
|
|
|
|
def nested_if_else_2(x):
|
|
y = paddle.reshape(x, [-1, 1])
|
|
b = 2
|
|
if b < 1:
|
|
# var `z` is not visible for outer scope
|
|
z = y
|
|
x_shape_0 = x.shape[0]
|
|
if x_shape_0 < 1:
|
|
if paddle.shape(y).numpy()[0] < 1:
|
|
res = paddle.tensor.fill_constant(
|
|
value=2, shape=x.shape, dtype="int32"
|
|
)
|
|
# `z` is a new var here.
|
|
z = y + 1
|
|
else:
|
|
res = paddle.tensor.fill_constant(
|
|
value=3, shape=x.shape, dtype="int32"
|
|
)
|
|
else:
|
|
res = x
|
|
return res
|
|
|
|
|
|
def nested_if_else_3(x):
|
|
y = paddle.reshape(x, [-1, 1])
|
|
b = 2
|
|
# var `z` is visible for func.body
|
|
if b < 1:
|
|
z = y
|
|
else:
|
|
z = x
|
|
|
|
if b < 1:
|
|
res = x
|
|
# var `out` is only visible for current `if`
|
|
if b > 1:
|
|
out = x + 1
|
|
else:
|
|
out = x - 1
|
|
else:
|
|
y_shape = paddle.shape(y)
|
|
if y_shape.numpy()[0] < 1:
|
|
res = paddle.tensor.fill_constant(
|
|
value=2, shape=x.shape, dtype="int32"
|
|
)
|
|
# `z` is created in above code block.
|
|
z = y + 1
|
|
out = x - 1
|
|
else:
|
|
res = paddle.tensor.fill_constant(
|
|
value=3, shape=x.shape, dtype="int32"
|
|
)
|
|
# `out` is a new var.
|
|
out = x + 1
|
|
z = y - 1
|
|
return res
|
|
|
|
|
|
class NetWithControlFlowIf(paddle.nn.Layer):
|
|
def __init__(self, hidden_dim=16):
|
|
super().__init__()
|
|
self.hidden_dim = hidden_dim
|
|
self.fc = paddle.nn.Linear(
|
|
in_features=hidden_dim,
|
|
out_features=5,
|
|
weight_attr=paddle.ParamAttr(
|
|
initializer=paddle.nn.initializer.Constant(value=0.99)
|
|
),
|
|
bias_attr=paddle.ParamAttr(
|
|
initializer=paddle.nn.initializer.Constant(value=0.5)
|
|
),
|
|
)
|
|
self.alpha = 10.0
|
|
self.constant_vars = {}
|
|
|
|
def forward(self, input):
|
|
hidden_dim = input.shape[-1]
|
|
if hidden_dim != self.hidden_dim:
|
|
raise ValueError(
|
|
f"hidden_dim {hidden_dim} of input is not equal to FC.weight[0]: {self.hidden_dim}"
|
|
)
|
|
|
|
self.constant_vars['bias'] = paddle.tensor.fill_constant(
|
|
[5], dtype='float32', value=1
|
|
)
|
|
# Control flow `if` statement
|
|
fc_out = self.fc(input)
|
|
if paddle.mean(fc_out).numpy() < 0:
|
|
y = fc_out + self.constant_vars['bias']
|
|
self.constant_vars['w'] = paddle.tensor.fill_constant(
|
|
[5], dtype='float32', value=10
|
|
)
|
|
if y.numpy()[0] < self.alpha:
|
|
# Create new var, but is not used.
|
|
x = 10
|
|
tmp = y * self.constant_vars['w']
|
|
y = paddle.nn.functional.relu(tmp)
|
|
# Nested `if/else`
|
|
if y.numpy()[-1] < self.alpha:
|
|
# Modify variable of class
|
|
self.constant_vars['w'] = paddle.tensor.fill_constant(
|
|
[hidden_dim], dtype='float32', value=9
|
|
)
|
|
y = paddle.abs(y)
|
|
else:
|
|
tmp = paddle.tensor.fill_constant(
|
|
y.shape, dtype='float32', value=-1
|
|
)
|
|
y = y - tmp
|
|
else:
|
|
y = fc_out - self.constant_vars['bias']
|
|
|
|
loss = paddle.mean(y)
|
|
return loss
|
|
|
|
|
|
def if_with_and_or(x_v, label=None):
|
|
batch_size = paddle.shape(x_v)
|
|
if (
|
|
x_v is not None
|
|
and (paddle.mean(x_v).numpy() > 0 or label is not None)
|
|
and batch_size[0] > 1
|
|
and True
|
|
):
|
|
x_v = x_v - 1
|
|
else:
|
|
x_v = x_v + 1
|
|
|
|
if label is not None:
|
|
loss = paddle.nn.functional.cross_entropy(
|
|
x_v, label, reduction='none', use_softmax=False
|
|
)
|
|
return loss
|
|
return x_v
|
|
|
|
|
|
def if_with_and_or_1(x, y=None):
|
|
batch_size = paddle.shape(x)
|
|
if batch_size[0] > 1 and y is not None:
|
|
x = x + 1
|
|
if y is not None or batch_size[0] > 1:
|
|
x = x - 1
|
|
return x
|
|
|
|
|
|
def if_with_and_or_2(x, y=None):
|
|
batch_size = paddle.shape(x)
|
|
if x is not None and batch_size[0] > 1 and y is not None:
|
|
x = x + 1
|
|
if batch_size[0] > 1 or y is not None or x is not None:
|
|
x = x - 1
|
|
return x
|
|
|
|
|
|
def if_with_and_or_3(x, y=None):
|
|
batch_size = paddle.shape(x)
|
|
mean_res = paddle.mean(x)
|
|
if (
|
|
x is not None
|
|
and batch_size[0] > 1
|
|
and y is not None
|
|
and mean_res.numpy() > 0
|
|
):
|
|
x = x + 1
|
|
if mean_res.numpy() > 0 and (x is not None and batch_size[0] > 1) and y:
|
|
x = x - 1
|
|
return x
|
|
|
|
|
|
def if_with_and_or_4(x, y=None):
|
|
batch_size = paddle.shape(x)
|
|
mean_res = paddle.mean(x)
|
|
if (x is not None and batch_size[0] > 1) or (
|
|
y is not None and mean_res.numpy() > 0
|
|
):
|
|
x = x + 1
|
|
if (x is not None or batch_size[0] > 1) and (
|
|
y is not None or mean_res.numpy() > 0
|
|
):
|
|
x = x - 1
|
|
return x
|
|
|
|
|
|
def if_with_class_var(x, y=None):
|
|
class Foo:
|
|
def __init__(self):
|
|
self.a = 1
|
|
self.b = 2
|
|
|
|
foo = Foo()
|
|
batch_size = paddle.shape(x)
|
|
mean_res = paddle.mean(x)
|
|
|
|
if batch_size[0] > foo.a:
|
|
x = x + foo.b
|
|
else:
|
|
x = x - foo.b
|
|
return x
|
|
|
|
|
|
def if_tensor_case(x):
|
|
x = paddle.assign(x)
|
|
mean = paddle.mean(x)
|
|
# It is equivalent to `if mean != 0`
|
|
if mean:
|
|
for i in range(0, 10):
|
|
if i > 5:
|
|
x += 1
|
|
break
|
|
x += 1
|
|
else:
|
|
for i in range(0, 37):
|
|
x += 1
|
|
break
|
|
x += i
|
|
|
|
# join `and`/`or`
|
|
if paddle.mean(x) + 1 and mean > 1 and x is not None or 2 > 1:
|
|
x -= 1
|
|
|
|
# `not` statement
|
|
if not (x[0][0] and (mean * x)[0][0]):
|
|
x += 1
|
|
|
|
return x
|
|
|
|
|
|
def dyfunc_ifelse_ret_int1(x):
|
|
index = 0
|
|
pred = paddle.to_tensor([1])
|
|
if pred:
|
|
y = x[index] + 1
|
|
index = index + 1
|
|
return y, index
|
|
else:
|
|
y = x[index] + 2
|
|
index = index + 1
|
|
return y, index
|
|
|
|
|
|
def dyfunc_ifelse_ret_int2(x):
|
|
index = 0
|
|
pred = paddle.to_tensor([1])
|
|
if pred:
|
|
y = x[index] + 1
|
|
index = index + 1
|
|
return y, index
|
|
else:
|
|
y = x[index] + 2
|
|
index = index + 1
|
|
return y
|
|
|
|
|
|
def dyfunc_ifelse_ret_int3(x):
|
|
index = 0
|
|
pred = paddle.to_tensor([1])
|
|
if pred:
|
|
y = x[index] + 1
|
|
index = index + 1
|
|
return index
|
|
else:
|
|
y = x[index] + 2
|
|
return y
|
|
|
|
|
|
def dyfunc_ifelse_ret_int4(x):
|
|
index = 0
|
|
pred = paddle.to_tensor([1])
|
|
if pred:
|
|
y = x[index] + 1
|
|
index = index + 1
|
|
return 'unsupported ret'
|
|
else:
|
|
y = x[index] + 2
|
|
return y
|