Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
gaoqiong
pybind11
Commits
30d43c49
Commit
30d43c49
authored
Apr 14, 2017
by
Cris Luengo
Committed by
Dean Moldovan
May 08, 2017
Browse files
Now `shape`, `size`, `ndims` and `itemsize` are also signed integers.
parent
b68959e8
Changes
13
Hide whitespace changes
Inline
Side-by-side
Showing
13 changed files
with
207 additions
and
205 deletions
+207
-205
docs/advanced/pycpp/numpy.rst
docs/advanced/pycpp/numpy.rst
+17
-20
include/pybind11/buffer_info.h
include/pybind11/buffer_info.h
+14
-14
include/pybind11/class_support.h
include/pybind11/class_support.h
+4
-6
include/pybind11/eigen.h
include/pybind11/eigen.h
+5
-5
include/pybind11/numpy.h
include/pybind11/numpy.h
+90
-90
include/pybind11/pytypes.h
include/pybind11/pytypes.h
+5
-5
include/pybind11/stl_bind.h
include/pybind11/stl_bind.h
+7
-7
tests/test_buffers.cpp
tests/test_buffers.cpp
+21
-21
tests/test_eigen.py
tests/test_eigen.py
+2
-2
tests/test_numpy_array.cpp
tests/test_numpy_array.cpp
+31
-29
tests/test_numpy_array.py
tests/test_numpy_array.py
+6
-1
tests/test_numpy_dtypes.cpp
tests/test_numpy_dtypes.cpp
+3
-3
tests/test_numpy_vectorize.cpp
tests/test_numpy_vectorize.cpp
+2
-2
No files found.
docs/advanced/pycpp/numpy.rst
View file @
30d43c49
...
...
@@ -57,10 +57,10 @@ specification.
struct buffer_info {
void *ptr;
size_t itemsize;
s
size_t itemsize;
std::string format;
in
t ndim;
std::vector<size_t> shape;
ssize_
t ndim;
std::vector<
s
size_t> shape;
std::vector<ssize_t> strides;
};
...
...
@@ -95,8 +95,8 @@ buffer objects (e.g. a NumPy matrix).
throw std::runtime_error("Incompatible buffer dimension!");
auto strides = Strides(
info.strides[rowMajor ? 0 : 1] / sizeof(Scalar),
info.strides[rowMajor ? 1 : 0] / sizeof(Scalar));
info.strides[rowMajor ? 0 : 1] /
(py::ssize_t)
sizeof(Scalar),
info.strides[rowMajor ? 1 : 0] /
(py::ssize_t)
sizeof(Scalar));
auto map = Eigen::Map<Matrix, 0, Strides>(
static_cat<Scalar *>(info.ptr), info.shape[0], info.shape[1], strides);
...
...
@@ -111,17 +111,14 @@ as follows:
.def_buffer([](Matrix &m) -> py::buffer_info {
return py::buffer_info(
m.data(), /* Pointer to buffer */
sizeof(Scalar), /* Size of one scalar */
/* Python struct-style format descriptor */
py::format_descriptor<Scalar>::format(),
/* Number of dimensions */
2,
/* Buffer dimensions */
{ m.rows(), m.cols() },
/* Strides (in bytes) for each index */
m.data(), /* Pointer to buffer */
sizeof(Scalar), /* Size of one scalar */
py::format_descriptor<Scalar>::format(), /* Python struct-style format descriptor */
2, /* Number of dimensions */
{ m.rows(), m.cols() }, /* Buffer dimensions */
{ sizeof(Scalar) * (rowMajor ? m.cols() : 1),
sizeof(Scalar) * (rowMajor ? 1 : m.rows()) }
/* Strides (in bytes) for each index */
);
})
...
...
@@ -321,17 +318,17 @@ where ``N`` gives the required dimensionality of the array:
m.def("sum_3d", [](py::array_t<double> x) {
auto r = x.unchecked<3>(); // x must have ndim = 3; can be non-writeable
double sum = 0;
for (size_t i = 0; i < r.shape(0); i++)
for (size_t j = 0; j < r.shape(1); j++)
for (size_t k = 0; k < r.shape(2); k++)
for (
s
size_t i = 0; i < r.shape(0); i++)
for (
s
size_t j = 0; j < r.shape(1); j++)
for (
s
size_t k = 0; k < r.shape(2); k++)
sum += r(i, j, k);
return sum;
});
m.def("increment_3d", [](py::array_t<double> x) {
auto r = x.mutable_unchecked<3>(); // Will throw if ndim != 3 or flags.writeable is false
for (size_t i = 0; i < r.shape(0); i++)
for (size_t j = 0; j < r.shape(1); j++)
for (size_t k = 0; k < r.shape(2); k++)
for (
s
size_t i = 0; i < r.shape(0); i++)
for (
s
size_t j = 0; j < r.shape(1); j++)
for (
s
size_t k = 0; k < r.shape(2); k++)
r(i, j, k) += 1.0;
}, py::arg().noconvert());
...
...
include/pybind11/buffer_info.h
View file @
30d43c49
...
...
@@ -15,31 +15,31 @@ NAMESPACE_BEGIN(pybind11)
/// Information record describing a Python buffer object
struct
buffer_info
{
void
*
ptr
=
nullptr
;
// Pointer to the underlying storage
size_t
itemsize
=
0
;
// Size of individual items in bytes
size_t
size
=
0
;
// Total number of entries
std
::
string
format
;
// For homogeneous buffers, this should be set to format_descriptor<T>::format()
size_t
ndim
=
0
;
// Number of dimensions
std
::
vector
<
size_t
>
shape
;
// Shape of the tensor (1 entry per dimension)
void
*
ptr
=
nullptr
;
// Pointer to the underlying storage
s
size_t
itemsize
=
0
;
// Size of individual items in bytes
s
size_t
size
=
0
;
// Total number of entries
std
::
string
format
;
// For homogeneous buffers, this should be set to format_descriptor<T>::format()
s
size_t
ndim
=
0
;
// Number of dimensions
std
::
vector
<
s
size_t
>
shape
;
// Shape of the tensor (1 entry per dimension)
std
::
vector
<
ssize_t
>
strides
;
// Number of entries between adjacent entries (for each per dimension)
buffer_info
()
{
}
buffer_info
(
void
*
ptr
,
size_t
itemsize
,
const
std
::
string
&
format
,
size_t
ndim
,
detail
::
any_container
<
size_t
>
shape_in
,
detail
::
any_container
<
ssize_t
>
strides_in
)
buffer_info
(
void
*
ptr
,
s
size_t
itemsize
,
const
std
::
string
&
format
,
s
size_t
ndim
,
detail
::
any_container
<
s
size_t
>
shape_in
,
detail
::
any_container
<
ssize_t
>
strides_in
)
:
ptr
(
ptr
),
itemsize
(
itemsize
),
size
(
1
),
format
(
format
),
ndim
(
ndim
),
shape
(
std
::
move
(
shape_in
)),
strides
(
std
::
move
(
strides_in
))
{
if
(
ndim
!=
shape
.
size
()
||
ndim
!=
strides
.
size
())
if
(
ndim
!=
(
ssize_t
)
shape
.
size
()
||
ndim
!=
(
ssize_t
)
strides
.
size
())
pybind11_fail
(
"buffer_info: ndim doesn't match shape and/or strides length"
);
for
(
size_t
i
=
0
;
i
<
ndim
;
++
i
)
for
(
size_t
i
=
0
;
i
<
(
size_t
)
ndim
;
++
i
)
size
*=
shape
[
i
];
}
buffer_info
(
void
*
ptr
,
size_t
itemsize
,
const
std
::
string
&
format
,
size_t
size
)
buffer_info
(
void
*
ptr
,
s
size_t
itemsize
,
const
std
::
string
&
format
,
s
size_t
size
)
:
buffer_info
(
ptr
,
itemsize
,
format
,
1
,
{
size
},
{
itemsize
})
{
}
explicit
buffer_info
(
Py_buffer
*
view
,
bool
ownview
=
true
)
:
buffer_info
(
view
->
buf
,
(
size_t
)
view
->
itemsize
,
view
->
format
,
(
size_t
)
view
->
ndim
,
:
buffer_info
(
view
->
buf
,
view
->
itemsize
,
view
->
format
,
view
->
ndim
,
{
view
->
shape
,
view
->
shape
+
view
->
ndim
},
{
view
->
strides
,
view
->
strides
+
view
->
ndim
})
{
this
->
view
=
view
;
this
->
ownview
=
ownview
;
...
...
@@ -78,13 +78,13 @@ NAMESPACE_BEGIN(detail)
template
<
typename
T
,
typename
SFINAE
=
void
>
struct
compare_buffer_info
{
static
bool
compare
(
const
buffer_info
&
b
)
{
return
b
.
format
==
format_descriptor
<
T
>::
format
()
&&
b
.
itemsize
==
sizeof
(
T
);
return
b
.
format
==
format_descriptor
<
T
>::
format
()
&&
b
.
itemsize
==
(
ssize_t
)
sizeof
(
T
);
}
};
template
<
typename
T
>
struct
compare_buffer_info
<
T
,
detail
::
enable_if_t
<
std
::
is_integral
<
T
>::
value
>>
{
static
bool
compare
(
const
buffer_info
&
b
)
{
return
b
.
itemsize
==
sizeof
(
T
)
&&
(
b
.
format
==
format_descriptor
<
T
>::
value
||
return
(
size_t
)
b
.
itemsize
==
sizeof
(
T
)
&&
(
b
.
format
==
format_descriptor
<
T
>::
value
||
((
sizeof
(
T
)
==
sizeof
(
long
))
&&
b
.
format
==
(
std
::
is_unsigned
<
T
>::
value
?
"L"
:
"l"
))
||
((
sizeof
(
T
)
==
sizeof
(
size_t
))
&&
b
.
format
==
(
std
::
is_unsigned
<
T
>::
value
?
"N"
:
"n"
)));
}
...
...
include/pybind11/class_support.h
View file @
30d43c49
...
...
@@ -433,7 +433,7 @@ inline void enable_dynamic_attributes(PyHeapTypeObject *heap_type) {
#endif
type
->
tp_flags
|=
Py_TPFLAGS_HAVE_GC
;
type
->
tp_dictoffset
=
type
->
tp_basicsize
;
// place dict at the end
type
->
tp_basicsize
+=
(
Py_
ssize_t
)
sizeof
(
PyObject
*
);
// and allocate enough space for it
type
->
tp_basicsize
+=
(
ssize_t
)
sizeof
(
PyObject
*
);
// and allocate enough space for it
type
->
tp_traverse
=
pybind11_traverse
;
type
->
tp_clear
=
pybind11_clear
;
...
...
@@ -459,18 +459,16 @@ extern "C" inline int pybind11_getbuffer(PyObject *obj, Py_buffer *view, int fla
view
->
ndim
=
1
;
view
->
internal
=
info
;
view
->
buf
=
info
->
ptr
;
view
->
itemsize
=
(
Py_ssize_t
)
info
->
itemsize
;
view
->
itemsize
=
info
->
itemsize
;
view
->
len
=
view
->
itemsize
;
for
(
auto
s
:
info
->
shape
)
view
->
len
*=
(
Py_ssize_t
)
s
;
view
->
len
*=
s
;
if
((
flags
&
PyBUF_FORMAT
)
==
PyBUF_FORMAT
)
view
->
format
=
const_cast
<
char
*>
(
info
->
format
.
c_str
());
if
((
flags
&
PyBUF_STRIDES
)
==
PyBUF_STRIDES
)
{
view
->
ndim
=
(
int
)
info
->
ndim
;
view
->
strides
=
&
info
->
strides
[
0
];
// Next is a pointer cast, let's make sure it's safe.
static_assert
(
sizeof
(
Py_ssize_t
)
==
sizeof
(
info
->
shape
[
0
]),
"sizeof(Py_ssize_t) != sizeof(size_t)"
);
view
->
shape
=
(
Py_ssize_t
*
)
&
info
->
shape
[
0
];
view
->
shape
=
&
info
->
shape
[
0
];
}
Py_INCREF
(
view
->
obj
);
return
0
;
...
...
include/pybind11/eigen.h
View file @
30d43c49
...
...
@@ -68,7 +68,7 @@ template <typename T> using is_eigen_other = all_of<
template
<
bool
EigenRowMajor
>
struct
EigenConformable
{
bool
conformable
=
false
;
EigenIndex
rows
=
0
,
cols
=
0
;
EigenDStride
stride
{
0
,
0
};
// Only valid if negativestride
e
s is false!
EigenDStride
stride
{
0
,
0
};
// Only valid if negativestrides is false!
bool
negativestrides
=
false
;
// If true, do not use stride!
EigenConformable
(
bool
fits
=
false
)
:
conformable
{
fits
}
{}
...
...
@@ -207,7 +207,7 @@ template <typename Type_> struct EigenProps {
// Casts an Eigen type to numpy array. If given a base, the numpy array references the src data,
// otherwise it'll make a copy. writeable lets you turn off the writeable flag for the array.
template
<
typename
props
>
handle
eigen_array_cast
(
typename
props
::
Type
const
&
src
,
handle
base
=
handle
(),
bool
writeable
=
true
)
{
constexpr
size_t
elem_size
=
sizeof
(
typename
props
::
Scalar
);
constexpr
s
size_t
elem_size
=
sizeof
(
typename
props
::
Scalar
);
array
a
;
if
(
props
::
vector
)
a
=
array
({
src
.
size
()
},
{
elem_size
*
src
.
innerStride
()
},
src
.
data
(),
base
);
...
...
@@ -581,9 +581,9 @@ struct type_caster<Type, enable_if_t<is_eigen_sparse<Type>::value>> {
object
matrix_type
=
module
::
import
(
"scipy.sparse"
).
attr
(
rowMajor
?
"csr_matrix"
:
"csc_matrix"
);
array
data
(
(
size_t
)
src
.
nonZeros
(),
src
.
valuePtr
());
array
outerIndices
(
(
size_t
)
(
rowMajor
?
src
.
rows
()
:
src
.
cols
())
+
1
,
src
.
outerIndexPtr
());
array
innerIndices
(
(
size_t
)
src
.
nonZeros
(),
src
.
innerIndexPtr
());
array
data
(
src
.
nonZeros
(),
src
.
valuePtr
());
array
outerIndices
((
rowMajor
?
src
.
rows
()
:
src
.
cols
())
+
1
,
src
.
outerIndexPtr
());
array
innerIndices
(
src
.
nonZeros
(),
src
.
innerIndexPtr
());
return
matrix_type
(
std
::
make_tuple
(
data
,
innerIndices
,
outerIndices
),
...
...
include/pybind11/numpy.h
View file @
30d43c49
...
...
@@ -251,10 +251,10 @@ template <typename T> using is_pod_struct = all_of<
satisfies_none_of
<
T
,
std
::
is_reference
,
std
::
is_array
,
is_std_array
,
std
::
is_arithmetic
,
is_complex
,
std
::
is_enum
>
>
;
template
<
size_t
Dim
=
0
,
typename
Strides
>
ssize_t
byte_offset_unsafe
(
const
Strides
&
)
{
return
0
;
}
template
<
size_t
Dim
=
0
,
typename
Strides
,
typename
...
Ix
>
ssize_t
byte_offset_unsafe
(
const
Strides
&
strides
,
size_t
i
,
Ix
...
index
)
{
return
static_cast
<
ssize_t
>
(
i
)
*
strides
[
Dim
]
+
byte_offset_unsafe
<
Dim
+
1
>
(
strides
,
index
...);
template
<
s
size_t
Dim
=
0
,
typename
Strides
>
ssize_t
byte_offset_unsafe
(
const
Strides
&
)
{
return
0
;
}
template
<
s
size_t
Dim
=
0
,
typename
Strides
,
typename
...
Ix
>
ssize_t
byte_offset_unsafe
(
const
Strides
&
strides
,
s
size_t
i
,
Ix
...
index
)
{
return
i
*
strides
[
Dim
]
+
byte_offset_unsafe
<
Dim
+
1
>
(
strides
,
index
...);
}
/** Proxy class providing unsafe, unchecked const access to array data. This is constructed through
...
...
@@ -268,23 +268,23 @@ protected:
const
unsigned
char
*
data_
;
// Storing the shape & strides in local variables (i.e. these arrays) allows the compiler to
// make large performance gains on big, nested loops, but requires compile-time dimensions
conditional_t
<
Dynamic
,
const
size_t
*
,
std
::
array
<
size_t
,
(
size_t
)
Dims
>>
shape_
;
conditional_t
<
Dynamic
,
const
ssize_t
*
,
std
::
array
<
ssize_t
,
(
size_t
)
Dims
>>
strides_
;
const
size_t
dims_
;
conditional_t
<
Dynamic
,
const
s
size_t
*
,
std
::
array
<
s
size_t
,
(
size_t
)
Dims
>>
shape_
,
strides_
;
const
s
size_t
dims_
;
friend
class
pybind11
::
array
;
// Constructor for compile-time dimensions:
template
<
bool
Dyn
=
Dynamic
>
unchecked_reference
(
const
void
*
data
,
const
size_t
*
shape
,
const
ssize_t
*
strides
,
enable_if_t
<!
Dyn
,
size_t
>
)
unchecked_reference
(
const
void
*
data
,
const
s
size_t
*
shape
,
const
ssize_t
*
strides
,
enable_if_t
<!
Dyn
,
s
size_t
>
)
:
data_
{
reinterpret_cast
<
const
unsigned
char
*>
(
data
)},
dims_
{
Dims
}
{
for
(
size_t
i
=
0
;
i
<
dims_
;
i
++
)
{
for
(
size_t
i
=
0
;
i
<
(
size_t
)
dims_
;
i
++
)
{
shape_
[
i
]
=
shape
[
i
];
strides_
[
i
]
=
strides
[
i
];
}
}
// Constructor for runtime dimensions:
template
<
bool
Dyn
=
Dynamic
>
unchecked_reference
(
const
void
*
data
,
const
size_t
*
shape
,
const
ssize_t
*
strides
,
enable_if_t
<
Dyn
,
size_t
>
dims
)
unchecked_reference
(
const
void
*
data
,
const
s
size_t
*
shape
,
const
ssize_t
*
strides
,
enable_if_t
<
Dyn
,
s
size_t
>
dims
)
:
data_
{
reinterpret_cast
<
const
unsigned
char
*>
(
data
)},
shape_
{
shape
},
strides_
{
strides
},
dims_
{
dims
}
{}
public:
...
...
@@ -295,39 +295,39 @@ public:
template
<
typename
...
Ix
>
const
T
&
operator
()(
Ix
...
index
)
const
{
static_assert
(
sizeof
...(
Ix
)
==
Dims
||
Dynamic
,
"Invalid number of indices for unchecked array reference"
);
return
*
reinterpret_cast
<
const
T
*>
(
data_
+
byte_offset_unsafe
(
strides_
,
size_t
(
index
)...));
return
*
reinterpret_cast
<
const
T
*>
(
data_
+
byte_offset_unsafe
(
strides_
,
s
size_t
(
index
)...));
}
/** Unchecked const reference access to data; this operator only participates if the reference
* is to a 1-dimensional array. When present, this is exactly equivalent to `obj(index)`.
*/
template
<
size_t
D
=
Dims
,
typename
=
enable_if_t
<
D
==
1
||
Dynamic
>
>
const
T
&
operator
[](
size_t
index
)
const
{
return
operator
()(
index
);
}
template
<
s
size_t
D
=
Dims
,
typename
=
enable_if_t
<
D
==
1
||
Dynamic
>
>
const
T
&
operator
[](
s
size_t
index
)
const
{
return
operator
()(
index
);
}
/// Pointer access to the data at the given indices.
template
<
typename
...
Ix
>
const
T
*
data
(
Ix
...
ix
)
const
{
return
&
operator
()(
size_t
(
ix
)...);
}
template
<
typename
...
Ix
>
const
T
*
data
(
Ix
...
ix
)
const
{
return
&
operator
()(
s
size_t
(
ix
)...);
}
/// Returns the item size, i.e. sizeof(T)
constexpr
static
size_t
itemsize
()
{
return
sizeof
(
T
);
}
constexpr
static
s
size_t
itemsize
()
{
return
sizeof
(
T
);
}
/// Returns the shape (i.e. size) of dimension `dim`
size_t
shape
(
size_t
dim
)
const
{
return
shape_
[
dim
];
}
s
size_t
shape
(
s
size_t
dim
)
const
{
return
shape_
[
(
size_t
)
dim
];
}
/// Returns the number of dimensions of the array
size_t
ndim
()
const
{
return
dims_
;
}
s
size_t
ndim
()
const
{
return
dims_
;
}
/// Returns the total number of elements in the referenced array, i.e. the product of the shapes
template
<
bool
Dyn
=
Dynamic
>
enable_if_t
<!
Dyn
,
size_t
>
size
()
const
{
return
std
::
accumulate
(
shape_
.
begin
(),
shape_
.
end
(),
(
size_t
)
1
,
std
::
multiplies
<
size_t
>
());
enable_if_t
<!
Dyn
,
s
size_t
>
size
()
const
{
return
std
::
accumulate
(
shape_
.
begin
(),
shape_
.
end
(),
(
s
size_t
)
1
,
std
::
multiplies
<
s
size_t
>
());
}
template
<
bool
Dyn
=
Dynamic
>
enable_if_t
<
Dyn
,
size_t
>
size
()
const
{
return
std
::
accumulate
(
shape_
,
shape_
+
ndim
(),
(
size_t
)
1
,
std
::
multiplies
<
size_t
>
());
enable_if_t
<
Dyn
,
s
size_t
>
size
()
const
{
return
std
::
accumulate
(
shape_
,
shape_
+
ndim
(),
(
s
size_t
)
1
,
std
::
multiplies
<
s
size_t
>
());
}
/// Returns the total number of bytes used by the referenced data. Note that the actual span in
/// memory may be larger if the referenced array has non-contiguous strides (e.g. for a slice).
size_t
nbytes
()
const
{
s
size_t
nbytes
()
const
{
return
size
()
*
itemsize
();
}
};
...
...
@@ -349,11 +349,11 @@ public:
* reference is to a 1-dimensional array (or has runtime dimensions). When present, this is
* exactly equivalent to `obj(index)`.
*/
template
<
size_t
D
=
Dims
,
typename
=
enable_if_t
<
D
==
1
||
Dynamic
>
>
T
&
operator
[](
size_t
index
)
{
return
operator
()(
index
);
}
template
<
s
size_t
D
=
Dims
,
typename
=
enable_if_t
<
D
==
1
||
Dynamic
>
>
T
&
operator
[](
s
size_t
index
)
{
return
operator
()(
index
);
}
/// Mutable pointer access to the data at the given indices.
template
<
typename
...
Ix
>
T
*
mutable_data
(
Ix
...
ix
)
{
return
&
operator
()(
size_t
(
ix
)...);
}
template
<
typename
...
Ix
>
T
*
mutable_data
(
Ix
...
ix
)
{
return
&
operator
()(
s
size_t
(
ix
)...);
}
};
template
<
typename
T
,
ssize_t
Dim
>
...
...
@@ -381,7 +381,7 @@ public:
dtype
(
const
char
*
format
)
:
dtype
(
std
::
string
(
format
))
{
}
dtype
(
list
names
,
list
formats
,
list
offsets
,
size_t
itemsize
)
{
dtype
(
list
names
,
list
formats
,
list
offsets
,
s
size_t
itemsize
)
{
dict
args
;
args
[
"names"
]
=
names
;
args
[
"formats"
]
=
formats
;
...
...
@@ -404,8 +404,8 @@ public:
}
/// Size of the data type in bytes.
size_t
itemsize
()
const
{
return
(
size_t
)
detail
::
array_descriptor_proxy
(
m_ptr
)
->
elsize
;
s
size_t
itemsize
()
const
{
return
detail
::
array_descriptor_proxy
(
m_ptr
)
->
elsize
;
}
/// Returns true for structured data types.
...
...
@@ -425,7 +425,7 @@ private:
return
reinterpret_borrow
<
object
>
(
obj
);
}
dtype
strip_padding
(
size_t
itemsize
)
{
dtype
strip_padding
(
s
size_t
itemsize
)
{
// Recursively strip all void fields with empty names that are generated for
// padding fields (as of NumPy v1.11).
if
(
!
has_fields
())
...
...
@@ -501,7 +501,7 @@ public:
api
.
PyArray_Type_
,
descr
.
release
().
ptr
(),
(
int
)
ndim
,
shape
->
data
(),
strides
->
data
(),
const_cast
<
void
*>
(
ptr
),
flags
,
nullptr
));
if
(
!
tmp
)
pybind11_fail
(
"NumPy: unable to create array!"
);
throw
error_already_set
(
);
if
(
ptr
)
{
if
(
base
)
{
api
.
PyArray_SetBaseObject_
(
tmp
.
ptr
(),
base
.
inc_ref
().
ptr
());
...
...
@@ -528,7 +528,7 @@ public:
:
array
(
std
::
move
(
shape
),
{},
ptr
,
base
)
{
}
template
<
typename
T
>
explicit
array
(
size_t
count
,
const
T
*
ptr
,
handle
base
=
handle
())
:
array
({
count
},
{},
ptr
,
base
)
{
}
explicit
array
(
s
size_t
count
,
const
T
*
ptr
,
handle
base
=
handle
())
:
array
({
count
},
{},
ptr
,
base
)
{
}
explicit
array
(
const
buffer_info
&
info
)
:
array
(
pybind11
::
dtype
(
info
),
info
.
shape
,
info
.
strides
,
info
.
ptr
)
{
}
...
...
@@ -539,23 +539,23 @@ public:
}
/// Total number of elements
size_t
size
()
const
{
return
std
::
accumulate
(
shape
(),
shape
()
+
ndim
(),
(
size_t
)
1
,
std
::
multiplies
<
size_t
>
());
s
size_t
size
()
const
{
return
std
::
accumulate
(
shape
(),
shape
()
+
ndim
(),
(
s
size_t
)
1
,
std
::
multiplies
<
s
size_t
>
());
}
/// Byte size of a single element
size_t
itemsize
()
const
{
return
(
size_t
)
detail
::
array_descriptor_proxy
(
detail
::
array_proxy
(
m_ptr
)
->
descr
)
->
elsize
;
s
size_t
itemsize
()
const
{
return
detail
::
array_descriptor_proxy
(
detail
::
array_proxy
(
m_ptr
)
->
descr
)
->
elsize
;
}
/// Total number of bytes
size_t
nbytes
()
const
{
s
size_t
nbytes
()
const
{
return
size
()
*
itemsize
();
}
/// Number of dimensions
size_t
ndim
()
const
{
return
(
size_t
)
detail
::
array_proxy
(
m_ptr
)
->
nd
;
s
size_t
ndim
()
const
{
return
detail
::
array_proxy
(
m_ptr
)
->
nd
;
}
/// Base object
...
...
@@ -564,12 +564,12 @@ public:
}
/// Dimensions of the array
const
size_t
*
shape
()
const
{
return
reinterpret_cast
<
const
size_t
*>
(
detail
::
array_proxy
(
m_ptr
)
->
dimensions
)
;
const
s
size_t
*
shape
()
const
{
return
detail
::
array_proxy
(
m_ptr
)
->
dimensions
;
}
/// Dimension along a given axis
size_t
shape
(
size_t
dim
)
const
{
s
size_t
shape
(
s
size_t
dim
)
const
{
if
(
dim
>=
ndim
())
fail_dim_check
(
dim
,
"invalid axis"
);
return
shape
()[
dim
];
...
...
@@ -577,11 +577,11 @@ public:
/// Strides of the array
const
ssize_t
*
strides
()
const
{
return
reinterpret_cast
<
const
ssize_t
*>
(
detail
::
array_proxy
(
m_ptr
)
->
strides
)
;
return
detail
::
array_proxy
(
m_ptr
)
->
strides
;
}
/// Stride along a given axis
ssize_t
strides
(
size_t
dim
)
const
{
ssize_t
strides
(
s
size_t
dim
)
const
{
if
(
dim
>=
ndim
())
fail_dim_check
(
dim
,
"invalid axis"
);
return
strides
()[
dim
];
...
...
@@ -619,9 +619,9 @@ public:
/// Byte offset from beginning of the array to a given index (full or partial).
/// May throw if the index would lead to out of bounds access.
template
<
typename
...
Ix
>
ssize_t
offset_at
(
Ix
...
index
)
const
{
if
(
sizeof
...(
index
)
>
ndim
())
if
(
(
ssize_t
)
sizeof
...(
index
)
>
ndim
())
fail_dim_check
(
sizeof
...(
index
),
"too many indices for an array"
);
return
byte_offset
(
size_t
(
index
)...);
return
byte_offset
(
s
size_t
(
index
)...);
}
ssize_t
offset_at
()
const
{
return
0
;
}
...
...
@@ -629,7 +629,7 @@ public:
/// Item count from beginning of the array to a given index (full or partial).
/// May throw if the index would lead to out of bounds access.
template
<
typename
...
Ix
>
ssize_t
index_at
(
Ix
...
index
)
const
{
return
offset_at
(
index
...)
/
static_cast
<
ssize_t
>
(
itemsize
()
)
;
return
offset_at
(
index
...)
/
itemsize
();
}
/** Returns a proxy object that provides access to the array's data without bounds or
...
...
@@ -638,7 +638,7 @@ public:
* and the caller must take care not to access invalid dimensions or dimension indices.
*/
template
<
typename
T
,
ssize_t
Dims
=
-
1
>
detail
::
unchecked_mutable_reference
<
T
,
Dims
>
mutable_unchecked
()
{
if
(
Dims
>=
0
&&
ndim
()
!=
(
size_t
)
Dims
)
if
(
Dims
>=
0
&&
ndim
()
!=
Dims
)
throw
std
::
domain_error
(
"array has incorrect number of dimensions: "
+
std
::
to_string
(
ndim
())
+
"; expected "
+
std
::
to_string
(
Dims
));
return
detail
::
unchecked_mutable_reference
<
T
,
Dims
>
(
mutable_data
(),
shape
(),
strides
(),
ndim
());
...
...
@@ -651,7 +651,7 @@ public:
* invalid dimensions or dimension indices.
*/
template
<
typename
T
,
ssize_t
Dims
=
-
1
>
detail
::
unchecked_reference
<
T
,
Dims
>
unchecked
()
const
{
if
(
Dims
>=
0
&&
ndim
()
!=
(
size_t
)
Dims
)
if
(
Dims
>=
0
&&
ndim
()
!=
Dims
)
throw
std
::
domain_error
(
"array has incorrect number of dimensions: "
+
std
::
to_string
(
ndim
())
+
"; expected "
+
std
::
to_string
(
Dims
));
return
detail
::
unchecked_reference
<
T
,
Dims
>
(
data
(),
shape
(),
strides
(),
ndim
());
...
...
@@ -690,14 +690,14 @@ public:
protected:
template
<
typename
,
typename
>
friend
struct
detail
::
npy_format_descriptor
;
void
fail_dim_check
(
size_t
dim
,
const
std
::
string
&
msg
)
const
{
void
fail_dim_check
(
s
size_t
dim
,
const
std
::
string
&
msg
)
const
{
throw
index_error
(
msg
+
": "
+
std
::
to_string
(
dim
)
+
" (ndim = "
+
std
::
to_string
(
ndim
())
+
")"
);
}
template
<
typename
...
Ix
>
ssize_t
byte_offset
(
Ix
...
index
)
const
{
check_dimensions
(
index
...);
return
detail
::
byte_offset_unsafe
(
strides
(),
size_t
(
index
)...);
return
detail
::
byte_offset_unsafe
(
strides
(),
s
size_t
(
index
)...);
}
void
check_writeable
()
const
{
...
...
@@ -705,7 +705,7 @@ protected:
throw
std
::
domain_error
(
"array is not writeable"
);
}
static
std
::
vector
<
ssize
_t
>
default_strides
(
const
std
::
vector
<
size
_t
>&
shape
,
size_t
itemsize
)
{
static
std
::
vector
<
Py_intptr
_t
>
default_strides
(
const
std
::
vector
<
Py_intptr
_t
>&
shape
,
s
size_t
itemsize
)
{
auto
ndim
=
shape
.
size
();
std
::
vector
<
ssize_t
>
strides
(
ndim
);
if
(
ndim
)
{
...
...
@@ -718,12 +718,12 @@ protected:
}
template
<
typename
...
Ix
>
void
check_dimensions
(
Ix
...
index
)
const
{
check_dimensions_impl
(
size_t
(
0
),
shape
(),
size_t
(
index
)...);
check_dimensions_impl
(
s
size_t
(
0
),
shape
(),
s
size_t
(
index
)...);
}
void
check_dimensions_impl
(
size_t
,
const
size_t
*
)
const
{
}
void
check_dimensions_impl
(
s
size_t
,
const
s
size_t
*
)
const
{
}
template
<
typename
...
Ix
>
void
check_dimensions_impl
(
size_t
axis
,
const
size_t
*
shape
,
size_t
i
,
Ix
...
index
)
const
{
template
<
typename
...
Ix
>
void
check_dimensions_impl
(
s
size_t
axis
,
const
s
size_t
*
shape
,
s
size_t
i
,
Ix
...
index
)
const
{
if
(
i
>=
*
shape
)
{
throw
index_error
(
std
::
string
(
"index "
)
+
std
::
to_string
(
i
)
+
" is out of bounds for axis "
+
std
::
to_string
(
axis
)
+
...
...
@@ -772,12 +772,12 @@ public:
explicit
array_t
(
size_t
count
,
const
T
*
ptr
=
nullptr
,
handle
base
=
handle
())
:
array
({
count
},
{},
ptr
,
base
)
{
}
constexpr
size_t
itemsize
()
const
{
constexpr
s
size_t
itemsize
()
const
{
return
sizeof
(
T
);
}
template
<
typename
...
Ix
>
ssize_t
index_at
(
Ix
...
index
)
const
{
return
offset_at
(
index
...)
/
static_cast
<
ssize_t
>
(
itemsize
()
)
;
return
offset_at
(
index
...)
/
itemsize
();
}
template
<
typename
...
Ix
>
const
T
*
data
(
Ix
...
index
)
const
{
...
...
@@ -792,14 +792,14 @@ public:
template
<
typename
...
Ix
>
const
T
&
at
(
Ix
...
index
)
const
{
if
(
sizeof
...(
index
)
!=
ndim
())
fail_dim_check
(
sizeof
...(
index
),
"index dimension mismatch"
);
return
*
(
static_cast
<
const
T
*>
(
array
::
data
())
+
byte_offset
(
size_t
(
index
)...)
/
static_cast
<
ssize_t
>
(
itemsize
())
)
;
return
*
(
static_cast
<
const
T
*>
(
array
::
data
())
+
byte_offset
(
s
size_t
(
index
)...)
/
itemsize
());
}
// Mutable reference to element at a given index
template
<
typename
...
Ix
>
T
&
mutable_at
(
Ix
...
index
)
{
if
(
sizeof
...(
index
)
!=
ndim
())
fail_dim_check
(
sizeof
...(
index
),
"index dimension mismatch"
);
return
*
(
static_cast
<
T
*>
(
array
::
mutable_data
())
+
byte_offset
(
size_t
(
index
)...)
/
static_cast
<
ssize_t
>
(
itemsize
())
)
;
return
*
(
static_cast
<
T
*>
(
array
::
mutable_data
())
+
byte_offset
(
s
size_t
(
index
)...)
/
itemsize
());
}
/** Returns a proxy object that provides access to the array's data without bounds or
...
...
@@ -949,16 +949,16 @@ public:
struct
field_descriptor
{
const
char
*
name
;
size_t
offset
;
size_t
size
;
size_t
alignment
;
s
size_t
offset
;
s
size_t
size
;
s
size_t
alignment
;
std
::
string
format
;
dtype
descr
;
};
inline
PYBIND11_NOINLINE
void
register_structured_dtype
(
const
std
::
initializer_list
<
field_descriptor
>&
fields
,
const
std
::
type_info
&
tinfo
,
size_t
itemsize
,
const
std
::
type_info
&
tinfo
,
s
size_t
itemsize
,
bool
(
*
direct_converter
)(
PyObject
*
,
void
*&
))
{
auto
&
numpy_internals
=
get_numpy_internals
();
...
...
@@ -986,7 +986,7 @@ inline PYBIND11_NOINLINE void register_structured_dtype(
std
::
vector
<
field_descriptor
>
ordered_fields
(
fields
);
std
::
sort
(
ordered_fields
.
begin
(),
ordered_fields
.
end
(),
[](
const
field_descriptor
&
a
,
const
field_descriptor
&
b
)
{
return
a
.
offset
<
b
.
offset
;
});
size_t
offset
=
0
;
s
size_t
offset
=
0
;
std
::
ostringstream
oss
;
oss
<<
"T{"
;
for
(
auto
&
field
:
ordered_fields
)
{
...
...
@@ -1142,7 +1142,7 @@ public:
common_iterator
()
:
p_ptr
(
0
),
m_strides
()
{}
common_iterator
(
void
*
ptr
,
const
container_type
&
strides
,
const
std
::
vector
<
size_t
>
&
shape
)
common_iterator
(
void
*
ptr
,
const
container_type
&
strides
,
const
container_type
&
shape
)
:
p_ptr
(
reinterpret_cast
<
char
*>
(
ptr
)),
m_strides
(
strides
.
size
())
{
m_strides
.
back
()
=
static_cast
<
value_type
>
(
strides
.
back
());
for
(
size_type
i
=
m_strides
.
size
()
-
1
;
i
!=
0
;
--
i
)
{
...
...
@@ -1167,18 +1167,18 @@ private:
template
<
size_t
N
>
class
multi_array_iterator
{
public:
using
container_type
=
std
::
vector
<
size_t
>
;
using
container_type
=
std
::
vector
<
s
size_t
>
;
multi_array_iterator
(
const
std
::
array
<
buffer_info
,
N
>
&
buffers
,
const
std
::
vector
<
size_t
>
&
shape
)
const
container_type
&
shape
)
:
m_shape
(
shape
.
size
()),
m_index
(
shape
.
size
(),
0
),
m_common_iterator
()
{
// Manual copy to avoid conversion warning if using std::copy
for
(
size_t
i
=
0
;
i
<
shape
.
size
();
++
i
)
m_shape
[
i
]
=
static_cast
<
container_type
::
value_type
>
(
shape
[
i
]
)
;
m_shape
[
i
]
=
shape
[
i
];
std
::
vector
<
ssize_t
>
strides
(
shape
.
size
());
container_type
strides
(
shape
.
size
());
for
(
size_t
i
=
0
;
i
<
N
;
++
i
)
init_common_iterator
(
buffers
[
i
],
shape
,
m_common_iterator
[
i
],
strides
);
}
...
...
@@ -1205,8 +1205,9 @@ private:
using
common_iter
=
common_iterator
;
void
init_common_iterator
(
const
buffer_info
&
buffer
,
const
std
::
vector
<
size_t
>
&
shape
,
common_iter
&
iterator
,
std
::
vector
<
ssize_t
>
&
strides
)
{
const
container_type
&
shape
,
common_iter
&
iterator
,
container_type
&
strides
)
{
auto
buffer_shape_iter
=
buffer
.
shape
.
rbegin
();
auto
buffer_strides_iter
=
buffer
.
strides
.
rbegin
();
auto
shape_iter
=
shape
.
rbegin
();
...
...
@@ -1245,13 +1246,13 @@ enum class broadcast_trivial { non_trivial, c_trivial, f_trivial };
// singleton or a full-size, C-contiguous (`c_trivial`) or Fortran-contiguous (`f_trivial`) storage
// buffer; returns `non_trivial` otherwise.
template
<
size_t
N
>
broadcast_trivial
broadcast
(
const
std
::
array
<
buffer_info
,
N
>
&
buffers
,
size_t
&
ndim
,
std
::
vector
<
size_t
>
&
shape
)
{
ndim
=
std
::
accumulate
(
buffers
.
begin
(),
buffers
.
end
(),
size_t
(
0
),
[](
size_t
res
,
const
buffer_info
&
buf
)
{
broadcast_trivial
broadcast
(
const
std
::
array
<
buffer_info
,
N
>
&
buffers
,
s
size_t
&
ndim
,
std
::
vector
<
s
size_t
>
&
shape
)
{
ndim
=
std
::
accumulate
(
buffers
.
begin
(),
buffers
.
end
(),
s
size_t
(
0
),
[](
s
size_t
res
,
const
buffer_info
&
buf
)
{
return
std
::
max
(
res
,
buf
.
ndim
);
});
shape
.
clear
();
shape
.
resize
(
ndim
,
1
);
shape
.
resize
(
(
size_t
)
ndim
,
1
);
// Figure out the output size, and make sure all input arrays conform (i.e. are either size 1 or
// the full size).
...
...
@@ -1286,11 +1287,10 @@ broadcast_trivial broadcast(const std::array<buffer_info, N> &buffers, size_t &n
// Check for C contiguity (but only if previous inputs were also C contiguous)
if
(
trivial_broadcast_c
)
{
ssize_t
expect_stride
=
static_cast
<
ssize_t
>
(
buffers
[
i
].
itemsize
)
;
ssize_t
expect_stride
=
buffers
[
i
].
itemsize
;
auto
end
=
buffers
[
i
].
shape
.
crend
();
auto
shape_iter
=
buffers
[
i
].
shape
.
crbegin
();
auto
stride_iter
=
buffers
[
i
].
strides
.
crbegin
();
for
(;
trivial_broadcast_c
&&
shape_iter
!=
end
;
++
shape_iter
,
++
stride_iter
)
{
for
(
auto
shape_iter
=
buffers
[
i
].
shape
.
crbegin
(),
stride_iter
=
buffers
[
i
].
strides
.
crbegin
();
trivial_broadcast_c
&&
shape_iter
!=
end
;
++
shape_iter
,
++
stride_iter
)
{
if
(
expect_stride
==
*
stride_iter
)
expect_stride
*=
*
shape_iter
;
else
...
...
@@ -1300,11 +1300,10 @@ broadcast_trivial broadcast(const std::array<buffer_info, N> &buffers, size_t &n
// Check for Fortran contiguity (if previous inputs were also F contiguous)
if
(
trivial_broadcast_f
)
{
ssize_t
expect_stride
=
static_cast
<
ssize_t
>
(
buffers
[
i
].
itemsize
)
;
ssize_t
expect_stride
=
buffers
[
i
].
itemsize
;
auto
end
=
buffers
[
i
].
shape
.
cend
();
auto
shape_iter
=
buffers
[
i
].
shape
.
cbegin
();
auto
stride_iter
=
buffers
[
i
].
strides
.
cbegin
();
for
(;
trivial_broadcast_f
&&
shape_iter
!=
end
;
++
shape_iter
,
++
stride_iter
)
{
for
(
auto
shape_iter
=
buffers
[
i
].
shape
.
cbegin
(),
stride_iter
=
buffers
[
i
].
strides
.
cbegin
();
trivial_broadcast_f
&&
shape_iter
!=
end
;
++
shape_iter
,
++
stride_iter
)
{
if
(
expect_stride
==
*
stride_iter
)
expect_stride
*=
*
shape_iter
;
else
...
...
@@ -1336,25 +1335,26 @@ struct vectorize_helper {
std
::
array
<
buffer_info
,
N
>
buffers
{{
args
.
request
()...
}};
/* Determine dimensions parameters of output array */
size_t
ndim
=
0
;
std
::
vector
<
size_t
>
shape
(
0
);
auto
trivial
=
broadcast
(
buffers
,
ndim
,
shape
);
ssize_t
nd
=
0
;
std
::
vector
<
ssize_t
>
shape
(
0
);
auto
trivial
=
broadcast
(
buffers
,
nd
,
shape
);
size_t
ndim
=
(
size_t
)
nd
;
size_t
size
=
1
;
s
size_t
size
=
1
;
std
::
vector
<
ssize_t
>
strides
(
ndim
);
if
(
ndim
>
0
)
{
if
(
trivial
==
broadcast_trivial
::
f_trivial
)
{
strides
[
0
]
=
static_cast
<
ssize_t
>
(
sizeof
(
Return
)
)
;
strides
[
0
]
=
sizeof
(
Return
);
for
(
size_t
i
=
1
;
i
<
ndim
;
++
i
)
{
strides
[
i
]
=
strides
[
i
-
1
]
*
static_cast
<
ssize_t
>
(
shape
[
i
-
1
]
)
;
strides
[
i
]
=
strides
[
i
-
1
]
*
shape
[
i
-
1
];
size
*=
shape
[
i
-
1
];
}
size
*=
shape
[
ndim
-
1
];
}
else
{
strides
[
ndim
-
1
]
=
static_cast
<
ssize_t
>
(
sizeof
(
Return
)
)
;
strides
[
ndim
-
1
]
=
sizeof
(
Return
);
for
(
size_t
i
=
ndim
-
1
;
i
>
0
;
--
i
)
{
strides
[
i
-
1
]
=
strides
[
i
]
*
static_cast
<
ssize_t
>
(
shape
[
i
]
)
;
strides
[
i
-
1
]
=
strides
[
i
]
*
shape
[
i
];
size
*=
shape
[
i
];
}
size
*=
shape
[
0
];
...
...
@@ -1372,7 +1372,7 @@ struct vectorize_helper {
if
(
trivial
==
broadcast_trivial
::
non_trivial
)
{
apply_broadcast
<
Index
...
>
(
buffers
,
buf
,
index
);
}
else
{
for
(
size_t
i
=
0
;
i
<
size
;
++
i
)
for
(
s
size_t
i
=
0
;
i
<
size
;
++
i
)
output
[
i
]
=
f
((
reinterpret_cast
<
Args
*>
(
buffers
[
Index
].
ptr
)[
buffers
[
Index
].
size
==
1
?
0
:
i
])...);
}
...
...
include/pybind11/pytypes.h
View file @
30d43c49
...
...
@@ -1165,15 +1165,15 @@ public:
static
std
::
vector
<
Py_ssize_t
>
py_strides
{
};
static
std
::
vector
<
Py_ssize_t
>
py_shape
{
};
buf
.
buf
=
info
.
ptr
;
buf
.
itemsize
=
(
Py_ssize_t
)
info
.
itemsize
;
buf
.
itemsize
=
info
.
itemsize
;
buf
.
format
=
const_cast
<
char
*>
(
info
.
format
.
c_str
());
buf
.
ndim
=
(
int
)
info
.
ndim
;
buf
.
len
=
(
Py_ssize_t
)
info
.
size
;
buf
.
len
=
info
.
size
;
py_strides
.
clear
();
py_shape
.
clear
();
for
(
size_t
i
=
0
;
i
<
info
.
ndim
;
++
i
)
{
py_strides
.
push_back
(
(
Py_ssize_t
)
info
.
strides
[
i
]);
py_shape
.
push_back
(
(
Py_ssize_t
)
info
.
shape
[
i
]);
for
(
size_t
i
=
0
;
i
<
(
size_t
)
info
.
ndim
;
++
i
)
{
py_strides
.
push_back
(
info
.
strides
[
i
]);
py_shape
.
push_back
(
info
.
shape
[
i
]);
}
buf
.
strides
=
py_strides
.
data
();
buf
.
shape
=
py_shape
.
data
();
...
...
include/pybind11/stl_bind.h
View file @
30d43c49
...
...
@@ -345,21 +345,21 @@ vector_buffer(Class_& cl) {
format_descriptor
<
T
>::
format
();
cl
.
def_buffer
([](
Vector
&
v
)
->
buffer_info
{
return
buffer_info
(
v
.
data
(),
sizeof
(
T
),
format_descriptor
<
T
>::
format
(),
1
,
{
v
.
size
()},
{
sizeof
(
T
)});
return
buffer_info
(
v
.
data
(),
static_cast
<
ssize_t
>
(
sizeof
(
T
)
)
,
format_descriptor
<
T
>::
format
(),
1
,
{
v
.
size
()},
{
sizeof
(
T
)});
});
cl
.
def
(
"__init__"
,
[](
Vector
&
vec
,
buffer
buf
)
{
auto
info
=
buf
.
request
();
if
(
info
.
ndim
!=
1
||
info
.
strides
[
0
]
<=
0
||
info
.
strides
[
0
]
%
static_cast
<
ssize_t
>
(
sizeof
(
T
)))
if
(
info
.
ndim
!=
1
||
info
.
strides
[
0
]
%
static_cast
<
ssize_t
>
(
sizeof
(
T
)))
throw
type_error
(
"Only valid 1D buffers can be copied to a vector"
);
if
(
!
detail
::
compare_buffer_info
<
T
>::
compare
(
info
)
||
sizeof
(
T
)
!=
info
.
itemsize
)
if
(
!
detail
::
compare_buffer_info
<
T
>::
compare
(
info
)
||
(
ssize_t
)
sizeof
(
T
)
!=
info
.
itemsize
)
throw
type_error
(
"Format mismatch (Python: "
+
info
.
format
+
" C++: "
+
format_descriptor
<
T
>::
format
()
+
")"
);
new
(
&
vec
)
Vector
();
vec
.
reserve
(
info
.
shape
[
0
]);
vec
.
reserve
(
(
size_t
)
info
.
shape
[
0
]);
T
*
p
=
static_cast
<
T
*>
(
info
.
ptr
);
auto
step
=
info
.
strides
[
0
]
/
static_cast
<
ssize_t
>
(
sizeof
(
T
));
T
*
end
=
p
+
static_cast
<
ssize_t
>
(
info
.
shape
[
0
]
)
*
step
;
for
(;
p
<
end
;
p
+=
step
)
ssize_t
step
=
info
.
strides
[
0
]
/
static_cast
<
ssize_t
>
(
sizeof
(
T
));
T
*
end
=
p
+
info
.
shape
[
0
]
*
step
;
for
(;
p
!=
end
;
p
+=
step
)
vec
.
push_back
(
*
p
);
});
...
...
tests/test_buffers.cpp
View file @
30d43c49
...
...
@@ -12,16 +12,16 @@
class
Matrix
{
public:
Matrix
(
size_t
rows
,
size_t
cols
)
:
m_rows
(
rows
),
m_cols
(
cols
)
{
Matrix
(
s
size_t
rows
,
s
size_t
cols
)
:
m_rows
(
rows
),
m_cols
(
cols
)
{
print_created
(
this
,
std
::
to_string
(
m_rows
)
+
"x"
+
std
::
to_string
(
m_cols
)
+
" matrix"
);
m_data
=
new
float
[
rows
*
cols
];
memset
(
m_data
,
0
,
sizeof
(
float
)
*
rows
*
cols
);
m_data
=
new
float
[
(
size_t
)
(
rows
*
cols
)
];
memset
(
m_data
,
0
,
sizeof
(
float
)
*
(
size_t
)
(
rows
*
cols
)
)
;
}
Matrix
(
const
Matrix
&
s
)
:
m_rows
(
s
.
m_rows
),
m_cols
(
s
.
m_cols
)
{
print_copy_created
(
this
,
std
::
to_string
(
m_rows
)
+
"x"
+
std
::
to_string
(
m_cols
)
+
" matrix"
);
m_data
=
new
float
[
m_rows
*
m_cols
];
memcpy
(
m_data
,
s
.
m_data
,
sizeof
(
float
)
*
m_rows
*
m_cols
);
m_data
=
new
float
[
(
size_t
)
(
m_rows
*
m_cols
)
];
memcpy
(
m_data
,
s
.
m_data
,
sizeof
(
float
)
*
(
size_t
)
(
m_rows
*
m_cols
)
)
;
}
Matrix
(
Matrix
&&
s
)
:
m_rows
(
s
.
m_rows
),
m_cols
(
s
.
m_cols
),
m_data
(
s
.
m_data
)
{
...
...
@@ -41,8 +41,8 @@ public:
delete
[]
m_data
;
m_rows
=
s
.
m_rows
;
m_cols
=
s
.
m_cols
;
m_data
=
new
float
[
m_rows
*
m_cols
];
memcpy
(
m_data
,
s
.
m_data
,
sizeof
(
float
)
*
m_rows
*
m_cols
);
m_data
=
new
float
[
(
size_t
)
(
m_rows
*
m_cols
)
];
memcpy
(
m_data
,
s
.
m_data
,
sizeof
(
float
)
*
(
size_t
)
(
m_rows
*
m_cols
)
)
;
return
*
this
;
}
...
...
@@ -56,47 +56,47 @@ public:
return
*
this
;
}
float
operator
()(
size_t
i
,
size_t
j
)
const
{
return
m_data
[
i
*
m_cols
+
j
];
float
operator
()(
s
size_t
i
,
s
size_t
j
)
const
{
return
m_data
[
(
size_t
)
(
i
*
m_cols
+
j
)
];
}
float
&
operator
()(
size_t
i
,
size_t
j
)
{
return
m_data
[
i
*
m_cols
+
j
];
float
&
operator
()(
s
size_t
i
,
s
size_t
j
)
{
return
m_data
[
(
size_t
)
(
i
*
m_cols
+
j
)
];
}
float
*
data
()
{
return
m_data
;
}
size_t
rows
()
const
{
return
m_rows
;
}
size_t
cols
()
const
{
return
m_cols
;
}
s
size_t
rows
()
const
{
return
m_rows
;
}
s
size_t
cols
()
const
{
return
m_cols
;
}
private:
size_t
m_rows
;
size_t
m_cols
;
s
size_t
m_rows
;
s
size_t
m_cols
;
float
*
m_data
;
};
test_initializer
buffers
([](
py
::
module
&
m
)
{
py
::
class_
<
Matrix
>
mtx
(
m
,
"Matrix"
,
py
::
buffer_protocol
());
mtx
.
def
(
py
::
init
<
size_t
,
size_t
>
())
mtx
.
def
(
py
::
init
<
s
size_t
,
s
size_t
>
())
/// Construct from a buffer
.
def
(
"__init__"
,
[](
Matrix
&
v
,
py
::
buffer
b
)
{
py
::
buffer_info
info
=
b
.
request
();
if
(
info
.
format
!=
py
::
format_descriptor
<
float
>::
format
()
||
info
.
ndim
!=
2
)
throw
std
::
runtime_error
(
"Incompatible buffer format!"
);
new
(
&
v
)
Matrix
(
info
.
shape
[
0
],
info
.
shape
[
1
]);
memcpy
(
v
.
data
(),
info
.
ptr
,
sizeof
(
float
)
*
v
.
rows
()
*
v
.
cols
());
memcpy
(
v
.
data
(),
info
.
ptr
,
sizeof
(
float
)
*
(
size_t
)
(
v
.
rows
()
*
v
.
cols
())
)
;
})
.
def
(
"rows"
,
&
Matrix
::
rows
)
.
def
(
"cols"
,
&
Matrix
::
cols
)
/// Bare bones interface
.
def
(
"__getitem__"
,
[](
const
Matrix
&
m
,
std
::
pair
<
size_t
,
size_t
>
i
)
{
.
def
(
"__getitem__"
,
[](
const
Matrix
&
m
,
std
::
pair
<
s
size_t
,
s
size_t
>
i
)
{
if
(
i
.
first
>=
m
.
rows
()
||
i
.
second
>=
m
.
cols
())
throw
py
::
index_error
();
return
m
(
i
.
first
,
i
.
second
);
})
.
def
(
"__setitem__"
,
[](
Matrix
&
m
,
std
::
pair
<
size_t
,
size_t
>
i
,
float
v
)
{
.
def
(
"__setitem__"
,
[](
Matrix
&
m
,
std
::
pair
<
s
size_t
,
s
size_t
>
i
,
float
v
)
{
if
(
i
.
first
>=
m
.
rows
()
||
i
.
second
>=
m
.
cols
())
throw
py
::
index_error
();
m
(
i
.
first
,
i
.
second
)
=
v
;
...
...
@@ -109,8 +109,8 @@ test_initializer buffers([](py::module &m) {
py
::
format_descriptor
<
float
>::
format
(),
/* Python struct-style format descriptor */
2
,
/* Number of dimensions */
{
m
.
rows
(),
m
.
cols
()
},
/* Buffer dimensions */
{
static_cast
<
ssize_t
>
(
sizeof
(
float
)
*
m
.
rows
()),
/* Strides (in bytes) for each index */
static_cast
<
ssize_t
>
(
sizeof
(
float
)
)
}
{
sizeof
(
float
)
*
size_t
(
m
.
rows
()),
/* Strides (in bytes) for each index */
sizeof
(
float
)
}
);
})
;
...
...
tests/test_eigen.py
View file @
30d43c49
...
...
@@ -186,7 +186,7 @@ def test_negative_stride_from_python(msg):
double_threer
(
second_row
)
assert
msg
(
excinfo
.
value
)
==
"""
double_threer(): incompatible function arguments. The following argument types are supported:
1. (numpy.ndarray[float32[1, 3], flags.writeable]) ->
arg0:
None
1. (
arg0:
numpy.ndarray[float32[1, 3], flags.writeable]) -> None
Invoked with: array([ 5., 4., 3.], dtype=float32)
"""
...
...
@@ -195,7 +195,7 @@ def test_negative_stride_from_python(msg):
double_threec
(
second_col
)
assert
msg
(
excinfo
.
value
)
==
"""
double_threec(): incompatible function arguments. The following argument types are supported:
1. (numpy.ndarray[float32[3, 1], flags.writeable]) ->
arg0:
None
1. (
arg0:
numpy.ndarray[float32[3, 1], flags.writeable]) -> None
Invoked with: array([ 7., 4., 1.], dtype=float32)
"""
...
...
tests/test_numpy_array.cpp
View file @
30d43c49
...
...
@@ -13,53 +13,52 @@
#include <pybind11/stl.h>
#include <cstdint>
#include <vector>
using
arr
=
py
::
array
;
using
arr_t
=
py
::
array_t
<
uint16_t
,
0
>
;
static_assert
(
std
::
is_same
<
arr_t
::
value_type
,
uint16_t
>::
value
,
""
);
template
<
typename
...
Ix
>
arr
data
(
const
arr
&
a
,
Ix
...
index
)
{
return
arr
(
a
.
nbytes
()
-
size_t
(
a
.
offset_at
(
index
...)
)
,
(
const
uint8_t
*
)
a
.
data
(
index
...));
return
arr
(
a
.
nbytes
()
-
a
.
offset_at
(
index
...),
(
const
uint8_t
*
)
a
.
data
(
index
...));
}
template
<
typename
...
Ix
>
arr
data_t
(
const
arr_t
&
a
,
Ix
...
index
)
{
return
arr
(
a
.
size
()
-
size_t
(
a
.
index_at
(
index
...)
)
,
a
.
data
(
index
...));
return
arr
(
a
.
size
()
-
a
.
index_at
(
index
...),
a
.
data
(
index
...));
}
arr
&
mutate_data
(
arr
&
a
)
{
auto
ptr
=
(
uint8_t
*
)
a
.
mutable_data
();
for
(
size_t
i
=
0
;
i
<
a
.
nbytes
();
i
++
)
for
(
s
size_t
i
=
0
;
i
<
a
.
nbytes
();
i
++
)
ptr
[
i
]
=
(
uint8_t
)
(
ptr
[
i
]
*
2
);
return
a
;
}
arr_t
&
mutate_data_t
(
arr_t
&
a
)
{
auto
ptr
=
a
.
mutable_data
();
for
(
size_t
i
=
0
;
i
<
a
.
size
();
i
++
)
for
(
s
size_t
i
=
0
;
i
<
a
.
size
();
i
++
)
ptr
[
i
]
++
;
return
a
;
}
template
<
typename
...
Ix
>
arr
&
mutate_data
(
arr
&
a
,
Ix
...
index
)
{
auto
ptr
=
(
uint8_t
*
)
a
.
mutable_data
(
index
...);
for
(
size_t
i
=
0
;
i
<
a
.
nbytes
()
-
size_t
(
a
.
offset_at
(
index
...)
)
;
i
++
)
for
(
s
size_t
i
=
0
;
i
<
a
.
nbytes
()
-
a
.
offset_at
(
index
...);
i
++
)
ptr
[
i
]
=
(
uint8_t
)
(
ptr
[
i
]
*
2
);
return
a
;
}
template
<
typename
...
Ix
>
arr_t
&
mutate_data_t
(
arr_t
&
a
,
Ix
...
index
)
{
auto
ptr
=
a
.
mutable_data
(
index
...);
for
(
size_t
i
=
0
;
i
<
a
.
size
()
-
size_t
(
a
.
index_at
(
index
...)
)
;
i
++
)
for
(
s
size_t
i
=
0
;
i
<
a
.
size
()
-
a
.
index_at
(
index
...);
i
++
)
ptr
[
i
]
++
;
return
a
;
}
template
<
typename
...
Ix
>
py
::
ssize_t
index_at
(
const
arr
&
a
,
Ix
...
idx
)
{
return
a
.
index_at
(
idx
...);
}
template
<
typename
...
Ix
>
py
::
ssize_t
index_at_t
(
const
arr_t
&
a
,
Ix
...
idx
)
{
return
a
.
index_at
(
idx
...);
}
template
<
typename
...
Ix
>
py
::
ssize_t
offset_at
(
const
arr
&
a
,
Ix
...
idx
)
{
return
a
.
offset_at
(
idx
...);
}
template
<
typename
...
Ix
>
py
::
ssize_t
offset_at_t
(
const
arr_t
&
a
,
Ix
...
idx
)
{
return
a
.
offset_at
(
idx
...);
}
template
<
typename
...
Ix
>
py
::
ssize_t
at_t
(
const
arr_t
&
a
,
Ix
...
idx
)
{
return
a
.
at
(
idx
...);
}
template
<
typename
...
Ix
>
ssize_t
index_at
(
const
arr
&
a
,
Ix
...
idx
)
{
return
a
.
index_at
(
idx
...);
}
template
<
typename
...
Ix
>
ssize_t
index_at_t
(
const
arr_t
&
a
,
Ix
...
idx
)
{
return
a
.
index_at
(
idx
...);
}
template
<
typename
...
Ix
>
ssize_t
offset_at
(
const
arr
&
a
,
Ix
...
idx
)
{
return
a
.
offset_at
(
idx
...);
}
template
<
typename
...
Ix
>
ssize_t
offset_at_t
(
const
arr_t
&
a
,
Ix
...
idx
)
{
return
a
.
offset_at
(
idx
...);
}
template
<
typename
...
Ix
>
ssize_t
at_t
(
const
arr_t
&
a
,
Ix
...
idx
)
{
return
a
.
at
(
idx
...);
}
template
<
typename
...
Ix
>
arr_t
&
mutate_at_t
(
arr_t
&
a
,
Ix
...
idx
)
{
a
.
mutable_at
(
idx
...)
++
;
return
a
;
}
#define def_index_fn(name, type) \
...
...
@@ -88,9 +87,9 @@ test_initializer numpy_array([](py::module &m) {
sm
.
def
(
"ndim"
,
[](
const
arr
&
a
)
{
return
a
.
ndim
();
});
sm
.
def
(
"shape"
,
[](
const
arr
&
a
)
{
return
arr
(
a
.
ndim
(),
a
.
shape
());
});
sm
.
def
(
"shape"
,
[](
const
arr
&
a
,
size_t
dim
)
{
return
a
.
shape
(
dim
);
});
sm
.
def
(
"shape"
,
[](
const
arr
&
a
,
s
size_t
dim
)
{
return
a
.
shape
(
dim
);
});
sm
.
def
(
"strides"
,
[](
const
arr
&
a
)
{
return
arr
(
a
.
ndim
(),
a
.
strides
());
});
sm
.
def
(
"strides"
,
[](
const
arr
&
a
,
size_t
dim
)
{
return
a
.
strides
(
dim
);
});
sm
.
def
(
"strides"
,
[](
const
arr
&
a
,
s
size_t
dim
)
{
return
a
.
strides
(
dim
);
});
sm
.
def
(
"writeable"
,
[](
const
arr
&
a
)
{
return
a
.
writeable
();
});
sm
.
def
(
"size"
,
[](
const
arr
&
a
)
{
return
a
.
size
();
});
sm
.
def
(
"itemsize"
,
[](
const
arr
&
a
)
{
return
a
.
itemsize
();
});
...
...
@@ -202,33 +201,33 @@ test_initializer numpy_array([](py::module &m) {
sm
.
def
(
"proxy_add2"
,
[](
py
::
array_t
<
double
>
a
,
double
v
)
{
auto
r
=
a
.
mutable_unchecked
<
2
>
();
for
(
size_t
i
=
0
;
i
<
r
.
shape
(
0
);
i
++
)
for
(
size_t
j
=
0
;
j
<
r
.
shape
(
1
);
j
++
)
for
(
s
size_t
i
=
0
;
i
<
r
.
shape
(
0
);
i
++
)
for
(
s
size_t
j
=
0
;
j
<
r
.
shape
(
1
);
j
++
)
r
(
i
,
j
)
+=
v
;
},
py
::
arg
().
noconvert
(),
py
::
arg
());
sm
.
def
(
"proxy_init3"
,
[](
double
start
)
{
py
::
array_t
<
double
,
py
::
array
::
c_style
>
a
({
3
,
3
,
3
});
auto
r
=
a
.
mutable_unchecked
<
3
>
();
for
(
size_t
i
=
0
;
i
<
r
.
shape
(
0
);
i
++
)
for
(
size_t
j
=
0
;
j
<
r
.
shape
(
1
);
j
++
)
for
(
size_t
k
=
0
;
k
<
r
.
shape
(
2
);
k
++
)
for
(
s
size_t
i
=
0
;
i
<
r
.
shape
(
0
);
i
++
)
for
(
s
size_t
j
=
0
;
j
<
r
.
shape
(
1
);
j
++
)
for
(
s
size_t
k
=
0
;
k
<
r
.
shape
(
2
);
k
++
)
r
(
i
,
j
,
k
)
=
start
++
;
return
a
;
});
sm
.
def
(
"proxy_init3F"
,
[](
double
start
)
{
py
::
array_t
<
double
,
py
::
array
::
f_style
>
a
({
3
,
3
,
3
});
auto
r
=
a
.
mutable_unchecked
<
3
>
();
for
(
size_t
k
=
0
;
k
<
r
.
shape
(
2
);
k
++
)
for
(
size_t
j
=
0
;
j
<
r
.
shape
(
1
);
j
++
)
for
(
size_t
i
=
0
;
i
<
r
.
shape
(
0
);
i
++
)
for
(
s
size_t
k
=
0
;
k
<
r
.
shape
(
2
);
k
++
)
for
(
s
size_t
j
=
0
;
j
<
r
.
shape
(
1
);
j
++
)
for
(
s
size_t
i
=
0
;
i
<
r
.
shape
(
0
);
i
++
)
r
(
i
,
j
,
k
)
=
start
++
;
return
a
;
});
sm
.
def
(
"proxy_squared_L2_norm"
,
[](
py
::
array_t
<
double
>
a
)
{
auto
r
=
a
.
unchecked
<
1
>
();
double
sumsq
=
0
;
for
(
size_t
i
=
0
;
i
<
r
.
shape
(
0
);
i
++
)
for
(
s
size_t
i
=
0
;
i
<
r
.
shape
(
0
);
i
++
)
sumsq
+=
r
[
i
]
*
r
(
i
);
// Either notation works for a 1D array
return
sumsq
;
});
...
...
@@ -243,17 +242,17 @@ test_initializer numpy_array([](py::module &m) {
sm
.
def
(
"proxy_add2_dyn"
,
[](
py
::
array_t
<
double
>
a
,
double
v
)
{
auto
r
=
a
.
mutable_unchecked
();
if
(
r
.
ndim
()
!=
2
)
throw
std
::
domain_error
(
"error: ndim != 2"
);
for
(
size_t
i
=
0
;
i
<
r
.
shape
(
0
);
i
++
)
for
(
size_t
j
=
0
;
j
<
r
.
shape
(
1
);
j
++
)
for
(
s
size_t
i
=
0
;
i
<
r
.
shape
(
0
);
i
++
)
for
(
s
size_t
j
=
0
;
j
<
r
.
shape
(
1
);
j
++
)
r
(
i
,
j
)
+=
v
;
},
py
::
arg
().
noconvert
(),
py
::
arg
());
sm
.
def
(
"proxy_init3_dyn"
,
[](
double
start
)
{
py
::
array_t
<
double
,
py
::
array
::
c_style
>
a
({
3
,
3
,
3
});
auto
r
=
a
.
mutable_unchecked
();
if
(
r
.
ndim
()
!=
3
)
throw
std
::
domain_error
(
"error: ndim != 3"
);
for
(
size_t
i
=
0
;
i
<
r
.
shape
(
0
);
i
++
)
for
(
size_t
j
=
0
;
j
<
r
.
shape
(
1
);
j
++
)
for
(
size_t
k
=
0
;
k
<
r
.
shape
(
2
);
k
++
)
for
(
s
size_t
i
=
0
;
i
<
r
.
shape
(
0
);
i
++
)
for
(
s
size_t
j
=
0
;
j
<
r
.
shape
(
1
);
j
++
)
for
(
s
size_t
k
=
0
;
k
<
r
.
shape
(
2
);
k
++
)
r
(
i
,
j
,
k
)
=
start
++
;
return
a
;
});
...
...
@@ -269,6 +268,9 @@ test_initializer numpy_array([](py::module &m) {
sm
.
def
(
"array_fail_test"
,
[]()
{
return
py
::
array
(
py
::
object
());
});
sm
.
def
(
"array_t_fail_test"
,
[]()
{
return
py
::
array_t
<
double
>
(
py
::
object
());
});
// Make sure the error from numpy is being passed through:
sm
.
def
(
"array_fail_test_negative_size"
,
[]()
{
int
c
=
0
;
return
py
::
array
(
-
1
,
&
c
);
});
// Issue (unnumbered; reported in #788): regression: initializer lists can be ambiguous
sm
.
def
(
"array_initializer_list"
,
[]()
{
return
py
::
array_t
<
float
>
(
1
);
});
// { 1 } also works, but clang warns about it
sm
.
def
(
"array_initializer_list"
,
[]()
{
return
py
::
array_t
<
float
>
({
1
,
2
});
});
...
...
@@ -277,7 +279,7 @@ test_initializer numpy_array([](py::module &m) {
// reshape array to 2D without changing size
sm
.
def
(
"array_reshape2"
,
[](
py
::
array_t
<
double
>
a
)
{
const
size_t
dim_sz
=
(
size_t
)
std
::
sqrt
(
a
.
size
());
const
s
size_t
dim_sz
=
(
s
size_t
)
std
::
sqrt
(
a
.
size
());
if
(
dim_sz
*
dim_sz
!=
a
.
size
())
throw
std
::
domain_error
(
"array_reshape2: input array total size is not a squared integer"
);
a
.
resize
({
dim_sz
,
dim_sz
});
...
...
tests/test_numpy_array.py
View file @
30d43c49
...
...
@@ -384,7 +384,8 @@ def test_array_unchecked_dyn_dims(msg):
def
test_array_failure
():
from
pybind11_tests.array
import
array_fail_test
,
array_t_fail_test
from
pybind11_tests.array
import
(
array_fail_test
,
array_t_fail_test
,
array_fail_test_negative_size
)
with
pytest
.
raises
(
ValueError
)
as
excinfo
:
array_fail_test
()
...
...
@@ -394,6 +395,10 @@ def test_array_failure():
array_t_fail_test
()
assert
str
(
excinfo
.
value
)
==
'cannot create a pybind11::array_t from a nullptr'
with
pytest
.
raises
(
ValueError
)
as
excinfo
:
array_fail_test_negative_size
()
assert
str
(
excinfo
.
value
)
==
'negative dimensions are not allowed'
def
test_array_resize
(
msg
):
from
pybind11_tests.array
import
(
array_reshape2
,
array_resize3
)
...
...
tests/test_numpy_dtypes.cpp
View file @
30d43c49
...
...
@@ -149,7 +149,7 @@ py::array_t<StringStruct, 0> create_string_array(bool non_empty) {
if
(
non_empty
)
{
auto
req
=
arr
.
request
();
auto
ptr
=
static_cast
<
StringStruct
*>
(
req
.
ptr
);
for
(
size_t
i
=
0
;
i
<
req
.
size
*
req
.
itemsize
;
i
++
)
for
(
s
size_t
i
=
0
;
i
<
req
.
size
*
req
.
itemsize
;
i
++
)
static_cast
<
char
*>
(
req
.
ptr
)[
i
]
=
0
;
ptr
[
1
].
a
[
0
]
=
'a'
;
ptr
[
1
].
b
[
0
]
=
'a'
;
ptr
[
2
].
a
[
0
]
=
'a'
;
ptr
[
2
].
b
[
0
]
=
'a'
;
...
...
@@ -178,7 +178,7 @@ py::list print_recarray(py::array_t<S, 0> arr) {
const
auto
req
=
arr
.
request
();
const
auto
ptr
=
static_cast
<
S
*>
(
req
.
ptr
);
auto
l
=
py
::
list
();
for
(
size_t
i
=
0
;
i
<
req
.
size
;
i
++
)
{
for
(
s
size_t
i
=
0
;
i
<
req
.
size
;
i
++
)
{
std
::
stringstream
ss
;
ss
<<
ptr
[
i
];
l
.
append
(
py
::
str
(
ss
.
str
()));
...
...
@@ -225,7 +225,7 @@ py::array_t<int32_t, 0> test_array_ctors(int i) {
using
arr_t
=
py
::
array_t
<
int32_t
,
0
>
;
std
::
vector
<
int32_t
>
data
{
1
,
2
,
3
,
4
,
5
,
6
};
std
::
vector
<
size_t
>
shape
{
3
,
2
};
std
::
vector
<
s
size_t
>
shape
{
3
,
2
};
std
::
vector
<
ssize_t
>
strides
{
8
,
4
};
auto
ptr
=
data
.
data
();
...
...
tests/test_numpy_vectorize.cpp
View file @
30d43c49
...
...
@@ -50,8 +50,8 @@ test_initializer numpy_vectorize([](py::module &m) {
py
::
array_t
<
float
,
py
::
array
::
forcecast
>
arg2
,
py
::
array_t
<
double
,
py
::
array
::
forcecast
>
arg3
)
{
size_t
ndim
;
std
::
vector
<
size_t
>
shape
;
s
size_t
ndim
;
std
::
vector
<
s
size_t
>
shape
;
std
::
array
<
py
::
buffer_info
,
3
>
buffers
{{
arg1
.
request
(),
arg2
.
request
(),
arg3
.
request
()
}};
return
py
::
detail
::
broadcast
(
buffers
,
ndim
,
shape
);
});
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment