Supported Grammars¶
Abstract¶
In this section we will talk about the supported grammars and unsupported grammars, also give some suggestions when the grammar is unsupported.
This article is mainly for the following scene :
Not sure whether the dynamic graph model can be converted into a static graph correctly.
There was a problem in the conversion process but don’t know how to troubleshoot.
How to modify the source code to adapt dynamictostatic grammar when there is an unsupported grammar
If you are new to the dynamictostatic module, or are not familiar with this function, you are recommended to read Introduction to dynamic and static documents ;
If you want to export model for prediction, or want to learn more about this module, you are recommended to read: Predictive Model Export Tutorial ;
If you encounter problems with @to_static, or want to learn about debugging skills, you are recommended to read Error Debugging Experience.
Supported Grammars¶
category  python grammar  is it supported ?  summary 

Control flow keywords  ifelse  Yes  Adaptively recognize and convert to cond Op, or keep python ifelse 
while  Yes  Adaptively recognize and convert to While Op, or keep python ifelse  
for  Yes  Support iterative access to Tensor by for _ in x 

break continue 
Yes  Support putting break and continue at any position inside a loop 

return  Yes  Support putting return inside a loop 

operators  +  * / ** > < >= <= == 
Yes  Support by operator overloading 
and, or, not  Yes  1.if both are tensors, convert to static graph. 2.if neither is tensor, degenerate to python operator. 3. If only one is tensor, degenerate to python operator, but the results is correct. 

Type conversion operator  Yes  Convert to paddle.cast if operand is tensor 

Paddle shape  Tensor.shape()  Partial support  Support obtaining shape information, but there may be 1 
python functions  print(x)  Yes  Convert to PrintOp if operand is tensor 
len()  Yes  Return the shape[0] if operand is tensor. 

lambda expression  Yes  Use convert_call to convert lambda function 

function call  Yes  Use convert_call to convert function recursively 

recursively call  No  While converting, converted function can't get value from static tensor  
list sort  No  Lists will be converted to TensorArray and TensorArray don't support sort 

Errors and Exceptions  assert  Yes  Convert to Assert if operand is tensor 
Python containers  list  Partial support  Lists will be converted to TensorArray . Only append , pop , index is supported 
dict  Yes  @to_static will add the Tensors in a dict into PaddlePaddle static graph Program , so dict is supported by @to_static. 

Third party library  numpy  Partial support  We suggest to use PaddlePaddle APIs to replace numpy API in this case. 
Details¶
ifelse¶
principle :¶
While in the dynamic graph, the code is interpreted and executed line by line, so the value of condition variables used by if
are determined, which means that the logic branch of False will not be executed.
However, in the static graph, the control flow is realized through the cond
operators. Each branch is represented by true_fn
and false_fn
respectively . Under this circumstance, the false_fn
will be executed to build the computation graph.
When the condition variables in If
are Tensor
, ifelse
will be transformed to a cond
operators.
When the condition variables in If
aren’t Tensor
, ifelse
will be executed as a python ifelse
code.
Note: When the condition variables in
If
areTensor
, thetensor.numel()
must equal to 1.
Error correction guide:¶
When using
if
statement, please determine whether the type of condition variable isPaddle.Tensor
. If it is not a Tensor type, it will be executed according to the normal python logic and will not be converted into a static graph.Check the
numel()
of the condition variable is 1 .
while¶
principle :¶
When the condition variable in the while
loop is Tensor
, the while statement will be converted into the while_loop
API statement in the static graph, otherwise it will run as a normal Python while.
Note: When the condition variables in
If
areTensor
, thetensor.numel()
must equal to 1.
Error correction guide:¶
The same as ifelifelse
for loops¶
principle :¶
For loops
have different semantics according to different usage. Normally, the use of for loops
can be divided into the following categories:
for _ in range(len)
: The statement will be converted into an equivalent Python while loop, and then the dynamic and static conversion will be performed according to the logic of the while loop.for _ in x
: When x is a python container or iterator, it will run with normal Python logic. When x isTensor
, it will be converted to obtainx[0]
,x[1]
,… in sequence.for idx, val in enumerate(x)
: When x is a Python container or iterator, it will run with normal python logic. When x is aTensor
, idx will be converted into a 1D Tensor with value 0, 1, … in sequence. val will be converted intox[0]
,x[1]
, … every time in the loop.
In views of implementation, the forloop
will eventually be transformed into the corresponding while statement, and then use WhileOp
for static graph.
examples :¶
def ForTensor(x):
"""Fetch element in x and print the square of each x element"""
for i in x :
print (i * i)
#usage: ForTensor(paddle.to_tensor(x))
return / break / continue¶
principle :¶
The current dynamictostatic supports adding break and continue statements in for, while loops to change the control flow. It also supports adding return statements at any position inside the loop, and supports return tuples with different lengths and different dtypes
of Tensor
.
examples :¶
# break usage example :
def break_usage(x):
tensor_idx = 1
for idx, val in enumerate(x) :
if val == 2.0 :
tensor_idx = idx
break # < jump out of while loop when break ;
return tensor_idx
When you execute
paddle.jit.to_static(break_usage)(paddle.to_tensor([1.0, 2.0, 3.0])
the tensor_idx is Tensor([1])
Note : Although idx is integer here, the return value is still Tensor. Because tensor_idx is converted into Tensor in the while loop.
and / or / not¶
principle :¶
The dynamictostatic module supports the conversion of and, or, and nonoperators . According to the types of the two operands x
and y
, there will be different semantics:
If both x, y are tensors, this statement will be converted to static graph.
If neither x, y is tensor, this statement will degenerate to python operator.
If only one of them is tensor, this statement will degenerate to python operator, but the results are always correct.
Note : If executed according to the semantics of paddle,
and
or
, andnot
no longer support the lazy mode, it means that both expressions will be evaluated, instead of evaluating y according to the value of x.
examples :¶
def and(x, y):
z = y and x
return z
Type conversion operator¶
principle :¶
In dynamic graphs, you can directly use python’s type conversion to convert Tensor types. For example, if x is a Tensor, float(x) can convert the data type of x to float.
Dynamictostatic module will judge whether x is a Tensor
at runtime. If so, use the static graph paddle.cast
interface to convert to the target data type.
examples :¶
def float_convert(x):
z = float(x)
return z
# if the x = Tensor([True]) ，then z = Tensor([1.0])
python function calls¶
Most circumstances of python function call is supported. The function calls will be converted into the form of convert_xxx()
, and the data type of arguments will be determined during running the function. If some of arguments is Tensor
, it will be transformed into a static computation graph; otherwise, it will be executed according to the original python semantics.
Some common functions are illustrated:
print (x)
If the parameter is Tensor,print(x)
can print the value of x in dynamic graph mode.While in dynamictostatic graph model, It will be converted into aPrint
call. If the parameter is not Tensor, it will be executed according to python’s print statement.len (x)
If the parameter is Tensor,len(x)
can get the length of the 0th dimension of tensor x. While in dynamictostatic graph model, It will be converted into acontrol_flow.array_length
call. If the parameter is not Tensor, it will be executed according to python’s print statement.lambda
Theto_static
function will callconvert_call
to convert lambda function as it’s a normal function.function call (not recursively) The
to_static
function will callconvert_call
to convert called function as it’s a normal function. Just put@to_static
in the toplevel function once.
examples :¶
def lambda_call(x):
t = lambda x : x * x
z = t(x)
return z
# if the x is Tensor([2.0]) ，then z equals to Tensor([4.0]).
unsupported usage¶
recursively call While converting, converted function can’t get value from static tensor.
def recur_call(x):
if x > 10:
return x
return recur_call(x * x) # <  If x = Tensor([2.0]) ，in dygraph mode the output is Tensor([16])，while in dygraphtostatic graph mode call stack overflows
list / dict¶
principle :¶
list: if all elements in a list are Tensors, then @to_static converts it to TensorArray. PaddlePaddle static graph TensorArray supports append, pop, and modify, other list operations such as sort cannot be supported. When not all elements in a list are Tensors, @to_static will treat it as normal Python list.
dict: @to_static will add the Tensors in a dict into PaddlePaddle static graph Program, so dict is supported by @to_static.
def list_example(x, y):
a = [ x ] # <  supported
a.append(x) # <  supported
a[1] = y # <  supported
return a[0] # <  supported
Note: List does not support multiple nesting and other operations. For specific error cases, see below.
unsupported usage¶
multiple nesting
For example: l = [[tensor1, tensor2], [tensor3, tensor4]], because @to_static transformed a list whose elements are all Tensors into PaddlePaddle static graph TensorArray, but TensorArray doesn’t support multidimensions, @to_static cannot run this case.
We suggest to use 1D list at most time, or use PaddlePaddle API create_array, array_read, array_write to control TensorArray.
complex operators, such as sort
def sort_list(x, y):
a = [x, y]
sort(a) # <  unsupported
return a
paddle.shape¶
principle :¶
partial supported
Support simple usage of
shape
, such as get the shape of a tensor.Don’t support get shape after a reshape operators. You may get a 1 in shape value.
For example, x = reshape(x, shape=shape_tensor)
, then use x.shape[0]
to do other operation. Due to the difference between dynamic and static graph, it is okay in dynamic but it will fail in static graph. The reason is that APIs return computation result in dynamic graph mode, so x.shape has deterministic value after calling reshape . However, static graph doesn’t have the value shape_tensor during building network, so PaddlePaddle doesn’t know the value of x.shape after calling reshape. PaddlePaddle static graph will set 1 to represent unknown shape value for each dimension of x.shape in this case, not the expected value. Similarily, calling the shape of the output tensor of those APIs which change the shape, such as expend, cannot be converted into static graph properly.
examples :¶
def get_shape(x):
return x.shape[0] # < supported
def error_shape(x, y):
y = y.cast('int32')
t = x.reshape(y)
return t.shape[0] # < don't supported ; if x = Tensor([2.0, 1.0])，y = Tensor([2])，in dygraph mode the output is 2，while in dygraphtostatic graph mode the output is 1.