From b15a37bcf672ca2a6830c844de84a64534f3c1d8 Mon Sep 17 00:00:00 2001 From: Anastasia Kuporosova Date: Wed, 20 Nov 2024 22:59:33 +0000 Subject: [PATCH 01/29] [PyOV] OpExtension --- .../python/src/pyopenvino/graph/op.cpp | 58 ++++++++----------- .../python/src/pyopenvino/graph/op.hpp | 26 +++++++++ .../python/src/pyopenvino/pyopenvino.cpp | 2 + 3 files changed, 51 insertions(+), 35 deletions(-) diff --git a/src/bindings/python/src/pyopenvino/graph/op.cpp b/src/bindings/python/src/pyopenvino/graph/op.cpp index 93d13033a1e017..285b55ae5cdcc0 100644 --- a/src/bindings/python/src/pyopenvino/graph/op.cpp +++ b/src/bindings/python/src/pyopenvino/graph/op.cpp @@ -16,47 +16,35 @@ namespace py = pybind11; -/// Trampoline class to support inheritance from TorchDecoder in Python -class PyOp : public ov::op::Op { -public: - using ov::op::Op::Op; - - // Keeps a reference to the Python object to manage its lifetime - PyOp(const py::object& py_obj) : py_handle(py_obj) {} - - void validate_and_infer_types() override { - PYBIND11_OVERRIDE(void, ov::op::Op, validate_and_infer_types); - } - - bool visit_attributes(ov::AttributeVisitor& value) override { - pybind11::gil_scoped_acquire gil; // Acquire the GIL while in this scope. - // Try to look up the overridden method on the Python side. - pybind11::function overrided_py_method = pybind11::get_override(this, "visit_attributes"); - if (overrided_py_method) { // method is found - return static_cast(overrided_py_method(&value)); // Call the Python function. - } - return false; - } +void PyOp::validate_and_infer_types() { + PYBIND11_OVERRIDE(void, ov::op::Op, validate_and_infer_types); +} - std::shared_ptr clone_with_new_inputs(const ov::OutputVector& new_args) const override { - PYBIND11_OVERRIDE_PURE(std::shared_ptr, ov::op::Op, clone_with_new_inputs, new_args); +bool PyOp::visit_attributes(ov::AttributeVisitor& value) { + py::gil_scoped_acquire gil; // Acquire the GIL while in this scope. + // Try to look up the overridden method on the Python side. + py::function overrided_py_method = pybind11::get_override(this, "visit_attributes"); + if (overrided_py_method) { // method is found + return static_cast(overrided_py_method(&value)); // Call the Python function. } + return false; +} - const type_info_t& get_type_info() const override { - PYBIND11_OVERRIDE(const ov::Node::type_info_t&, ov::op::Op, get_type_info); - } +std::shared_ptr PyOp::clone_with_new_inputs(const ov::OutputVector& new_args) const { + PYBIND11_OVERRIDE_PURE(std::shared_ptr, ov::op::Op, clone_with_new_inputs, new_args); +} - bool evaluate(ov::TensorVector& output_values, const ov::TensorVector& input_values) const override { - PYBIND11_OVERRIDE(bool, ov::op::Op, evaluate, output_values, input_values); - } +const ov::op::Op::type_info_t& PyOp::get_type_info() const { + PYBIND11_OVERRIDE(const ov::Node::type_info_t&, ov::op::Op, get_type_info); +} - bool has_evaluate() const override { - PYBIND11_OVERRIDE(bool, ov::op::Op, has_evaluate); - } +bool PyOp::evaluate(ov::TensorVector& output_values, const ov::TensorVector& input_values) const { + PYBIND11_OVERRIDE(bool, ov::op::Op, evaluate, output_values, input_values); +} -private: - py::object py_handle; // Holds the Python object to manage its lifetime -}; +bool PyOp::has_evaluate() const { + PYBIND11_OVERRIDE(bool, ov::op::Op, has_evaluate); +} void regclass_graph_Op(py::module m) { py::class_, PyOp, ov::Node> op(m, "Op"); diff --git a/src/bindings/python/src/pyopenvino/graph/op.hpp b/src/bindings/python/src/pyopenvino/graph/op.hpp index 916a3471f9dade..896a55aedd8a38 100644 --- a/src/bindings/python/src/pyopenvino/graph/op.hpp +++ b/src/bindings/python/src/pyopenvino/graph/op.hpp @@ -6,6 +6,32 @@ #include +#include "openvino/op/op.hpp" + namespace py = pybind11; +/// Trampoline class to support inheritance from TorchDecoder in Python +class PyOp : public ov::op::Op { +public: + using ov::op::Op::Op; + + // Keeps a reference to the Python object to manage its lifetime + PyOp(const py::object& py_obj) : py_handle(py_obj) {} + + void validate_and_infer_types() override; + + bool visit_attributes(ov::AttributeVisitor& value) override; + + std::shared_ptr clone_with_new_inputs(const ov::OutputVector& new_args) const override; + + const type_info_t& get_type_info() const override; + + bool evaluate(ov::TensorVector& output_values, const ov::TensorVector& input_values) const override; + + bool has_evaluate() const override; + +private: + py::object py_handle; // Holds the Python object to manage its lifetime +}; + void regclass_graph_Op(py::module m); diff --git a/src/bindings/python/src/pyopenvino/pyopenvino.cpp b/src/bindings/python/src/pyopenvino/pyopenvino.cpp index ee3ef1c8b8144e..12d9c0bde410a7 100644 --- a/src/bindings/python/src/pyopenvino/pyopenvino.cpp +++ b/src/bindings/python/src/pyopenvino/pyopenvino.cpp @@ -20,6 +20,7 @@ #include "pyopenvino/graph/node_input.hpp" #include "pyopenvino/graph/node_output.hpp" #include +#include #if defined(ENABLE_OV_ONNX_FRONTEND) # include "pyopenvino/graph/onnx_import/onnx_import.hpp" #endif @@ -228,6 +229,7 @@ PYBIND11_MODULE(_pyopenvino, m) { regclass_graph_PartialShape(m); regclass_graph_Node(m); regclass_graph_Op(m); + regclass_graph_OpExtension(m); regclass_graph_Input(m); regclass_graph_NodeFactory(m); regclass_graph_Strides(m); From f3b49c23a329bfe769e47ccf2f20001a894108c4 Mon Sep 17 00:00:00 2001 From: Anastasia Kuporosova Date: Wed, 20 Nov 2024 23:33:01 +0000 Subject: [PATCH 02/29] python files --- src/bindings/python/src/openvino/__init__.py | 1 + tools/benchmark_tool/openvino/__init__.py | 1 + tools/mo/openvino/__init__.py | 1 + tools/ovc/openvino/__init__.py | 1 + 4 files changed, 4 insertions(+) diff --git a/src/bindings/python/src/openvino/__init__.py b/src/bindings/python/src/openvino/__init__.py index 57f03f00c2eebf..dbf9be7f30257d 100644 --- a/src/bindings/python/src/openvino/__init__.py +++ b/src/bindings/python/src/openvino/__init__.py @@ -55,6 +55,7 @@ from openvino._pyopenvino import RemoteContext from openvino._pyopenvino import RemoteTensor from openvino._pyopenvino import Op +from openvino._pyopenvino import OpExtension # libva related: from openvino._pyopenvino import VAContext diff --git a/tools/benchmark_tool/openvino/__init__.py b/tools/benchmark_tool/openvino/__init__.py index 57f03f00c2eebf..dbf9be7f30257d 100644 --- a/tools/benchmark_tool/openvino/__init__.py +++ b/tools/benchmark_tool/openvino/__init__.py @@ -55,6 +55,7 @@ from openvino._pyopenvino import RemoteContext from openvino._pyopenvino import RemoteTensor from openvino._pyopenvino import Op +from openvino._pyopenvino import OpExtension # libva related: from openvino._pyopenvino import VAContext diff --git a/tools/mo/openvino/__init__.py b/tools/mo/openvino/__init__.py index b015570964c520..d8abdcec0a0708 100644 --- a/tools/mo/openvino/__init__.py +++ b/tools/mo/openvino/__init__.py @@ -51,6 +51,7 @@ from openvino._pyopenvino import RemoteContext from openvino._pyopenvino import RemoteTensor from openvino._pyopenvino import Op + from openvino._pyopenvino import OpExtension # libva related: from openvino._pyopenvino import VAContext diff --git a/tools/ovc/openvino/__init__.py b/tools/ovc/openvino/__init__.py index 57f03f00c2eebf..dbf9be7f30257d 100644 --- a/tools/ovc/openvino/__init__.py +++ b/tools/ovc/openvino/__init__.py @@ -55,6 +55,7 @@ from openvino._pyopenvino import RemoteContext from openvino._pyopenvino import RemoteTensor from openvino._pyopenvino import Op +from openvino._pyopenvino import OpExtension # libva related: from openvino._pyopenvino import VAContext From 2f988c44461e06db4fdd8ac0ad94b8e1ade4eb5e Mon Sep 17 00:00:00 2001 From: Anastasia Kuporosova Date: Thu, 21 Nov 2024 09:01:33 +0000 Subject: [PATCH 03/29] op_ext --- .../src/pyopenvino/graph/op_extension.cpp | 25 +++++++++++++++++++ .../src/pyopenvino/graph/op_extension.hpp | 11 ++++++++ 2 files changed, 36 insertions(+) create mode 100644 src/bindings/python/src/pyopenvino/graph/op_extension.cpp create mode 100644 src/bindings/python/src/pyopenvino/graph/op_extension.hpp diff --git a/src/bindings/python/src/pyopenvino/graph/op_extension.cpp b/src/bindings/python/src/pyopenvino/graph/op_extension.cpp new file mode 100644 index 00000000000000..357ba5701aeedb --- /dev/null +++ b/src/bindings/python/src/pyopenvino/graph/op_extension.cpp @@ -0,0 +1,25 @@ +// Copyright (C) 2018-2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "openvino/core/op_extension.hpp" + +#include + +#include +#include "pyopenvino/graph/op.hpp" +#include "pyopenvino/core/common.hpp" + +namespace py = pybind11; + +void regclass_graph_OpExtension(py::module m) { + py::class_, std::shared_ptr>, ov::Extension> op_extension(m, "OpExtension"); + op_extension.doc() = "openvino.OpExtension provides the base interface for OpenVINO extensions."; + + op_extension.def("__repr__", [](const ov::OpExtension& self) { + return Common::get_simple_repr(self); + }); + + op_extension.def(py::init<>()); + op_extension.def(py::init<>()); +} \ No newline at end of file diff --git a/src/bindings/python/src/pyopenvino/graph/op_extension.hpp b/src/bindings/python/src/pyopenvino/graph/op_extension.hpp new file mode 100644 index 00000000000000..2bccdeea95a239 --- /dev/null +++ b/src/bindings/python/src/pyopenvino/graph/op_extension.hpp @@ -0,0 +1,11 @@ +// Copyright (C) 2018-2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include + +namespace py = pybind11; + +void regclass_graph_OpExtension(py::module m); From 8330567a133fb2ec70eb813b24f682cfc113a64b Mon Sep 17 00:00:00 2001 From: Anastasia Kuporosova Date: Thu, 21 Nov 2024 10:15:44 +0000 Subject: [PATCH 04/29] remove from MO --- src/bindings/python/src/pyopenvino/graph/op_extension.cpp | 2 +- tools/mo/openvino/__init__.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/bindings/python/src/pyopenvino/graph/op_extension.cpp b/src/bindings/python/src/pyopenvino/graph/op_extension.cpp index 357ba5701aeedb..2000283f94a157 100644 --- a/src/bindings/python/src/pyopenvino/graph/op_extension.cpp +++ b/src/bindings/python/src/pyopenvino/graph/op_extension.cpp @@ -22,4 +22,4 @@ void regclass_graph_OpExtension(py::module m) { op_extension.def(py::init<>()); op_extension.def(py::init<>()); -} \ No newline at end of file +} diff --git a/tools/mo/openvino/__init__.py b/tools/mo/openvino/__init__.py index d8abdcec0a0708..b015570964c520 100644 --- a/tools/mo/openvino/__init__.py +++ b/tools/mo/openvino/__init__.py @@ -51,7 +51,6 @@ from openvino._pyopenvino import RemoteContext from openvino._pyopenvino import RemoteTensor from openvino._pyopenvino import Op - from openvino._pyopenvino import OpExtension # libva related: from openvino._pyopenvino import VAContext From 0255696724901eb713e09c9d88fb99ce3b5a806b Mon Sep 17 00:00:00 2001 From: Anastasia Kuporosova Date: Mon, 2 Dec 2024 19:53:37 +0000 Subject: [PATCH 05/29] fix import error --- .../python/src/pyopenvino/frontend/extension.cpp | 3 ++- .../python/src/pyopenvino/graph/op_extension.cpp | 11 +++++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/bindings/python/src/pyopenvino/frontend/extension.cpp b/src/bindings/python/src/pyopenvino/frontend/extension.cpp index 4446ea2c9acc33..c2c656c05aa63c 100644 --- a/src/bindings/python/src/pyopenvino/frontend/extension.cpp +++ b/src/bindings/python/src/pyopenvino/frontend/extension.cpp @@ -142,7 +142,8 @@ void regclass_frontend_ProgressReporterExtension(py::module m) { } void regclass_frontend_OpExtension(py::module m) { - py::class_, std::shared_ptr>, ConversionExtension> ext(m, + py::module frontend = m.def_submodule("frontend"); + py::class_, std::shared_ptr>, ConversionExtension> ext(frontend, "OpExtension", py::dynamic_attr()); diff --git a/src/bindings/python/src/pyopenvino/graph/op_extension.cpp b/src/bindings/python/src/pyopenvino/graph/op_extension.cpp index 2000283f94a157..57d493811c77f7 100644 --- a/src/bindings/python/src/pyopenvino/graph/op_extension.cpp +++ b/src/bindings/python/src/pyopenvino/graph/op_extension.cpp @@ -13,7 +13,7 @@ namespace py = pybind11; void regclass_graph_OpExtension(py::module m) { - py::class_, std::shared_ptr>, ov::Extension> op_extension(m, "OpExtension"); + py::class_, std::shared_ptr>> op_extension(m, "OpExtension"); op_extension.doc() = "openvino.OpExtension provides the base interface for OpenVINO extensions."; op_extension.def("__repr__", [](const ov::OpExtension& self) { @@ -21,5 +21,12 @@ void regclass_graph_OpExtension(py::module m) { }); op_extension.def(py::init<>()); - op_extension.def(py::init<>()); + op_extension.def(py::init([](py::object dtype) { + if (py::isinstance(dtype)) { + return ov::OpExtension(); + } + std::stringstream str; + str << "Unsupported data type : '" << dtype << "' is passed as an argument."; + OPENVINO_THROW(str.str()); + })); } From 37275fb07ed699cbe6ded50a897d9ab82c1fefa2 Mon Sep 17 00:00:00 2001 From: Anastasia Kuporosova Date: Mon, 2 Dec 2024 22:06:32 +0000 Subject: [PATCH 06/29] now extension may be added --- src/bindings/python/src/pyopenvino/core/extension.cpp | 2 +- src/bindings/python/src/pyopenvino/graph/op_extension.cpp | 6 ++++-- src/bindings/python/src/pyopenvino/pyopenvino.cpp | 3 +-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/bindings/python/src/pyopenvino/core/extension.cpp b/src/bindings/python/src/pyopenvino/core/extension.cpp index 651c2ed7131ea6..bf340de043cb62 100644 --- a/src/bindings/python/src/pyopenvino/core/extension.cpp +++ b/src/bindings/python/src/pyopenvino/core/extension.cpp @@ -9,7 +9,7 @@ #include #include -#include "openvino/frontend/manager.hpp" +#include "openvino/core/extension.hpp" #include "pyopenvino/core/common.hpp" namespace py = pybind11; diff --git a/src/bindings/python/src/pyopenvino/graph/op_extension.cpp b/src/bindings/python/src/pyopenvino/graph/op_extension.cpp index 57d493811c77f7..782dc51c903bc1 100644 --- a/src/bindings/python/src/pyopenvino/graph/op_extension.cpp +++ b/src/bindings/python/src/pyopenvino/graph/op_extension.cpp @@ -9,11 +9,12 @@ #include #include "pyopenvino/graph/op.hpp" #include "pyopenvino/core/common.hpp" +#include "pyopenvino/core/extension.hpp" namespace py = pybind11; void regclass_graph_OpExtension(py::module m) { - py::class_, std::shared_ptr>> op_extension(m, "OpExtension"); + py::class_, std::shared_ptr>, ov::Extension> op_extension(m, "OpExtension"); op_extension.doc() = "openvino.OpExtension provides the base interface for OpenVINO extensions."; op_extension.def("__repr__", [](const ov::OpExtension& self) { @@ -22,7 +23,8 @@ void regclass_graph_OpExtension(py::module m) { op_extension.def(py::init<>()); op_extension.def(py::init([](py::object dtype) { - if (py::isinstance(dtype)) { + py::object py_issubclass = py::module::import("builtins").attr("issubclass"); + if (py_issubclass(dtype, py::type::of())) { return ov::OpExtension(); } std::stringstream str; diff --git a/src/bindings/python/src/pyopenvino/pyopenvino.cpp b/src/bindings/python/src/pyopenvino/pyopenvino.cpp index 12d9c0bde410a7..e50d314fd08ab2 100644 --- a/src/bindings/python/src/pyopenvino/pyopenvino.cpp +++ b/src/bindings/python/src/pyopenvino/pyopenvino.cpp @@ -219,7 +219,7 @@ PYBIND11_MODULE(_pyopenvino, m) { You might want to use this function if you are developing a dynamically-loaded library which should clean up all resources after itself when the library is unloaded. )"); - + regclass_Extension(m); regclass_graph_PyRTMap(m); regmodule_graph_types(m); regclass_graph_Symbol(m); // Symbol must be registered before Dimension @@ -276,7 +276,6 @@ PYBIND11_MODULE(_pyopenvino, m) { regclass_Version(m); regclass_AsyncInferQueue(m); regclass_ProfilingInfo(m); - regclass_Extension(m); regclass_RemoteContext(m); regclass_RemoteTensor(m); From fdb460c7803c9023a51de081780695d47bb56928 Mon Sep 17 00:00:00 2001 From: Anastasia Kuporosova Date: Mon, 2 Dec 2024 22:09:21 +0000 Subject: [PATCH 07/29] code style --- src/bindings/python/src/pyopenvino/frontend/extension.cpp | 5 ++--- src/bindings/python/src/pyopenvino/graph/op.cpp | 8 ++++---- src/bindings/python/src/pyopenvino/graph/op_extension.cpp | 7 +++++-- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/bindings/python/src/pyopenvino/frontend/extension.cpp b/src/bindings/python/src/pyopenvino/frontend/extension.cpp index c2c656c05aa63c..9bb39aa8ea9030 100644 --- a/src/bindings/python/src/pyopenvino/frontend/extension.cpp +++ b/src/bindings/python/src/pyopenvino/frontend/extension.cpp @@ -143,9 +143,8 @@ void regclass_frontend_ProgressReporterExtension(py::module m) { void regclass_frontend_OpExtension(py::module m) { py::module frontend = m.def_submodule("frontend"); - py::class_, std::shared_ptr>, ConversionExtension> ext(frontend, - "OpExtension", - py::dynamic_attr()); + py::class_, std::shared_ptr>, ConversionExtension> + ext(frontend, "OpExtension", py::dynamic_attr()); ext.def(py::init([](const std::string& fw_type_name, const std::map& attr_names_map, diff --git a/src/bindings/python/src/pyopenvino/graph/op.cpp b/src/bindings/python/src/pyopenvino/graph/op.cpp index 285b55ae5cdcc0..08923bf65c0593 100644 --- a/src/bindings/python/src/pyopenvino/graph/op.cpp +++ b/src/bindings/python/src/pyopenvino/graph/op.cpp @@ -20,7 +20,7 @@ void PyOp::validate_and_infer_types() { PYBIND11_OVERRIDE(void, ov::op::Op, validate_and_infer_types); } -bool PyOp::visit_attributes(ov::AttributeVisitor& value) { +bool PyOp::visit_attributes(ov::AttributeVisitor& value) { py::gil_scoped_acquire gil; // Acquire the GIL while in this scope. // Try to look up the overridden method on the Python side. py::function overrided_py_method = pybind11::get_override(this, "visit_attributes"); @@ -30,7 +30,7 @@ bool PyOp::visit_attributes(ov::AttributeVisitor& value) { return false; } -std::shared_ptr PyOp::clone_with_new_inputs(const ov::OutputVector& new_args) const { +std::shared_ptr PyOp::clone_with_new_inputs(const ov::OutputVector& new_args) const { PYBIND11_OVERRIDE_PURE(std::shared_ptr, ov::op::Op, clone_with_new_inputs, new_args); } @@ -38,11 +38,11 @@ const ov::op::Op::type_info_t& PyOp::get_type_info() const { PYBIND11_OVERRIDE(const ov::Node::type_info_t&, ov::op::Op, get_type_info); } -bool PyOp::evaluate(ov::TensorVector& output_values, const ov::TensorVector& input_values) const { +bool PyOp::evaluate(ov::TensorVector& output_values, const ov::TensorVector& input_values) const { PYBIND11_OVERRIDE(bool, ov::op::Op, evaluate, output_values, input_values); } -bool PyOp::has_evaluate() const { +bool PyOp::has_evaluate() const { PYBIND11_OVERRIDE(bool, ov::op::Op, has_evaluate); } diff --git a/src/bindings/python/src/pyopenvino/graph/op_extension.cpp b/src/bindings/python/src/pyopenvino/graph/op_extension.cpp index 782dc51c903bc1..3adfc78e899b05 100644 --- a/src/bindings/python/src/pyopenvino/graph/op_extension.cpp +++ b/src/bindings/python/src/pyopenvino/graph/op_extension.cpp @@ -7,14 +7,17 @@ #include #include -#include "pyopenvino/graph/op.hpp" + #include "pyopenvino/core/common.hpp" #include "pyopenvino/core/extension.hpp" +#include "pyopenvino/graph/op.hpp" namespace py = pybind11; void regclass_graph_OpExtension(py::module m) { - py::class_, std::shared_ptr>, ov::Extension> op_extension(m, "OpExtension"); + py::class_, std::shared_ptr>, ov::Extension> op_extension( + m, + "OpExtension"); op_extension.doc() = "openvino.OpExtension provides the base interface for OpenVINO extensions."; op_extension.def("__repr__", [](const ov::OpExtension& self) { From be9535c469c74568ba214bac86508c938258cafe Mon Sep 17 00:00:00 2001 From: Anastasia Kuporosova Date: Tue, 3 Dec 2024 21:03:34 +0000 Subject: [PATCH 08/29] test op-ext --- .../python/tests/test_graph/test_custom_op.py | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/src/bindings/python/tests/test_graph/test_custom_op.py b/src/bindings/python/tests/test_graph/test_custom_op.py index 8643844e2c54fd..71b040305a3716 100644 --- a/src/bindings/python/tests/test_graph/test_custom_op.py +++ b/src/bindings/python/tests/test_graph/test_custom_op.py @@ -197,3 +197,53 @@ def test_custom_op(): input_tensor = Tensor(input_data) results = request.infer({"data": input_tensor}) assert np.allclose(results[list(results)[0]], expected_output, 1e-4, 1e-4) + + +def test_op_extension(): + import numpy as np + from openvino import Core, OpExtension + from openvino import Op + from openvino import CompiledModel, Model, Dimension, Shape, Tensor, compile_model, serialize + from openvino.runtime import DiscreteTypeInfo + import openvino.runtime.opset14 as ops + + + class CustomOpWithAttribute(Op): + class_type_info = DiscreteTypeInfo("CustomOpWithAttribute", "extension") + + def __init__(self, inputs, attrs): + super().__init__(self) + self._attrs = attrs + self.set_arguments(inputs) + self.constructor_validate_and_infer_types() + + def validate_and_infer_types(self): + self.set_output_type(0, self.get_input_element_type(0), self.get_input_partial_shape(0)) + + def clone_with_new_inputs(self, new_inputs): + return CustomOpWithAttribute(new_inputs) + + def get_type_info(self): + return CustomOpWithAttribute.class_type_info + + def visit_attributes(self, visitor): + visitor.on_attributes(self._attrs) + return True + + + #core.add_extension(); + core = Core() + core.add_extension(OpExtension(CustomOpWithAttribute)) + input_shape = [2, 1] + + param1 = ops.parameter(Shape(input_shape), dtype=np.float32, name="data1") + param2 = ops.parameter(Shape(input_shape), dtype=np.float32, name="data2") + custom = CustomOpWithAttribute(inputs=[param1, param2], attrs={"value_str": "test_attribute"}) + #res = ops.result(custom, name="result") + model_with_op_attr = Model(custom, [param1, param2], "CustomModel") + + serialize(model_with_op_attr, "model_with_custom.xml", "model_with_custom.bin") + m = core.read_model("model_with_custom.xml") + + + From 27673506ff3e6afa3815310b72dfc2cdfe666f5f Mon Sep 17 00:00:00 2001 From: Anastasia Kuporosova Date: Wed, 4 Dec 2024 18:07:20 +0000 Subject: [PATCH 09/29] add default constructor --- src/bindings/python/src/pyopenvino/graph/op.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/bindings/python/src/pyopenvino/graph/op.hpp b/src/bindings/python/src/pyopenvino/graph/op.hpp index 896a55aedd8a38..743a4dda882323 100644 --- a/src/bindings/python/src/pyopenvino/graph/op.hpp +++ b/src/bindings/python/src/pyopenvino/graph/op.hpp @@ -15,6 +15,8 @@ class PyOp : public ov::op::Op { public: using ov::op::Op::Op; + PyOp() = default; + // Keeps a reference to the Python object to manage its lifetime PyOp(const py::object& py_obj) : py_handle(py_obj) {} From 7dec005bd8f313c0edaeb4984eef281b741dade9 Mon Sep 17 00:00:00 2001 From: Anastasia Kuporosova Date: Thu, 5 Dec 2024 08:53:00 +0000 Subject: [PATCH 10/29] py wrapper for op extension --- .../src/pyopenvino/graph/op_extension.cpp | 50 +++++++++++++++++-- 1 file changed, 46 insertions(+), 4 deletions(-) diff --git a/src/bindings/python/src/pyopenvino/graph/op_extension.cpp b/src/bindings/python/src/pyopenvino/graph/op_extension.cpp index 3adfc78e899b05..972fee1d034afb 100644 --- a/src/bindings/python/src/pyopenvino/graph/op_extension.cpp +++ b/src/bindings/python/src/pyopenvino/graph/op_extension.cpp @@ -11,24 +11,66 @@ #include "pyopenvino/core/common.hpp" #include "pyopenvino/core/extension.hpp" #include "pyopenvino/graph/op.hpp" +#include "pyopenvino/graph/node_output.hpp" namespace py = pybind11; +class PyOpExtension : public ov::BaseOpExtension { +public: + PyOpExtension(const py::object& dtype) : py_handle_dtype{dtype} { + auto type_info = py_handle_dtype.attr("get_type_info")(); + if (!py::isinstance(type_info)) { + OPENVINO_THROW("blahbla"); + } + m_type_info = py::cast(type_info); + const auto& ext_type = get_type_info(); + OPENVINO_ASSERT(ext_type.name != nullptr && ext_type.version_id != nullptr, + "Extension type should have information about operation set and operation type."); + } + + const ov::DiscreteTypeInfo& get_type_info() const override { + return m_type_info; + } + + ov::OutputVector create(const ov::OutputVector& inputs, ov::AttributeVisitor& visitor) const override { + // TODO: Create new python object using some python API under GIL then call its method + py::gil_scoped_acquire acquire; + const auto node = py_handle_dtype(); + + // node->set_arguments(inputs); + auto py_in = py::cast(inputs); + node.attr("set_arguments")(py_in); + // if (node.attr("visit_attributes")(&visitor)) { + // node.attr("constructor_validate_and_infer_types")(); + // } + const auto q = node.attr("outputs")(); + const auto t = py::cast(q); + return t; + } + + std::vector get_attached_extensions() const override { + return {}; + } + +private: + py::object py_handle_dtype; // Holds the Python object to manage its lifetime + ov::DiscreteTypeInfo m_type_info; +}; + void regclass_graph_OpExtension(py::module m) { - py::class_, std::shared_ptr>, ov::Extension> op_extension( + py::class_, ov::Extension> op_extension( m, "OpExtension"); op_extension.doc() = "openvino.OpExtension provides the base interface for OpenVINO extensions."; - op_extension.def("__repr__", [](const ov::OpExtension& self) { + op_extension.def("__repr__", [](const PyOpExtension& self) { return Common::get_simple_repr(self); }); - op_extension.def(py::init<>()); op_extension.def(py::init([](py::object dtype) { py::object py_issubclass = py::module::import("builtins").attr("issubclass"); if (py_issubclass(dtype, py::type::of())) { - return ov::OpExtension(); + return PyOpExtension(dtype); } std::stringstream str; str << "Unsupported data type : '" << dtype << "' is passed as an argument."; From 2cd726acd8c49430d0552aef4ff15a559c7ec25a Mon Sep 17 00:00:00 2001 From: Anastasia Kuporosova Date: Sun, 8 Dec 2024 01:06:17 +0000 Subject: [PATCH 11/29] hide methods --- .../pyopenvino/graph/discrete_type_info.cpp | 17 --------- .../pyopenvino/graph/discrete_type_info.hpp | 20 +++++++++++ .../python/src/pyopenvino/graph/node.cpp | 1 + .../python/src/pyopenvino/graph/op.cpp | 19 ++++++++-- .../python/src/pyopenvino/graph/op.hpp | 16 ++++++++- .../src/pyopenvino/graph/op_extension.cpp | 35 ++++++++++++------- 6 files changed, 75 insertions(+), 33 deletions(-) diff --git a/src/bindings/python/src/pyopenvino/graph/discrete_type_info.cpp b/src/bindings/python/src/pyopenvino/graph/discrete_type_info.cpp index cb4e192bfcc3af..78ea3f38240f64 100644 --- a/src/bindings/python/src/pyopenvino/graph/discrete_type_info.cpp +++ b/src/bindings/python/src/pyopenvino/graph/discrete_type_info.cpp @@ -8,28 +8,11 @@ #include #include -#include "openvino/core/type.hpp" #include "pyopenvino/core/common.hpp" #include "pyopenvino/utils/utils.hpp" namespace py = pybind11; -// DiscreteTypeInfo doesn't own provided memory. Wrapper allows to avoid leaks. -class DiscreteTypeInfoWrapper : public ov::DiscreteTypeInfo { -private: - const std::string name_str; - const std::string version_id_str; - -public: - DiscreteTypeInfoWrapper(std::string _name_str, std::string _version_id_str) - : DiscreteTypeInfo(nullptr, nullptr, nullptr), - name_str(std::move(_name_str)), - version_id_str(std::move(_version_id_str)) { - name = name_str.c_str(); - version_id = version_id_str.c_str(); - } -}; - void regclass_graph_DiscreteTypeInfo(py::module m) { py::class_> discrete_type_info(m, "DiscreteTypeInfo"); discrete_type_info.doc() = "openvino.runtime.DiscreteTypeInfo wraps ov::DiscreteTypeInfo"; diff --git a/src/bindings/python/src/pyopenvino/graph/discrete_type_info.hpp b/src/bindings/python/src/pyopenvino/graph/discrete_type_info.hpp index a94ed020cc983c..14e3474af949e8 100644 --- a/src/bindings/python/src/pyopenvino/graph/discrete_type_info.hpp +++ b/src/bindings/python/src/pyopenvino/graph/discrete_type_info.hpp @@ -6,6 +6,26 @@ #include +#include "openvino/core/type.hpp" +#include namespace py = pybind11; + +// DiscreteTypeInfo doesn't own provided memory. Wrapper allows to avoid leaks. +class DiscreteTypeInfoWrapper : public ov::DiscreteTypeInfo { +private: + const std::string name_str; + const std::string version_id_str; + +public: + DiscreteTypeInfoWrapper(std::string _name_str, std::string _version_id_str) + : DiscreteTypeInfo(nullptr, nullptr, nullptr), + name_str(std::move(_name_str)), + version_id_str(std::move(_version_id_str)) { + name = name_str.c_str(); + version_id = version_id_str.c_str(); + } +}; + + void regclass_graph_DiscreteTypeInfo(py::module m); diff --git a/src/bindings/python/src/pyopenvino/graph/node.cpp b/src/bindings/python/src/pyopenvino/graph/node.cpp index 3aa737928a46d6..290f3bb34376a9 100644 --- a/src/bindings/python/src/pyopenvino/graph/node.cpp +++ b/src/bindings/python/src/pyopenvino/graph/node.cpp @@ -439,6 +439,7 @@ void regclass_graph_Node(py::module m) { node.def_property_readonly("type_info", &ov::Node::get_type_info); node.def_property("friendly_name", &ov::Node::get_friendly_name, &ov::Node::set_friendly_name); + node.def("visit_attributes", &ov::Node::visit_attributes); node.def("get_attributes", [](const std::shared_ptr& self) { util::DictAttributeSerializer dict_serializer(self); return dict_serializer.get_attributes(); diff --git a/src/bindings/python/src/pyopenvino/graph/op.cpp b/src/bindings/python/src/pyopenvino/graph/op.cpp index 08923bf65c0593..feac0ad4cbeb20 100644 --- a/src/bindings/python/src/pyopenvino/graph/op.cpp +++ b/src/bindings/python/src/pyopenvino/graph/op.cpp @@ -10,9 +10,11 @@ #include #include +#include #include "openvino/core/attribute_visitor.hpp" #include "openvino/core/node.hpp" +#include "openvino/core/type.hpp" namespace py = pybind11; @@ -27,15 +29,26 @@ bool PyOp::visit_attributes(ov::AttributeVisitor& value) { if (overrided_py_method) { // method is found return static_cast(overrided_py_method(&value)); // Call the Python function. } - return false; + return true; } std::shared_ptr PyOp::clone_with_new_inputs(const ov::OutputVector& new_args) const { - PYBIND11_OVERRIDE_PURE(std::shared_ptr, ov::op::Op, clone_with_new_inputs, new_args); + py::gil_scoped_acquire gil; // Acquire the GIL while in this scope. + // Try to look up the overridden method on the Python side. + py::function overrided_py_method = pybind11::get_override(this, "clone_with_new_inputs"); + if (overrided_py_method) { // method is found + auto result = overrided_py_method(new_args); // Call the Python function. + return result.cast>(); + } + // Default implementation for clone_with_new_inputs + auto py_handle_type = py_handle.get_type(); + auto new_py_object = py_handle_type(new_args); + return new_py_object.cast>(); } const ov::op::Op::type_info_t& PyOp::get_type_info() const { - PYBIND11_OVERRIDE(const ov::Node::type_info_t&, ov::op::Op, get_type_info); + std::cout << "from cpp get type: " << m_type_info.name << std::endl; + return m_type_info; } bool PyOp::evaluate(ov::TensorVector& output_values, const ov::TensorVector& input_values) const { diff --git a/src/bindings/python/src/pyopenvino/graph/op.hpp b/src/bindings/python/src/pyopenvino/graph/op.hpp index 743a4dda882323..40591e661d1a86 100644 --- a/src/bindings/python/src/pyopenvino/graph/op.hpp +++ b/src/bindings/python/src/pyopenvino/graph/op.hpp @@ -18,7 +18,19 @@ class PyOp : public ov::op::Op { PyOp() = default; // Keeps a reference to the Python object to manage its lifetime - PyOp(const py::object& py_obj) : py_handle(py_obj) {} + PyOp(const py::object& py_obj) : py_handle(py_obj) { + py::gil_scoped_acquire gil; // Acquire the GIL while in this scope. + // Try to look up the overridden method on the Python side. + py::function overrided_py_method = pybind11::get_override(this, "get_type_info"); + if (overrided_py_method) { // method is found + auto result = overrided_py_method(); // Call the Python function. + m_type_info = result.cast(); + } else { + py_class_name = py_handle.get_type().attr("__name__").cast(); + m_type_info = ov::DiscreteTypeInfo(py_class_name.c_str(), "extension"); + std::cout << "from op: " << m_type_info.name << std::endl; + } + } void validate_and_infer_types() override; @@ -34,6 +46,8 @@ class PyOp : public ov::op::Op { private: py::object py_handle; // Holds the Python object to manage its lifetime + ov::DiscreteTypeInfo m_type_info; + std::string py_class_name; }; void regclass_graph_Op(py::module m); diff --git a/src/bindings/python/src/pyopenvino/graph/op_extension.cpp b/src/bindings/python/src/pyopenvino/graph/op_extension.cpp index 972fee1d034afb..9d573a5c7310a5 100644 --- a/src/bindings/python/src/pyopenvino/graph/op_extension.cpp +++ b/src/bindings/python/src/pyopenvino/graph/op_extension.cpp @@ -18,13 +18,26 @@ namespace py = pybind11; class PyOpExtension : public ov::BaseOpExtension { public: PyOpExtension(const py::object& dtype) : py_handle_dtype{dtype} { - auto type_info = py_handle_dtype.attr("get_type_info")(); + py::handle type_info; + std::cout << "1"; + try { + // get_type_info() is a static method + type_info = py_handle_dtype.attr("get_type_info")(); + } catch (const std::exception& exc) { + try { + // get_type_info() is a class method + std::cout << 2 << std::endl; + type_info = py_handle_dtype(py_handle_dtype()).attr("get_type_info")(); + } catch (const std::exception& exc) { + OPENVINO_THROW("Both options failed to get type_info."); + } + } + std::cout << "3 " << py::str(type_info.attr("name")); if (!py::isinstance(type_info)) { OPENVINO_THROW("blahbla"); } m_type_info = py::cast(type_info); - const auto& ext_type = get_type_info(); - OPENVINO_ASSERT(ext_type.name != nullptr && ext_type.version_id != nullptr, + OPENVINO_ASSERT(m_type_info.name != nullptr && m_type_info.version_id != nullptr, "Extension type should have information about operation set and operation type."); } @@ -35,17 +48,15 @@ class PyOpExtension : public ov::BaseOpExtension { ov::OutputVector create(const ov::OutputVector& inputs, ov::AttributeVisitor& visitor) const override { // TODO: Create new python object using some python API under GIL then call its method py::gil_scoped_acquire acquire; + // add check for default ctor const auto node = py_handle_dtype(); - // node->set_arguments(inputs); - auto py_in = py::cast(inputs); - node.attr("set_arguments")(py_in); - // if (node.attr("visit_attributes")(&visitor)) { - // node.attr("constructor_validate_and_infer_types")(); - // } - const auto q = node.attr("outputs")(); - const auto t = py::cast(q); - return t; + node.attr("set_arguments")(py::cast(inputs)); + if (node.attr("visit_attributes")(&visitor)) { + node.attr("constructor_validate_and_infer_types")(); + } + + return py::cast(node.attr("outputs")()); } std::vector get_attached_extensions() const override { From 0b0927fc6505d92debe382915f6e4a4aedd5423f Mon Sep 17 00:00:00 2001 From: Anastasia Kuporosova Date: Mon, 9 Dec 2024 03:27:42 +0000 Subject: [PATCH 12/29] default impl for get_type_info --- src/bindings/python/src/pyopenvino/graph/op.cpp | 3 +-- src/bindings/python/src/pyopenvino/graph/op.hpp | 13 ++++++------- .../python/src/pyopenvino/graph/op_extension.cpp | 16 +++++++--------- 3 files changed, 14 insertions(+), 18 deletions(-) diff --git a/src/bindings/python/src/pyopenvino/graph/op.cpp b/src/bindings/python/src/pyopenvino/graph/op.cpp index feac0ad4cbeb20..658ec39ec7ddb1 100644 --- a/src/bindings/python/src/pyopenvino/graph/op.cpp +++ b/src/bindings/python/src/pyopenvino/graph/op.cpp @@ -47,8 +47,7 @@ std::shared_ptr PyOp::clone_with_new_inputs(const ov::OutputVector& ne } const ov::op::Op::type_info_t& PyOp::get_type_info() const { - std::cout << "from cpp get type: " << m_type_info.name << std::endl; - return m_type_info; + return *m_type_info; } bool PyOp::evaluate(ov::TensorVector& output_values, const ov::TensorVector& input_values) const { diff --git a/src/bindings/python/src/pyopenvino/graph/op.hpp b/src/bindings/python/src/pyopenvino/graph/op.hpp index 40591e661d1a86..f5253838853976 100644 --- a/src/bindings/python/src/pyopenvino/graph/op.hpp +++ b/src/bindings/python/src/pyopenvino/graph/op.hpp @@ -7,6 +7,7 @@ #include #include "openvino/op/op.hpp" +#include "pyopenvino/graph/discrete_type_info.hpp" namespace py = pybind11; @@ -23,12 +24,11 @@ class PyOp : public ov::op::Op { // Try to look up the overridden method on the Python side. py::function overrided_py_method = pybind11::get_override(this, "get_type_info"); if (overrided_py_method) { // method is found - auto result = overrided_py_method(); // Call the Python function. - m_type_info = result.cast(); + const auto result = overrided_py_method(); // Call the Python function. + m_type_info = result.cast>(); } else { - py_class_name = py_handle.get_type().attr("__name__").cast(); - m_type_info = ov::DiscreteTypeInfo(py_class_name.c_str(), "extension"); - std::cout << "from op: " << m_type_info.name << std::endl; + const auto py_class_name = py_handle.get_type().attr("__name__").cast(); + m_type_info = std::make_shared(py_class_name, "extension"); } } @@ -46,8 +46,7 @@ class PyOp : public ov::op::Op { private: py::object py_handle; // Holds the Python object to manage its lifetime - ov::DiscreteTypeInfo m_type_info; - std::string py_class_name; + std::shared_ptr m_type_info; }; void regclass_graph_Op(py::module m); diff --git a/src/bindings/python/src/pyopenvino/graph/op_extension.cpp b/src/bindings/python/src/pyopenvino/graph/op_extension.cpp index 9d573a5c7310a5..0a7483ae7e4657 100644 --- a/src/bindings/python/src/pyopenvino/graph/op_extension.cpp +++ b/src/bindings/python/src/pyopenvino/graph/op_extension.cpp @@ -12,37 +12,35 @@ #include "pyopenvino/core/extension.hpp" #include "pyopenvino/graph/op.hpp" #include "pyopenvino/graph/node_output.hpp" +#include "pyopenvino/graph/discrete_type_info.hpp" namespace py = pybind11; class PyOpExtension : public ov::BaseOpExtension { public: PyOpExtension(const py::object& dtype) : py_handle_dtype{dtype} { - py::handle type_info; - std::cout << "1"; + py::object type_info; try { // get_type_info() is a static method type_info = py_handle_dtype.attr("get_type_info")(); } catch (const std::exception& exc) { try { // get_type_info() is a class method - std::cout << 2 << std::endl; - type_info = py_handle_dtype(py_handle_dtype()).attr("get_type_info")(); + type_info = py_handle_dtype().attr("get_type_info")(); } catch (const std::exception& exc) { OPENVINO_THROW("Both options failed to get type_info."); } } - std::cout << "3 " << py::str(type_info.attr("name")); if (!py::isinstance(type_info)) { OPENVINO_THROW("blahbla"); } - m_type_info = py::cast(type_info); - OPENVINO_ASSERT(m_type_info.name != nullptr && m_type_info.version_id != nullptr, + m_type_info = type_info.cast>(); + OPENVINO_ASSERT(m_type_info->name != nullptr && m_type_info->version_id != nullptr, "Extension type should have information about operation set and operation type."); } const ov::DiscreteTypeInfo& get_type_info() const override { - return m_type_info; + return *m_type_info; } ov::OutputVector create(const ov::OutputVector& inputs, ov::AttributeVisitor& visitor) const override { @@ -65,7 +63,7 @@ class PyOpExtension : public ov::BaseOpExtension { private: py::object py_handle_dtype; // Holds the Python object to manage its lifetime - ov::DiscreteTypeInfo m_type_info; + std::shared_ptr m_type_info; }; void regclass_graph_OpExtension(py::module m) { From 056c1132c9796ca4125be6ae2099e9ff1ac6d0d8 Mon Sep 17 00:00:00 2001 From: Anastasia Kuporosova Date: Mon, 9 Dec 2024 08:00:37 +0000 Subject: [PATCH 13/29] new add_ext to core --- .../python/src/pyopenvino/core/core.cpp | 14 +++++ .../src/pyopenvino/graph/op_extension.cpp | 50 ------------------ .../src/pyopenvino/graph/op_extension.hpp | 52 +++++++++++++++++++ 3 files changed, 66 insertions(+), 50 deletions(-) diff --git a/src/bindings/python/src/pyopenvino/core/core.cpp b/src/bindings/python/src/pyopenvino/core/core.cpp index 68e3e5cc4841ed..18e426d94ff71d 100644 --- a/src/bindings/python/src/pyopenvino/core/core.cpp +++ b/src/bindings/python/src/pyopenvino/core/core.cpp @@ -14,6 +14,7 @@ #include "common.hpp" #include "pyopenvino/core/remote_context.hpp" +#include "pyopenvino/graph/op_extension.hpp" #include "pyopenvino/utils/utils.hpp" namespace py = pybind11; @@ -676,6 +677,19 @@ void regclass_Core(py::module m) { :type extension: openvino.runtime.Extension )"); + cls.def( + "add_extension", + [](ov::Core& self, py::object dtype) { + self.add_extension(PyOpExtension(dtype)); + }, + py::arg("custom_op"), + R"( + Registers custom Op to a Core object. + + :param custom_op: type of custom Op + :type custom_op: openvino.Op + )"); + cls.def( "add_extension", static_cast>&)>(&ov::Core::add_extension), diff --git a/src/bindings/python/src/pyopenvino/graph/op_extension.cpp b/src/bindings/python/src/pyopenvino/graph/op_extension.cpp index 0a7483ae7e4657..c288f9ddbfaf4f 100644 --- a/src/bindings/python/src/pyopenvino/graph/op_extension.cpp +++ b/src/bindings/python/src/pyopenvino/graph/op_extension.cpp @@ -16,56 +16,6 @@ namespace py = pybind11; -class PyOpExtension : public ov::BaseOpExtension { -public: - PyOpExtension(const py::object& dtype) : py_handle_dtype{dtype} { - py::object type_info; - try { - // get_type_info() is a static method - type_info = py_handle_dtype.attr("get_type_info")(); - } catch (const std::exception& exc) { - try { - // get_type_info() is a class method - type_info = py_handle_dtype().attr("get_type_info")(); - } catch (const std::exception& exc) { - OPENVINO_THROW("Both options failed to get type_info."); - } - } - if (!py::isinstance(type_info)) { - OPENVINO_THROW("blahbla"); - } - m_type_info = type_info.cast>(); - OPENVINO_ASSERT(m_type_info->name != nullptr && m_type_info->version_id != nullptr, - "Extension type should have information about operation set and operation type."); - } - - const ov::DiscreteTypeInfo& get_type_info() const override { - return *m_type_info; - } - - ov::OutputVector create(const ov::OutputVector& inputs, ov::AttributeVisitor& visitor) const override { - // TODO: Create new python object using some python API under GIL then call its method - py::gil_scoped_acquire acquire; - // add check for default ctor - const auto node = py_handle_dtype(); - - node.attr("set_arguments")(py::cast(inputs)); - if (node.attr("visit_attributes")(&visitor)) { - node.attr("constructor_validate_and_infer_types")(); - } - - return py::cast(node.attr("outputs")()); - } - - std::vector get_attached_extensions() const override { - return {}; - } - -private: - py::object py_handle_dtype; // Holds the Python object to manage its lifetime - std::shared_ptr m_type_info; -}; - void regclass_graph_OpExtension(py::module m) { py::class_, ov::Extension> op_extension( m, diff --git a/src/bindings/python/src/pyopenvino/graph/op_extension.hpp b/src/bindings/python/src/pyopenvino/graph/op_extension.hpp index 2bccdeea95a239..d4ee2955273992 100644 --- a/src/bindings/python/src/pyopenvino/graph/op_extension.hpp +++ b/src/bindings/python/src/pyopenvino/graph/op_extension.hpp @@ -6,6 +6,58 @@ #include +#include "openvino/core/op_extension.hpp" + namespace py = pybind11; +class PyOpExtension : public ov::BaseOpExtension { +public: + PyOpExtension(const py::object& dtype) : py_handle_dtype{dtype} { + py::object type_info; + try { + // get_type_info() is a static method + type_info = py_handle_dtype.attr("get_type_info")(); + } catch (const std::exception& exc) { + try { + // get_type_info() is a class method + type_info = py_handle_dtype().attr("get_type_info")(); + } catch (const std::exception& exc) { + OPENVINO_THROW("Both options failed to get type_info."); + } + } + if (!py::isinstance(type_info)) { + OPENVINO_THROW("blahbla"); + } + m_type_info = type_info.cast>(); + OPENVINO_ASSERT(m_type_info->name != nullptr && m_type_info->version_id != nullptr, + "Extension type should have information about operation set and operation type."); + } + + const ov::DiscreteTypeInfo& get_type_info() const override { + return *m_type_info; + } + + ov::OutputVector create(const ov::OutputVector& inputs, ov::AttributeVisitor& visitor) const override { + // TODO: Create new python object using some python API under GIL then call its method + py::gil_scoped_acquire acquire; + // add check for default ctor + const auto node = py_handle_dtype(); + + node.attr("set_arguments")(py::cast(inputs)); + if (node.attr("visit_attributes")(&visitor)) { + node.attr("constructor_validate_and_infer_types")(); + } + + return py::cast(node.attr("outputs")()); + } + + std::vector get_attached_extensions() const override { + return {}; + } + +private: + py::object py_handle_dtype; // Holds the Python object to manage its lifetime + std::shared_ptr m_type_info; +}; + void regclass_graph_OpExtension(py::module m); From 2947d50e19f3c9b1dd45ea2189a1ea060e1627fd Mon Sep 17 00:00:00 2001 From: Anastasia Kuporosova Date: Mon, 9 Dec 2024 08:03:42 +0000 Subject: [PATCH 14/29] cpp codestyle --- src/bindings/python/src/pyopenvino/core/core.cpp | 2 +- src/bindings/python/src/pyopenvino/graph/op.cpp | 6 ++---- src/bindings/python/src/pyopenvino/graph/op_extension.cpp | 8 +++----- 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/src/bindings/python/src/pyopenvino/core/core.cpp b/src/bindings/python/src/pyopenvino/core/core.cpp index 18e426d94ff71d..56d20ae741426d 100644 --- a/src/bindings/python/src/pyopenvino/core/core.cpp +++ b/src/bindings/python/src/pyopenvino/core/core.cpp @@ -689,7 +689,7 @@ void regclass_Core(py::module m) { :param custom_op: type of custom Op :type custom_op: openvino.Op )"); - + cls.def( "add_extension", static_cast>&)>(&ov::Core::add_extension), diff --git a/src/bindings/python/src/pyopenvino/graph/op.cpp b/src/bindings/python/src/pyopenvino/graph/op.cpp index 658ec39ec7ddb1..2f95bf30c8b888 100644 --- a/src/bindings/python/src/pyopenvino/graph/op.cpp +++ b/src/bindings/python/src/pyopenvino/graph/op.cpp @@ -10,11 +10,9 @@ #include #include -#include #include "openvino/core/attribute_visitor.hpp" #include "openvino/core/node.hpp" -#include "openvino/core/type.hpp" namespace py = pybind11; @@ -36,8 +34,8 @@ std::shared_ptr PyOp::clone_with_new_inputs(const ov::OutputVector& ne py::gil_scoped_acquire gil; // Acquire the GIL while in this scope. // Try to look up the overridden method on the Python side. py::function overrided_py_method = pybind11::get_override(this, "clone_with_new_inputs"); - if (overrided_py_method) { // method is found - auto result = overrided_py_method(new_args); // Call the Python function. + if (overrided_py_method) { // method is found + auto result = overrided_py_method(new_args); // Call the Python function. return result.cast>(); } // Default implementation for clone_with_new_inputs diff --git a/src/bindings/python/src/pyopenvino/graph/op_extension.cpp b/src/bindings/python/src/pyopenvino/graph/op_extension.cpp index c288f9ddbfaf4f..0bedfffedda0be 100644 --- a/src/bindings/python/src/pyopenvino/graph/op_extension.cpp +++ b/src/bindings/python/src/pyopenvino/graph/op_extension.cpp @@ -10,16 +10,14 @@ #include "pyopenvino/core/common.hpp" #include "pyopenvino/core/extension.hpp" -#include "pyopenvino/graph/op.hpp" -#include "pyopenvino/graph/node_output.hpp" #include "pyopenvino/graph/discrete_type_info.hpp" +#include "pyopenvino/graph/node_output.hpp" +#include "pyopenvino/graph/op.hpp" namespace py = pybind11; void regclass_graph_OpExtension(py::module m) { - py::class_, ov::Extension> op_extension( - m, - "OpExtension"); + py::class_, ov::Extension> op_extension(m, "OpExtension"); op_extension.doc() = "openvino.OpExtension provides the base interface for OpenVINO extensions."; op_extension.def("__repr__", [](const PyOpExtension& self) { From 41a3038d3c7d53aa1c738361593d90768ee9ce71 Mon Sep 17 00:00:00 2001 From: Anastasia Kuporosova Date: Mon, 9 Dec 2024 12:14:45 +0000 Subject: [PATCH 15/29] add test --- .../python/tests/test_graph/test_custom_op.py | 77 +++++++++++-------- 1 file changed, 43 insertions(+), 34 deletions(-) diff --git a/src/bindings/python/tests/test_graph/test_custom_op.py b/src/bindings/python/tests/test_graph/test_custom_op.py index 71b040305a3716..19f33f297ae6d8 100644 --- a/src/bindings/python/tests/test_graph/test_custom_op.py +++ b/src/bindings/python/tests/test_graph/test_custom_op.py @@ -7,12 +7,12 @@ import numpy as np from contextlib import nullcontext as does_not_raise -from openvino import Op -from openvino import CompiledModel, Model, Dimension, Shape, Tensor, compile_model, serialize +from openvino import Op, OpExtension +from openvino import CompiledModel, Core, Model, Dimension, Shape, Tensor, compile_model, serialize from openvino.runtime import DiscreteTypeInfo import openvino.runtime.opset14 as ops -from tests.utils.helpers import create_filenames_for_ir +from tests.utils.helpers import create_filenames_for_ir, compare_models class CustomOp(Op): @@ -199,51 +199,60 @@ def test_custom_op(): assert np.allclose(results[list(results)[0]], expected_output, 1e-4, 1e-4) -def test_op_extension(): - import numpy as np - from openvino import Core, OpExtension - from openvino import Op - from openvino import CompiledModel, Model, Dimension, Shape, Tensor, compile_model, serialize - from openvino.runtime import DiscreteTypeInfo - import openvino.runtime.opset14 as ops +class CustomSimpleOp(Op): + def __init__(self, inputs=None): + super().__init__(self) + if inputs is not None: + self.set_arguments(inputs) + self.constructor_validate_and_infer_types() + + def validate_and_infer_types(self): + self.set_output_type(0, self.get_input_element_type(0), self.get_input_partial_shape(0)) - class CustomOpWithAttribute(Op): - class_type_info = DiscreteTypeInfo("CustomOpWithAttribute", "extension") +class CustomSimpleOpWithAttribute(Op): + class_type_info = DiscreteTypeInfo("CustomSimpleOpWithAttribute", "extension") - def __init__(self, inputs, attrs): - super().__init__(self) - self._attrs = attrs + def __init__(self, inputs=None, attrs=None): + super().__init__(self) + self._attrs = attrs + if attrs is not None or inputs is not None: self.set_arguments(inputs) self.constructor_validate_and_infer_types() - def validate_and_infer_types(self): - self.set_output_type(0, self.get_input_element_type(0), self.get_input_partial_shape(0)) - - def clone_with_new_inputs(self, new_inputs): - return CustomOpWithAttribute(new_inputs) + def validate_and_infer_types(self): + self.set_output_type(0, self.get_input_element_type(0), self.get_input_partial_shape(0)) - def get_type_info(self): - return CustomOpWithAttribute.class_type_info + @staticmethod + def get_type_info(): + return CustomSimpleOpWithAttribute.class_type_info - def visit_attributes(self, visitor): + def visit_attributes(self, visitor): + if self._attrs: visitor.on_attributes(self._attrs) - return True + return True - #core.add_extension(); - core = Core() - core.add_extension(OpExtension(CustomOpWithAttribute)) +def test_op_extension(prepared_paths): input_shape = [2, 1] + core = Core() + core.add_extension(CustomSimpleOp) + core.add_extension(OpExtension(CustomSimpleOpWithAttribute)) + param1 = ops.parameter(Shape(input_shape), dtype=np.float32, name="data1") param2 = ops.parameter(Shape(input_shape), dtype=np.float32, name="data2") - custom = CustomOpWithAttribute(inputs=[param1, param2], attrs={"value_str": "test_attribute"}) - #res = ops.result(custom, name="result") - model_with_op_attr = Model(custom, [param1, param2], "CustomModel") - - serialize(model_with_op_attr, "model_with_custom.xml", "model_with_custom.bin") - m = core.read_model("model_with_custom.xml") - + custom_simple = CustomSimpleOp(inputs=[param1, param2]) + custom_simple.set_friendly_name("test_add") + custom_with_attribute = CustomSimpleOpWithAttribute(inputs=[custom_simple], attrs={"value_str": "test_attribute"}) + custom_add = CustomAdd(inputs=[custom_with_attribute]) + res = ops.result(custom_with_attribute, name="result") + simple_model = Model(res, [param1, param2], "SimpleModel") + cloned_model = simple_model.clone() + assert compare_models(simple_model, cloned_model) + xml_path, bin_path = prepared_paths + serialize(simple_model, xml_path, bin_path) + model_with_custom_op = core.read_model(xml_path) + assert compare_models(simple_model, model_with_custom_op) From ac6bf7062ca8caddef1bfb3da29cff620d101f42 Mon Sep 17 00:00:00 2001 From: Anastasia Kuporosova Date: Mon, 9 Dec 2024 19:11:13 +0000 Subject: [PATCH 16/29] ci error --- .../python/src/pyopenvino/core/core.cpp | 22 +++++++++---------- .../src/pyopenvino/graph/op_extension.hpp | 7 ++++-- .../python/tests/test_graph/test_custom_op.py | 2 +- 3 files changed, 17 insertions(+), 14 deletions(-) diff --git a/src/bindings/python/src/pyopenvino/core/core.cpp b/src/bindings/python/src/pyopenvino/core/core.cpp index 56d20ae741426d..3ce52b0d391097 100644 --- a/src/bindings/python/src/pyopenvino/core/core.cpp +++ b/src/bindings/python/src/pyopenvino/core/core.cpp @@ -677,6 +677,17 @@ void regclass_Core(py::module m) { :type extension: openvino.runtime.Extension )"); + cls.def( + "add_extension", + static_cast>&)>(&ov::Core::add_extension), + py::arg("extensions"), + R"( + Registers extensions to a Core object. + + :param extensions: List of Extension objects. + :type extensions: list[openvino.runtime.Extension] + )"); + cls.def( "add_extension", [](ov::Core& self, py::object dtype) { @@ -690,17 +701,6 @@ void regclass_Core(py::module m) { :type custom_op: openvino.Op )"); - cls.def( - "add_extension", - static_cast>&)>(&ov::Core::add_extension), - py::arg("extensions"), - R"( - Registers extensions to a Core object. - - :param extensions: List of Extension objects. - :type extensions: list[openvino.runtime.Extension] - )"); - cls.def("get_available_devices", &ov::Core::get_available_devices, py::call_guard(), diff --git a/src/bindings/python/src/pyopenvino/graph/op_extension.hpp b/src/bindings/python/src/pyopenvino/graph/op_extension.hpp index d4ee2955273992..42aaef68766347 100644 --- a/src/bindings/python/src/pyopenvino/graph/op_extension.hpp +++ b/src/bindings/python/src/pyopenvino/graph/op_extension.hpp @@ -6,6 +6,9 @@ #include +#include +#include + #include "openvino/core/op_extension.hpp" namespace py = pybind11; @@ -17,11 +20,11 @@ class PyOpExtension : public ov::BaseOpExtension { try { // get_type_info() is a static method type_info = py_handle_dtype.attr("get_type_info")(); - } catch (const std::exception& exc) { + } catch (const std::exception&) { try { // get_type_info() is a class method type_info = py_handle_dtype().attr("get_type_info")(); - } catch (const std::exception& exc) { + } catch (const std::exception&) { OPENVINO_THROW("Both options failed to get type_info."); } } diff --git a/src/bindings/python/tests/test_graph/test_custom_op.py b/src/bindings/python/tests/test_graph/test_custom_op.py index 19f33f297ae6d8..46758ab93041eb 100644 --- a/src/bindings/python/tests/test_graph/test_custom_op.py +++ b/src/bindings/python/tests/test_graph/test_custom_op.py @@ -247,7 +247,7 @@ def test_op_extension(prepared_paths): custom_with_attribute = CustomSimpleOpWithAttribute(inputs=[custom_simple], attrs={"value_str": "test_attribute"}) custom_add = CustomAdd(inputs=[custom_with_attribute]) res = ops.result(custom_with_attribute, name="result") - simple_model = Model(res, [param1, param2], "SimpleModel") + simple_model = Model(res, [param1, param2], "SimpleModel") cloned_model = simple_model.clone() assert compare_models(simple_model, cloned_model) From 7a473f01ab1e0ea7efbc7769826d6ad753bc5c08 Mon Sep 17 00:00:00 2001 From: Anastasia Kuporosova Date: Mon, 9 Dec 2024 23:36:02 +0000 Subject: [PATCH 17/29] prettify error messages + test --- .../src/pyopenvino/graph/op_extension.cpp | 8 +-- .../src/pyopenvino/graph/op_extension.hpp | 17 +++++-- .../python/tests/test_graph/test_custom_op.py | 51 +++++++++++++++---- 3 files changed, 55 insertions(+), 21 deletions(-) diff --git a/src/bindings/python/src/pyopenvino/graph/op_extension.cpp b/src/bindings/python/src/pyopenvino/graph/op_extension.cpp index 0bedfffedda0be..0a159396a4975d 100644 --- a/src/bindings/python/src/pyopenvino/graph/op_extension.cpp +++ b/src/bindings/python/src/pyopenvino/graph/op_extension.cpp @@ -25,12 +25,6 @@ void regclass_graph_OpExtension(py::module m) { }); op_extension.def(py::init([](py::object dtype) { - py::object py_issubclass = py::module::import("builtins").attr("issubclass"); - if (py_issubclass(dtype, py::type::of())) { - return PyOpExtension(dtype); - } - std::stringstream str; - str << "Unsupported data type : '" << dtype << "' is passed as an argument."; - OPENVINO_THROW(str.str()); + return PyOpExtension(dtype); })); } diff --git a/src/bindings/python/src/pyopenvino/graph/op_extension.hpp b/src/bindings/python/src/pyopenvino/graph/op_extension.hpp index 42aaef68766347..be05e07eed21f2 100644 --- a/src/bindings/python/src/pyopenvino/graph/op_extension.hpp +++ b/src/bindings/python/src/pyopenvino/graph/op_extension.hpp @@ -10,12 +10,20 @@ #include #include "openvino/core/op_extension.hpp" +#include "pyopenvino/graph/op.hpp" namespace py = pybind11; class PyOpExtension : public ov::BaseOpExtension { public: PyOpExtension(const py::object& dtype) : py_handle_dtype{dtype} { + py::object py_issubclass = py::module::import("builtins").attr("issubclass"); + if (!py_issubclass(dtype, py::type::of()).cast()) { + std::stringstream str; + str << "Unsupported data type : '" << dtype << "' is passed as an argument."; + OPENVINO_THROW(str.str()); + } + py::object type_info; try { // get_type_info() is a static method @@ -24,12 +32,12 @@ class PyOpExtension : public ov::BaseOpExtension { try { // get_type_info() is a class method type_info = py_handle_dtype().attr("get_type_info")(); - } catch (const std::exception&) { - OPENVINO_THROW("Both options failed to get type_info."); + } catch (const std::exception &exc) { + OPENVINO_THROW("Creation of OpExtension failed: ", exc.what()); } } if (!py::isinstance(type_info)) { - OPENVINO_THROW("blahbla"); + OPENVINO_THROW("operation type_info must be an instance of DiscreteTypeInfo, but ", py::str(py::type::of(type_info)), " is passed."); } m_type_info = type_info.cast>(); OPENVINO_ASSERT(m_type_info->name != nullptr && m_type_info->version_id != nullptr, @@ -41,9 +49,8 @@ class PyOpExtension : public ov::BaseOpExtension { } ov::OutputVector create(const ov::OutputVector& inputs, ov::AttributeVisitor& visitor) const override { - // TODO: Create new python object using some python API under GIL then call its method py::gil_scoped_acquire acquire; - // add check for default ctor + const auto node = py_handle_dtype(); node.attr("set_arguments")(py::cast(inputs)); diff --git a/src/bindings/python/tests/test_graph/test_custom_op.py b/src/bindings/python/tests/test_graph/test_custom_op.py index 46758ab93041eb..f6a1955ff25a84 100644 --- a/src/bindings/python/tests/test_graph/test_custom_op.py +++ b/src/bindings/python/tests/test_graph/test_custom_op.py @@ -55,10 +55,11 @@ def create_snake_model(): class CustomAdd(Op): class_type_info = DiscreteTypeInfo("CustomAdd", "extension") - def __init__(self, inputs): + def __init__(self, inputs=None): super().__init__(self) - self.set_arguments(inputs) - self.constructor_validate_and_infer_types() + if inputs is not None: + self.set_arguments(inputs) + self.constructor_validate_and_infer_types() def validate_and_infer_types(self): self.set_output_type(0, self.get_input_element_type(0), self.get_input_partial_shape(0)) @@ -85,11 +86,12 @@ def create_add_model(): class CustomOpWithAttribute(Op): class_type_info = DiscreteTypeInfo("CustomOpWithAttribute", "extension") - def __init__(self, inputs, attrs): + def __init__(self, inputs=None, attrs=None): super().__init__(self) - self._attrs = attrs - self.set_arguments(inputs) - self.constructor_validate_and_infer_types() + if attrs is not None or inputs is not None: + self._attrs = attrs + self.set_arguments(inputs) + self.constructor_validate_and_infer_types() def validate_and_infer_types(self): self.set_output_type(0, self.get_input_element_type(0), self.get_input_partial_shape(0)) @@ -239,14 +241,14 @@ def test_op_extension(prepared_paths): core = Core() core.add_extension(CustomSimpleOp) core.add_extension(OpExtension(CustomSimpleOpWithAttribute)) - + core.add_extension(OpExtension(CustomAdd)) param1 = ops.parameter(Shape(input_shape), dtype=np.float32, name="data1") param2 = ops.parameter(Shape(input_shape), dtype=np.float32, name="data2") custom_simple = CustomSimpleOp(inputs=[param1, param2]) custom_simple.set_friendly_name("test_add") custom_with_attribute = CustomSimpleOpWithAttribute(inputs=[custom_simple], attrs={"value_str": "test_attribute"}) custom_add = CustomAdd(inputs=[custom_with_attribute]) - res = ops.result(custom_with_attribute, name="result") + res = ops.result(custom_add, name="result") simple_model = Model(res, [param1, param2], "SimpleModel") cloned_model = simple_model.clone() @@ -256,3 +258,34 @@ def test_op_extension(prepared_paths): serialize(simple_model, xml_path, bin_path) model_with_custom_op = core.read_model(xml_path) assert compare_models(simple_model, model_with_custom_op) + + +def test_fail_create_op_ext(): + class BrokeOp(Op): + class_type_info = "BrokeOp" + + def __init__(self, inputs=None): + super().__init__(self) + if inputs is not None: + self.set_arguments(inputs) + self.constructor_validate_and_infer_types() + + def get_type_info(self): + return BrokeOp.class_type_info + + core = Core() + with pytest.raises(RuntimeError) as e: + core.add_extension(CustomOp) + assert "CustomOp.__init__() missing 1 required positional argument: 'inputs'" in str(e.value) + + with pytest.raises(RuntimeError) as e: + core.add_extension(OpExtension(bool)) + assert "Unsupported data type : '' is passed as an argument." in str(e.value) + + with pytest.raises(RuntimeError) as e: + core.add_extension(bool) + assert "Unsupported data type : '' is passed as an argument." in str(e.value) + + with pytest.raises(RuntimeError) as e: + core.add_extension(BrokeOp) + assert "operation type_info must be an instance of DiscreteTypeInfo, but is passed." in str(e.value) From 151c9e74fefc87e641216400be000a59b44b0db0 Mon Sep 17 00:00:00 2001 From: Anastasia Kuporosova Date: Tue, 10 Dec 2024 09:08:14 +0000 Subject: [PATCH 18/29] fix commit --- src/bindings/python/tests/test_graph/test_custom_op.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bindings/python/tests/test_graph/test_custom_op.py b/src/bindings/python/tests/test_graph/test_custom_op.py index f6a1955ff25a84..78f8c0acac2fb0 100644 --- a/src/bindings/python/tests/test_graph/test_custom_op.py +++ b/src/bindings/python/tests/test_graph/test_custom_op.py @@ -276,7 +276,7 @@ def get_type_info(self): core = Core() with pytest.raises(RuntimeError) as e: core.add_extension(CustomOp) - assert "CustomOp.__init__() missing 1 required positional argument: 'inputs'" in str(e.value) + assert "__init__() missing 1 required positional argument: 'inputs'" in str(e.value) with pytest.raises(RuntimeError) as e: core.add_extension(OpExtension(bool)) From 4fda06d97526560493530e119f987ed278daf28f Mon Sep 17 00:00:00 2001 From: Anastasia Kuporosova Date: Tue, 10 Dec 2024 13:35:30 +0000 Subject: [PATCH 19/29] fix --- src/bindings/python/src/openvino/frontend/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bindings/python/src/openvino/frontend/__init__.py b/src/bindings/python/src/openvino/frontend/__init__.py index 93abf226dca827..1970933ac9906d 100644 --- a/src/bindings/python/src/openvino/frontend/__init__.py +++ b/src/bindings/python/src/openvino/frontend/__init__.py @@ -22,7 +22,7 @@ # extensions from openvino._pyopenvino import DecoderTransformationExtension from openvino._pyopenvino import ConversionExtension -from openvino._pyopenvino import OpExtension +from openvino._pyopenvino.frontend import OpExtension from openvino._pyopenvino import ProgressReporterExtension from openvino._pyopenvino import TelemetryExtension From 17f5e46d35aad975311d775922510a69ded6da02 Mon Sep 17 00:00:00 2001 From: Anastasia Kuporosova Date: Tue, 10 Dec 2024 22:07:31 +0000 Subject: [PATCH 20/29] imports to pass ci --- tools/mo/openvino/__init__.py | 1 + tools/openvino_dev/src/openvino/__init__.py | 1 + 2 files changed, 2 insertions(+) diff --git a/tools/mo/openvino/__init__.py b/tools/mo/openvino/__init__.py index b015570964c520..d8abdcec0a0708 100644 --- a/tools/mo/openvino/__init__.py +++ b/tools/mo/openvino/__init__.py @@ -51,6 +51,7 @@ from openvino._pyopenvino import RemoteContext from openvino._pyopenvino import RemoteTensor from openvino._pyopenvino import Op + from openvino._pyopenvino import OpExtension # libva related: from openvino._pyopenvino import VAContext diff --git a/tools/openvino_dev/src/openvino/__init__.py b/tools/openvino_dev/src/openvino/__init__.py index b015570964c520..d8abdcec0a0708 100644 --- a/tools/openvino_dev/src/openvino/__init__.py +++ b/tools/openvino_dev/src/openvino/__init__.py @@ -51,6 +51,7 @@ from openvino._pyopenvino import RemoteContext from openvino._pyopenvino import RemoteTensor from openvino._pyopenvino import Op + from openvino._pyopenvino import OpExtension # libva related: from openvino._pyopenvino import VAContext From 0c75473c7e2a1da87bb4faf50865e126c7289481 Mon Sep 17 00:00:00 2001 From: Anastasia Kuporosova Date: Fri, 13 Dec 2024 09:58:05 +0000 Subject: [PATCH 21/29] apply comments p1 --- .../src/pyopenvino/graph/op_extension.cpp | 21 +++++++++++++++++++ .../src/pyopenvino/graph/op_extension.hpp | 21 +++---------------- .../python/tests/test_graph/test_custom_op.py | 8 +++---- 3 files changed, 28 insertions(+), 22 deletions(-) diff --git a/src/bindings/python/src/pyopenvino/graph/op_extension.cpp b/src/bindings/python/src/pyopenvino/graph/op_extension.cpp index 0a159396a4975d..9922493efdf28d 100644 --- a/src/bindings/python/src/pyopenvino/graph/op_extension.cpp +++ b/src/bindings/python/src/pyopenvino/graph/op_extension.cpp @@ -16,6 +16,27 @@ namespace py = pybind11; +const ov::DiscreteTypeInfo& PyOpExtension::get_type_info() const { + return *m_type_info; +} + +ov::OutputVector PyOpExtension::create(const ov::OutputVector& inputs, ov::AttributeVisitor& visitor) const { + py::gil_scoped_acquire acquire; + + const auto node = py_handle_dtype(); + + node.attr("set_arguments")(py::cast(inputs)); + if (node.attr("visit_attributes")(&visitor)) { + node.attr("constructor_validate_and_infer_types")(); + } + + return py::cast(node.attr("outputs")()); +} + +std::vector PyOpExtension::get_attached_extensions() const { + return {}; +} + void regclass_graph_OpExtension(py::module m) { py::class_, ov::Extension> op_extension(m, "OpExtension"); op_extension.doc() = "openvino.OpExtension provides the base interface for OpenVINO extensions."; diff --git a/src/bindings/python/src/pyopenvino/graph/op_extension.hpp b/src/bindings/python/src/pyopenvino/graph/op_extension.hpp index be05e07eed21f2..a61b3bb6dd88f7 100644 --- a/src/bindings/python/src/pyopenvino/graph/op_extension.hpp +++ b/src/bindings/python/src/pyopenvino/graph/op_extension.hpp @@ -44,26 +44,11 @@ class PyOpExtension : public ov::BaseOpExtension { "Extension type should have information about operation set and operation type."); } - const ov::DiscreteTypeInfo& get_type_info() const override { - return *m_type_info; - } - - ov::OutputVector create(const ov::OutputVector& inputs, ov::AttributeVisitor& visitor) const override { - py::gil_scoped_acquire acquire; + const ov::DiscreteTypeInfo& get_type_info() const override; - const auto node = py_handle_dtype(); - - node.attr("set_arguments")(py::cast(inputs)); - if (node.attr("visit_attributes")(&visitor)) { - node.attr("constructor_validate_and_infer_types")(); - } + ov::OutputVector create(const ov::OutputVector& inputs, ov::AttributeVisitor& visitor) const override; - return py::cast(node.attr("outputs")()); - } - - std::vector get_attached_extensions() const override { - return {}; - } + std::vector get_attached_extensions() const override; private: py::object py_handle_dtype; // Holds the Python object to manage its lifetime diff --git a/src/bindings/python/tests/test_graph/test_custom_op.py b/src/bindings/python/tests/test_graph/test_custom_op.py index 78f8c0acac2fb0..adf4080915459f 100644 --- a/src/bindings/python/tests/test_graph/test_custom_op.py +++ b/src/bindings/python/tests/test_graph/test_custom_op.py @@ -261,8 +261,8 @@ def test_op_extension(prepared_paths): def test_fail_create_op_ext(): - class BrokeOp(Op): - class_type_info = "BrokeOp" + class OpWithBadClassTypeInfo(Op): + class_type_info = "OpWithBadClassTypeInfo" def __init__(self, inputs=None): super().__init__(self) @@ -271,7 +271,7 @@ def __init__(self, inputs=None): self.constructor_validate_and_infer_types() def get_type_info(self): - return BrokeOp.class_type_info + return OpWithBadClassTypeInfo.class_type_info core = Core() with pytest.raises(RuntimeError) as e: @@ -287,5 +287,5 @@ def get_type_info(self): assert "Unsupported data type : '' is passed as an argument." in str(e.value) with pytest.raises(RuntimeError) as e: - core.add_extension(BrokeOp) + core.add_extension(OpWithBadClassTypeInfo) assert "operation type_info must be an instance of DiscreteTypeInfo, but is passed." in str(e.value) From ca510f15f9d03014f13f0dc75bc3c9df73123c5b Mon Sep 17 00:00:00 2001 From: Anastasia Kuporosova Date: Wed, 18 Dec 2024 20:05:30 +0000 Subject: [PATCH 22/29] hide to super-init --- src/bindings/python/src/pyopenvino/graph/op.cpp | 13 +++++++++++++ src/bindings/python/src/pyopenvino/graph/op.hpp | 9 ++++++++- .../python/tests/test_graph/test_custom_op.py | 13 ++++++++----- 3 files changed, 29 insertions(+), 6 deletions(-) diff --git a/src/bindings/python/src/pyopenvino/graph/op.cpp b/src/bindings/python/src/pyopenvino/graph/op.cpp index 2f95bf30c8b888..81ba7bf83e4fba 100644 --- a/src/bindings/python/src/pyopenvino/graph/op.cpp +++ b/src/bindings/python/src/pyopenvino/graph/op.cpp @@ -45,6 +45,7 @@ std::shared_ptr PyOp::clone_with_new_inputs(const ov::OutputVector& ne } const ov::op::Op::type_info_t& PyOp::get_type_info() const { + std::cout << reinterpret_cast(m_type_info.get()) << std::endl; return *m_type_info; } @@ -60,6 +61,18 @@ void regclass_graph_Op(py::module m) { py::class_, PyOp, ov::Node> op(m, "Op"); op.def(py::init([](const py::object& py_obj) { + std::cout << "lel" < #include "openvino/op/op.hpp" +#include "openvino/core/node.hpp" #include "pyopenvino/graph/discrete_type_info.hpp" namespace py = pybind11; @@ -19,8 +20,9 @@ class PyOp : public ov::op::Op { PyOp() = default; // Keeps a reference to the Python object to manage its lifetime - PyOp(const py::object& py_obj) : py_handle(py_obj) { + PyOp(const py::object& py_obj, const py::object& inputs=py::none()) : py_handle(py_obj) { py::gil_scoped_acquire gil; // Acquire the GIL while in this scope. + std::cout << "jello" << std::endl; // Try to look up the overridden method on the Python side. py::function overrided_py_method = pybind11::get_override(this, "get_type_info"); if (overrided_py_method) { // method is found @@ -30,6 +32,11 @@ class PyOp : public ov::op::Op { const auto py_class_name = py_handle.get_type().attr("__name__").cast(); m_type_info = std::make_shared(py_class_name, "extension"); } + if (!inputs.is_none()) { + std::cout << "here" << std::endl; + this->set_arguments(inputs.cast()); + this->constructor_validate_and_infer_types(); + } } void validate_and_infer_types() override; diff --git a/src/bindings/python/tests/test_graph/test_custom_op.py b/src/bindings/python/tests/test_graph/test_custom_op.py index adf4080915459f..769c9aac49e7c9 100644 --- a/src/bindings/python/tests/test_graph/test_custom_op.py +++ b/src/bindings/python/tests/test_graph/test_custom_op.py @@ -203,10 +203,7 @@ def test_custom_op(): class CustomSimpleOp(Op): def __init__(self, inputs=None): - super().__init__(self) - if inputs is not None: - self.set_arguments(inputs) - self.constructor_validate_and_infer_types() + super().__init__(self, inputs) def validate_and_infer_types(self): self.set_output_type(0, self.get_input_element_type(0), self.get_input_partial_shape(0)) @@ -239,12 +236,18 @@ def test_op_extension(prepared_paths): input_shape = [2, 1] core = Core() + print("1") core.add_extension(CustomSimpleOp) + print("2") core.add_extension(OpExtension(CustomSimpleOpWithAttribute)) + print("3") core.add_extension(OpExtension(CustomAdd)) + print("4") param1 = ops.parameter(Shape(input_shape), dtype=np.float32, name="data1") param2 = ops.parameter(Shape(input_shape), dtype=np.float32, name="data2") + print("5") custom_simple = CustomSimpleOp(inputs=[param1, param2]) + print("6") custom_simple.set_friendly_name("test_add") custom_with_attribute = CustomSimpleOpWithAttribute(inputs=[custom_simple], attrs={"value_str": "test_attribute"}) custom_add = CustomAdd(inputs=[custom_with_attribute]) @@ -260,7 +263,7 @@ def test_op_extension(prepared_paths): assert compare_models(simple_model, model_with_custom_op) -def test_fail_create_op_ext(): +def test_fail_create_op_extension(): class OpWithBadClassTypeInfo(Op): class_type_info = "OpWithBadClassTypeInfo" From 1d89fb40e81a914543c64b0ee065182cc764e69d Mon Sep 17 00:00:00 2001 From: Anastasia Kuporosova Date: Wed, 8 Jan 2025 15:10:47 +0100 Subject: [PATCH 23/29] remove changes from deleted components --- tools/mo/openvino/__init__.py | 1 - tools/openvino_dev/src/openvino/__init__.py | 1 - 2 files changed, 2 deletions(-) diff --git a/tools/mo/openvino/__init__.py b/tools/mo/openvino/__init__.py index 7b7807b624f07e..46e35babdc9fad 100644 --- a/tools/mo/openvino/__init__.py +++ b/tools/mo/openvino/__init__.py @@ -51,7 +51,6 @@ from openvino._pyopenvino import RemoteContext from openvino._pyopenvino import RemoteTensor from openvino._pyopenvino import Op - from openvino._pyopenvino import OpExtension # libva related: from openvino._pyopenvino import VAContext diff --git a/tools/openvino_dev/src/openvino/__init__.py b/tools/openvino_dev/src/openvino/__init__.py index 7b7807b624f07e..46e35babdc9fad 100644 --- a/tools/openvino_dev/src/openvino/__init__.py +++ b/tools/openvino_dev/src/openvino/__init__.py @@ -51,7 +51,6 @@ from openvino._pyopenvino import RemoteContext from openvino._pyopenvino import RemoteTensor from openvino._pyopenvino import Op - from openvino._pyopenvino import OpExtension # libva related: from openvino._pyopenvino import VAContext From ac5223317e1eef0c1d79892a3e9363cfc915e522 Mon Sep 17 00:00:00 2001 From: Anastasia Kuporosova Date: Fri, 10 Jan 2025 00:42:35 +0000 Subject: [PATCH 24/29] reorg code: better hide methods in py wrapper --- src/bindings/python/src/openvino/__init__.py | 2 +- src/bindings/python/src/openvino/_ov_api.py | 12 +++++++ .../python/src/pyopenvino/graph/op.cpp | 33 ++++++++++++------- .../python/src/pyopenvino/graph/op.hpp | 25 ++++---------- .../src/pyopenvino/graph/op_extension.hpp | 8 ++++- .../python/tests/test_graph/test_custom_op.py | 31 +++++++++-------- tools/benchmark_tool/openvino/__init__.py | 2 +- tools/ovc/openvino/__init__.py | 2 +- 8 files changed, 66 insertions(+), 49 deletions(-) diff --git a/src/bindings/python/src/openvino/__init__.py b/src/bindings/python/src/openvino/__init__.py index 9da5ab6c26d8fe..ff616f21030334 100644 --- a/src/bindings/python/src/openvino/__init__.py +++ b/src/bindings/python/src/openvino/__init__.py @@ -32,6 +32,7 @@ from openvino._ov_api import CompiledModel from openvino._ov_api import InferRequest from openvino._ov_api import AsyncInferQueue +from openvino._ov_api import Op from openvino.runtime import Symbol from openvino.runtime import Dimension @@ -55,7 +56,6 @@ from openvino._pyopenvino import RemoteContext from openvino._pyopenvino import RemoteTensor -from openvino._pyopenvino import Op from openvino._pyopenvino import OpExtension # Import opsets diff --git a/src/bindings/python/src/openvino/_ov_api.py b/src/bindings/python/src/openvino/_ov_api.py index 53d0fa5316498b..c272f8b685e888 100644 --- a/src/bindings/python/src/openvino/_ov_api.py +++ b/src/bindings/python/src/openvino/_ov_api.py @@ -13,6 +13,7 @@ from openvino._pyopenvino import Core as CoreBase from openvino._pyopenvino import CompiledModel as CompiledModelBase from openvino._pyopenvino import AsyncInferQueue as AsyncInferQueueBase +from openvino._pyopenvino import Op as OpBase from openvino._pyopenvino import Tensor from openvino._pyopenvino import Node @@ -24,6 +25,17 @@ ) +class Op(OpBase): + def __init__(self, py_obj, inputs=None) -> None: + super().__init__(py_obj) + print("super ok") + self._initialize_type_info() + print("__initialize_type_info ok") + if inputs is not None: + self.set_arguments(inputs) + self.constructor_validate_and_infer_types() + + class Model: def __init__(self, *args: Any, **kwargs: Any) -> None: if args and not kwargs: diff --git a/src/bindings/python/src/pyopenvino/graph/op.cpp b/src/bindings/python/src/pyopenvino/graph/op.cpp index 81ba7bf83e4fba..69ef475f408095 100644 --- a/src/bindings/python/src/pyopenvino/graph/op.cpp +++ b/src/bindings/python/src/pyopenvino/graph/op.cpp @@ -45,7 +45,7 @@ std::shared_ptr PyOp::clone_with_new_inputs(const ov::OutputVector& ne } const ov::op::Op::type_info_t& PyOp::get_type_info() const { - std::cout << reinterpret_cast(m_type_info.get()) << std::endl; + // std::cout << reinterpret_cast(m_type_info.get()) << std::endl; return *m_type_info; } @@ -57,6 +57,23 @@ bool PyOp::has_evaluate() const { PYBIND11_OVERRIDE(bool, ov::op::Op, has_evaluate); } +void PyOp::initialize_type_info() { + py::gil_scoped_acquire gil; // Acquire the GIL while in this scope. + std::cout << "jello" << std::endl; + // Try to look up the overridden method on the Python side. + py::function overriden_py_method = pybind11::get_override(this, "get_type_info"); + if (overriden_py_method) { + // TODO: Write a test on this behavior! + std::cout << "use type name" << std::endl; + const auto type_info_from_py = overriden_py_method(); + if (!py::isinstance(type_info_from_py)) { + // TODO: Rewrite me? + OPENVINO_THROW("operation type_info must be an instance of DiscreteTypeInfo, but ", py::str(py::type::of(type_info_from_py)), " is passed."); + } + m_type_info = type_info_from_py.cast>(); + } +} + void regclass_graph_Op(py::module m) { py::class_, PyOp, ov::Node> op(m, "Op"); @@ -65,14 +82,8 @@ void regclass_graph_Op(py::module m) { return PyOp(py_obj); })); - op.def(py::init([](const py::object& py_obj, const py::object& inputs) { - return PyOp(py_obj, inputs); - })); - - // op.def(py::init([](const py::object& py_obj, py::object& inputs) { - // if (inputs.is_none()) { - // return PyOp(py_obj); - // } - // return PyOp(py_obj); - // }), py::arg("py_obj"), py::arg("inputs")); + op.def("_initialize_type_info", [](PyOp& self){ + std::cout << "init ti" << std::endl; + self.initialize_type_info(); + }); } diff --git a/src/bindings/python/src/pyopenvino/graph/op.hpp b/src/bindings/python/src/pyopenvino/graph/op.hpp index 6e82c881c9a354..924208e807a138 100644 --- a/src/bindings/python/src/pyopenvino/graph/op.hpp +++ b/src/bindings/python/src/pyopenvino/graph/op.hpp @@ -20,25 +20,14 @@ class PyOp : public ov::op::Op { PyOp() = default; // Keeps a reference to the Python object to manage its lifetime - PyOp(const py::object& py_obj, const py::object& inputs=py::none()) : py_handle(py_obj) { - py::gil_scoped_acquire gil; // Acquire the GIL while in this scope. - std::cout << "jello" << std::endl; - // Try to look up the overridden method on the Python side. - py::function overrided_py_method = pybind11::get_override(this, "get_type_info"); - if (overrided_py_method) { // method is found - const auto result = overrided_py_method(); // Call the Python function. - m_type_info = result.cast>(); - } else { - const auto py_class_name = py_handle.get_type().attr("__name__").cast(); - m_type_info = std::make_shared(py_class_name, "extension"); - } - if (!inputs.is_none()) { - std::cout << "here" << std::endl; - this->set_arguments(inputs.cast()); - this->constructor_validate_and_infer_types(); - } + PyOp(const py::object& py_obj) : py_handle(py_obj) { + // Set default value for DiscreteTypeInfo + const auto py_class_name = py_handle.get_type().attr("__name__").cast(); + m_type_info = std::make_shared(py_class_name, "extension"); } + void initialize_type_info(); + void validate_and_infer_types() override; bool visit_attributes(ov::AttributeVisitor& value) override; @@ -53,7 +42,7 @@ class PyOp : public ov::op::Op { private: py::object py_handle; // Holds the Python object to manage its lifetime - std::shared_ptr m_type_info; + std::shared_ptr m_type_info; }; void regclass_graph_Op(py::module m); diff --git a/src/bindings/python/src/pyopenvino/graph/op_extension.hpp b/src/bindings/python/src/pyopenvino/graph/op_extension.hpp index a61b3bb6dd88f7..65c8796da8ed31 100644 --- a/src/bindings/python/src/pyopenvino/graph/op_extension.hpp +++ b/src/bindings/python/src/pyopenvino/graph/op_extension.hpp @@ -27,11 +27,17 @@ class PyOpExtension : public ov::BaseOpExtension { py::object type_info; try { // get_type_info() is a static method + std::cout << "before static methods" << std::endl; type_info = py_handle_dtype.attr("get_type_info")(); + std::cout << "after static methods" << std::endl; } catch (const std::exception&) { try { // get_type_info() is a class method - type_info = py_handle_dtype().attr("get_type_info")(); + std::cout << "before class methods" << std::endl; + auto obj = py_handle_dtype(); + std::cout << "afte obj" << std::endl; + type_info = obj.attr("get_type_info")(); + std::cout << "afte class methods" << std::endl; } catch (const std::exception &exc) { OPENVINO_THROW("Creation of OpExtension failed: ", exc.what()); } diff --git a/src/bindings/python/tests/test_graph/test_custom_op.py b/src/bindings/python/tests/test_graph/test_custom_op.py index 769c9aac49e7c9..48e4f7acbe8622 100644 --- a/src/bindings/python/tests/test_graph/test_custom_op.py +++ b/src/bindings/python/tests/test_graph/test_custom_op.py @@ -56,12 +56,13 @@ class CustomAdd(Op): class_type_info = DiscreteTypeInfo("CustomAdd", "extension") def __init__(self, inputs=None): - super().__init__(self) - if inputs is not None: - self.set_arguments(inputs) - self.constructor_validate_and_infer_types() + super().__init__(self, inputs) + # if inputs is not None: + # self.set_arguments(inputs) + # self.constructor_validate_and_infer_types() def validate_and_infer_types(self): + print("from validate") self.set_output_type(0, self.get_input_element_type(0), self.get_input_partial_shape(0)) def clone_with_new_inputs(self, new_inputs): @@ -87,11 +88,12 @@ class CustomOpWithAttribute(Op): class_type_info = DiscreteTypeInfo("CustomOpWithAttribute", "extension") def __init__(self, inputs=None, attrs=None): - super().__init__(self) - if attrs is not None or inputs is not None: - self._attrs = attrs - self.set_arguments(inputs) - self.constructor_validate_and_infer_types() + super().__init__(self, inputs) + self._attrs = attrs + # if attrs is not None or inputs is not None: + # self._attrs = attrs + # self.set_arguments(inputs) + # self.constructor_validate_and_infer_types() def validate_and_infer_types(self): self.set_output_type(0, self.get_input_element_type(0), self.get_input_partial_shape(0)) @@ -212,12 +214,9 @@ def validate_and_infer_types(self): class CustomSimpleOpWithAttribute(Op): class_type_info = DiscreteTypeInfo("CustomSimpleOpWithAttribute", "extension") - def __init__(self, inputs=None, attrs=None): - super().__init__(self) + def __init__(self, inputs=None, **attrs): + super().__init__(self, inputs) self._attrs = attrs - if attrs is not None or inputs is not None: - self.set_arguments(inputs) - self.constructor_validate_and_infer_types() def validate_and_infer_types(self): self.set_output_type(0, self.get_input_element_type(0), self.get_input_partial_shape(0)) @@ -249,7 +248,7 @@ def test_op_extension(prepared_paths): custom_simple = CustomSimpleOp(inputs=[param1, param2]) print("6") custom_simple.set_friendly_name("test_add") - custom_with_attribute = CustomSimpleOpWithAttribute(inputs=[custom_simple], attrs={"value_str": "test_attribute"}) + custom_with_attribute = CustomSimpleOpWithAttribute(inputs=[custom_simple], value_str="test_attribute") custom_add = CustomAdd(inputs=[custom_with_attribute]) res = ops.result(custom_add, name="result") simple_model = Model(res, [param1, param2], "SimpleModel") @@ -291,4 +290,4 @@ def get_type_info(self): with pytest.raises(RuntimeError) as e: core.add_extension(OpWithBadClassTypeInfo) - assert "operation type_info must be an instance of DiscreteTypeInfo, but is passed." in str(e.value) + assert "operation type_info must be an instance of DiscreteTypeInfo, but is passed." in str(e.value) diff --git a/tools/benchmark_tool/openvino/__init__.py b/tools/benchmark_tool/openvino/__init__.py index 9da5ab6c26d8fe..ff616f21030334 100644 --- a/tools/benchmark_tool/openvino/__init__.py +++ b/tools/benchmark_tool/openvino/__init__.py @@ -32,6 +32,7 @@ from openvino._ov_api import CompiledModel from openvino._ov_api import InferRequest from openvino._ov_api import AsyncInferQueue +from openvino._ov_api import Op from openvino.runtime import Symbol from openvino.runtime import Dimension @@ -55,7 +56,6 @@ from openvino._pyopenvino import RemoteContext from openvino._pyopenvino import RemoteTensor -from openvino._pyopenvino import Op from openvino._pyopenvino import OpExtension # Import opsets diff --git a/tools/ovc/openvino/__init__.py b/tools/ovc/openvino/__init__.py index 9da5ab6c26d8fe..ff616f21030334 100644 --- a/tools/ovc/openvino/__init__.py +++ b/tools/ovc/openvino/__init__.py @@ -32,6 +32,7 @@ from openvino._ov_api import CompiledModel from openvino._ov_api import InferRequest from openvino._ov_api import AsyncInferQueue +from openvino._ov_api import Op from openvino.runtime import Symbol from openvino.runtime import Dimension @@ -55,7 +56,6 @@ from openvino._pyopenvino import RemoteContext from openvino._pyopenvino import RemoteTensor -from openvino._pyopenvino import Op from openvino._pyopenvino import OpExtension # Import opsets From bf9d9e429c5dd714c2ad4c771cd1df2f04e23e87 Mon Sep 17 00:00:00 2001 From: Anastasia Kuporosova Date: Fri, 10 Jan 2025 01:06:40 +0000 Subject: [PATCH 25/29] fix after master merge --- src/bindings/python/src/openvino/__init__.py | 74 ++++++++++++-------- tools/benchmark_tool/openvino/__init__.py | 74 ++++++++++++-------- tools/ovc/openvino/__init__.py | 74 ++++++++++++-------- 3 files changed, 132 insertions(+), 90 deletions(-) diff --git a/src/bindings/python/src/openvino/__init__.py b/src/bindings/python/src/openvino/__init__.py index ff616f21030334..9ae5244665a6e6 100644 --- a/src/bindings/python/src/openvino/__init__.py +++ b/src/bindings/python/src/openvino/__init__.py @@ -7,7 +7,7 @@ # Required for Windows OS platforms # Note: always top-level try: - from openvino.utils import _add_openvino_libs_to_search_path + from openvino.package_utils import _add_openvino_libs_to_search_path _add_openvino_libs_to_search_path() except ImportError: pass @@ -17,16 +17,40 @@ # # This __init__.py forces checking of runtime modules to propagate errors. # # It is not compared with init files from openvino-dev package. # # -# Import all public modules -from openvino import runtime as runtime -from openvino import frontend as frontend -from openvino import helpers as helpers -from openvino import experimental as experimental -from openvino import preprocess as preprocess -from openvino import utils as utils -from openvino import properties as properties -# Import most important classes and functions from openvino.runtime +# Openvino pybind bindings +from openvino._pyopenvino import AxisSet +from openvino._pyopenvino import AxisVector +from openvino._pyopenvino import ConstOutput +from openvino._pyopenvino import Coordinate +from openvino._pyopenvino import CoordinateDiff +from openvino._pyopenvino import DiscreteTypeInfo +from openvino._pyopenvino import Extension +from openvino._pyopenvino import ProfilingInfo +from openvino._pyopenvino import RTMap +from openvino._pyopenvino import Version +from openvino._pyopenvino import Symbol +from openvino._pyopenvino import Dimension +from openvino._pyopenvino import Input +from openvino._pyopenvino import Output +from openvino._pyopenvino import Node +from openvino._pyopenvino import Strides +from openvino._pyopenvino import PartialShape +from openvino._pyopenvino import Shape +from openvino._pyopenvino import Layout +from openvino._pyopenvino import Type +from openvino._pyopenvino import Tensor +from openvino._pyopenvino import OVAny +from openvino._pyopenvino import get_batch +from openvino._pyopenvino import set_batch +from openvino._pyopenvino import serialize +from openvino._pyopenvino import shutdown +from openvino._pyopenvino import save_model +from openvino._pyopenvino import layout_helpers +from openvino._pyopenvino import RemoteContext +from openvino._pyopenvino import RemoteTensor + +# Import public classes from _ov_api from openvino._ov_api import Model from openvino._ov_api import Core from openvino._ov_api import CompiledModel @@ -34,29 +58,19 @@ from openvino._ov_api import AsyncInferQueue from openvino._ov_api import Op -from openvino.runtime import Symbol -from openvino.runtime import Dimension -from openvino.runtime import Strides -from openvino.runtime import PartialShape -from openvino.runtime import Shape -from openvino.runtime import Layout -from openvino.runtime import Type -from openvino.runtime import Tensor -from openvino.runtime import OVAny +# Import all public modules +from openvino import runtime as runtime +from openvino import frontend as frontend +from openvino import helpers as helpers +from openvino import experimental as experimental +from openvino import preprocess as preprocess +from openvino import utils as utils +from openvino import properties as properties # Helper functions for openvino module -from openvino.runtime.utils.data_helpers import tensor_from_file +from openvino.utils.data_helpers import tensor_from_file from openvino._ov_api import compile_model -from openvino.runtime import get_batch -from openvino.runtime import set_batch -from openvino.runtime import serialize -from openvino.runtime import shutdown -from openvino.runtime import save_model -from openvino.runtime import layout_helpers -from openvino._pyopenvino import RemoteContext -from openvino._pyopenvino import RemoteTensor -from openvino._pyopenvino import OpExtension # Import opsets from openvino import op @@ -82,7 +96,7 @@ from openvino._pyopenvino import VASurfaceTensor # Set version for openvino package -from openvino.runtime import get_version +from openvino._pyopenvino import get_version __version__ = get_version() # Tools diff --git a/tools/benchmark_tool/openvino/__init__.py b/tools/benchmark_tool/openvino/__init__.py index ff616f21030334..9ae5244665a6e6 100644 --- a/tools/benchmark_tool/openvino/__init__.py +++ b/tools/benchmark_tool/openvino/__init__.py @@ -7,7 +7,7 @@ # Required for Windows OS platforms # Note: always top-level try: - from openvino.utils import _add_openvino_libs_to_search_path + from openvino.package_utils import _add_openvino_libs_to_search_path _add_openvino_libs_to_search_path() except ImportError: pass @@ -17,16 +17,40 @@ # # This __init__.py forces checking of runtime modules to propagate errors. # # It is not compared with init files from openvino-dev package. # # -# Import all public modules -from openvino import runtime as runtime -from openvino import frontend as frontend -from openvino import helpers as helpers -from openvino import experimental as experimental -from openvino import preprocess as preprocess -from openvino import utils as utils -from openvino import properties as properties -# Import most important classes and functions from openvino.runtime +# Openvino pybind bindings +from openvino._pyopenvino import AxisSet +from openvino._pyopenvino import AxisVector +from openvino._pyopenvino import ConstOutput +from openvino._pyopenvino import Coordinate +from openvino._pyopenvino import CoordinateDiff +from openvino._pyopenvino import DiscreteTypeInfo +from openvino._pyopenvino import Extension +from openvino._pyopenvino import ProfilingInfo +from openvino._pyopenvino import RTMap +from openvino._pyopenvino import Version +from openvino._pyopenvino import Symbol +from openvino._pyopenvino import Dimension +from openvino._pyopenvino import Input +from openvino._pyopenvino import Output +from openvino._pyopenvino import Node +from openvino._pyopenvino import Strides +from openvino._pyopenvino import PartialShape +from openvino._pyopenvino import Shape +from openvino._pyopenvino import Layout +from openvino._pyopenvino import Type +from openvino._pyopenvino import Tensor +from openvino._pyopenvino import OVAny +from openvino._pyopenvino import get_batch +from openvino._pyopenvino import set_batch +from openvino._pyopenvino import serialize +from openvino._pyopenvino import shutdown +from openvino._pyopenvino import save_model +from openvino._pyopenvino import layout_helpers +from openvino._pyopenvino import RemoteContext +from openvino._pyopenvino import RemoteTensor + +# Import public classes from _ov_api from openvino._ov_api import Model from openvino._ov_api import Core from openvino._ov_api import CompiledModel @@ -34,29 +58,19 @@ from openvino._ov_api import AsyncInferQueue from openvino._ov_api import Op -from openvino.runtime import Symbol -from openvino.runtime import Dimension -from openvino.runtime import Strides -from openvino.runtime import PartialShape -from openvino.runtime import Shape -from openvino.runtime import Layout -from openvino.runtime import Type -from openvino.runtime import Tensor -from openvino.runtime import OVAny +# Import all public modules +from openvino import runtime as runtime +from openvino import frontend as frontend +from openvino import helpers as helpers +from openvino import experimental as experimental +from openvino import preprocess as preprocess +from openvino import utils as utils +from openvino import properties as properties # Helper functions for openvino module -from openvino.runtime.utils.data_helpers import tensor_from_file +from openvino.utils.data_helpers import tensor_from_file from openvino._ov_api import compile_model -from openvino.runtime import get_batch -from openvino.runtime import set_batch -from openvino.runtime import serialize -from openvino.runtime import shutdown -from openvino.runtime import save_model -from openvino.runtime import layout_helpers -from openvino._pyopenvino import RemoteContext -from openvino._pyopenvino import RemoteTensor -from openvino._pyopenvino import OpExtension # Import opsets from openvino import op @@ -82,7 +96,7 @@ from openvino._pyopenvino import VASurfaceTensor # Set version for openvino package -from openvino.runtime import get_version +from openvino._pyopenvino import get_version __version__ = get_version() # Tools diff --git a/tools/ovc/openvino/__init__.py b/tools/ovc/openvino/__init__.py index ff616f21030334..9ae5244665a6e6 100644 --- a/tools/ovc/openvino/__init__.py +++ b/tools/ovc/openvino/__init__.py @@ -7,7 +7,7 @@ # Required for Windows OS platforms # Note: always top-level try: - from openvino.utils import _add_openvino_libs_to_search_path + from openvino.package_utils import _add_openvino_libs_to_search_path _add_openvino_libs_to_search_path() except ImportError: pass @@ -17,16 +17,40 @@ # # This __init__.py forces checking of runtime modules to propagate errors. # # It is not compared with init files from openvino-dev package. # # -# Import all public modules -from openvino import runtime as runtime -from openvino import frontend as frontend -from openvino import helpers as helpers -from openvino import experimental as experimental -from openvino import preprocess as preprocess -from openvino import utils as utils -from openvino import properties as properties -# Import most important classes and functions from openvino.runtime +# Openvino pybind bindings +from openvino._pyopenvino import AxisSet +from openvino._pyopenvino import AxisVector +from openvino._pyopenvino import ConstOutput +from openvino._pyopenvino import Coordinate +from openvino._pyopenvino import CoordinateDiff +from openvino._pyopenvino import DiscreteTypeInfo +from openvino._pyopenvino import Extension +from openvino._pyopenvino import ProfilingInfo +from openvino._pyopenvino import RTMap +from openvino._pyopenvino import Version +from openvino._pyopenvino import Symbol +from openvino._pyopenvino import Dimension +from openvino._pyopenvino import Input +from openvino._pyopenvino import Output +from openvino._pyopenvino import Node +from openvino._pyopenvino import Strides +from openvino._pyopenvino import PartialShape +from openvino._pyopenvino import Shape +from openvino._pyopenvino import Layout +from openvino._pyopenvino import Type +from openvino._pyopenvino import Tensor +from openvino._pyopenvino import OVAny +from openvino._pyopenvino import get_batch +from openvino._pyopenvino import set_batch +from openvino._pyopenvino import serialize +from openvino._pyopenvino import shutdown +from openvino._pyopenvino import save_model +from openvino._pyopenvino import layout_helpers +from openvino._pyopenvino import RemoteContext +from openvino._pyopenvino import RemoteTensor + +# Import public classes from _ov_api from openvino._ov_api import Model from openvino._ov_api import Core from openvino._ov_api import CompiledModel @@ -34,29 +58,19 @@ from openvino._ov_api import AsyncInferQueue from openvino._ov_api import Op -from openvino.runtime import Symbol -from openvino.runtime import Dimension -from openvino.runtime import Strides -from openvino.runtime import PartialShape -from openvino.runtime import Shape -from openvino.runtime import Layout -from openvino.runtime import Type -from openvino.runtime import Tensor -from openvino.runtime import OVAny +# Import all public modules +from openvino import runtime as runtime +from openvino import frontend as frontend +from openvino import helpers as helpers +from openvino import experimental as experimental +from openvino import preprocess as preprocess +from openvino import utils as utils +from openvino import properties as properties # Helper functions for openvino module -from openvino.runtime.utils.data_helpers import tensor_from_file +from openvino.utils.data_helpers import tensor_from_file from openvino._ov_api import compile_model -from openvino.runtime import get_batch -from openvino.runtime import set_batch -from openvino.runtime import serialize -from openvino.runtime import shutdown -from openvino.runtime import save_model -from openvino.runtime import layout_helpers -from openvino._pyopenvino import RemoteContext -from openvino._pyopenvino import RemoteTensor -from openvino._pyopenvino import OpExtension # Import opsets from openvino import op @@ -82,7 +96,7 @@ from openvino._pyopenvino import VASurfaceTensor # Set version for openvino package -from openvino.runtime import get_version +from openvino._pyopenvino import get_version __version__ = get_version() # Tools From 7987ad15c4d54f5322ec565e18402a2fbf3dd43f Mon Sep 17 00:00:00 2001 From: Anastasia Kuporosova Date: Fri, 10 Jan 2025 01:06:40 +0000 Subject: [PATCH 26/29] fix after master merge --- src/bindings/python/src/openvino/__init__.py | 1 + tools/benchmark_tool/openvino/__init__.py | 1 + tools/ovc/openvino/__init__.py | 1 + 3 files changed, 3 insertions(+) diff --git a/src/bindings/python/src/openvino/__init__.py b/src/bindings/python/src/openvino/__init__.py index 9ae5244665a6e6..ed4ce45b6b8444 100644 --- a/src/bindings/python/src/openvino/__init__.py +++ b/src/bindings/python/src/openvino/__init__.py @@ -32,6 +32,7 @@ from openvino._pyopenvino import Symbol from openvino._pyopenvino import Dimension from openvino._pyopenvino import Input +from openvino._pyopenvino import OpExtension from openvino._pyopenvino import Output from openvino._pyopenvino import Node from openvino._pyopenvino import Strides diff --git a/tools/benchmark_tool/openvino/__init__.py b/tools/benchmark_tool/openvino/__init__.py index 9ae5244665a6e6..ed4ce45b6b8444 100644 --- a/tools/benchmark_tool/openvino/__init__.py +++ b/tools/benchmark_tool/openvino/__init__.py @@ -32,6 +32,7 @@ from openvino._pyopenvino import Symbol from openvino._pyopenvino import Dimension from openvino._pyopenvino import Input +from openvino._pyopenvino import OpExtension from openvino._pyopenvino import Output from openvino._pyopenvino import Node from openvino._pyopenvino import Strides diff --git a/tools/ovc/openvino/__init__.py b/tools/ovc/openvino/__init__.py index 9ae5244665a6e6..ed4ce45b6b8444 100644 --- a/tools/ovc/openvino/__init__.py +++ b/tools/ovc/openvino/__init__.py @@ -32,6 +32,7 @@ from openvino._pyopenvino import Symbol from openvino._pyopenvino import Dimension from openvino._pyopenvino import Input +from openvino._pyopenvino import OpExtension from openvino._pyopenvino import Output from openvino._pyopenvino import Node from openvino._pyopenvino import Strides From 5a5081e59d1bef161a8ff1956df3e0901350fbac Mon Sep 17 00:00:00 2001 From: Anastasia Kuporosova Date: Fri, 10 Jan 2025 11:19:55 +0000 Subject: [PATCH 27/29] fix linters --- src/bindings/python/src/openvino/_ov_api.py | 4 +-- .../python/src/pyopenvino/graph/op.cpp | 18 +++++------- .../python/src/pyopenvino/graph/op.hpp | 2 +- .../src/pyopenvino/graph/op_extension.hpp | 22 +++------------ .../python/tests/test_graph/test_custom_op.py | 28 ++++++++----------- 5 files changed, 24 insertions(+), 50 deletions(-) diff --git a/src/bindings/python/src/openvino/_ov_api.py b/src/bindings/python/src/openvino/_ov_api.py index 4780e63505b5a0..9b8f5f3ca06d71 100644 --- a/src/bindings/python/src/openvino/_ov_api.py +++ b/src/bindings/python/src/openvino/_ov_api.py @@ -26,9 +26,7 @@ class Op(OpBase): def __init__(self, py_obj, inputs=None) -> None: super().__init__(py_obj) - print("super ok") - self._initialize_type_info() - print("__initialize_type_info ok") + self._update_type_info() if inputs is not None: self.set_arguments(inputs) self.constructor_validate_and_infer_types() diff --git a/src/bindings/python/src/pyopenvino/graph/op.cpp b/src/bindings/python/src/pyopenvino/graph/op.cpp index 69ef475f408095..53489b67de886e 100644 --- a/src/bindings/python/src/pyopenvino/graph/op.cpp +++ b/src/bindings/python/src/pyopenvino/graph/op.cpp @@ -45,7 +45,6 @@ std::shared_ptr PyOp::clone_with_new_inputs(const ov::OutputVector& ne } const ov::op::Op::type_info_t& PyOp::get_type_info() const { - // std::cout << reinterpret_cast(m_type_info.get()) << std::endl; return *m_type_info; } @@ -57,18 +56,17 @@ bool PyOp::has_evaluate() const { PYBIND11_OVERRIDE(bool, ov::op::Op, has_evaluate); } -void PyOp::initialize_type_info() { +void PyOp::update_type_info() { py::gil_scoped_acquire gil; // Acquire the GIL while in this scope. - std::cout << "jello" << std::endl; + // Try to look up the overridden method on the Python side. py::function overriden_py_method = pybind11::get_override(this, "get_type_info"); if (overriden_py_method) { - // TODO: Write a test on this behavior! - std::cout << "use type name" << std::endl; const auto type_info_from_py = overriden_py_method(); if (!py::isinstance(type_info_from_py)) { - // TODO: Rewrite me? - OPENVINO_THROW("operation type_info must be an instance of DiscreteTypeInfo, but ", py::str(py::type::of(type_info_from_py)), " is passed."); + OPENVINO_THROW("operation type_info must be an instance of DiscreteTypeInfo, but ", + py::str(py::type::of(type_info_from_py)), + " is passed."); } m_type_info = type_info_from_py.cast>(); } @@ -78,12 +76,10 @@ void regclass_graph_Op(py::module m) { py::class_, PyOp, ov::Node> op(m, "Op"); op.def(py::init([](const py::object& py_obj) { - std::cout << "lel" <(py_class_name, "extension"); } - void initialize_type_info(); + void update_type_info(); void validate_and_infer_types() override; diff --git a/src/bindings/python/src/pyopenvino/graph/op_extension.hpp b/src/bindings/python/src/pyopenvino/graph/op_extension.hpp index 65c8796da8ed31..1f5f0e42d0c702 100644 --- a/src/bindings/python/src/pyopenvino/graph/op_extension.hpp +++ b/src/bindings/python/src/pyopenvino/graph/op_extension.hpp @@ -26,25 +26,11 @@ class PyOpExtension : public ov::BaseOpExtension { py::object type_info; try { - // get_type_info() is a static method - std::cout << "before static methods" << std::endl; - type_info = py_handle_dtype.attr("get_type_info")(); - std::cout << "after static methods" << std::endl; - } catch (const std::exception&) { - try { - // get_type_info() is a class method - std::cout << "before class methods" << std::endl; - auto obj = py_handle_dtype(); - std::cout << "afte obj" << std::endl; - type_info = obj.attr("get_type_info")(); - std::cout << "afte class methods" << std::endl; - } catch (const std::exception &exc) { - OPENVINO_THROW("Creation of OpExtension failed: ", exc.what()); - } - } - if (!py::isinstance(type_info)) { - OPENVINO_THROW("operation type_info must be an instance of DiscreteTypeInfo, but ", py::str(py::type::of(type_info)), " is passed."); + type_info = py_handle_dtype().attr("get_type_info")(); + } catch (const std::exception &exc) { + OPENVINO_THROW("Creation of OpExtension failed: ", exc.what()); } + m_type_info = type_info.cast>(); OPENVINO_ASSERT(m_type_info->name != nullptr && m_type_info->version_id != nullptr, "Extension type should have information about operation set and operation type."); diff --git a/src/bindings/python/tests/test_graph/test_custom_op.py b/src/bindings/python/tests/test_graph/test_custom_op.py index 48e4f7acbe8622..a2e5588d97836c 100644 --- a/src/bindings/python/tests/test_graph/test_custom_op.py +++ b/src/bindings/python/tests/test_graph/test_custom_op.py @@ -56,13 +56,12 @@ class CustomAdd(Op): class_type_info = DiscreteTypeInfo("CustomAdd", "extension") def __init__(self, inputs=None): - super().__init__(self, inputs) - # if inputs is not None: - # self.set_arguments(inputs) - # self.constructor_validate_and_infer_types() + super().__init__(self) + if inputs is not None: + self.set_arguments(inputs) + self.constructor_validate_and_infer_types() def validate_and_infer_types(self): - print("from validate") self.set_output_type(0, self.get_input_element_type(0), self.get_input_partial_shape(0)) def clone_with_new_inputs(self, new_inputs): @@ -89,11 +88,10 @@ class CustomOpWithAttribute(Op): def __init__(self, inputs=None, attrs=None): super().__init__(self, inputs) - self._attrs = attrs - # if attrs is not None or inputs is not None: - # self._attrs = attrs - # self.set_arguments(inputs) - # self.constructor_validate_and_infer_types() + if attrs is not None or inputs is not None: + self._attrs = attrs + self.set_arguments(inputs) + self.constructor_validate_and_infer_types() def validate_and_infer_types(self): self.set_output_type(0, self.get_input_element_type(0), self.get_input_partial_shape(0)) @@ -235,18 +233,13 @@ def test_op_extension(prepared_paths): input_shape = [2, 1] core = Core() - print("1") core.add_extension(CustomSimpleOp) - print("2") core.add_extension(OpExtension(CustomSimpleOpWithAttribute)) - print("3") core.add_extension(OpExtension(CustomAdd)) - print("4") + param1 = ops.parameter(Shape(input_shape), dtype=np.float32, name="data1") param2 = ops.parameter(Shape(input_shape), dtype=np.float32, name="data2") - print("5") custom_simple = CustomSimpleOp(inputs=[param1, param2]) - print("6") custom_simple.set_friendly_name("test_add") custom_with_attribute = CustomSimpleOpWithAttribute(inputs=[custom_simple], value_str="test_attribute") custom_add = CustomAdd(inputs=[custom_with_attribute]) @@ -272,7 +265,8 @@ def __init__(self, inputs=None): self.set_arguments(inputs) self.constructor_validate_and_infer_types() - def get_type_info(self): + @staticmethod + def get_type_info(): return OpWithBadClassTypeInfo.class_type_info core = Core() From 2caf7e568d9425d1f1b56b3a440f6655c40d2a35 Mon Sep 17 00:00:00 2001 From: Anastasia Kuporosova Date: Fri, 10 Jan 2025 11:37:58 +0000 Subject: [PATCH 28/29] add types --- src/bindings/python/src/openvino/_ov_api.py | 7 +++---- src/bindings/python/tests/test_graph/test_custom_op.py | 6 +++--- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/bindings/python/src/openvino/_ov_api.py b/src/bindings/python/src/openvino/_ov_api.py index 9b8f5f3ca06d71..88182f5ce7b9ff 100644 --- a/src/bindings/python/src/openvino/_ov_api.py +++ b/src/bindings/python/src/openvino/_ov_api.py @@ -3,7 +3,7 @@ # SPDX-License-Identifier: Apache-2.0 from types import TracebackType -from typing import Any, Iterable, Union, Optional, Dict, Type +from typing import Any, Iterable, Union, Optional, Dict, Type, List from pathlib import Path @@ -12,8 +12,7 @@ from openvino._pyopenvino import CompiledModel as CompiledModelBase from openvino._pyopenvino import AsyncInferQueue as AsyncInferQueueBase from openvino._pyopenvino import Op as OpBase -from openvino._pyopenvino import Tensor -from openvino._pyopenvino import Node +from openvino._pyopenvino import Node, Output, Tensor from openvino.utils.data_helpers import ( OVDict, @@ -24,7 +23,7 @@ class Op(OpBase): - def __init__(self, py_obj, inputs=None) -> None: + def __init__(self, py_obj: "Op", inputs: Optional[List[Union[Node, Output]]] = None) -> None: super().__init__(py_obj) self._update_type_info() if inputs is not None: diff --git a/src/bindings/python/tests/test_graph/test_custom_op.py b/src/bindings/python/tests/test_graph/test_custom_op.py index a2e5588d97836c..63ac8e8f4b1d78 100644 --- a/src/bindings/python/tests/test_graph/test_custom_op.py +++ b/src/bindings/python/tests/test_graph/test_custom_op.py @@ -87,7 +87,7 @@ class CustomOpWithAttribute(Op): class_type_info = DiscreteTypeInfo("CustomOpWithAttribute", "extension") def __init__(self, inputs=None, attrs=None): - super().__init__(self, inputs) + super().__init__(self) if attrs is not None or inputs is not None: self._attrs = attrs self.set_arguments(inputs) @@ -239,9 +239,9 @@ def test_op_extension(prepared_paths): param1 = ops.parameter(Shape(input_shape), dtype=np.float32, name="data1") param2 = ops.parameter(Shape(input_shape), dtype=np.float32, name="data2") - custom_simple = CustomSimpleOp(inputs=[param1, param2]) + custom_simple = CustomSimpleOp([param1, param2]) custom_simple.set_friendly_name("test_add") - custom_with_attribute = CustomSimpleOpWithAttribute(inputs=[custom_simple], value_str="test_attribute") + custom_with_attribute = CustomSimpleOpWithAttribute([custom_simple], value_str="test_attribute") custom_add = CustomAdd(inputs=[custom_with_attribute]) res = ops.result(custom_add, name="result") simple_model = Model(res, [param1, param2], "SimpleModel") From 27f6df2d331ad6a5d3fab00965aec9f29e3eb4c8 Mon Sep 17 00:00:00 2001 From: Anastasia Kuporosova Date: Mon, 13 Jan 2025 16:04:14 +0000 Subject: [PATCH 29/29] apply changes from comments --- src/bindings/python/src/openvino/_ov_api.py | 8 ++++++-- src/bindings/python/tests/test_graph/test_custom_op.py | 9 +++++---- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/bindings/python/src/openvino/_ov_api.py b/src/bindings/python/src/openvino/_ov_api.py index 88182f5ce7b9ff..152e84a265f7ec 100644 --- a/src/bindings/python/src/openvino/_ov_api.py +++ b/src/bindings/python/src/openvino/_ov_api.py @@ -3,7 +3,7 @@ # SPDX-License-Identifier: Apache-2.0 from types import TracebackType -from typing import Any, Iterable, Union, Optional, Dict, Type, List +from typing import Any, Iterable, Union, Optional, Dict, Tuple, Type, List from pathlib import Path @@ -23,9 +23,13 @@ class Op(OpBase): - def __init__(self, py_obj: "Op", inputs: Optional[List[Union[Node, Output]]] = None) -> None: + def __init__(self, py_obj: "Op", inputs: Optional[Union[List[Union[Node, Output]], Tuple[Union[Node, Output, List[Union[Node, Output]]]]]] = None) -> None: super().__init__(py_obj) self._update_type_info() + if isinstance(inputs, tuple): + inputs = None if len(inputs) == 0 else list(inputs) + if inputs is not None and len(inputs) == 1 and isinstance(inputs[0], list): + inputs = inputs[0] if inputs is not None: self.set_arguments(inputs) self.constructor_validate_and_infer_types() diff --git a/src/bindings/python/tests/test_graph/test_custom_op.py b/src/bindings/python/tests/test_graph/test_custom_op.py index 63ac8e8f4b1d78..9371355dcb3b4b 100644 --- a/src/bindings/python/tests/test_graph/test_custom_op.py +++ b/src/bindings/python/tests/test_graph/test_custom_op.py @@ -88,8 +88,8 @@ class CustomOpWithAttribute(Op): def __init__(self, inputs=None, attrs=None): super().__init__(self) - if attrs is not None or inputs is not None: - self._attrs = attrs + self._attrs = attrs + if inputs is not None: self.set_arguments(inputs) self.constructor_validate_and_infer_types() @@ -202,7 +202,7 @@ def test_custom_op(): class CustomSimpleOp(Op): - def __init__(self, inputs=None): + def __init__(self, *inputs): super().__init__(self, inputs) def validate_and_infer_types(self): @@ -239,7 +239,8 @@ def test_op_extension(prepared_paths): param1 = ops.parameter(Shape(input_shape), dtype=np.float32, name="data1") param2 = ops.parameter(Shape(input_shape), dtype=np.float32, name="data2") - custom_simple = CustomSimpleOp([param1, param2]) + custom_simple = CustomSimpleOp(param1, param2) + assert custom_simple.get_type_name() == "CustomSimpleOp" custom_simple.set_friendly_name("test_add") custom_with_attribute = CustomSimpleOpWithAttribute([custom_simple], value_str="test_attribute") custom_add = CustomAdd(inputs=[custom_with_attribute])