diff --git a/src/frontends/tensorflow/src/op_table.cpp b/src/frontends/tensorflow/src/op_table.cpp index e4511472a12..beaba8032ce 100644 --- a/src/frontends/tensorflow/src/op_table.cpp +++ b/src/frontends/tensorflow/src/op_table.cpp @@ -233,6 +233,7 @@ const std::map get_supported_ops() { {"Merge", CreatorFunction(translate_merge_op)}, {"MirrorPad", CreatorFunction(translate_mirror_pad_op)}, {"MulNoNan", CreatorFunction(translate_mul_no_nan_op)}, + {"Multinomial", CreatorFunction(translate_multinomial_op)}, {"MutableHashTable", CreatorFunction(translate_hash_table_op)}, {"MutableHashTableV2", CreatorFunction(translate_hash_table_op)}, {"NonMaxSuppression", CreatorFunction(translate_non_max_suppression_op)}, diff --git a/src/frontends/tensorflow_common/include/common_op_table.hpp b/src/frontends/tensorflow_common/include/common_op_table.hpp index 6671922f704..da1f7f96e49 100644 --- a/src/frontends/tensorflow_common/include/common_op_table.hpp +++ b/src/frontends/tensorflow_common/include/common_op_table.hpp @@ -102,6 +102,7 @@ OP_CONVERTER(translate_max_pool_op); OP_CONVERTER_NAMED(translate_max_pool_with_argmax); OP_CONVERTER(translate_mirror_pad_op); OP_CONVERTER(translate_mul_no_nan_op); +OP_CONVERTER(translate_multinomial_op); OP_CONVERTER_NAMED(translate_non_max_suppression_op); OP_CONVERTER(translate_parallel_dynamic_stitch_op); OP_CONVERTER(translate_placeholder_op); diff --git a/src/frontends/tensorflow_common/src/op/multinomial.cpp b/src/frontends/tensorflow_common/src/op/multinomial.cpp new file mode 100644 index 00000000000..15953c02b59 --- /dev/null +++ b/src/frontends/tensorflow_common/src/op/multinomial.cpp @@ -0,0 +1,31 @@ +// Copyright (C) 2018-2023 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "openvino/op/multinomial.hpp" + +#include "common_op_table.hpp" + +namespace ov { +namespace frontend { +namespace tensorflow { +namespace op { + +OutputVector translate_multinomial_op(const NodeContext& node) { + default_op_checks(node, 2, {"Multinomial"}); + auto logits = node.get_input(0); + auto num_samples = node.get_input(1); + auto global_seed = node.get_attribute("seed", 0); + auto op_seed = node.get_attribute("seed2", 0); + auto output_type = node.get_attribute("output_dtype"); + + auto res = + std::make_shared(logits, num_samples, output_type, true, true, global_seed, op_seed); + set_node_name(node.get_name(), res); + return res->outputs(); +} + +} // namespace op +} // namespace tensorflow +} // namespace frontend +} // namespace ov diff --git a/tests/layer_tests/pytest.ini b/tests/layer_tests/pytest.ini index a7f6e772eac..db450888077 100644 --- a/tests/layer_tests/pytest.ini +++ b/tests/layer_tests/pytest.ini @@ -2,6 +2,7 @@ markers = nightly precommit + precommit_tf_fe precommit_ts_backend precommit_fx_backend timeout diff --git a/tests/layer_tests/tensorflow_tests/test_tf_Multinomial.py b/tests/layer_tests/tensorflow_tests/test_tf_Multinomial.py new file mode 100644 index 00000000000..e83875d1726 --- /dev/null +++ b/tests/layer_tests/tensorflow_tests/test_tf_Multinomial.py @@ -0,0 +1,144 @@ +# Copyright (C) 2018-2023 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 + +import pytest +import tensorflow as tf +from common.tf_layer_test_class import CommonTFLayerTest +import numpy as np + + +class TestMultinomial(CommonTFLayerTest): + def _prepare_input(self, inputs_dict, kwargs): + inputs_dict["num_samples"] = np.array(kwargs["num_samples"], dtype=np.int32) + inputs_dict["probs"] = kwargs["input"] + return inputs_dict + + def create_tf_multinomial_net_shape( + self, global_seed, op_seed, logits_shape, input_type, out_type + ): + tf.compat.v1.reset_default_graph() + # Configuration required to make multinomial randomness predictable across devices, results depends on TF parallel execution. + session_conf = tf.compat.v1.ConfigProto( + intra_op_parallelism_threads=1, inter_op_parallelism_threads=1 + ) + # Create the graph and model + with tf.compat.v1.Session(config=session_conf) as sess: + probs = tf.compat.v1.placeholder(input_type, logits_shape, "probs") + num_samples = tf.compat.v1.placeholder(tf.int32, [], "num_samples") + if global_seed is not None: + tf.random.set_seed(global_seed) + tf.raw_ops.ZerosLike( + x=tf.raw_ops.Multinomial( + logits=tf.math.log(probs), + num_samples=num_samples, + seed=global_seed, + seed2=op_seed, + output_dtype=out_type, + ) + ) + + tf.compat.v1.global_variables_initializer() + tf_net = sess.graph_def + + return tf_net, None + + def create_tf_multinomial_net_exact( + self, global_seed, op_seed, logits_shape, input_type, out_type + ): + tf.compat.v1.reset_default_graph() + # Configuration required to make multinomial randomness predictable across devices, results depends on TF parallel execution. + session_conf = tf.compat.v1.ConfigProto( + intra_op_parallelism_threads=1, inter_op_parallelism_threads=1 + ) + # Create the graph and model + with tf.compat.v1.Session(config=session_conf) as sess: + probs = tf.compat.v1.placeholder(input_type, logits_shape, "probs") + num_samples = tf.compat.v1.placeholder(tf.int32, [], "num_samples") + if global_seed is not None: + tf.random.set_seed(global_seed) + tf.raw_ops.Multinomial( + logits=tf.math.log(probs), + num_samples=num_samples, + seed=global_seed, + seed2=op_seed, + output_dtype=out_type, + ) + + tf.compat.v1.global_variables_initializer() + tf_net = sess.graph_def + + return tf_net, None + + @pytest.mark.parametrize("out_type", [tf.int32, tf.int64]) + @pytest.mark.parametrize( + ("input", "num_samples", "seed", "test_type"), + [ + ( + np.array([[0, 1, 0, 0], [0, 0, 0, 1], [1, 0, 0, 0]], dtype=np.float32), + 1024, + [32465, 48971], + "exact", + ), + ( + np.array( + [ + [0.001, 0.001, 0.1, 0.9], + [5, 10, 1e-5, 256], + [1, 1e-5, 1e-5, 1e-5], + ], + dtype=np.float64, + ), + 256, + [32465, 48971], + "shape", + ), + (np.array([[1, 1, 1, 1]], dtype=np.float16), 1024, [1, 1], "shape"), + ( + np.array([[1, 2, 3, 4], [4, 3, 2, 1], [1, 0, 0, 0]], dtype=np.float32), + 1, + [78132, None], + "shape", + ), + ( + np.array([[7, 7, 7, 7], [7, 7, 7, 7], [7, 7, 7, 7]], dtype=np.float32), + 1024, + [32465, None], + "shape", + ), + ], + ) + @pytest.mark.nightly + @pytest.mark.precommit_tf_fe + def test_multinomial_basic( + self, + input, + num_samples, + seed, + out_type, + test_type, + ie_device, + precision, + ir_version, + temp_dir, + use_new_frontend, + use_old_api, + ): + if ie_device == "GPU": + pytest.skip("Multinomial is not supported on GPU") + net = getattr(self, f"create_tf_multinomial_net_{test_type}") + self._test( + *net( + global_seed=seed[0], + op_seed=seed[1], + logits_shape=input.shape, + input_type=input.dtype, + out_type=out_type, + ), + ie_device, + precision, + temp_dir=temp_dir, + ir_version=ir_version, + use_new_frontend=use_new_frontend, + use_old_api=use_old_api, + kwargs_to_prepare_input={"input": input, "num_samples": num_samples}, + )