Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
597c1e3
[ref][one_hot]: Added support for negative indicies. Fixed tests.
pkowalc1 Jul 31, 2025
04e718e
[ref][one_hot]: Fixed code style.
pkowalc1 Jul 31, 2025
972560b
[onnx]: Disabled onehot test.
pkowalc1 Aug 1, 2025
7304114
[gpu][one_hot]: Added support for negative indicies, added unittests.
pkowalc1 Aug 1, 2025
2f937d5
[cpu]: [one_hot]: Added support for negative indicies and and added t…
pkowalc1 Aug 4, 2025
deb076b
[doc]:[one_hot]: Added info about supporting negative indicies.
pkowalc1 Aug 4, 2025
5e61894
Merge branch 'master' into fix_one_hot_negative_indicies
pkowalc1 Aug 4, 2025
0ea1888
[TEMP][CI]: Add printing input to failing tests.
pkowalc1 Aug 5, 2025
b76c367
[ref]: Added NegativeIndicesMode to OneHot. Fixed ref and onnx tests.
pkowalc1 Aug 7, 2025
33459d7
[onnx]: Fixed doc check.
pkowalc1 Aug 7, 2025
c0c8d45
[one_hot]: Fixed code style.
pkowalc1 Aug 7, 2025
2d6e42c
[cpu][onehot]: Added support for oh mode. Fixed tests.
pkowalc1 Aug 8, 2025
f8ce920
[gpu][one_hot]: Added support for indicies mode with tests.
pkowalc1 Aug 8, 2025
fd4c4d8
Fixed clang-format.
pkowalc1 Aug 8, 2025
77fffc3
Merge branch 'master' into fix_one_hot_negative_indicies
pkowalc1 Aug 8, 2025
889ed07
[doc][onehot]: Added description of negative indices mode.
pkowalc1 Aug 11, 2025
3ff9d3d
Merge branch 'fix_one_hot_negative_indicies' of https://github.com/pk…
pkowalc1 Aug 11, 2025
42580f2
[gpu][cpu][ref]: Added OneHot v16 WIP
pkowalc1 Aug 28, 2025
28ad97c
[core]: Enabled shape infer for v16 One Hot.
pkowalc1 Aug 29, 2025
c275abe
[cpu]: Added tests for v16 shape infer.
pkowalc1 Aug 29, 2025
6727743
[onnx]: Fixed frontend tests.
pkowalc1 Aug 29, 2025
b11a989
[core]: Added shape inference tests for one hot v16.
pkowalc1 Aug 29, 2025
cc8c572
[core]: Add constant folding tests for OneHot v16.
pkowalc1 Aug 29, 2025
dcf4e91
[core]: Added attributes tests for one hot v16.
pkowalc1 Aug 29, 2025
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
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,24 @@ The types of input scalars ``on_value`` and ``off_value`` should match and be eq
* **Type**: ``int``
* **Required**: *yes*

* *negative_indices_mode*

* **Description**: controls how negative indices in indices tensor are handled.
* **Range of values**:

* ``ignore-negative``: negative indices are ignored, and the corresponding output rows are filled with ``off_value``.
* ``normalize``: negative indices in the range ``[-depth, -1]`` are normalized by ``depth + index``; which effectively maps the range ``[-depth, depth-1]`` to ``[0, depth-1]``.

* **Type**: ``string``
* **Default value**: ignore-negative
* **Required**: *no*

.. note::
Behavior before 2025.4 OpenVINO release: negative_indices_mode was not supported and negative indices were handled according to ignore-negative mode.

**Inputs**:

* **1**: ``indices``: input tensor of type *T1* with non-negative indices, behavior for negative indices is undefined. Can be 0D. **Required.**
* **1**: ``indices``: input tensor of type *T1* with indices. Can be 0D. **Required.**
* **2**: ``depth``: positive scalar (0D tensor) of type *T1* that specifies the number of classes and thus the size of the one-hot dimension. **Required.**
* **3**: ``on_value``: scalar (0D tensor) of type *T2* that fills the locations in output tensor specified in ``indices``. **Required.**
* **4**: ``off_value``: scalar (0D tensor) of type *T2* that fills the locations not represented in ``indices``. **Required.**
Expand All @@ -60,9 +75,9 @@ The types of input scalars ``on_value`` and ``off_value`` should match and be eq
:force:

<layer ... type="OneHot" ...>
<data axis="-1"/>
<data axis="-1" negative_indices_mode="normalize"/>
<input>
<port id="0"> <!-- indices value: [0, 3, 1, 2] -->
<port id="0"> <!-- indices value: [0, -5, -2, 2] -->
<dim>4</dim>
</port>
<port id="1"> <!-- depth value: 3 -->
Expand Down
76 changes: 65 additions & 11 deletions src/core/include/openvino/op/one_hot.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@

#pragma once

#include "openvino/op/op.hpp"
#include "openvino/op/util/one_hot_base.hpp"

namespace ov {
namespace op {
namespace v1 {
/// \brief OneHot operation.
///
/// \ingroup ov_ops_cpp_api
class OPENVINO_API OneHot : public Op {
class OPENVINO_API OneHot : public util::OneHotBase {
public:
OPENVINO_OP("OneHot", "opset1", op::Op);

Expand Down Expand Up @@ -40,19 +40,73 @@ class OPENVINO_API OneHot : public Op {

bool evaluate(TensorVector& outputs, const TensorVector& inputs) const override;
bool has_evaluate() const override;
};
} // namespace v1
namespace v16 {
/// \brief OneHot operation.
///
/// \ingroup ov_ops_cpp_api
class OPENVINO_API OneHot : public util::OneHotBase {
public:
OPENVINO_OP("OneHot", "opset16", op::Op);

/// \brief Lists the supported negative indices modes for this version of the operator.
/// See the specification for the description of how negative indices are handled.
enum class NegativeIndicesMode { IGNORE_NEGATIVE, NORMALIZE };

/// \brief Constructs a one-hot operation.
OneHot() = default;
/// \brief Constructs a one-hot operation.
///
/// \param indices Input tensor containing indices.
/// \param depth Specifies number of classes and the size of one-hot dimension.
/// \param on_value Specifies value that the locations in output tensor represented
/// by indices in input take.
/// \param off_value Specifies value that the locations in output tensor not
/// represented
/// by indices in input take.
/// \param axis Axis along which one-hot representation in added.
OneHot(const Output<Node>& indices,
const Output<Node>& depth,
const Output<Node>& on_value,
const Output<Node>& off_value,
int64_t axis,
NegativeIndicesMode mode = NegativeIndicesMode::IGNORE_NEGATIVE);

bool visit_attributes(AttributeVisitor& visitor) override;
std::shared_ptr<Node> clone_with_new_inputs(const OutputVector& new_args) const override;
void validate_and_infer_types() override;

bool evaluate(TensorVector& outputs, const TensorVector& inputs) const override;
bool has_evaluate() const override;

/// \return The index of the one-hot axis.
const int64_t& get_axis() const {
return m_axis;
/// \brief Sets the negative indices mode.
void set_negative_indices_mode(NegativeIndicesMode mode) {
m_negative_indices_mode = mode;
}
/// \return The negative indices mode.
NegativeIndicesMode get_negative_indices_mode() const {
return m_negative_indices_mode;
}
void set_axis(int64_t axis);

protected:
int64_t m_axis;

private:
friend void inline resolve_axis(OneHot* op);
NegativeIndicesMode m_negative_indices_mode;
};
} // namespace v1
} // namespace v16
} // namespace op

OPENVINO_API
std::ostream& operator<<(std::ostream& s, const op::v16::OneHot::NegativeIndicesMode& mode);

template <>
class OPENVINO_API AttributeAdapter<op::v16::OneHot::NegativeIndicesMode>
: public EnumAttributeAdapterBase<op::v16::OneHot::NegativeIndicesMode> {
public:
AttributeAdapter(op::v16::OneHot::NegativeIndicesMode& value)
: EnumAttributeAdapterBase<op::v16::OneHot::NegativeIndicesMode>(value) {}

OPENVINO_RTTI("AttributeAdapter<ov::op::v16::OneHot::NegativeIndicesMode>");
~AttributeAdapter() override;
};

} // namespace ov
56 changes: 56 additions & 0 deletions src/core/include/openvino/op/util/one_hot_base.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// Copyright (C) 2018-2025 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//

#pragma once

#include "openvino/op/op.hpp"
#include "openvino/op/util/attr_types.hpp"

namespace ov {
namespace op {
namespace util {
class OPENVINO_API OneHotBase : public Op {
public:
OPENVINO_OP("OneHot", "util");

/// \brief Constructs a one-hot operation.
OneHotBase() = default;

/// \brief Constructs a one-hot operation.
///
/// \param indices Input tensor containing indices.
/// \param depth Specifies number of classes and the size of one-hot dimension.
/// \param on_value Specifies value that the locations in output tensor represented
/// by indices in input take.
/// \param off_value Specifies value that the locations in output tensor not
/// represented
/// by indices in input take.
/// \param axis Axis along which one-hot representation in added.
OneHotBase(const Output<Node>& indices,
const Output<Node>& depth,
const Output<Node>& on_value,
const Output<Node>& off_value,
int64_t axis);

bool visit_attributes(AttributeVisitor& visitor) override;
void validate_and_infer_types() override;

/// \return The index of the one-hot axis.
const int64_t& get_axis() const {
return m_axis;
}

/// @brief Sets the index of the one-hot axis.
/// @param axis The index of the one-hot axis.
void set_axis(int64_t axis);

protected:
int64_t m_axis;

private:
friend void inline resolve_axis(OneHotBase* op);
};
} // namespace util
} // namespace op
} // namespace ov
1 change: 1 addition & 0 deletions src/core/include/openvino/opsets/opset16_tbl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,4 @@ _OPENVINO_OP_REG(Identity, ov::op::v16)
_OPENVINO_OP_REG(ISTFT, ov::op::v16)
_OPENVINO_OP_REG(SegmentMax, ov::op::v16)
_OPENVINO_OP_REG(SparseFillEmptyRows, ov::op::v16)
_OPENVINO_OP_REG(OneHot, ov::op::v16)
18 changes: 12 additions & 6 deletions src/core/reference/include/openvino/reference/one_hot.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#pragma once

#include "openvino/core/shape.hpp"
#include "openvino/op/one_hot.hpp"

namespace ov {
namespace reference {
Expand All @@ -16,7 +17,9 @@ void one_hot(const INPUT_TYPE* indices,
const size_t depth,
const int64_t one_hot_axis,
const char* on_value,
const char* off_value) {
const char* off_value,
const op::v16::OneHot::NegativeIndicesMode mode) {
const bool is_mode_normalize = mode == op::v16::OneHot::NegativeIndicesMode::NORMALIZE;
const size_t num_ind = shape_size(indices_shape);
// Step 1: Set off_value to the output.
for (auto p = out; p < out + num_ind * depth * out_elem_size; p += out_elem_size)
Expand All @@ -31,11 +34,14 @@ void one_hot(const INPUT_TYPE* indices,
// Step 2: Write on_value at needed positions
for (size_t outer_i = 0; outer_i < num_ind; outer_i += inner_block) {
for (size_t inner_i = 0; inner_i < inner_block; inner_i++) {
auto input_val = indices[outer_i + inner_i];
// Negative indices are ignored
if ((input_val >= 0) && (static_cast<size_t>(input_val) < depth)) {
auto oh_index = static_cast<size_t>(input_val);
size_t output_offset = out_elem_size * (outer_i * depth + inner_i + oh_index * inner_block);
const int64_t input_val = static_cast<int64_t>(indices[outer_i + inner_i]);
const int64_t depth_i64 = static_cast<int64_t>(depth);
const int64_t actual_index = (input_val < 0 && is_mode_normalize) ? depth_i64 + input_val : input_val;
const int64_t max_valid = depth_i64 - 1;

if (actual_index >= 0 && actual_index <= max_valid) {
const size_t output_offset =
out_elem_size * (outer_i * depth + inner_i + static_cast<size_t>(actual_index) * inner_block);
std::copy(on_value, on_value + out_elem_size, out + output_offset);
}
}
Expand Down
33 changes: 25 additions & 8 deletions src/core/shape_inference/include/one_hot_shape_inference.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
namespace ov {
namespace op {
namespace util {

template <class T>
struct GetNotNegative {
const Node* m_op;
Expand All @@ -23,22 +22,21 @@ struct GetNotNegative {
return static_cast<T>(v);
}
};
} // namespace util
namespace v1 {
void inline resolve_axis(OneHot* op) {

void inline resolve_axis(OneHotBase* op) {
if (op->get_input_size() < 1) {
return;
}
const auto& indices_shape = op->get_input_partial_shape(0);
if (indices_shape.rank().is_static()) {
op->m_axis = ov::util::try_normalize_axis(op->m_axis, indices_shape.rank() + 1, *op);
}
}
} // namespace util

template <class T, class TRShape = result_shape_t<T>>
std::vector<TRShape> shape_infer(const OneHot* op,
const std::vector<T>& input_shapes,
const ITensorAccessor& ta = make_tensor_accessor()) {
std::vector<TRShape> shape_infer_base(const OneHotBase* op,
const std::vector<T>& input_shapes,
const ITensorAccessor& ta = make_tensor_accessor()) {
NODE_VALIDATION_CHECK(op, input_shapes.size() == 4);
using DimType = typename T::value_type;
const auto& indices_shape = input_shapes[0];
Expand Down Expand Up @@ -77,6 +75,25 @@ std::vector<TRShape> shape_infer(const OneHot* op,
}
return output_shapes;
}
} // namespace util

namespace v1 {
template <class TShape, class TRShape = result_shape_t<TShape>>
std::vector<TRShape> shape_infer(const OneHot* op,
const std::vector<TShape>& input_shapes,
const ITensorAccessor& ta = make_tensor_accessor()) {
return util::shape_infer_base(op, input_shapes, ta);
}
} // namespace v1

namespace v16 {
template <class TShape, class TRShape = result_shape_t<TShape>>
std::vector<TRShape> shape_infer(const OneHot* op,
const std::vector<TShape>& input_shapes,
const ITensorAccessor& ta = make_tensor_accessor()) {
return util::shape_infer_base(op, input_shapes, ta);
}
} // namespace v16

} // namespace op
} // namespace ov
Loading