Files
paddlepaddle--paddle/python/paddle/tensorrt/impls/math.py
T
2026-07-13 12:40:42 +08:00

582 lines
18 KiB
Python

# Copyright (c) 2024 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 numpy as np
import tensorrt as trt
from paddle.tensorrt.converter_utils import (
add_1D_constant_layer,
add_cast_reduce_layer,
add_constant_layer,
add_elementwise_layer,
add_reduce_layer,
broadcast,
cast_tensor,
fill_constant_layer,
get_axes_for_reduce_op,
get_axis_length,
get_input_constant_value,
get_shape_tensor_element,
set_layer_name,
trt_cast,
trt_concat,
trt_equal,
trt_expand,
trt_max,
trt_reshape,
trt_shape,
)
from paddle.tensorrt.register import converter_registry
@converter_registry.register("pd_op.add")
@converter_registry.register("pd_op.add_")
def add_converter(network, paddle_op, inputs):
return add_elementwise_layer(
network, paddle_op, inputs, trt.ElementWiseOperation.SUM
)
@converter_registry.register("pd_op.scale")
def scale_converter(network, paddle_op, inputs):
x = inputs[0]
bias = paddle_op.attrs().get("bias", 0.0)
bias_after_scale = paddle_op.attrs().get("bias_after_scale", True)
is_int = x.dtype == trt.DataType.INT32
if is_int:
bias_tensor = add_1D_constant_layer(
network,
int(bias + 0.5) if bias > 0 else int(bias - 0.5),
name=[paddle_op.name(), "bias_tensor"],
)
else:
bias_tensor = add_1D_constant_layer(
network,
bias,
dtype=np.float32,
name=[paddle_op.name(), "bias_tensor"],
)
is_bias_0 = bias == 0
bias_shapes = [1] * len(x.shape)
bias_shapes_tensor = add_1D_constant_layer(
network, bias_shapes, name=[paddle_op.name(), "bias_shapes_tensor"]
)
reshape_layer_bias = network.add_shuffle(bias_tensor)
reshape_layer_bias.set_input(1, bias_shapes_tensor)
set_layer_name(reshape_layer_bias, paddle_op)
scale = get_input_constant_value(paddle_op, inputs, 1)
if scale is not None:
scale = scale[0]
has_scale_tensor = False
if is_int:
scale_tensor = add_1D_constant_layer(
network,
int(scale + 0.5 if scale > 0 else scale - 0.5),
name=[paddle_op.name(), "scale_tensor"],
)
else:
scale_tensor = add_1D_constant_layer(
network,
scale,
dtype=np.float32,
name=[paddle_op.name(), "scale_tensor"],
)
is_scale_1 = scale == 1
else:
has_scale_tensor = True
scale_tensor = inputs[1]
is_scale_1 = False
scale_shapes = [1] * len(x.shape)
scale_shapes_tensor = add_1D_constant_layer(
network, scale_shapes, name=[paddle_op.name(), "scale_shapes_tensor"]
)
reshape_layer_scale = network.add_shuffle(scale_tensor)
reshape_layer_scale.set_input(1, scale_shapes_tensor)
set_layer_name(reshape_layer_scale, paddle_op)
# Initialize the layer variable to ensure it's defined in all branches
layer = None
if not has_scale_tensor and is_scale_1 and is_bias_0:
layer = network.add_identity(x)
set_layer_name(layer, paddle_op)
else:
if bias_after_scale:
if not is_scale_1:
layer = network.add_elementwise(
x,
reshape_layer_scale.get_output(0),
trt.ElementWiseOperation.PROD,
)
set_layer_name(layer, paddle_op)
x = layer.get_output(0)
if not is_bias_0:
layer = network.add_elementwise(
x,
reshape_layer_bias.get_output(0),
trt.ElementWiseOperation.SUM,
)
set_layer_name(layer, paddle_op)
else:
if not is_bias_0:
layer = network.add_elementwise(
x,
reshape_layer_bias.get_output(0),
trt.ElementWiseOperation.SUM,
)
set_layer_name(layer, paddle_op)
x = layer.get_output(0)
if not is_scale_1:
layer = network.add_elementwise(
x,
reshape_layer_scale.get_output(0),
trt.ElementWiseOperation.PROD,
)
set_layer_name(layer, paddle_op)
return layer.get_output(0)
@converter_registry.register("pd_op.max")
def max_converter(network, paddle_op, inputs):
input_tensor = inputs[0]
axis = get_input_constant_value(paddle_op, inputs, 1)
input_shape = input_tensor.shape
keepdim = paddle_op.attrs()["keepdim"]
if network.has_implicit_batch_dimension:
assert axis != 0, (
"can't reduce on axis == 0 when network has implicit batch dimension"
)
if len(axis) == 0:
axis = list(range(len(input_shape)))
for i in range(len(axis)):
if axis[i] < 0:
axis[i] = len(input_shape) + axis[i]
layer = network.add_reduce(
input_tensor,
trt.ReduceOperation.MAX,
axes=get_axes_for_reduce_op(axis),
keep_dims=keepdim,
)
set_layer_name(layer, paddle_op)
return layer.get_output(0)
@converter_registry.register("pd_op.divide")
def divide_converter(network, paddle_op, inputs):
return add_elementwise_layer(
network, paddle_op, inputs, trt.ElementWiseOperation.DIV
)
@converter_registry.register("pd_op.subtract")
def subtract_converter(network, paddle_op, inputs):
return add_elementwise_layer(
network, paddle_op, inputs, trt.ElementWiseOperation.SUB
)
@converter_registry.register("pd_op.multiply")
def multiply_converter(network, paddle_op, inputs):
return add_elementwise_layer(
network, paddle_op, inputs, trt.ElementWiseOperation.PROD
)
@converter_registry.register("pd_op.clip")
def clip_converter(network, paddle_op, inputs):
def _get_constant_or_expand_tensor(
value, constant_inputs, input_shape_tensor, rank, name=None
):
if value is not None:
return fill_constant_layer(
network,
input_shape_tensor,
rank,
value,
input_tensor.dtype,
name=name,
)
else:
expanded_tensor = trt_expand(
network, constant_inputs, 1, input_shape_tensor, rank, name=name
)
if expanded_tensor.dtype != input_tensor.dtype:
expanded_tensor = cast_tensor(
network, expanded_tensor, input_tensor.dtype, name=name
)
return expanded_tensor
input_tensor = inputs[0]
input_shape = input_tensor.shape
rank = len(input_shape)
input_shape_tensor = network.add_shape(input_tensor)
set_layer_name(input_shape_tensor, paddle_op)
input_shape_tensor = input_shape_tensor.get_output(0)
# handle min operation
min_value = get_input_constant_value(paddle_op, inputs, 1)
alpha_t = _get_constant_or_expand_tensor(
min_value, inputs[1], input_shape_tensor, rank
)
# handle max operation
max_value = get_input_constant_value(paddle_op, inputs, 2)
beta_t = _get_constant_or_expand_tensor(
max_value,
inputs[2],
input_shape_tensor,
rank,
name=[paddle_op.name(), 'beta_t'],
)
# run the clip operation
lower_clip = trt_max(
network, input_tensor, alpha_t, name=[paddle_op.name(), 'lower_clip']
)
layer = network.add_elementwise(
lower_clip, beta_t, trt.ElementWiseOperation.MIN
)
set_layer_name(layer, paddle_op)
return layer.get_output(0)
@converter_registry.register("pd_op.pow")
def pow_converter(network, paddle_op, inputs):
from paddle.tensorrt.util import support_fp32_mix_precision
x = inputs[0]
factor = paddle_op.attrs()["y"]
dims_x = x.shape
trt_dims_y = trt.Dims([1] * len(dims_x))
w_data = [factor]
y = add_constant_layer(
network, w_data, trt_dims_y, np.float32, name=[paddle_op.name(), 'y']
)
layer = network.add_elementwise(x, y, trt.ElementWiseOperation.POW)
set_layer_name(layer, paddle_op)
support_fp32_mix_precision(paddle_op.name(), layer)
return layer.get_output(0)
@converter_registry.register("pd_op.remainder")
@converter_registry.register("pd_op.remainder_")
def remainder_converter(network, paddle_op, inputs):
from paddle.tensorrt.util import support_fp32_mix_precision
weight_shape = paddle_op.operands()[1].source().shape
input_shape = inputs[0].shape
weight_tensor = inputs[1]
input_tensor = inputs[0]
if type(inputs[1]) == trt.Weights:
weight_tensor = network.add_constant(weight_shape, inputs[1])
set_layer_name(weight_tensor, paddle_op)
weight_tensor = weight_tensor.get_output(0)
if type(inputs[0]) == trt.Weights:
input_tensor = network.add_constant(input_shape, inputs[0])
set_layer_name(input_tensor, paddle_op)
input_tensor = input_tensor.get_output(0)
lhs_val, rhs_val = broadcast(
network,
input_tensor,
weight_tensor,
"input_tensor_broadcast",
"weight_tensor_broadcast",
paddle_op,
)
is_floor_div = input_tensor.dtype != trt.DataType.INT32
if is_floor_div:
quotient_layer = network.add_elementwise(
lhs_val, rhs_val, trt.ElementWiseOperation.FLOOR_DIV
)
else:
quotient_layer = network.add_elementwise(
lhs_val, rhs_val, trt.ElementWiseOperation.DIV
)
set_layer_name(quotient_layer, paddle_op)
quotient = quotient_layer.get_output(0)
support_fp32_mix_precision(paddle_op.name(), quotient_layer)
# Multiply rhs by the quotient
product_layer = network.add_elementwise(
rhs_val, quotient, trt.ElementWiseOperation.PROD
)
set_layer_name(product_layer, paddle_op)
product = product_layer.get_output(0)
support_fp32_mix_precision(paddle_op.name(), product_layer)
remainder_layer = network.add_elementwise(
lhs_val, product, trt.ElementWiseOperation.SUB
)
set_layer_name(remainder_layer, paddle_op)
remainder = remainder_layer.get_output(0)
support_fp32_mix_precision(paddle_op.name(), remainder_layer)
return remainder
@converter_registry.register("pd_op.min")
def min_converter(network, paddle_op, inputs):
return add_reduce_layer(network, paddle_op, inputs, trt.ReduceOperation.MIN)
@converter_registry.register("pd_op.sum")
def sum_converter(network, paddle_op, inputs):
return add_reduce_layer(network, paddle_op, inputs, trt.ReduceOperation.SUM)
@converter_registry.register("pd_op.mean")
def mean_converter(network, paddle_op, inputs):
return add_reduce_layer(network, paddle_op, inputs, trt.ReduceOperation.AVG)
@converter_registry.register("pd_op.any")
def any_converter(network, paddle_op, inputs):
return add_cast_reduce_layer(
network, paddle_op, inputs, trt.ReduceOperation.MAX
)
@converter_registry.register("pd_op.all")
def all_converter(network, paddle_op, inputs):
return add_cast_reduce_layer(
network, paddle_op, inputs, trt.ReduceOperation.MIN
)
@converter_registry.register("pd_op.cumsum")
def cumsum_converter(network, paddle_op, inputs):
input_tensor = inputs[0]
dtype = input_tensor.dtype
axis = get_input_constant_value(paddle_op, inputs, 1)[0]
input_shape = input_tensor.shape
rank = len(input_shape)
if axis < 0:
axis += rank
axis = int(axis)
# Obtain the number of cycles
if input_shape[axis] > 0:
trip_limit = add_1D_constant_layer(
network,
input_shape[axis],
is_scalar=True,
name=[paddle_op.name(), 'trip_limit'],
)
else:
dynamic_shape = trt_shape(
network, input_tensor, name=[paddle_op.name(), 'dynamic_shape']
)
trip_limit = get_shape_tensor_element(
network,
dynamic_shape,
axis,
True,
name=[paddle_op.name(), 'trip_limit'],
)
# Obtain the slice shape
shape_list = []
for i in range(rank):
if i == axis:
shape_list.append(
add_1D_constant_layer(
network, [1], name=[paddle_op.name(), f'shape_list_{i}']
)
)
else:
shape_list.append(
get_axis_length(
network,
input_tensor,
i,
name=[paddle_op.name(), f'shape_list_{i}'],
)
)
slice_shape = trt_concat(
network, shape_list, name=[paddle_op.name(), 'slice_shape']
)
start = [0] * rank
size = [1] * rank
stride = [1] * rank
input_sliced = network.add_slice(input_tensor, start, size, stride)
input_sliced.set_input(2, slice_shape)
set_layer_name(input_sliced, paddle_op)
# squeeze axis
if rank > 1:
shape_list.pop(axis)
new_shape = trt_concat(
network, shape_list, name=[paddle_op.name(), 'new_shape']
)
squeeze_output = trt_reshape(
network,
input_sliced.get_output(0),
new_shape,
is_shape_tensor=True,
name=[paddle_op.name(), 'squeeze_output'],
)
loop = network.add_loop()
loop.add_trip_limit(trip_limit, trt.TripLimit.COUNT)
iterator = loop.add_iterator(input_tensor, axis)
set_layer_name(iterator, paddle_op)
data = iterator.get_output(0)
# create zero tensor
zero_vec = np.array([0.0], dtype=np.float32)
zero = add_1D_constant_layer(
network, zero_vec, name=[paddle_op.name(), 'zero']
)
lhs_val, rhs_val = broadcast(
network,
squeeze_output,
zero,
"squeeze_output_broadcast",
"zero_output_broadcast",
paddle_op,
)
cast_tensor = trt_cast(
network, rhs_val, dtype, name=[paddle_op.name(), 'cast_tensor']
)
zero_tensor = network.add_elementwise(
lhs_val, cast_tensor, trt.ElementWiseOperation.PROD
)
set_layer_name(zero_tensor, paddle_op)
zero_tensor = zero_tensor.get_output(0)
# Set as scalar
if rank == 1:
zero_tensor = trt_reshape(
network, zero_tensor, (), name=[paddle_op.name(), 'zero_tensor']
)
# Cycle and add according to the axis
running_sum = loop.add_recurrence(zero_tensor)
running_sum_tensor = running_sum.get_output(0)
cur_sum = network.add_elementwise(
data, running_sum_tensor, trt.ElementWiseOperation.SUM
)
set_layer_name(cur_sum, paddle_op)
cur_sum = cur_sum.get_output(0)
running_sum.set_input(1, cur_sum)
set_layer_name(running_sum, paddle_op)
reverse_flag = trt.LoopOutput.CONCATENATE
loop_out = loop.add_loop_output(cur_sum, reverse_flag, axis)
loop_out.set_input(1, trip_limit)
set_layer_name(loop_out, paddle_op)
return loop_out.get_output(0)
@converter_registry.register("pd_op.floor_divide")
def floor_divide_converter(network, paddle_op, inputs):
return add_elementwise_layer(
network, paddle_op, inputs, trt.ElementWiseOperation.FLOOR_DIV
)
@converter_registry.register("pd_op.log")
def log_converter(network, paddle_op, inputs):
input_tensor = trt_cast(
network, inputs[0], trt.float32, name=[paddle_op.name(), 'input_tensor']
)
layer = network.add_unary(input_tensor, trt.UnaryOperation.LOG)
set_layer_name(layer, paddle_op)
return layer.get_output(0)
@converter_registry.register("pd_op.elementwise_pow")
def elementwise_pow_converter(network, paddle_op, inputs):
return add_elementwise_layer(
network, paddle_op, inputs, trt.ElementWiseOperation.POW
)
@converter_registry.register("pd_op.isnan")
def isnan_converter(network, paddle_op, inputs):
input_tensor = inputs[0]
equal_tensor = trt_equal(
network,
input_tensor,
input_tensor,
name=[paddle_op.name(), 'equal_tensor'],
)
layer = network.add_unary(equal_tensor, trt.UnaryOperation.NOT)
set_layer_name(layer, paddle_op)
return layer.get_output(0)
@converter_registry.register("pd_op.minimum")
def minimum_converter(network, paddle_op, inputs):
min_layer = add_elementwise_layer(
network, paddle_op, inputs, trt.ElementWiseOperation.MIN
)
return min_layer
@converter_registry.register("pd_op.maximum")
def maximum_converter(network, paddle_op, inputs):
max_layer = add_elementwise_layer(
network, paddle_op, inputs, trt.ElementWiseOperation.MAX
)
return max_layer
@converter_registry.register("pd_op.greater_equal")
@converter_registry.register("pd_op.greater_equal_")
def greater_equal_converter(network, paddle_op, inputs):
greater_layer_output = add_elementwise_layer(
network, paddle_op, inputs, trt.ElementWiseOperation.GREATER
)
equal_layer_output = add_elementwise_layer(
network, paddle_op, inputs, trt.ElementWiseOperation.EQUAL
)
or_layer = add_elementwise_layer(
network,
paddle_op,
[greater_layer_output, equal_layer_output],
trt.ElementWiseOperation.OR,
)
return or_layer
@converter_registry.register("pd_op.less_equal")
@converter_registry.register("pd_op.less_equal_")
def less_equal_converter(network, paddle_op, inputs):
less_layer_output = add_elementwise_layer(
network, paddle_op, inputs, trt.ElementWiseOperation.LESS
)
equal_layer_output = add_elementwise_layer(
network, paddle_op, inputs, trt.ElementWiseOperation.EQUAL
)
or_layer = add_elementwise_layer(
network,
paddle_op,
[less_layer_output, equal_layer_output],
trt.ElementWiseOperation.OR,
)
return or_layer