28#include <ChimeraTK/Exception.h>
29#include <ChimeraTK/SupportedUserTypes.h>
31#include <pybind11/numpy.h>
35 namespace py = pybind11;
44 template<
typename expectedUserType>
48 py::object clean_input = input;
49 if(py::hasattr(input,
"item")) {
51 clean_input = input.attr(
"item")();
55 if(py::isinstance<py::str>(clean_input)) {
56 return userTypeToUserType<expectedUserType>(clean_input.cast<std::string>());
60 if(py::isinstance<py::float_>(clean_input)) {
61 return userTypeToUserType<expectedUserType>(clean_input.cast<
double>());
64 if(py::isinstance<py::int_>(clean_input)) {
69 if(clean_input.attr(
"__lt__")(py::int_(0)).cast<
bool>()) {
70 return userTypeToUserType<expectedUserType>(clean_input.cast<int64_t>());
72 return userTypeToUserType<expectedUserType>(clean_input.cast<uint64_t>());
75 return userTypeToUserType<expectedUserType>(clean_input.cast<
double>());
81 template<
typename expectedUserType,
typename FROM>
83 std::vector<expectedUserType> converted(arr.size());
85 auto typed_arr = arr.cast<py::array_t<FROM>>();
89 auto r = typed_arr.template unchecked<1>();
91 if constexpr(std::is_same_v<expectedUserType, FROM>) {
93 std::memcpy(converted.data(), r.data(0), r.shape(0) * r.itemsize());
97 for(ssize_t i = 0; i < r.shape(0); ++i) {
100 converted[i] = userTypeToUserType<expectedUserType>(r(i));
114 template<
typename expectedUserType>
116 py::dtype dt = arr.dtype();
118 if(dt.is(py::dtype::of<int8_t>())) {
119 return convertTypedPyArray<expectedUserType, int8_t>(arr);
121 if(dt.is(py::dtype::of<int16_t>())) {
122 return convertTypedPyArray<expectedUserType, int16_t>(arr);
124 if(dt.is(py::dtype::of<int32_t>())) {
125 return convertTypedPyArray<expectedUserType, int32_t>(arr);
127 if(dt.is(py::dtype::of<int64_t>())) {
128 return convertTypedPyArray<expectedUserType, int64_t>(arr);
130 if(dt.is(py::dtype::of<uint8_t>())) {
131 return convertTypedPyArray<expectedUserType, uint8_t>(arr);
133 if(dt.is(py::dtype::of<uint16_t>())) {
134 return convertTypedPyArray<expectedUserType, uint16_t>(arr);
136 if(dt.is(py::dtype::of<uint32_t>())) {
137 return convertTypedPyArray<expectedUserType, uint32_t>(arr);
139 if(dt.is(py::dtype::of<uint64_t>())) {
140 return convertTypedPyArray<expectedUserType, uint64_t>(arr);
142 if(dt.is(py::dtype::of<float>())) {
143 return convertTypedPyArray<expectedUserType, float>(arr);
145 if(dt.is(py::dtype::of<double>())) {
146 return convertTypedPyArray<expectedUserType, double>(arr);
148 if(dt.is(py::dtype::of<bool>())) {
149 return convertTypedPyArray<expectedUserType, bool>(arr);
160 template<
typename expectedUserType>
166 auto seq = input.cast<py::sequence>();
167 std::vector<expectedUserType> converted(seq.size());
169 std::transform(seq.begin(), seq.end(), converted.begin(), [](
const auto& itemHandle) {
170 auto v = py::reinterpret_borrow<py::object>(itemHandle);
171 return convertPyScalar<expectedUserType>(v);
179 template<
typename expectedUserType>
180 std::vector<expectedUserType>
convertPyObject(
const py::object& input,
bool allowScalar,
bool allowArray) {
181 assert(allowScalar || allowArray);
184 if(py::isinstance<py::array>(input)) {
185 auto arr = input.cast<py::array>();
186 return convertPyArray<expectedUserType>(arr);
191 if(py::isinstance<py::sequence>(input) && !py::isinstance<py::str>(input)) {
192 return convertPyList<expectedUserType>(input);
197 return {convertPyScalar<expectedUserType>(input)};
204 return convertPyList<expectedUserType>(input);
213 template<
typename expectedUserType,
typename FROM>
215 auto typed = arr.cast<py::array_t<FROM>>();
216 auto r = typed.template unchecked<2>();
217 ssize_t rows = arr.shape(0);
218 ssize_t cols = arr.shape(1);
219 std::vector<std::vector<expectedUserType>> converted(rows, std::vector<expectedUserType>(cols));
221 if constexpr(std::is_same_v<expectedUserType, FROM>) {
222 if(arr.flags() & py::array::c_style) {
224 for(ssize_t i = 0; i < rows; ++i) {
225 std::memcpy(converted[i].data(), r.data(i, 0), cols * r.itemsize());
230 for(ssize_t i = 0; i < rows; ++i) {
231 for(ssize_t j = 0; j < cols; ++j) {
232 converted[i][j] = userTypeToUserType<expectedUserType>(r(i, j));
241 template<
typename expectedUserType>
243 py::dtype dt = arr.dtype();
244 if(dt.is(py::dtype::of<int8_t>())) {
245 return convertTypedPyArray2D<expectedUserType, int8_t>(arr);
247 if(dt.is(py::dtype::of<int16_t>())) {
248 return convertTypedPyArray2D<expectedUserType, int16_t>(arr);
250 if(dt.is(py::dtype::of<int32_t>())) {
251 return convertTypedPyArray2D<expectedUserType, int32_t>(arr);
253 if(dt.is(py::dtype::of<int64_t>())) {
254 return convertTypedPyArray2D<expectedUserType, int64_t>(arr);
256 if(dt.is(py::dtype::of<uint8_t>())) {
257 return convertTypedPyArray2D<expectedUserType, uint8_t>(arr);
259 if(dt.is(py::dtype::of<uint16_t>())) {
260 return convertTypedPyArray2D<expectedUserType, uint16_t>(arr);
262 if(dt.is(py::dtype::of<uint32_t>())) {
263 return convertTypedPyArray2D<expectedUserType, uint32_t>(arr);
265 if(dt.is(py::dtype::of<uint64_t>())) {
266 return convertTypedPyArray2D<expectedUserType, uint64_t>(arr);
268 if(dt.is(py::dtype::of<float>())) {
269 return convertTypedPyArray2D<expectedUserType, float>(arr);
271 if(dt.is(py::dtype::of<double>())) {
272 return convertTypedPyArray2D<expectedUserType, double>(arr);
274 if(dt.is(py::dtype::of<bool>())) {
275 return convertTypedPyArray2D<expectedUserType, bool>(arr);
283 template<
typename expectedUserType>
285 if(py::isinstance<py::array>(input)) {
286 auto arr = input.cast<py::array>();
287 if(arr.ndim() != 2) {
290 return convertPyArray2D<expectedUserType>(arr);
294 auto outer = input.cast<py::sequence>();
295 std::vector<std::vector<expectedUserType>> result;
296 result.reserve(outer.size());
297 for(
const auto& rowHandle : outer) {
298 auto row = py::reinterpret_borrow<py::object>(rowHandle);
299 result.push_back(convertPyObject<expectedUserType>(row,
false,
true));
309 template<
typename expectedUserType>
311 std::vector<expectedUserType> ret = convertPyObject<expectedUserType>(input,
true,
true);
312 if(ret.size() != 1) {
319 template<
typename expectedUserType>
321 std::vector<expectedUserType> ret = convertPyObject<expectedUserType>(input,
true,
true);
Exception thrown when a logic error has occured.
This header collects some conversion routines which should be useful both for ApplicationCore Python ...
std::vector< std::vector< expectedUserType > > convertTypedPyArray2D(const py::array &arr)
helper for 2D numpy array conversion when the data type is known at compile time
expectedUserType convertPyScalar(const py::object &input)
Convert a python scalar (bool, int, float, str; also numpy scalars) into the expected UserType.
std::optional< expectedUserType > convertScalarOptionalValue(const py::object &input)
Just like convertScalarValue, but input may be empty.
expectedUserType convertScalarValue(const py::object &input)
Convert a python object (scalar, 1-element list, or 1-element array) into a UserType scalar value.
std::vector< expectedUserType > convertTypedPyArray(const py::array &arr)
helper for 1D numpy array conversion when data types are know at compile time
std::vector< expectedUserType > convertPyList(const py::object &input)
this takes native python lists, content possibly of mixed types convertToPyArr should be preferred wh...
std::vector< expectedUserType > convertPyArray(const py::array &arr)
This takes 1D numpy arrays of any type.
std::vector< expectedUserType > convertPyObject(const py::object &input, bool allowScalar, bool allowArray)
input may be numpy arrays, numpy scalars, python primitives, and lists of numpy scalars or python pri...
std::vector< std::vector< expectedUserType > > convertPyArray2D(const py::array &arr)
Take a 2D numpy array and convert it into a vector of vectors of expectedUserType (one inner vector p...
std::vector< std::vector< expectedUserType > > convertPyObject2D(const py::object &input)
input may be a nested python list (list of lists) or a 2D numpy array; result is one inner vector per...