[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:
Jan IwaszkiewiczandAlexey Lebedev authored and GitHub committed 2022-04-05 18:01:36 +03:00
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