Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 21 additions & 1 deletion src/frontends/paddle/src/op/expand_v2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
// SPDX-License-Identifier: Apache-2.0
//

#include <numeric>

#include "default_opset.hpp"
#include "openvino/frontend/paddle/node_context.hpp"

Expand Down Expand Up @@ -55,7 +57,25 @@ NamedOutputs expand_v2(const NodeContext& node) {
// if -1 in shape we will copy the original value from input
auto zero_node = Constant::create(ov::element::i32, {1}, {0});
auto mask_node = std::make_shared<Greater>(shape_expected_node, zero_node);
auto fixed_shape_node = std::make_shared<Select>(mask_node, shape_expected_node, fixed_input_shape_node);
Output<Node> fixed_shape_node = std::make_shared<Select>(mask_node, shape_expected_node, fixed_input_shape_node);

// The target shape vector can end up with a dynamic length (e.g. when it is assembled from a
// dynamic-length Slice inside a control-flow body). That would make the Broadcast output rank
// dynamic, which some plugins (e.g. CPU) do not support. The expand output rank is statically
// known from the Paddle op output var descriptor, so pin the target shape length to that static
// rank to keep the Broadcast output rank static without constraining the (possibly dynamic) dims.
// A Gather with constant indices [0, out_rank) is used (rather than a Reshape) so the static
// length survives offline shape-of subgraph simplification / nop elimination: the gathered axis
// length is dynamic, so GatherNopElimination cannot fold it away.
const auto output_info = node.get_output_port_infos("Out");
if (!output_info.empty() && output_info[0].second.rank().is_static()) {
const auto out_rank = static_cast<size_t>(output_info[0].second.rank().get_length());
std::vector<int32_t> indices(out_rank);
std::iota(indices.begin(), indices.end(), 0);
const auto gather_indices = Constant::create(element::i32, {out_rank}, indices);
const auto gather_axis = Constant::create(element::i32, {}, {0});
fixed_shape_node = std::make_shared<Gather>(fixed_shape_node, gather_indices, gather_axis);
}
return node.default_single_output_mapping({std::make_shared<Broadcast>(x, fixed_shape_node)}, {"Out"});
}

Expand Down
1 change: 1 addition & 0 deletions src/frontends/paddle/tests/op_fuzzy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ static const std::vector<std::string> models{
std::string("expand_v2_tensor/expand_v2_tensor.pdmodel"),
std::string("expand_v2_tensor_list/expand_v2_tensor_list.pdmodel"),
std::string("expand_v2_tensor_list2/expand_v2_tensor_list2.pdmodel"),
std::string("expand_v2_conditional/expand_v2_conditional.pdmodel"),
std::string("expand_as_v2_1/expand_as_v2_1.pdmodel"),
std::string("expand_as_v2_2/expand_as_v2_2.pdmodel"),
std::string("exp_test_float32/exp_test_float32.pdmodel"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,11 @@
data_type = 'float32'


def expand_v2(name:str, x, shape:list):
def expand_v2(name: str, x, shape: list):
paddle.enable_static()

with paddle.static.program_guard(paddle.static.Program(), paddle.static.Program()):
with paddle.static.program_guard(paddle.static.Program(),
paddle.static.Program()):
node_x = paddle.static.data(name='x', shape=x.shape, dtype=data_type)
out = paddle.expand(node_x, shape=shape, name='expand_v2')

Expand All @@ -24,23 +25,28 @@ def expand_v2(name:str, x, shape:list):
# startup program will call initializer to initialize the parameters.
exe.run(paddle.static.default_startup_program())

outs = exe.run(
feed={'x': x},
fetch_list=[out])
outs = exe.run(feed={'x': x}, fetch_list=[out])

saveModel(name, exe, feed_vars=[node_x], fetchlist=[out],
inputs=[x], outputs=[outs[0]], target_dir=sys.argv[1])
saveModel(name,
exe,
feed_vars=[node_x],
fetchlist=[out],
inputs=[x],
outputs=[outs[0]],
target_dir=sys.argv[1])

return outs[0]


def expand_v2_tensor(name:str, x, out_shape, use_tensor_in_list):
def expand_v2_tensor(name: str, x, out_shape, use_tensor_in_list):
paddle.enable_static()

with paddle.static.program_guard(paddle.static.Program(), paddle.static.Program()):
with paddle.static.program_guard(paddle.static.Program(),
paddle.static.Program()):
node_x = paddle.static.data(name='x', shape=x.shape, dtype=data_type)
if use_tensor_in_list:
out_shape[0] = paddle.assign(np.array((out_shape[0],)).astype('int32'))
out_shape[0] = paddle.assign(
np.array((out_shape[0], )).astype('int32'))
out = paddle.expand(node_x, shape=out_shape, name='expand_v2')
else:
out_shape = np.array(out_shape).astype('int32')
Expand All @@ -52,19 +58,24 @@ def expand_v2_tensor(name:str, x, out_shape, use_tensor_in_list):
# startup program will call initializer to initialize the parameters.
exe.run(paddle.static.default_startup_program())

outs = exe.run(
feed={'x': x},
fetch_list=[out])
outs = exe.run(feed={'x': x}, fetch_list=[out])

saveModel(name, exe, feed_vars=[node_x], fetchlist=[out],
inputs=[x], outputs=[outs[0]], target_dir=sys.argv[1])
saveModel(name,
exe,
feed_vars=[node_x],
fetchlist=[out],
inputs=[x],
outputs=[outs[0]],
target_dir=sys.argv[1])

return outs[0]

def expand_as_v2(name:str, x, y):

def expand_as_v2(name: str, x, y):
paddle.enable_static()

with paddle.static.program_guard(paddle.static.Program(), paddle.static.Program()):
with paddle.static.program_guard(paddle.static.Program(),
paddle.static.Program()):
node_x = paddle.static.data(name='x', shape=x.shape, dtype=data_type)
node_y = paddle.static.data(name='y', shape=y.shape, dtype=data_type)
out = paddle.expand_as(node_x, node_y, name='expand_as_v2')
Expand All @@ -74,15 +85,64 @@ def expand_as_v2(name:str, x, y):
# startup program will call initializer to initialize the parameters.
exe.run(paddle.static.default_startup_program())

outs = exe.run(
feed={'x': x, 'y': y},
fetch_list=[out])
outs = exe.run(feed={'x': x, 'y': y}, fetch_list=[out])

saveModel(name, exe, feed_vars=[node_x, node_y], fetchlist=[out],
inputs=[x, y], outputs=[outs[0]], target_dir=sys.argv[1])
saveModel(name,
exe,
feed_vars=[node_x, node_y],
fetchlist=[out],
inputs=[x, y],
outputs=[outs[0]],
target_dir=sys.argv[1])

return outs[0]


def expand_v2_conditional(name: str, x, rows):
# expand_v2 nested inside a conditional_block, mirroring the control-flow
# structure of the FastRCNN model where the expand target shape is assembled
# at runtime. Exercises the expand_v2 translator path that pins the Broadcast
# output rank to the statically-known op output rank.
paddle.enable_static()

with paddle.static.program_guard(paddle.static.Program(),
paddle.static.Program()):
node_x = paddle.static.data(name='x', shape=x.shape, dtype=data_type)
node_im = paddle.static.data(name='im',
shape=[-1, x.shape[-1]],
dtype=data_type)

def true_fn():
n = paddle.shape(node_im)[0].reshape([1]).astype('int32')
tgt = paddle.concat(
[n, paddle.assign(np.array([x.shape[-1]]).astype('int32'))])
return paddle.expand(node_x, shape=tgt, name='expand_v2')
Comment thread
liubo-intel marked this conversation as resolved.

def false_fn():
return paddle.expand(node_x, shape=list(x.shape), name='expand_v2')

out = paddle.static.nn.cond(
paddle.shape(node_im)[0] > 0, true_fn, false_fn)

cpu = paddle.static.cpu_places(1)
exe = paddle.static.Executor(cpu[0])
# startup program will call initializer to initialize the parameters.
exe.run(paddle.static.default_startup_program())

im = np.random.rand(rows, x.shape[-1]).astype(data_type)
outs = exe.run(feed={'x': x, 'im': im}, fetch_list=[out])

saveModel(name,
exe,
feed_vars=[node_x, node_im],
fetchlist=[out],
inputs=[x, im],
outputs=[outs[0]],
target_dir=sys.argv[1])

return outs[0]


def main():
data = np.random.rand(1, 1, 6).astype(data_type)

Expand All @@ -98,5 +158,10 @@ def main():
expand_as_v2("expand_as_v2_1", data_x, data_y1)
expand_as_v2("expand_as_v2_2", data_x, data_y2)

# expand_v2 inside control flow (FastRCNN-like)
expand_v2_conditional("expand_v2_conditional",
np.random.rand(1, 4).astype(data_type), 3)


if __name__ == "__main__":
main()
Loading