[PYTHON] Properties API, improvements of OVAny (#11389)
* WIP for POC * Bulkwork * Clean up current solution * Extend Core methods * Refactor Any python converts * add submodule to runtime * Add initial tests * Fix copy-paste error * Extend test * Improve casting options and move common parts to utils * Add newline * Fix properties, remove class approach, move to all string Properties API * Fix codestyle * Fix pystyle * ResolveTODOs, better align and extend python api * Add extended test cases * Fix properties in one of compile_model overload. Co-authored-by: Alexey Lebedev <alexey.lebedev@intel.com> * Update src/bindings/python/tests/test_inference_engine/test_properties.py Co-authored-by: Alexey Lebedev <alexey.lebedev@intel.com> Co-authored-by: Alexey Lebedev <alexey.lebedev@intel.com>
This commit is contained in:
1 parent
fca2595293
commit
8e9fb18882
11 files changed
+486
-148
No files matched your search
@@ -57,6 +57,9 @@ from openvino.runtime import opset6
|
||||
from openvino.runtime import opset7
|
||||
from openvino.runtime import opset8
|
||||
|
||||
# Import properties API
|
||||
from openvino.pyopenvino import properties
|
||||
|
||||
# Helper functions for openvino module
|
||||
from openvino.runtime.ie_api import tensor_from_file
|
||||
from openvino.runtime.ie_api import compile_model
|
||||
|
||||
@@ -272,7 +272,7 @@ class Core(CoreBase):
|
||||
:type device_name: str
|
||||
:param config: Optional dict of pairs:
|
||||
(property name, property value) relevant only for this load operation.
|
||||
:type config: dict
|
||||
:type config: dict, optional
|
||||
:return: A compiled model.
|
||||
:rtype: openvino.runtime.CompiledModel
|
||||
"""
|
||||
@@ -296,9 +296,9 @@ class Core(CoreBase):
|
||||
Note: if device_name is not used to compile the original model,
|
||||
an exception is thrown.
|
||||
:type device_name: str
|
||||
:param properties: Optional map of pairs: (property name,
|
||||
property value) relevant only for this load operation.
|
||||
:type properties: dict, optional
|
||||
:param config: Optional dict of pairs:
|
||||
(property name, property value) relevant only for this load operation.
|
||||
:type config: dict, optional
|
||||
:return: A compiled model.
|
||||
:rtype: openvino.runtime.CompiledModel
|
||||
|
||||
|
||||
@@ -147,11 +147,7 @@ void regclass_CompiledModel(py::module m) {
|
||||
cls.def(
|
||||
"set_property",
|
||||
[](ov::CompiledModel& self, const std::map<std::string, py::object>& properties) {
|
||||
std::map<std::string, ov::Any> properties_to_cpp;
|
||||
for (const auto& property : properties) {
|
||||
properties_to_cpp[property.first] = ov::Any(py_object_to_any(property.second));
|
||||
}
|
||||
self.set_property({properties_to_cpp.begin(), properties_to_cpp.end()});
|
||||
self.set_property(Common::utils::properties_to_any_map(properties));
|
||||
},
|
||||
py::arg("properties"),
|
||||
R"(
|
||||
@@ -162,12 +158,27 @@ void regclass_CompiledModel(py::module m) {
|
||||
:rtype: None
|
||||
)");
|
||||
|
||||
// Overload for single tuple
|
||||
cls.def(
|
||||
"set_property",
|
||||
[](ov::CompiledModel& self, const std::pair<std::string, py::object>& property) {
|
||||
ov::AnyMap _properties{{property.first, py_object_to_any(property.second)}};
|
||||
self.set_property(_properties);
|
||||
},
|
||||
py::arg("property"),
|
||||
R"(
|
||||
Sets properties for current compiled model.
|
||||
|
||||
:param property: Tuple of (property name, matching property value).
|
||||
:type property: tuple
|
||||
)");
|
||||
|
||||
cls.def(
|
||||
"get_property",
|
||||
[](ov::CompiledModel& self, const std::string& name) -> py::object {
|
||||
return Common::utils::from_ov_any(self.get_property(name));
|
||||
[](ov::CompiledModel& self, const std::string& property) -> py::object {
|
||||
return Common::utils::from_ov_any(self.get_property(property));
|
||||
},
|
||||
py::arg("name"),
|
||||
py::arg("property"),
|
||||
R"(
|
||||
Gets properties for current compiled model.
|
||||
|
||||
|
||||
@@ -16,8 +16,6 @@
|
||||
|
||||
namespace py = pybind11;
|
||||
|
||||
using ConfigMap = std::map<std::string, std::string>;
|
||||
|
||||
std::string to_string(py::handle handle) {
|
||||
auto encodedString = PyUnicode_AsUTF8String(handle.ptr());
|
||||
return PyBytes_AsString(encodedString);
|
||||
@@ -35,28 +33,35 @@ void regclass_Core(py::module m) {
|
||||
cls.def(
|
||||
"set_property",
|
||||
[](ov::Core& self, const std::map<std::string, py::object>& properties) {
|
||||
std::map<std::string, ov::Any> properties_to_cpp;
|
||||
for (const auto& property : properties) {
|
||||
properties_to_cpp[property.first] = ov::Any(py_object_to_any(property.second));
|
||||
}
|
||||
self.set_property({properties_to_cpp.begin(), properties_to_cpp.end()});
|
||||
self.set_property(Common::utils::properties_to_any_map(properties));
|
||||
},
|
||||
py::arg("properties"),
|
||||
R"(
|
||||
Sets properties.
|
||||
|
||||
:param properties: Optional dict of pairs: (property name, property value).
|
||||
:param properties: Dict of pairs: (property name, property value).
|
||||
:type properties: dict
|
||||
)");
|
||||
|
||||
// Overload for single tuple
|
||||
cls.def(
|
||||
"set_property",
|
||||
[](ov::Core& self, const std::pair<std::string, py::object>& property) {
|
||||
ov::AnyMap _properties{{property.first, py_object_to_any(property.second)}};
|
||||
self.set_property(_properties);
|
||||
},
|
||||
py::arg("property"),
|
||||
R"(
|
||||
Sets properties for the device.
|
||||
|
||||
:param property: Tuple of (property name, matching property value).
|
||||
:type property: tuple
|
||||
)");
|
||||
|
||||
cls.def(
|
||||
"set_property",
|
||||
[](ov::Core& self, const std::string& device_name, const std::map<std::string, py::object>& properties) {
|
||||
std::map<std::string, ov::Any> properties_to_cpp;
|
||||
for (const auto& property : properties) {
|
||||
properties_to_cpp[property.first] = ov::Any(py_object_to_any(property.second));
|
||||
}
|
||||
self.set_property(device_name, {properties_to_cpp.begin(), properties_to_cpp.end()});
|
||||
self.set_property(device_name, Common::utils::properties_to_any_map(properties));
|
||||
},
|
||||
py::arg("device_name"),
|
||||
py::arg("properties"),
|
||||
@@ -65,22 +70,59 @@ void regclass_Core(py::module m) {
|
||||
|
||||
:param device_name: Name of the device.
|
||||
:type device_name: str
|
||||
:param properties: Optional dict of pairs: (property name, property value).
|
||||
:param properties: Dict of pairs: (property name, property value).
|
||||
:type properties: dict
|
||||
)");
|
||||
|
||||
// Overload for single tuple
|
||||
cls.def(
|
||||
"set_property",
|
||||
[](ov::Core& self, const std::string& device_name, const std::pair<std::string, py::object>& property) {
|
||||
ov::AnyMap _properties{{property.first, py_object_to_any(property.second)}};
|
||||
self.set_property(device_name, _properties);
|
||||
},
|
||||
py::arg("device_name"),
|
||||
py::arg("property"),
|
||||
R"(
|
||||
Sets properties for the device.
|
||||
|
||||
:param device_name: Name of the device.
|
||||
:type device_name: str
|
||||
:param property: Tuple of (property name, matching property value).
|
||||
:type property: tuple
|
||||
)");
|
||||
|
||||
cls.def(
|
||||
"get_property",
|
||||
[](ov::Core& self, const std::string& device_name, const std::string& property) -> py::object {
|
||||
return Common::utils::from_ov_any(self.get_property(device_name, property));
|
||||
},
|
||||
py::arg("device_name"),
|
||||
py::arg("property"),
|
||||
R"(
|
||||
Gets properties dedicated to device behaviour.
|
||||
|
||||
:param device_name: A name of a device to get a properties value.
|
||||
:type device_name: str
|
||||
:param property: Property or name of Property.
|
||||
:type property: str
|
||||
:return: Extracted information from property.
|
||||
:rtype: object
|
||||
)");
|
||||
|
||||
cls.def(
|
||||
"compile_model",
|
||||
[](ov::Core& self,
|
||||
const std::shared_ptr<const ov::Model>& model,
|
||||
const std::string& device_name,
|
||||
const std::map<std::string, std::string>& properties) {
|
||||
return self.compile_model(model, device_name, {properties.begin(), properties.end()});
|
||||
const std::map<std::string, py::object>& properties) {
|
||||
auto _properties = Common::utils::properties_to_any_map(properties);
|
||||
py::gil_scoped_release release;
|
||||
return self.compile_model(model, device_name, _properties);
|
||||
},
|
||||
py::call_guard<py::gil_scoped_release>(),
|
||||
py::arg("model"),
|
||||
py::arg("device_name"),
|
||||
py::arg("config") = py::dict(),
|
||||
py::arg("properties"),
|
||||
R"(
|
||||
Creates a compiled model from a source model object.
|
||||
Users can create as many compiled models as they need, and use them simultaneously
|
||||
@@ -102,12 +144,13 @@ void regclass_Core(py::module m) {
|
||||
"compile_model",
|
||||
[](ov::Core& self,
|
||||
const std::shared_ptr<const ov::Model>& model,
|
||||
const std::map<std::string, std::string>& config) {
|
||||
return self.compile_model(model, ov::AnyMap{config.begin(), config.end()});
|
||||
const std::map<std::string, py::object>& properties) {
|
||||
auto _properties = Common::utils::properties_to_any_map(properties);
|
||||
py::gil_scoped_release release;
|
||||
return self.compile_model(model, _properties);
|
||||
},
|
||||
py::call_guard<py::gil_scoped_release>(),
|
||||
py::arg("model"),
|
||||
py::arg("config") = py::dict(),
|
||||
py::arg("properties"),
|
||||
R"(
|
||||
Creates and loads a compiled model from a source model to the default OpenVINO device
|
||||
selected by AUTO plugin. Users can create as many compiled models as they need, and use
|
||||
@@ -128,13 +171,14 @@ void regclass_Core(py::module m) {
|
||||
[](ov::Core& self,
|
||||
const std::string& model_path,
|
||||
const std::string& device_name,
|
||||
const std::map<std::string, std::string>& config) {
|
||||
return self.compile_model(model_path, device_name, {config.begin(), config.end()});
|
||||
const std::map<std::string, py::object>& properties) {
|
||||
auto _properties = Common::utils::properties_to_any_map(properties);
|
||||
py::gil_scoped_release release;
|
||||
return self.compile_model(model_path, device_name, _properties);
|
||||
},
|
||||
py::call_guard<py::gil_scoped_release>(),
|
||||
py::arg("model_path"),
|
||||
py::arg("device_name"),
|
||||
py::arg("properties") = py::dict(),
|
||||
py::arg("properties"),
|
||||
R"(
|
||||
Reads model and creates a compiled model from IR / ONNX / PDPD file.
|
||||
This can be more efficient than using read_model + compile_model(model_in_memory_object) flow,
|
||||
@@ -154,12 +198,13 @@ void regclass_Core(py::module m) {
|
||||
|
||||
cls.def(
|
||||
"compile_model",
|
||||
[](ov::Core& self, const std::string& model_path, const std::map<std::string, std::string>& properties) {
|
||||
return self.compile_model(model_path, ov::AnyMap{properties.begin(), properties.end()});
|
||||
[](ov::Core& self, const std::string& model_path, const std::map<std::string, py::object>& properties) {
|
||||
auto _properties = Common::utils::properties_to_any_map(properties);
|
||||
py::gil_scoped_release release;
|
||||
return self.compile_model(model_path, _properties);
|
||||
},
|
||||
py::call_guard<py::gil_scoped_release>(),
|
||||
py::arg("model_path"),
|
||||
py::arg("config") = py::dict(),
|
||||
py::arg("properties"),
|
||||
R"(
|
||||
Reads model and creates a compiled model from IR / ONNX / PDPD file with device selected by AUTO plugin.
|
||||
This can be more efficient than using read_model + compile_model(model_in_memory_object) flow,
|
||||
@@ -280,7 +325,8 @@ void regclass_Core(py::module m) {
|
||||
:type model: str
|
||||
:param weights: A path to a data file For IR format (*.bin): if path is empty,
|
||||
it tries to read a bin file with the same name as xml and if the bin
|
||||
file with the same name was not found, loads IR without weights. For ONNX format (*.onnx): weights parameter is not used.
|
||||
file with the same name was not found, loads IR without weights.
|
||||
For ONNX format (*.onnx): weights parameter is not used.
|
||||
For PDPD format (*.pdmodel) weights parameter is not used.
|
||||
:type weights: str
|
||||
:return: A model.
|
||||
@@ -292,15 +338,16 @@ void regclass_Core(py::module m) {
|
||||
[](ov::Core& self,
|
||||
const std::string& model_stream,
|
||||
const std::string& device_name,
|
||||
const std::map<std::string, std::string>& properties) {
|
||||
const std::map<std::string, py::object>& properties) {
|
||||
auto _properties = Common::utils::properties_to_any_map(properties);
|
||||
py::gil_scoped_release release;
|
||||
std::stringstream _stream;
|
||||
_stream << model_stream;
|
||||
return self.import_model(_stream, device_name, {properties.begin(), properties.end()});
|
||||
return self.import_model(_stream, device_name, _properties);
|
||||
},
|
||||
py::call_guard<py::gil_scoped_release>(),
|
||||
py::arg("model_stream"),
|
||||
py::arg("device_name"),
|
||||
py::arg("properties") = py::none(),
|
||||
py::arg("properties"),
|
||||
R"(
|
||||
Imports a compiled model from a previously exported one.
|
||||
|
||||
@@ -335,7 +382,8 @@ void regclass_Core(py::module m) {
|
||||
[](ov::Core& self,
|
||||
const py::object& model_stream,
|
||||
const std::string& device_name,
|
||||
const std::map<std::string, std::string>& properties) {
|
||||
const std::map<std::string, py::object>& properties) {
|
||||
auto _properties = Common::utils::properties_to_any_map(properties);
|
||||
if (!(py::isinstance(model_stream, pybind11::module::import("io").attr("BytesIO")))) {
|
||||
throw py::type_error("CompiledModel.import_model(model_stream) incompatible function argument: "
|
||||
"`model_stream` must be an io.BytesIO object but " +
|
||||
@@ -347,11 +395,11 @@ void regclass_Core(py::module m) {
|
||||
.attr("read")() // alternative: model_stream.attr("get_value")()
|
||||
.cast<std::string>();
|
||||
py::gil_scoped_release release;
|
||||
return self.import_model(_stream, device_name, {properties.begin(), properties.end()});
|
||||
return self.import_model(_stream, device_name, _properties);
|
||||
},
|
||||
py::arg("model_stream"),
|
||||
py::arg("device_name"),
|
||||
py::arg("properties") = py::none(),
|
||||
py::arg("properties"),
|
||||
R"(
|
||||
Imports a compiled model from a previously exported one.
|
||||
|
||||
@@ -385,24 +433,6 @@ void regclass_Core(py::module m) {
|
||||
new_compiled = core.import_model(user_stream, "CPU")
|
||||
)");
|
||||
|
||||
cls.def(
|
||||
"get_property",
|
||||
[](ov::Core& self, const std::string& device_name, const std::string& name) -> py::object {
|
||||
return Common::utils::from_ov_any(self.get_property(device_name, name));
|
||||
},
|
||||
py::arg("device_name"),
|
||||
py::arg("name"),
|
||||
R"(
|
||||
Gets properties dedicated to device behaviour.
|
||||
|
||||
:param device_name: A name of a device to get a properties value.
|
||||
:type device_name: str
|
||||
:param name: Property name.
|
||||
:type name: str
|
||||
:return: Extracted information from property.
|
||||
:rtype: object
|
||||
)");
|
||||
|
||||
cls.def("register_plugin",
|
||||
&ov::Core::register_plugin,
|
||||
py::arg("plugin_name"),
|
||||
@@ -446,10 +476,11 @@ void regclass_Core(py::module m) {
|
||||
[](ov::Core& self,
|
||||
const std::shared_ptr<const ov::Model>& model,
|
||||
const std::string& device_name,
|
||||
const std::map<std::string, std::string>& properties) {
|
||||
return self.query_model(model, device_name, {properties.begin(), properties.end()});
|
||||
const std::map<std::string, py::object>& properties) {
|
||||
auto _properties = Common::utils::properties_to_any_map(properties);
|
||||
py::gil_scoped_release release;
|
||||
return self.query_model(model, device_name, _properties);
|
||||
},
|
||||
py::call_guard<py::gil_scoped_release>(),
|
||||
py::arg("model"),
|
||||
py::arg("device_name"),
|
||||
py::arg("properties") = py::dict(),
|
||||
|
||||
@@ -0,0 +1,164 @@
|
||||
// Copyright (C) 2018-2022 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#include "pyopenvino/core/properties/properties.hpp"
|
||||
|
||||
#include "pyopenvino/core/common.hpp"
|
||||
#include "pyopenvino/graph/any.hpp"
|
||||
|
||||
namespace py = pybind11;
|
||||
|
||||
void regmodule_properties(py::module m) {
|
||||
// Top submodule
|
||||
py::module m_properties = m.def_submodule("properties", "openvino.runtime.properties submodule");
|
||||
|
||||
// Submodule properties - enums
|
||||
py::enum_<ov::Affinity>(m_properties, "Affinity", py::arithmetic())
|
||||
.value("NONE", ov::Affinity::NONE)
|
||||
.value("CORE", ov::Affinity::CORE)
|
||||
.value("NUMA", ov::Affinity::NUMA)
|
||||
.value("HYBRID_AWARE", ov::Affinity::HYBRID_AWARE);
|
||||
|
||||
// Submodule properties - properties
|
||||
wrap_property_RW(m_properties, ov::enable_profiling, "enable_profiling");
|
||||
wrap_property_RW(m_properties, ov::cache_dir, "cache_dir");
|
||||
wrap_property_RW(m_properties, ov::auto_batch_timeout, "auto_batch_timeout");
|
||||
wrap_property_RW(m_properties, ov::num_streams, "num_streams");
|
||||
wrap_property_RW(m_properties, ov::inference_num_threads, "inference_num_threads");
|
||||
wrap_property_RW(m_properties, ov::compilation_num_threads, "compilation_num_threads");
|
||||
wrap_property_RW(m_properties, ov::affinity, "affinity");
|
||||
|
||||
wrap_property_RO(m_properties, ov::supported_properties, "supported_properties");
|
||||
wrap_property_RO(m_properties, ov::available_devices, "available_devices");
|
||||
wrap_property_RO(m_properties, ov::model_name, "model_name");
|
||||
wrap_property_RO(m_properties, ov::optimal_number_of_infer_requests, "optimal_number_of_infer_requests");
|
||||
wrap_property_RO(m_properties, ov::range_for_streams, "range_for_streams");
|
||||
wrap_property_RO(m_properties, ov::optimal_batch_size, "optimal_batch_size");
|
||||
wrap_property_RO(m_properties, ov::max_batch_size, "max_batch_size");
|
||||
wrap_property_RO(m_properties, ov::range_for_async_infer_requests, "range_for_async_infer_requests");
|
||||
|
||||
// Submodule hint
|
||||
py::module m_hint =
|
||||
m_properties.def_submodule("hint", "openvino.runtime.properties.hint submodule that simulates ov::hint");
|
||||
|
||||
// Submodule hint - enums
|
||||
py::enum_<ov::hint::Priority>(m_hint, "Priority", py::arithmetic())
|
||||
.value("LOW", ov::hint::Priority::LOW)
|
||||
.value("MEDIUM", ov::hint::Priority::MEDIUM)
|
||||
.value("HIGH", ov::hint::Priority::HIGH)
|
||||
.value("DEFAULT", ov::hint::Priority::DEFAULT);
|
||||
|
||||
py::enum_<ov::hint::PerformanceMode>(m_hint, "PerformanceMode", py::arithmetic())
|
||||
.value("UNDEFINED", ov::hint::PerformanceMode::UNDEFINED)
|
||||
.value("LATENCY", ov::hint::PerformanceMode::LATENCY)
|
||||
.value("THROUGHPUT", ov::hint::PerformanceMode::THROUGHPUT)
|
||||
.value("CUMULATIVE_THROUGHPUT", ov::hint::PerformanceMode::CUMULATIVE_THROUGHPUT);
|
||||
|
||||
// Submodule hint - properties
|
||||
wrap_property_RW(m_hint, ov::hint::inference_precision, "inference_precision");
|
||||
wrap_property_RW(m_hint, ov::hint::model_priority, "model_priority");
|
||||
wrap_property_RW(m_hint, ov::hint::performance_mode, "performance_mode");
|
||||
wrap_property_RW(m_hint, ov::hint::num_requests, "num_requests");
|
||||
wrap_property_RW(m_hint, ov::hint::model, "model");
|
||||
wrap_property_RW(m_hint, ov::hint::allow_auto_batching, "allow_auto_batching");
|
||||
|
||||
// Submodule device
|
||||
py::module m_device =
|
||||
m_properties.def_submodule("device", "openvino.runtime.properties.device submodule that simulates ov::device");
|
||||
|
||||
// Submodule device - enums
|
||||
py::enum_<ov::device::Type>(m_device, "Type", py::arithmetic())
|
||||
.value("INTEGRATED", ov::device::Type::INTEGRATED)
|
||||
.value("DISCRETE", ov::device::Type::DISCRETE);
|
||||
|
||||
py::class_<ov::device::Priorities, std::shared_ptr<ov::device::Priorities>> cls_priorities(m_device, "Priorities");
|
||||
|
||||
// Special case: ov::device::priorities
|
||||
m_device.def("priorities", []() {
|
||||
return ov::device::priorities.name();
|
||||
});
|
||||
|
||||
m_device.def("priorities", [](py::args& args) {
|
||||
std::string value{""};
|
||||
for (auto v : args) {
|
||||
if (py::isinstance<py::str>(v)) {
|
||||
value += py::cast<std::string>(v) + std::string{','};
|
||||
} else {
|
||||
throw py::type_error("Incorrect passed value: " + std::string(py::str(v)) +
|
||||
" , expected string values.");
|
||||
}
|
||||
}
|
||||
return ov::device::priorities(value);
|
||||
});
|
||||
|
||||
// Submodule device - properties
|
||||
wrap_property_RW(m_device, ov::device::id, "id");
|
||||
|
||||
wrap_property_RO(m_device, ov::device::full_name, "full_name");
|
||||
wrap_property_RO(m_device, ov::device::architecture, "architecture");
|
||||
wrap_property_RO(m_device, ov::device::type, "type");
|
||||
wrap_property_RO(m_device, ov::device::gops, "gops");
|
||||
wrap_property_RO(m_device, ov::device::thermal, "thermal");
|
||||
wrap_property_RO(m_device, ov::device::capabilities, "capabilities");
|
||||
|
||||
// Modules made in pybind cannot easily register attributes, thus workaround is needed.
|
||||
// Let's simulate module with attributes by creating empty proxy class called FakeModuleName.
|
||||
class FakeCapability {};
|
||||
|
||||
py::class_<FakeCapability, std::shared_ptr<FakeCapability>> m_capability(
|
||||
m_device,
|
||||
"Capability",
|
||||
"openvino.runtime.properties.device.Capability that simulates ov::device::capability");
|
||||
|
||||
m_capability.attr("FP32") = ov::device::capability::FP32;
|
||||
m_capability.attr("BF16") = ov::device::capability::BF16;
|
||||
m_capability.attr("FP16") = ov::device::capability::FP16;
|
||||
m_capability.attr("INT8") = ov::device::capability::INT8;
|
||||
m_capability.attr("INT16") = ov::device::capability::INT16;
|
||||
m_capability.attr("BIN") = ov::device::capability::BIN;
|
||||
m_capability.attr("WINOGRAD") = ov::device::capability::WINOGRAD;
|
||||
m_capability.attr("EXPORT_IMPORT") = ov::device::capability::EXPORT_IMPORT;
|
||||
|
||||
// Submodule log
|
||||
py::module m_log =
|
||||
m_properties.def_submodule("log", "openvino.runtime.properties.log submodule that simulates ov::log");
|
||||
|
||||
// Submodule log - enums
|
||||
py::enum_<ov::log::Level>(m_log, "Level", py::arithmetic())
|
||||
.value("NO", ov::log::Level::NO)
|
||||
.value("ERR", ov::log::Level::ERR)
|
||||
.value("WARNING", ov::log::Level::WARNING)
|
||||
.value("INFO", ov::log::Level::INFO)
|
||||
.value("DEBUG", ov::log::Level::DEBUG)
|
||||
.value("TRACE", ov::log::Level::TRACE);
|
||||
|
||||
// Submodule log - properties
|
||||
wrap_property_RW(m_log, ov::log::level, "level");
|
||||
|
||||
// Submodule streams
|
||||
py::module m_streams =
|
||||
m_properties.def_submodule("streams",
|
||||
"openvino.runtime.properties.streams submodule that simulates ov::streams");
|
||||
|
||||
py::class_<ov::streams::Num, std::shared_ptr<ov::streams::Num>> cls_num(m_streams, "Num");
|
||||
|
||||
cls_num.def(py::init<>());
|
||||
cls_num.def(py::init<const int32_t>());
|
||||
|
||||
// Covers static constexpr Num AUTO{-1};
|
||||
cls_num.attr("AUTO") = ov::streams::AUTO;
|
||||
// Covers static constexpr Num NUMA{-2};
|
||||
cls_num.attr("NUMA") = ov::streams::NUMA;
|
||||
|
||||
cls_num.def("to_integer", [](ov::streams::Num& self) {
|
||||
return self.num;
|
||||
});
|
||||
|
||||
// Submodule streams - properties RW
|
||||
wrap_property_RW(m_streams, ov::streams::num, "num");
|
||||
// Extra scenarios for ov::streams::num
|
||||
m_streams.def("num", [](const int32_t value) {
|
||||
return ov::streams::num(ov::streams::Num(value));
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
// Copyright (C) 2018-2022 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <pybind11/pybind11.h>
|
||||
#include <pybind11/stl.h>
|
||||
|
||||
#include "openvino/runtime/properties.hpp"
|
||||
#include "pyopenvino/core/properties/properties.hpp"
|
||||
|
||||
namespace py = pybind11;
|
||||
|
||||
template <typename T>
|
||||
void wrap_property_RO(py::module m, ov::Property<T, ov::PropertyMutability::RO> property, std::string func_name) {
|
||||
m.def(func_name.c_str(), [property]() {
|
||||
return property.name();
|
||||
});
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void wrap_property_RW(py::module m, ov::Property<T, ov::PropertyMutability::RW> property, std::string func_name) {
|
||||
m.def(func_name.c_str(), [property]() {
|
||||
return property.name();
|
||||
});
|
||||
|
||||
m.def(func_name.c_str(), [property](T value) {
|
||||
return property(value);
|
||||
});
|
||||
}
|
||||
|
||||
void regmodule_properties(py::module m);
|
||||
@@ -28,16 +28,20 @@ void regclass_graph_Any(py::module m) {
|
||||
return ret.str();
|
||||
});
|
||||
|
||||
ov_any.def("__hash__", [](ov::Any& self) {
|
||||
return Common::utils::from_ov_any(self).attr("__hash__")();
|
||||
});
|
||||
|
||||
ov_any.def("__getitem__", [](const ov::Any& self, py::object& k) {
|
||||
return Common::utils::from_ov_any(self)[k];
|
||||
return Common::utils::from_ov_any(self).attr("__getitem__")(k);
|
||||
});
|
||||
|
||||
ov_any.def("__setitem__", [](const ov::Any& self, py::object& k, const std::string& v) {
|
||||
Common::utils::from_ov_any(self)[k] = v;
|
||||
Common::utils::from_ov_any(self).attr("__setitem__")(k, v);
|
||||
});
|
||||
|
||||
ov_any.def("__setitem__", [](const ov::Any& self, py::object& k, const int64_t& v) {
|
||||
Common::utils::from_ov_any(self)[k] = v;
|
||||
Common::utils::from_ov_any(self).attr("__setitem__")(k, v);
|
||||
});
|
||||
|
||||
ov_any.def("__get__", [](const ov::Any& self) {
|
||||
@@ -49,9 +53,7 @@ void regclass_graph_Any(py::module m) {
|
||||
});
|
||||
|
||||
ov_any.def("__len__", [](const ov::Any& self) {
|
||||
py::handle some_object = Common::utils::from_ov_any(self);
|
||||
PyObject* source = some_object.ptr();
|
||||
return PyObject_Length(source);
|
||||
return Common::utils::from_ov_any(self).attr("__len__")();
|
||||
});
|
||||
|
||||
ov_any.def("__eq__", [](const ov::Any& a, const ov::Any& b) -> bool {
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include "pyopenvino/core/infer_request.hpp"
|
||||
#include "pyopenvino/core/offline_transformations.hpp"
|
||||
#include "pyopenvino/core/profiling_info.hpp"
|
||||
#include "pyopenvino/core/properties/properties.hpp"
|
||||
#include "pyopenvino/core/tensor.hpp"
|
||||
#include "pyopenvino/core/variable_state.hpp"
|
||||
#include "pyopenvino/core/version.hpp"
|
||||
@@ -202,6 +203,9 @@ PYBIND11_MODULE(pyopenvino, m) {
|
||||
regclass_ProfilingInfo(m);
|
||||
regclass_Extension(m);
|
||||
|
||||
// Properties and hints
|
||||
regmodule_properties(m);
|
||||
|
||||
// frontend
|
||||
regclass_frontend_Place(m);
|
||||
regclass_frontend_InitializationFailureFrontEnd(m);
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
#include <vector>
|
||||
|
||||
#include "Python.h"
|
||||
#include "openvino/runtime/properties.hpp"
|
||||
|
||||
namespace Common {
|
||||
namespace utils {
|
||||
@@ -24,117 +23,67 @@ py::object from_ov_any(const ov::Any& any) {
|
||||
}
|
||||
// Check for std::string
|
||||
else if (any.is<std::string>()) {
|
||||
return py::cast<py::object>(PyUnicode_FromString(any.as<std::string>().c_str()));
|
||||
return py::cast(any.as<std::string>().c_str());
|
||||
}
|
||||
// Check for int
|
||||
else if (any.is<int>()) {
|
||||
return py::cast<py::object>(PyLong_FromLong(any.as<int>()));
|
||||
return py::cast(any.as<int>());
|
||||
} else if (any.is<int64_t>()) {
|
||||
return py::cast<py::object>(PyLong_FromLong(any.as<int64_t>()));
|
||||
return py::cast(any.as<int64_t>());
|
||||
}
|
||||
// Check for unsigned int
|
||||
else if (any.is<unsigned int>()) {
|
||||
return py::cast<py::object>(PyLong_FromLong(any.as<unsigned int>()));
|
||||
return py::cast(any.as<unsigned int>());
|
||||
}
|
||||
// Check for float
|
||||
else if (any.is<float>()) {
|
||||
return py::cast<py::object>(PyFloat_FromDouble(any.as<float>()));
|
||||
return py::cast(any.as<float>());
|
||||
} else if (any.is<double>()) {
|
||||
return py::cast<py::object>(PyFloat_FromDouble(any.as<double>()));
|
||||
return py::cast(any.as<double>());
|
||||
}
|
||||
// Check for bool
|
||||
else if (any.is<bool>()) {
|
||||
return py::cast<py::object>(any.as<bool>() ? Py_True : Py_False);
|
||||
return py::cast(any.as<bool>());
|
||||
}
|
||||
// Check for std::vector<std::string>
|
||||
else if (any.is<std::vector<std::string>>()) {
|
||||
auto val = any.as<std::vector<std::string>>();
|
||||
PyObject* list = PyList_New(0);
|
||||
for (const auto& it : val) {
|
||||
PyObject* str_val = PyUnicode_FromString(it.c_str());
|
||||
PyList_Append(list, str_val);
|
||||
}
|
||||
return py::cast<py::object>(list);
|
||||
return py::cast(any.as<std::vector<std::string>>());
|
||||
}
|
||||
// Check for std::vector<int>
|
||||
else if (any.is<std::vector<int>>()) {
|
||||
auto val = any.as<std::vector<int>>();
|
||||
PyObject* list = PyList_New(0);
|
||||
for (const auto& it : val) {
|
||||
PyList_Append(list, PyLong_FromLong(it));
|
||||
}
|
||||
return py::cast<py::object>(list);
|
||||
return py::cast(any.as<std::vector<int>>());
|
||||
}
|
||||
// Check for std::vector<int64_t>
|
||||
else if (any.is<std::vector<int64_t>>()) {
|
||||
auto val = any.as<std::vector<int64_t>>();
|
||||
PyObject* list = PyList_New(0);
|
||||
for (const auto& it : val) {
|
||||
PyList_Append(list, PyLong_FromLong(it));
|
||||
}
|
||||
return py::cast<py::object>(list);
|
||||
return py::cast(any.as<std::vector<int64_t>>());
|
||||
}
|
||||
// Check for std::vector<unsigned int>
|
||||
else if (any.is<std::vector<unsigned int>>()) {
|
||||
auto val = any.as<std::vector<unsigned int>>();
|
||||
PyObject* list = PyList_New(0);
|
||||
for (const auto& it : val) {
|
||||
PyList_Append(list, PyLong_FromLong(it));
|
||||
}
|
||||
return py::cast<py::object>(list);
|
||||
return py::cast(any.as<std::vector<unsigned int>>());
|
||||
}
|
||||
// Check for std::vector<float>
|
||||
else if (any.is<std::vector<float>>()) {
|
||||
auto val = any.as<std::vector<float>>();
|
||||
PyObject* list = PyList_New(0);
|
||||
for (const auto& it : val) {
|
||||
PyList_Append(list, PyFloat_FromDouble((double)it));
|
||||
}
|
||||
return py::cast<py::object>(list);
|
||||
return py::cast(any.as<std::vector<float>>());
|
||||
}
|
||||
// Check for std::vector<double>
|
||||
else if (any.is<std::vector<double>>()) {
|
||||
auto val = any.as<std::vector<double>>();
|
||||
PyObject* list = PyList_New(0);
|
||||
for (const auto& it : val) {
|
||||
PyList_Append(list, PyFloat_FromDouble(it));
|
||||
}
|
||||
return py::cast<py::object>(list);
|
||||
return py::cast(any.as<std::vector<double>>());
|
||||
}
|
||||
// Check for std::tuple<unsigned int, unsigned int>
|
||||
else if (any.is<std::tuple<unsigned int, unsigned int>>()) {
|
||||
auto val = any.as<std::tuple<unsigned int, unsigned int>>();
|
||||
PyObject* tuple = PyTuple_New(2);
|
||||
PyTuple_SetItem(tuple, 0, PyLong_FromUnsignedLong((unsigned long)std::get<0>(val)));
|
||||
PyTuple_SetItem(tuple, 1, PyLong_FromUnsignedLong((unsigned long)std::get<1>(val)));
|
||||
return py::cast<py::object>(tuple);
|
||||
return py::cast(any.as<std::tuple<unsigned int, unsigned int>>());
|
||||
}
|
||||
// Check for std::tuple<unsigned int, unsigned int, unsigned int>
|
||||
else if (any.is<std::tuple<unsigned int, unsigned int, unsigned int>>()) {
|
||||
auto val = any.as<std::tuple<unsigned int, unsigned int, unsigned int>>();
|
||||
PyObject* tuple = PyTuple_New(3);
|
||||
PyTuple_SetItem(tuple, 0, PyLong_FromUnsignedLong((unsigned long)std::get<0>(val)));
|
||||
PyTuple_SetItem(tuple, 1, PyLong_FromUnsignedLong((unsigned long)std::get<1>(val)));
|
||||
PyTuple_SetItem(tuple, 2, PyLong_FromUnsignedLong((unsigned long)std::get<2>(val)));
|
||||
return py::cast<py::object>(tuple);
|
||||
return py::cast(any.as<std::tuple<unsigned int, unsigned int, unsigned int>>());
|
||||
}
|
||||
// Check for std::map<std::string, std::string>
|
||||
else if (any.is<std::map<std::string, std::string>>()) {
|
||||
auto val = any.as<std::map<std::string, std::string>>();
|
||||
PyObject* dict = PyDict_New();
|
||||
for (const auto& it : val) {
|
||||
PyDict_SetItemString(dict, it.first.c_str(), PyUnicode_FromString(it.second.c_str()));
|
||||
}
|
||||
return py::cast<py::object>(dict);
|
||||
return py::cast(any.as<std::map<std::string, std::string>>());
|
||||
}
|
||||
// Check for std::map<std::string, int>
|
||||
else if (any.is<std::map<std::string, int>>()) {
|
||||
auto val = any.as<std::map<std::string, int>>();
|
||||
PyObject* dict = PyDict_New();
|
||||
for (const auto& it : val) {
|
||||
PyDict_SetItemString(dict, it.first.c_str(), PyLong_FromLong((long)it.second));
|
||||
}
|
||||
return py::cast<py::object>(dict);
|
||||
return py::cast(any.as<std::map<std::string, int>>());
|
||||
}
|
||||
// Check for std::vector<ov::PropertyName>
|
||||
else if (any.is<std::vector<ov::PropertyName>>()) {
|
||||
@@ -146,10 +95,32 @@ py::object from_ov_any(const ov::Any& any) {
|
||||
PyDict_SetItemString(dict, property_name.c_str(), PyUnicode_FromString(mutability.c_str()));
|
||||
}
|
||||
return py::cast<py::object>(dict);
|
||||
} else if (any.is<ov::element::Type>()) {
|
||||
return py::cast(any.as<ov::element::Type>());
|
||||
} else if (any.is<ov::hint::Priority>()) {
|
||||
return py::cast(any.as<ov::hint::Priority>());
|
||||
} else if (any.is<ov::hint::PerformanceMode>()) {
|
||||
return py::cast(any.as<ov::hint::PerformanceMode>());
|
||||
} else if (any.is<ov::log::Level>()) {
|
||||
return py::cast(any.as<ov::log::Level>());
|
||||
} else if (any.is<ov::device::Type>()) {
|
||||
return py::cast(any.as<ov::device::Type>());
|
||||
} else if (any.is<ov::streams::Num>()) {
|
||||
return py::cast(any.as<ov::streams::Num>());
|
||||
} else if (any.is<ov::Affinity>()) {
|
||||
return py::cast(any.as<ov::Affinity>());
|
||||
} else {
|
||||
PyErr_SetString(PyExc_TypeError, "Failed to convert parameter to Python representation!");
|
||||
return py::cast<py::object>((PyObject*)NULL);
|
||||
}
|
||||
}
|
||||
|
||||
std::map<std::string, ov::Any> properties_to_any_map(const std::map<std::string, py::object>& properties) {
|
||||
std::map<std::string, ov::Any> properties_to_cpp;
|
||||
for (const auto& property : properties) {
|
||||
properties_to_cpp[property.first] = py_object_to_any(property.second);
|
||||
}
|
||||
return properties_to_cpp;
|
||||
}
|
||||
}; // namespace utils
|
||||
}; // namespace Common
|
||||
}; // namespace Common
|
||||
@@ -6,17 +6,21 @@
|
||||
|
||||
#include <pybind11/pybind11.h>
|
||||
#include <openvino/core/any.hpp>
|
||||
#include <openvino/core/type/element_type.hpp>
|
||||
#include <openvino/runtime/properties.hpp>
|
||||
|
||||
namespace py = pybind11;
|
||||
|
||||
namespace Common {
|
||||
namespace utils {
|
||||
|
||||
py::object from_ov_any(const ov::Any &any);
|
||||
|
||||
std::map<std::string, ov::Any> properties_to_any_map(const std::map<std::string, py::object>& properties);
|
||||
}; // namespace utils
|
||||
}; // namespace Common
|
||||
|
||||
inline ov::Any py_object_to_any(const py::object& py_obj) {
|
||||
// Python types
|
||||
if (py::isinstance<py::str>(py_obj)) {
|
||||
return py_obj.cast<std::string>();
|
||||
} else if (py::isinstance<py::bool_>(py_obj)) {
|
||||
@@ -60,7 +64,24 @@ inline ov::Any py_object_to_any(const py::object& py_obj) {
|
||||
default:
|
||||
OPENVINO_ASSERT(false, "Unsupported attribute type.");
|
||||
}
|
||||
|
||||
// OV types
|
||||
} else if (py::isinstance<ov::Any>(py_obj)) {
|
||||
return py::cast<ov::Any>(py_obj);
|
||||
} else if (py::isinstance<ov::element::Type>(py_obj)) {
|
||||
return py::cast<ov::element::Type>(py_obj);
|
||||
} else if (py::isinstance<ov::hint::Priority>(py_obj)) {
|
||||
return py::cast<ov::hint::Priority>(py_obj);
|
||||
} else if (py::isinstance<ov::hint::PerformanceMode>(py_obj)) {
|
||||
return py::cast<ov::hint::PerformanceMode>(py_obj);
|
||||
} else if (py::isinstance<ov::log::Level>(py_obj)) {
|
||||
return py::cast<ov::log::Level>(py_obj);
|
||||
} else if (py::isinstance<ov::device::Type>(py_obj)) {
|
||||
return py::cast<ov::device::Type>(py_obj);
|
||||
} else if (py::isinstance<ov::streams::Num>(py_obj)) {
|
||||
return py::cast<ov::streams::Num>(py_obj);
|
||||
} else if (py::isinstance<ov::Affinity>(py_obj)) {
|
||||
return py::cast<ov::Affinity>(py_obj);
|
||||
// If there is no match fallback to py::object
|
||||
} else if (py::isinstance<py::object>(py_obj)) {
|
||||
return py_obj;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
# Copyright (C) 2022 Intel Corporation
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import pytest
|
||||
import os
|
||||
|
||||
from openvino.runtime import Core, Type, OVAny
|
||||
from openvino.runtime import properties
|
||||
|
||||
|
||||
def test_property_RW():
|
||||
assert properties.device.priorities() == "MULTI_DEVICE_PRIORITIES"
|
||||
assert properties.device.priorities("CPU,GPU") == ("MULTI_DEVICE_PRIORITIES", OVAny("CPU,GPU,"))
|
||||
assert properties.device.priorities("CPU", "GPU") == ("MULTI_DEVICE_PRIORITIES", OVAny("CPU,GPU,"))
|
||||
|
||||
with pytest.raises(TypeError) as e:
|
||||
val = 6
|
||||
properties.device.priorities("CPU", val)
|
||||
assert f"Incorrect passed value: {val} , expected string values." in str(e.value)
|
||||
|
||||
|
||||
def test_property_RO():
|
||||
assert properties.available_devices() == "AVAILABLE_DEVICES"
|
||||
|
||||
with pytest.raises(TypeError) as e:
|
||||
properties.available_devices("something")
|
||||
assert "available_devices(): incompatible function arguments." in str(e.value)
|
||||
|
||||
|
||||
@pytest.mark.skipif(os.environ.get("TEST_DEVICE", "CPU") != "CPU",
|
||||
reason=f"Cannot run test on device {os.environ.get('TEST_DEVICE')}, Plugin specific test")
|
||||
def test_single_property_setting():
|
||||
core = Core()
|
||||
core.set_property("CPU", properties.streams.num(properties.streams.Num.AUTO))
|
||||
|
||||
assert properties.streams.Num.AUTO.to_integer() == -1
|
||||
assert type(core.get_property("CPU", properties.streams.num())) == int
|
||||
|
||||
|
||||
@pytest.mark.skipif(os.environ.get("TEST_DEVICE", "CPU") != "CPU",
|
||||
reason=f"Cannot run test on device {os.environ.get('TEST_DEVICE')}, Plugin specific test")
|
||||
@pytest.mark.parametrize("properties_to_set", [
|
||||
# Dict from list of tuples
|
||||
dict([ # noqa: C406
|
||||
properties.enable_profiling(True),
|
||||
properties.cache_dir("./"),
|
||||
properties.inference_num_threads(9),
|
||||
properties.affinity(properties.Affinity.NONE),
|
||||
properties.hint.inference_precision(Type.f32),
|
||||
properties.hint.performance_mode(properties.hint.PerformanceMode.LATENCY),
|
||||
properties.hint.num_requests(12),
|
||||
properties.streams.num(5),
|
||||
]),
|
||||
# Pure dict
|
||||
{
|
||||
properties.enable_profiling(): True,
|
||||
properties.cache_dir(): "./",
|
||||
properties.inference_num_threads(): 9,
|
||||
properties.affinity(): properties.Affinity.NONE,
|
||||
properties.hint.inference_precision(): Type.f32,
|
||||
properties.hint.performance_mode(): properties.hint.PerformanceMode.LATENCY,
|
||||
properties.hint.num_requests(): 12,
|
||||
properties.streams.num(): 5,
|
||||
},
|
||||
# Mixed dict
|
||||
{
|
||||
properties.enable_profiling(): True,
|
||||
"CACHE_DIR": "./",
|
||||
properties.inference_num_threads(): 9,
|
||||
properties.affinity(): "NONE",
|
||||
"INFERENCE_PRECISION_HINT": Type.f32,
|
||||
properties.hint.performance_mode(): properties.hint.PerformanceMode.LATENCY,
|
||||
properties.hint.num_requests(): 12,
|
||||
"NUM_STREAMS": properties.streams.Num(5),
|
||||
}
|
||||
])
|
||||
def test_properties_core(properties_to_set):
|
||||
core = Core()
|
||||
core.set_property(properties_to_set)
|
||||
|
||||
# RW properties
|
||||
assert core.get_property("CPU", properties.enable_profiling()) is True
|
||||
assert core.get_property("CPU", properties.cache_dir()) == "./"
|
||||
assert core.get_property("CPU", properties.inference_num_threads()) == 9
|
||||
assert core.get_property("CPU", properties.affinity()) == properties.Affinity.NONE
|
||||
assert core.get_property("CPU", properties.hint.inference_precision()) == Type.f32
|
||||
assert core.get_property("CPU", properties.hint.performance_mode()) == properties.hint.PerformanceMode.LATENCY
|
||||
assert core.get_property("CPU", properties.hint.num_requests()) == 12
|
||||
assert core.get_property("CPU", properties.streams.num()) == 5
|
||||
|
||||
# RO properties
|
||||
assert type(core.get_property("CPU", properties.supported_properties())) == dict
|
||||
assert type(core.get_property("CPU", properties.available_devices())) == list
|
||||
assert type(core.get_property("CPU", properties.optimal_number_of_infer_requests())) == int
|
||||
assert type(core.get_property("CPU", properties.range_for_streams())) == tuple
|
||||
assert type(core.get_property("CPU", properties.range_for_async_infer_requests())) == tuple
|
||||
assert type(core.get_property("CPU", properties.device.full_name())) == str
|
||||
assert type(core.get_property("CPU", properties.device.capabilities())) == list
|
||||
Reference in new issue
Block a user